1//===--- Marshallers.cpp ----------------------------------------*- 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 "Marshallers.h"
10#include "llvm/ADT/ArrayRef.h"
11#include "llvm/ADT/StringRef.h"
12#include "llvm/Support/Regex.h"
13#include <optional>
14#include <string>
15
16namespace clang::ast_matchers::dynamic::internal {
17
18VariantMatcher DynCastAllOfMatcherDescriptor::create(SourceRange,
19 ArrayRef<ParserValue> Args,
20 Diagnostics *Error) const {
21 std::vector<ast_matchers::internal::DynTypedMatcher> InnerMatchers;
22 InnerMatchers.reserve(n: Args.size());
23 for (size_t I = 0; I != Args.size(); ++I) {
24 const ParserValue &Arg = Args[I];
25 if (!Arg.Value.isMatcher() ||
26 !Arg.Value.getMatcher().hasTypedMatcher(NK: DerivedKind)) {
27 Error->addError(Range: Arg.Range, Error: Error->ET_RegistryWrongArgType)
28 << (I + 1) << ArgKind::MakeMatcherArg(MatcherKind: DerivedKind).asString()
29 << Arg.Value.getTypeAsString();
30 return {};
31 }
32 InnerMatchers.push_back(x: Arg.Value.getMatcher()
33 .getTypedMatcher(NK: DerivedKind)
34 .dynCastTo(Kind: DerivedKind));
35 }
36
37 ast_matchers::internal::DynTypedMatcher Result =
38 InnerMatchers.empty()
39 ? ast_matchers::internal::DynTypedMatcher::trueMatcher(NodeKind: DerivedKind)
40 : InnerMatchers.size() == 1
41 ? InnerMatchers.front()
42 : ast_matchers::internal::DynTypedMatcher::constructVariadic(
43 Op: ast_matchers::internal::DynTypedMatcher::VO_AllOf, SupportedKind: DerivedKind,
44 InnerMatchers: std::move(InnerMatchers));
45 Result = Result.dynCastTo(Kind: BaseKind);
46 Result.setAllowBind(true);
47 return VariantMatcher::SingleMatcher(Matcher: Result);
48}
49
50bool DynCastAllOfMatcherDescriptor::isConvertibleTo(
51 ASTNodeKind Kind, unsigned *Specificity,
52 ASTNodeKind *LeastDerivedKind) const {
53 if (!isRetKindConvertibleTo(RetKinds: ArrayRef(BaseKind), Kind, Specificity,
54 LeastDerivedKind))
55 return false;
56
57 // If Kind is not a base of DerivedKind, either DerivedKind is a base of Kind
58 // (in which case the match will always succeed) or Kind and DerivedKind are
59 // unrelated (in which case it will always fail), so set Specificity to 0.
60 if (Kind.isSame(Other: DerivedKind) || !Kind.isBaseOf(Other: DerivedKind)) {
61 if (Specificity)
62 *Specificity = 0;
63 }
64 return true;
65}
66
67} // namespace clang::ast_matchers::dynamic::internal
68
69static std::optional<std::string>
70getBestGuess(llvm::StringRef Search, llvm::ArrayRef<llvm::StringRef> Allowed,
71 llvm::StringRef DropPrefix = "", unsigned MaxEditDistance = 3) {
72 if (MaxEditDistance != ~0U)
73 ++MaxEditDistance;
74 llvm::StringRef Res;
75 for (const llvm::StringRef &Item : Allowed) {
76 if (Item.equals_insensitive(RHS: Search)) {
77 assert(Item != Search && "This should be handled earlier on.");
78 MaxEditDistance = 1;
79 Res = Item;
80 continue;
81 }
82 unsigned Distance = Item.edit_distance(Other: Search);
83 if (Distance < MaxEditDistance) {
84 MaxEditDistance = Distance;
85 Res = Item;
86 }
87 }
88 if (!Res.empty())
89 return Res.str();
90 if (!DropPrefix.empty()) {
91 --MaxEditDistance; // Treat dropping the prefix as 1 edit
92 for (const llvm::StringRef &Item : Allowed) {
93 auto NoPrefix = Item;
94 if (!NoPrefix.consume_front(Prefix: DropPrefix))
95 continue;
96 if (NoPrefix.equals_insensitive(RHS: Search)) {
97 if (NoPrefix == Search)
98 return Item.str();
99 MaxEditDistance = 1;
100 Res = Item;
101 continue;
102 }
103 unsigned Distance = NoPrefix.edit_distance(Other: Search);
104 if (Distance < MaxEditDistance) {
105 MaxEditDistance = Distance;
106 Res = Item;
107 }
108 }
109 if (!Res.empty())
110 return Res.str();
111 }
112 return std::nullopt;
113}
114
115std::optional<std::string>
116clang::ast_matchers::dynamic::internal::ArgTypeTraits<
117 clang::attr::Kind>::getBestGuess(const VariantValue &Value) {
118 static constexpr llvm::StringRef Allowed[] = {
119#define ATTR(X) "attr::" #X,
120#include "clang/Basic/AttrList.inc"
121 };
122 if (Value.isString())
123 return ::getBestGuess(Search: Value.getString(), Allowed: llvm::ArrayRef(Allowed), DropPrefix: "attr::");
124 return std::nullopt;
125}
126
127std::optional<std::string>
128clang::ast_matchers::dynamic::internal::ArgTypeTraits<
129 clang::CastKind>::getBestGuess(const VariantValue &Value) {
130 static constexpr llvm::StringRef Allowed[] = {
131#define CAST_OPERATION(Name) "CK_" #Name,
132#include "clang/AST/OperationKinds.def"
133 };
134 if (Value.isString())
135 return ::getBestGuess(Search: Value.getString(), Allowed: llvm::ArrayRef(Allowed), DropPrefix: "CK_");
136 return std::nullopt;
137}
138
139std::optional<std::string>
140clang::ast_matchers::dynamic::internal::ArgTypeTraits<
141 clang::OpenMPClauseKind>::getBestGuess(const VariantValue &Value) {
142 static constexpr llvm::StringRef Allowed[] = {
143#define GEN_CLANG_CLAUSE_CLASS
144#define CLAUSE_CLASS(Enum, Str, Class) #Enum,
145#include "llvm/Frontend/OpenMP/OMP.inc"
146 };
147 if (Value.isString())
148 return ::getBestGuess(Search: Value.getString(), Allowed: llvm::ArrayRef(Allowed), DropPrefix: "OMPC_");
149 return std::nullopt;
150}
151
152std::optional<std::string>
153clang::ast_matchers::dynamic::internal::ArgTypeTraits<
154 clang::UnaryExprOrTypeTrait>::getBestGuess(const VariantValue &Value) {
155 static constexpr llvm::StringRef Allowed[] = {
156#define UNARY_EXPR_OR_TYPE_TRAIT(Spelling, Name, Key) "UETT_" #Name,
157#define CXX11_UNARY_EXPR_OR_TYPE_TRAIT(Spelling, Name, Key) "UETT_" #Name,
158#include "clang/Basic/BuiltinTraits.inc"
159 };
160 if (Value.isString())
161 return ::getBestGuess(Search: Value.getString(), Allowed: llvm::ArrayRef(Allowed), DropPrefix: "UETT_");
162 return std::nullopt;
163}
164
165static constexpr std::pair<llvm::StringRef, llvm::Regex::RegexFlags>
166 RegexMap[] = {
167 {"NoFlags", llvm::Regex::RegexFlags::NoFlags},
168 {"IgnoreCase", llvm::Regex::RegexFlags::IgnoreCase},
169 {"Newline", llvm::Regex::RegexFlags::Newline},
170 {"BasicRegex", llvm::Regex::RegexFlags::BasicRegex},
171};
172
173static std::optional<llvm::Regex::RegexFlags>
174getRegexFlag(llvm::StringRef Flag) {
175 for (const auto &StringFlag : RegexMap) {
176 if (Flag == StringFlag.first)
177 return StringFlag.second;
178 }
179 return std::nullopt;
180}
181
182static std::optional<llvm::StringRef> getCloseRegexMatch(llvm::StringRef Flag) {
183 for (const auto &StringFlag : RegexMap) {
184 if (Flag.edit_distance(Other: StringFlag.first) < 3)
185 return StringFlag.first;
186 }
187 return std::nullopt;
188}
189
190std::optional<llvm::Regex::RegexFlags>
191clang::ast_matchers::dynamic::internal::ArgTypeTraits<
192 llvm::Regex::RegexFlags>::getFlags(llvm::StringRef Flags) {
193 std::optional<llvm::Regex::RegexFlags> Flag;
194 SmallVector<StringRef, 4> Split;
195 Flags.split(A&: Split, Separator: '|', MaxSplit: -1, KeepEmpty: false);
196 for (StringRef OrFlag : Split) {
197 if (std::optional<llvm::Regex::RegexFlags> NextFlag =
198 getRegexFlag(Flag: OrFlag.trim()))
199 Flag = Flag.value_or(u: llvm::Regex::NoFlags) | *NextFlag;
200 else
201 return std::nullopt;
202 }
203 return Flag;
204}
205
206std::optional<std::string>
207clang::ast_matchers::dynamic::internal::ArgTypeTraits<
208 llvm::Regex::RegexFlags>::getBestGuess(const VariantValue &Value) {
209 if (!Value.isString())
210 return std::nullopt;
211 SmallVector<StringRef, 4> Split;
212 llvm::StringRef(Value.getString()).split(A&: Split, Separator: '|', MaxSplit: -1, KeepEmpty: false);
213 for (llvm::StringRef &Flag : Split) {
214 if (std::optional<llvm::StringRef> BestGuess =
215 getCloseRegexMatch(Flag: Flag.trim()))
216 Flag = *BestGuess;
217 else
218 return std::nullopt;
219 }
220 if (Split.empty())
221 return std::nullopt;
222 return llvm::join(R&: Split, Separator: " | ");
223}
224