1ca7f80adSDmitri Gribenko //===--- CommentCommandTraits.cpp - Comment command properties --*- C++ -*-===//
2ca7f80adSDmitri Gribenko //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ca7f80adSDmitri Gribenko //
7ca7f80adSDmitri Gribenko //===----------------------------------------------------------------------===//
8ca7f80adSDmitri Gribenko 
9ca7f80adSDmitri Gribenko #include "clang/AST/CommentCommandTraits.h"
107acbf00fSDmitri Gribenko #include "llvm/ADT/STLExtras.h"
11eb812efaSJoerg Sonnenberger #include <cassert>
12ca7f80adSDmitri Gribenko 
13ca7f80adSDmitri Gribenko namespace clang {
14ca7f80adSDmitri Gribenko namespace comments {
15ca7f80adSDmitri Gribenko 
167acbf00fSDmitri Gribenko #include "clang/AST/CommentCommandInfo.inc"
17ca7f80adSDmitri Gribenko 
CommandTraits(llvm::BumpPtrAllocator & Allocator,const CommentOptions & CommentOptions)18acf2e786SDmitri Gribenko CommandTraits::CommandTraits(llvm::BumpPtrAllocator &Allocator,
19acf2e786SDmitri Gribenko                              const CommentOptions &CommentOptions) :
20*f15014ffSBenjamin Kramer     NextID(llvm::array_lengthof(Commands)), Allocator(Allocator) {
21acf2e786SDmitri Gribenko   registerCommentOptions(CommentOptions);
22acf2e786SDmitri Gribenko }
23acf2e786SDmitri Gribenko 
registerCommentOptions(const CommentOptions & CommentOptions)24acf2e786SDmitri Gribenko void CommandTraits::registerCommentOptions(
25acf2e786SDmitri Gribenko     const CommentOptions &CommentOptions) {
26acf2e786SDmitri Gribenko   for (CommentOptions::BlockCommandNamesTy::const_iterator
27acf2e786SDmitri Gribenko            I = CommentOptions.BlockCommandNames.begin(),
28acf2e786SDmitri Gribenko            E = CommentOptions.BlockCommandNames.end();
29acf2e786SDmitri Gribenko        I != E; I++) {
30acf2e786SDmitri Gribenko     registerBlockCommand(*I);
31acf2e786SDmitri Gribenko   }
32acf2e786SDmitri Gribenko }
33ca7f80adSDmitri Gribenko 
getCommandInfoOrNULL(StringRef Name) const347acbf00fSDmitri Gribenko const CommandInfo *CommandTraits::getCommandInfoOrNULL(StringRef Name) const {
357acbf00fSDmitri Gribenko   if (const CommandInfo *Info = getBuiltinCommandInfo(Name))
367acbf00fSDmitri Gribenko     return Info;
377acbf00fSDmitri Gribenko   return getRegisteredCommandInfo(Name);
38ca7f80adSDmitri Gribenko }
39ca7f80adSDmitri Gribenko 
getCommandInfo(unsigned CommandID) const407acbf00fSDmitri Gribenko const CommandInfo *CommandTraits::getCommandInfo(unsigned CommandID) const {
417acbf00fSDmitri Gribenko   if (const CommandInfo *Info = getBuiltinCommandInfo(CommandID))
427acbf00fSDmitri Gribenko     return Info;
437acbf00fSDmitri Gribenko   return getRegisteredCommandInfo(CommandID);
44ca7f80adSDmitri Gribenko }
45ca7f80adSDmitri Gribenko 
462d70976fSRichard Smith const CommandInfo *
getTypoCorrectCommandInfo(StringRef Typo) const472d70976fSRichard Smith CommandTraits::getTypoCorrectCommandInfo(StringRef Typo) const {
482d70976fSRichard Smith   // Single-character command impostures, such as \t or \n, should not go
492d70976fSRichard Smith   // through the fixit logic.
502d70976fSRichard Smith   if (Typo.size() <= 1)
512d70976fSRichard Smith     return nullptr;
522d70976fSRichard Smith 
532d70976fSRichard Smith   // The maximum edit distance we're prepared to accept.
546c7a1666SFariborz Jahanian   const unsigned MaxEditDistance = 1;
552d70976fSRichard Smith 
562d70976fSRichard Smith   unsigned BestEditDistance = MaxEditDistance;
572d70976fSRichard Smith   SmallVector<const CommandInfo *, 2> BestCommand;
582d70976fSRichard Smith 
592d70976fSRichard Smith   auto ConsiderCorrection = [&](const CommandInfo *Command) {
60aa9b280cSFariborz Jahanian     StringRef Name = Command->Name;
616c7a1666SFariborz Jahanian 
626c7a1666SFariborz Jahanian     unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
632d70976fSRichard Smith     if (MinPossibleEditDistance <= BestEditDistance) {
642d70976fSRichard Smith       unsigned EditDistance = Typo.edit_distance(Name, true, BestEditDistance);
652d70976fSRichard Smith       if (EditDistance < BestEditDistance) {
662d70976fSRichard Smith         BestEditDistance = EditDistance;
672d70976fSRichard Smith         BestCommand.clear();
682d70976fSRichard Smith       }
696c7a1666SFariborz Jahanian       if (EditDistance == BestEditDistance)
70aa9b280cSFariborz Jahanian         BestCommand.push_back(Command);
716c7a1666SFariborz Jahanian     }
722d70976fSRichard Smith   };
73aa9b280cSFariborz Jahanian 
742d70976fSRichard Smith   for (const auto &Command : Commands)
752d70976fSRichard Smith     ConsiderCorrection(&Command);
760321b8ffSFariborz Jahanian 
772d70976fSRichard Smith   for (const auto *Command : RegisteredCommands)
782d70976fSRichard Smith     if (!Command->IsUnknownCommand)
792d70976fSRichard Smith       ConsiderCorrection(Command);
80aa9b280cSFariborz Jahanian 
812d70976fSRichard Smith   return BestCommand.size() == 1 ? BestCommand[0] : nullptr;
826c7a1666SFariborz Jahanian }
836c7a1666SFariborz Jahanian 
createCommandInfoWithName(StringRef CommandName)84acf2e786SDmitri Gribenko CommandInfo *CommandTraits::createCommandInfoWithName(StringRef CommandName) {
85de4f4701SEli Friedman   char *Name = Allocator.Allocate<char>(CommandName.size() + 1);
867acbf00fSDmitri Gribenko   memcpy(Name, CommandName.data(), CommandName.size());
87de4f4701SEli Friedman   Name[CommandName.size()] = '\0';
887acbf00fSDmitri Gribenko 
897acbf00fSDmitri Gribenko   // Value-initialize (=zero-initialize in this case) a new CommandInfo.
907acbf00fSDmitri Gribenko   CommandInfo *Info = new (Allocator) CommandInfo();
917acbf00fSDmitri Gribenko   Info->Name = Name;
92a60f532aSDario Domizioli   // We only have a limited number of bits to encode command IDs in the
93a60f532aSDario Domizioli   // CommandInfo structure, so the ID numbers can potentially wrap around.
94a60f532aSDario Domizioli   assert((NextID < (1 << CommandInfo::NumCommandIDBits))
95a60f532aSDario Domizioli          && "Too many commands. We have limited bits for the command ID.");
967acbf00fSDmitri Gribenko   Info->ID = NextID++;
977acbf00fSDmitri Gribenko 
987acbf00fSDmitri Gribenko   RegisteredCommands.push_back(Info);
997acbf00fSDmitri Gribenko 
1007acbf00fSDmitri Gribenko   return Info;
101ca7f80adSDmitri Gribenko }
102ca7f80adSDmitri Gribenko 
registerUnknownCommand(StringRef CommandName)103acf2e786SDmitri Gribenko const CommandInfo *CommandTraits::registerUnknownCommand(
104acf2e786SDmitri Gribenko                                                   StringRef CommandName) {
105acf2e786SDmitri Gribenko   CommandInfo *Info = createCommandInfoWithName(CommandName);
106acf2e786SDmitri Gribenko   Info->IsUnknownCommand = true;
107acf2e786SDmitri Gribenko   return Info;
108acf2e786SDmitri Gribenko }
109acf2e786SDmitri Gribenko 
registerBlockCommand(StringRef CommandName)110acf2e786SDmitri Gribenko const CommandInfo *CommandTraits::registerBlockCommand(StringRef CommandName) {
111acf2e786SDmitri Gribenko   CommandInfo *Info = createCommandInfoWithName(CommandName);
112acf2e786SDmitri Gribenko   Info->IsBlockCommand = true;
113acf2e786SDmitri Gribenko   return Info;
114acf2e786SDmitri Gribenko }
115acf2e786SDmitri Gribenko 
getBuiltinCommandInfo(unsigned CommandID)1167acbf00fSDmitri Gribenko const CommandInfo *CommandTraits::getBuiltinCommandInfo(
1177acbf00fSDmitri Gribenko                                                   unsigned CommandID) {
118*f15014ffSBenjamin Kramer   if (CommandID < llvm::array_lengthof(Commands))
1197acbf00fSDmitri Gribenko     return &Commands[CommandID];
12036250ad6SCraig Topper   return nullptr;
121ca7f80adSDmitri Gribenko }
122ca7f80adSDmitri Gribenko 
getRegisteredCommandInfo(StringRef Name) const1237acbf00fSDmitri Gribenko const CommandInfo *CommandTraits::getRegisteredCommandInfo(
1247acbf00fSDmitri Gribenko                                                   StringRef Name) const {
1257acbf00fSDmitri Gribenko   for (unsigned i = 0, e = RegisteredCommands.size(); i != e; ++i) {
1267acbf00fSDmitri Gribenko     if (RegisteredCommands[i]->Name == Name)
1277acbf00fSDmitri Gribenko       return RegisteredCommands[i];
1287acbf00fSDmitri Gribenko   }
12936250ad6SCraig Topper   return nullptr;
130ba7aca3bSDmitri Gribenko }
131ba7aca3bSDmitri Gribenko 
getRegisteredCommandInfo(unsigned CommandID) const1327acbf00fSDmitri Gribenko const CommandInfo *CommandTraits::getRegisteredCommandInfo(
1337acbf00fSDmitri Gribenko                                                   unsigned CommandID) const {
134*f15014ffSBenjamin Kramer   return RegisteredCommands[CommandID - llvm::array_lengthof(Commands)];
135ca7f80adSDmitri Gribenko }
136ca7f80adSDmitri Gribenko 
137ca7f80adSDmitri Gribenko } // end namespace comments
138ca7f80adSDmitri Gribenko } // end namespace clang
139ca7f80adSDmitri Gribenko 
140