1 | //===--- CommentCommandTraits.cpp - Comment command properties --*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "clang/AST/CommentCommandTraits.h" |
10 | #include "llvm/ADT/STLExtras.h" |
11 | #include <cassert> |
12 | |
13 | namespace clang { |
14 | namespace comments { |
15 | |
16 | #include "clang/AST/CommentCommandInfo.inc" |
17 | |
18 | CommandTraits::CommandTraits(llvm::BumpPtrAllocator &Allocator, |
19 | const CommentOptions &) |
20 | : NextID(std::size(Commands)), Allocator(Allocator) { |
21 | registerCommentOptions(CommentOptions); |
22 | } |
23 | |
24 | void CommandTraits::registerCommentOptions( |
25 | const CommentOptions &) { |
26 | for (CommentOptions::BlockCommandNamesTy::const_iterator |
27 | I = CommentOptions.BlockCommandNames.begin(), |
28 | E = CommentOptions.BlockCommandNames.end(); |
29 | I != E; I++) { |
30 | registerBlockCommand(CommandName: *I); |
31 | } |
32 | } |
33 | |
34 | const CommandInfo *CommandTraits::getCommandInfoOrNULL(StringRef Name) const { |
35 | if (const CommandInfo *Info = getBuiltinCommandInfo(Name)) |
36 | return Info; |
37 | return getRegisteredCommandInfo(Name); |
38 | } |
39 | |
40 | const CommandInfo *CommandTraits::getCommandInfo(unsigned CommandID) const { |
41 | if (const CommandInfo *Info = getBuiltinCommandInfo(CommandID)) |
42 | return Info; |
43 | return getRegisteredCommandInfo(CommandID); |
44 | } |
45 | |
46 | const CommandInfo * |
47 | CommandTraits::getTypoCorrectCommandInfo(StringRef Typo) const { |
48 | // Single-character command impostures, such as \t or \n, should not go |
49 | // through the fixit logic. |
50 | if (Typo.size() <= 1) |
51 | return nullptr; |
52 | |
53 | // The maximum edit distance we're prepared to accept. |
54 | const unsigned MaxEditDistance = 1; |
55 | |
56 | unsigned BestEditDistance = MaxEditDistance; |
57 | SmallVector<const CommandInfo *, 2> BestCommand; |
58 | |
59 | auto ConsiderCorrection = [&](const CommandInfo *Command) { |
60 | StringRef Name = Command->Name; |
61 | |
62 | unsigned MinPossibleEditDistance = abs(x: (int)Name.size() - (int)Typo.size()); |
63 | if (MinPossibleEditDistance <= BestEditDistance) { |
64 | unsigned EditDistance = Typo.edit_distance(Other: Name, AllowReplacements: true, MaxEditDistance: BestEditDistance); |
65 | if (EditDistance < BestEditDistance) { |
66 | BestEditDistance = EditDistance; |
67 | BestCommand.clear(); |
68 | } |
69 | if (EditDistance == BestEditDistance) |
70 | BestCommand.push_back(Command); |
71 | } |
72 | }; |
73 | |
74 | for (const auto &Command : Commands) |
75 | ConsiderCorrection(&Command); |
76 | |
77 | for (const auto *Command : RegisteredCommands) |
78 | if (!Command->IsUnknownCommand) |
79 | ConsiderCorrection(Command); |
80 | |
81 | return BestCommand.size() == 1 ? BestCommand[0] : nullptr; |
82 | } |
83 | |
84 | CommandInfo *CommandTraits::createCommandInfoWithName(StringRef CommandName) { |
85 | char *Name = Allocator.Allocate<char>(CommandName.size() + 1); |
86 | memcpy(dest: Name, src: CommandName.data(), n: CommandName.size()); |
87 | Name[CommandName.size()] = '\0'; |
88 | |
89 | // Value-initialize (=zero-initialize in this case) a new CommandInfo. |
90 | CommandInfo *Info = new (Allocator) CommandInfo(); |
91 | Info->Name = Name; |
92 | // We only have a limited number of bits to encode command IDs in the |
93 | // CommandInfo structure, so the ID numbers can potentially wrap around. |
94 | assert((NextID < (1 << CommandInfo::NumCommandIDBits)) |
95 | && "Too many commands. We have limited bits for the command ID." ); |
96 | Info->ID = NextID++; |
97 | |
98 | RegisteredCommands.push_back(Info); |
99 | |
100 | return Info; |
101 | } |
102 | |
103 | const CommandInfo *CommandTraits::registerUnknownCommand( |
104 | StringRef CommandName) { |
105 | CommandInfo *Info = createCommandInfoWithName(CommandName); |
106 | Info->IsUnknownCommand = true; |
107 | return Info; |
108 | } |
109 | |
110 | const CommandInfo *CommandTraits::registerBlockCommand(StringRef CommandName) { |
111 | CommandInfo *Info = createCommandInfoWithName(CommandName); |
112 | Info->IsBlockCommand = true; |
113 | return Info; |
114 | } |
115 | |
116 | const CommandInfo *CommandTraits::getBuiltinCommandInfo( |
117 | unsigned CommandID) { |
118 | if (CommandID < std::size(Commands)) |
119 | return &Commands[CommandID]; |
120 | return nullptr; |
121 | } |
122 | |
123 | const CommandInfo *CommandTraits::getRegisteredCommandInfo( |
124 | StringRef Name) const { |
125 | for (unsigned i = 0, e = RegisteredCommands.size(); i != e; ++i) { |
126 | if (RegisteredCommands[i]->Name == Name) |
127 | return RegisteredCommands[i]; |
128 | } |
129 | return nullptr; |
130 | } |
131 | |
132 | const CommandInfo *CommandTraits::getRegisteredCommandInfo( |
133 | unsigned CommandID) const { |
134 | return RegisteredCommands[CommandID - std::size(Commands)]; |
135 | } |
136 | |
137 | } // end namespace comments |
138 | } // end namespace clang |
139 | |
140 | |