1 | //===- RegistryTest.cpp - Registry unit tests -----------------------------===// |
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 "../ASTMatchersTest.h" |
10 | #include "clang/ASTMatchers/Dynamic/Registry.h" |
11 | #include "gtest/gtest.h" |
12 | #include <optional> |
13 | #include <vector> |
14 | |
15 | namespace clang { |
16 | namespace ast_matchers { |
17 | namespace dynamic { |
18 | namespace { |
19 | |
20 | using ast_matchers::internal::Matcher; |
21 | |
22 | class RegistryTest : public ::testing::Test { |
23 | public: |
24 | std::vector<ParserValue> Args() { return std::vector<ParserValue>(); } |
25 | std::vector<ParserValue> Args(const VariantValue &Arg1) { |
26 | std::vector<ParserValue> Out(1); |
27 | Out[0].Value = Arg1; |
28 | return Out; |
29 | } |
30 | std::vector<ParserValue> Args(const VariantValue &Arg1, |
31 | const VariantValue &Arg2) { |
32 | std::vector<ParserValue> Out(2); |
33 | Out[0].Value = Arg1; |
34 | Out[1].Value = Arg2; |
35 | return Out; |
36 | } |
37 | |
38 | std::optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName) { |
39 | return Registry::lookupMatcherCtor(MatcherName); |
40 | } |
41 | |
42 | VariantMatcher constructMatcher(StringRef MatcherName, |
43 | Diagnostics *Error = nullptr) { |
44 | Diagnostics DummyError; |
45 | if (!Error) Error = &DummyError; |
46 | std::optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName); |
47 | VariantMatcher Out; |
48 | if (Ctor) |
49 | Out = Registry::constructMatcher(Ctor: *Ctor, NameRange: SourceRange(), Args: Args(), Error); |
50 | EXPECT_EQ("" , DummyError.toStringFull()); |
51 | return Out; |
52 | } |
53 | |
54 | VariantMatcher constructMatcher(StringRef MatcherName, |
55 | const VariantValue &Arg1, |
56 | Diagnostics *Error = nullptr) { |
57 | Diagnostics DummyError; |
58 | if (!Error) Error = &DummyError; |
59 | std::optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName); |
60 | VariantMatcher Out; |
61 | if (Ctor) |
62 | Out = Registry::constructMatcher(Ctor: *Ctor, NameRange: SourceRange(), Args: Args(Arg1), Error); |
63 | EXPECT_EQ("" , DummyError.toStringFull()) << MatcherName; |
64 | return Out; |
65 | } |
66 | |
67 | VariantMatcher constructMatcher(StringRef MatcherName, |
68 | const VariantValue &Arg1, |
69 | const VariantValue &Arg2, |
70 | Diagnostics *Error = nullptr) { |
71 | Diagnostics DummyError; |
72 | if (!Error) Error = &DummyError; |
73 | std::optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName); |
74 | VariantMatcher Out; |
75 | if (Ctor) |
76 | Out = Registry::constructMatcher(Ctor: *Ctor, NameRange: SourceRange(), Args: Args(Arg1, Arg2), |
77 | Error); |
78 | EXPECT_EQ("" , DummyError.toStringFull()); |
79 | return Out; |
80 | } |
81 | |
82 | typedef std::vector<MatcherCompletion> CompVector; |
83 | |
84 | CompVector getCompletions() { |
85 | std::vector<std::pair<MatcherCtor, unsigned> > Context; |
86 | return Registry::getMatcherCompletions( |
87 | AcceptedTypes: Registry::getAcceptedCompletionTypes(Context)); |
88 | } |
89 | |
90 | CompVector getCompletions(StringRef MatcherName1, unsigned ArgNo1) { |
91 | std::vector<std::pair<MatcherCtor, unsigned> > Context; |
92 | std::optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName: MatcherName1); |
93 | if (!Ctor) |
94 | return CompVector(); |
95 | Context.push_back(x: std::make_pair(x&: *Ctor, y&: ArgNo1)); |
96 | return Registry::getMatcherCompletions( |
97 | AcceptedTypes: Registry::getAcceptedCompletionTypes(Context)); |
98 | } |
99 | |
100 | CompVector getCompletions(StringRef MatcherName1, unsigned ArgNo1, |
101 | StringRef MatcherName2, unsigned ArgNo2) { |
102 | std::vector<std::pair<MatcherCtor, unsigned> > Context; |
103 | std::optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName: MatcherName1); |
104 | if (!Ctor) |
105 | return CompVector(); |
106 | Context.push_back(x: std::make_pair(x&: *Ctor, y&: ArgNo1)); |
107 | Ctor = lookupMatcherCtor(MatcherName: MatcherName2); |
108 | if (!Ctor) |
109 | return CompVector(); |
110 | Context.push_back(x: std::make_pair(x&: *Ctor, y&: ArgNo2)); |
111 | return Registry::getMatcherCompletions( |
112 | AcceptedTypes: Registry::getAcceptedCompletionTypes(Context)); |
113 | } |
114 | |
115 | bool hasCompletion(const CompVector &Comps, StringRef TypedText, |
116 | StringRef MatcherDecl = StringRef()) { |
117 | for (CompVector::const_iterator I = Comps.begin(), E = Comps.end(); I != E; |
118 | ++I) { |
119 | if (I->TypedText == TypedText && |
120 | (MatcherDecl.empty() || I->MatcherDecl == MatcherDecl)) { |
121 | return true; |
122 | } |
123 | } |
124 | return false; |
125 | } |
126 | }; |
127 | |
128 | TEST_F(RegistryTest, CanConstructNoArgs) { |
129 | Matcher<Stmt> IsArrowValue = constructMatcher( |
130 | MatcherName: "memberExpr" , Arg1: constructMatcher(MatcherName: "isArrow" )).getTypedMatcher<Stmt>(); |
131 | Matcher<Stmt> BoolValue = |
132 | constructMatcher(MatcherName: "cxxBoolLiteral" ).getTypedMatcher<Stmt>(); |
133 | |
134 | const std::string ClassSnippet = "struct Foo { int x; };\n" |
135 | "Foo *foo = new Foo;\n" |
136 | "int i = foo->x;\n" ; |
137 | const std::string BoolSnippet = "bool Foo = true;\n" ; |
138 | |
139 | EXPECT_TRUE(matches(ClassSnippet, IsArrowValue)); |
140 | EXPECT_TRUE(matches(BoolSnippet, BoolValue)); |
141 | EXPECT_FALSE(matches(ClassSnippet, BoolValue)); |
142 | EXPECT_FALSE(matches(BoolSnippet, IsArrowValue)); |
143 | } |
144 | |
145 | TEST_F(RegistryTest, ConstructWithSimpleArgs) { |
146 | Matcher<Decl> Value = constructMatcher( |
147 | MatcherName: "namedDecl" , Arg1: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("X" ))) |
148 | .getTypedMatcher<Decl>(); |
149 | EXPECT_TRUE(matches("class X {};" , Value)); |
150 | EXPECT_FALSE(matches("int x;" , Value)); |
151 | |
152 | Value = functionDecl(constructMatcher(MatcherName: "parameterCountIs" , Arg1: 2) |
153 | .getTypedMatcher<FunctionDecl>()); |
154 | EXPECT_TRUE(matches("void foo(int,int);" , Value)); |
155 | EXPECT_FALSE(matches("void foo(int);" , Value)); |
156 | } |
157 | |
158 | TEST_F(RegistryTest, ConstructWithMatcherArgs) { |
159 | Matcher<Decl> HasInitializerSimple = constructMatcher( |
160 | MatcherName: "varDecl" , Arg1: constructMatcher(MatcherName: "hasInitializer" , Arg1: constructMatcher(MatcherName: "stmt" ))) |
161 | .getTypedMatcher<Decl>(); |
162 | Matcher<Decl> HasInitializerComplex = constructMatcher( |
163 | MatcherName: "varDecl" , |
164 | Arg1: constructMatcher(MatcherName: "hasInitializer" , Arg1: constructMatcher(MatcherName: "callExpr" ))) |
165 | .getTypedMatcher<Decl>(); |
166 | |
167 | std::string code = "int i;" ; |
168 | EXPECT_FALSE(matches(code, HasInitializerSimple)); |
169 | EXPECT_FALSE(matches(code, HasInitializerComplex)); |
170 | |
171 | code = "int i = 1;" ; |
172 | EXPECT_TRUE(matches(code, HasInitializerSimple)); |
173 | EXPECT_FALSE(matches(code, HasInitializerComplex)); |
174 | |
175 | code = "int y(); int i = y();" ; |
176 | EXPECT_TRUE(matches(code, HasInitializerSimple)); |
177 | EXPECT_TRUE(matches(code, HasInitializerComplex)); |
178 | |
179 | Matcher<Decl> HasParameter = |
180 | functionDecl(constructMatcher( |
181 | MatcherName: "hasParameter" , Arg1: 1, Arg2: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("x" ))) |
182 | .getTypedMatcher<FunctionDecl>()); |
183 | EXPECT_TRUE(matches("void f(int a, int x);" , HasParameter)); |
184 | EXPECT_FALSE(matches("void f(int x, int a);" , HasParameter)); |
185 | } |
186 | |
187 | TEST_F(RegistryTest, OverloadedMatchers) { |
188 | Matcher<Stmt> CallExpr0 = constructMatcher( |
189 | MatcherName: "callExpr" , |
190 | Arg1: constructMatcher(MatcherName: "callee" , Arg1: constructMatcher(MatcherName: "memberExpr" , |
191 | Arg1: constructMatcher(MatcherName: "isArrow" )))) |
192 | .getTypedMatcher<Stmt>(); |
193 | |
194 | Matcher<Stmt> CallExpr1 = constructMatcher( |
195 | MatcherName: "callExpr" , |
196 | Arg1: constructMatcher( |
197 | MatcherName: "callee" , |
198 | Arg1: constructMatcher(MatcherName: "cxxMethodDecl" , |
199 | Arg1: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("x" ))))) |
200 | .getTypedMatcher<Stmt>(); |
201 | |
202 | Matcher<Stmt> ObjCMsgExpr = |
203 | constructMatcher( |
204 | MatcherName: "objcMessageExpr" , |
205 | Arg1: constructMatcher( |
206 | MatcherName: "callee" , |
207 | Arg1: constructMatcher(MatcherName: "objcMethodDecl" , |
208 | Arg1: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("x" ))))) |
209 | .getTypedMatcher<Stmt>(); |
210 | |
211 | std::string Code = "class Y { public: void x(); }; void z() { Y y; y.x(); }" ; |
212 | EXPECT_FALSE(matches(Code, CallExpr0)); |
213 | EXPECT_TRUE(matches(Code, CallExpr1)); |
214 | EXPECT_FALSE(matches(Code, ObjCMsgExpr)); |
215 | |
216 | Code = "class Z { public: void z() { this->z(); } };" ; |
217 | EXPECT_TRUE(matches(Code, CallExpr0)); |
218 | EXPECT_FALSE(matches(Code, CallExpr1)); |
219 | EXPECT_FALSE(matches(Code, ObjCMsgExpr)); |
220 | |
221 | Code = "@interface I " |
222 | "+ (void)x; " |
223 | "@end\n" |
224 | "int main() { [I x]; }" ; |
225 | EXPECT_FALSE(matchesObjC(Code, CallExpr0)); |
226 | EXPECT_FALSE(matchesObjC(Code, CallExpr1)); |
227 | EXPECT_TRUE(matchesObjC(Code, ObjCMsgExpr)); |
228 | |
229 | Matcher<Decl> DeclDecl = declaratorDecl(hasTypeLoc( |
230 | Inner: constructMatcher( |
231 | MatcherName: "loc" , Arg1: constructMatcher(MatcherName: "asString" , Arg1: StringRef("const double *" ))) |
232 | .getTypedMatcher<TypeLoc>())); |
233 | |
234 | Matcher<NestedNameSpecifierLoc> NNSL = |
235 | constructMatcher( |
236 | MatcherName: "loc" , Arg1: VariantMatcher::SingleMatcher(Matcher: nestedNameSpecifier( |
237 | specifiesType(InnerMatcher: hasDeclaration(InnerMatcher: recordDecl(hasName(Name: "A" ))))))) |
238 | .getTypedMatcher<NestedNameSpecifierLoc>(); |
239 | |
240 | Code = "const double * x = 0;" ; |
241 | EXPECT_TRUE(matches(Code, DeclDecl)); |
242 | EXPECT_FALSE(matches(Code, NNSL)); |
243 | |
244 | Code = "struct A { struct B {}; }; A::B a_b;" ; |
245 | EXPECT_FALSE(matches(Code, DeclDecl)); |
246 | EXPECT_TRUE(matches(Code, NNSL)); |
247 | } |
248 | |
249 | TEST_F(RegistryTest, PolymorphicMatchers) { |
250 | const VariantMatcher IsDefinition = constructMatcher(MatcherName: "isDefinition" ); |
251 | Matcher<Decl> Var = |
252 | constructMatcher(MatcherName: "varDecl" , Arg1: IsDefinition).getTypedMatcher<Decl>(); |
253 | Matcher<Decl> Class = |
254 | constructMatcher(MatcherName: "recordDecl" , Arg1: IsDefinition).getTypedMatcher<Decl>(); |
255 | Matcher<Decl> Func = |
256 | constructMatcher(MatcherName: "functionDecl" , Arg1: IsDefinition).getTypedMatcher<Decl>(); |
257 | EXPECT_TRUE(matches("int a;" , Var)); |
258 | EXPECT_FALSE(matches("extern int a;" , Var)); |
259 | EXPECT_TRUE(matches("class A {};" , Class)); |
260 | EXPECT_FALSE(matches("class A;" , Class)); |
261 | EXPECT_TRUE(matches("void f(){};" , Func)); |
262 | EXPECT_FALSE(matches("void f();" , Func)); |
263 | |
264 | Matcher<Decl> Anything = constructMatcher(MatcherName: "anything" ).getTypedMatcher<Decl>(); |
265 | Matcher<Decl> RecordDecl = constructMatcher( |
266 | MatcherName: "recordDecl" , Arg1: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("Foo" )), |
267 | Arg2: VariantMatcher::SingleMatcher(Matcher: Anything)).getTypedMatcher<Decl>(); |
268 | |
269 | EXPECT_TRUE(matches("int Foo;" , Anything)); |
270 | EXPECT_TRUE(matches("class Foo {};" , Anything)); |
271 | EXPECT_TRUE(matches("void Foo(){};" , Anything)); |
272 | EXPECT_FALSE(matches("int Foo;" , RecordDecl)); |
273 | EXPECT_TRUE(matches("class Foo {};" , RecordDecl)); |
274 | EXPECT_FALSE(matches("void Foo(){};" , RecordDecl)); |
275 | |
276 | Matcher<Stmt> ConstructExpr = constructMatcher( |
277 | MatcherName: "cxxConstructExpr" , |
278 | Arg1: constructMatcher( |
279 | MatcherName: "hasDeclaration" , |
280 | Arg1: constructMatcher( |
281 | MatcherName: "cxxMethodDecl" , |
282 | Arg1: constructMatcher( |
283 | MatcherName: "ofClass" , Arg1: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("Foo" )))))) |
284 | .getTypedMatcher<Stmt>(); |
285 | EXPECT_FALSE(matches("class Foo { public: Foo(); };" , ConstructExpr)); |
286 | EXPECT_TRUE( |
287 | matches("class Foo { public: Foo(); }; Foo foo = Foo();" , ConstructExpr)); |
288 | } |
289 | |
290 | TEST_F(RegistryTest, TemplateArgument) { |
291 | Matcher<Decl> HasTemplateArgument = constructMatcher( |
292 | MatcherName: "classTemplateSpecializationDecl" , |
293 | Arg1: constructMatcher( |
294 | MatcherName: "hasAnyTemplateArgument" , |
295 | Arg1: constructMatcher(MatcherName: "refersToType" , |
296 | Arg1: constructMatcher(MatcherName: "asString" , Arg1: StringRef("int" ))))) |
297 | .getTypedMatcher<Decl>(); |
298 | EXPECT_TRUE(matches("template<typename T> class A {}; A<int> a;" , |
299 | HasTemplateArgument)); |
300 | EXPECT_FALSE(matches("template<typename T> class A {}; A<char> a;" , |
301 | HasTemplateArgument)); |
302 | } |
303 | |
304 | TEST_F(RegistryTest, TypeTraversal) { |
305 | Matcher<Type> M = constructMatcher( |
306 | MatcherName: "pointerType" , |
307 | Arg1: constructMatcher(MatcherName: "pointee" , Arg1: constructMatcher(MatcherName: "isConstQualified" ), |
308 | Arg2: constructMatcher(MatcherName: "isInteger" ))).getTypedMatcher<Type>(); |
309 | EXPECT_FALSE(matches("int *a;" , M)); |
310 | EXPECT_TRUE(matches("int const *b;" , M)); |
311 | |
312 | M = constructMatcher( |
313 | MatcherName: "arrayType" , |
314 | Arg1: constructMatcher(MatcherName: "hasElementType" , Arg1: constructMatcher(MatcherName: "builtinType" ))) |
315 | .getTypedMatcher<Type>(); |
316 | EXPECT_FALSE(matches("struct A{}; A a[7];;" , M)); |
317 | EXPECT_TRUE(matches("int b[7];" , M)); |
318 | } |
319 | |
320 | TEST_F(RegistryTest, CXXBaseSpecifier) { |
321 | // TODO: rewrite with top-level cxxBaseSpecifier matcher when available |
322 | DeclarationMatcher ClassHasAnyDirectBase = |
323 | constructMatcher(MatcherName: "cxxRecordDecl" , |
324 | Arg1: constructMatcher(MatcherName: "hasDirectBase" , |
325 | Arg1: constructMatcher(MatcherName: "cxxBaseSpecifier" ))) |
326 | .getTypedMatcher<Decl>(); |
327 | EXPECT_TRUE(matches("class X {}; class Y : X {};" , ClassHasAnyDirectBase)); |
328 | EXPECT_TRUE(notMatches("class X {};" , ClassHasAnyDirectBase)); |
329 | } |
330 | |
331 | TEST_F(RegistryTest, CXXCtorInitializer) { |
332 | Matcher<Decl> CtorDecl = constructMatcher( |
333 | MatcherName: "cxxConstructorDecl" , |
334 | Arg1: constructMatcher( |
335 | MatcherName: "hasAnyConstructorInitializer" , |
336 | Arg1: constructMatcher(MatcherName: "forField" , |
337 | Arg1: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("foo" ))))) |
338 | .getTypedMatcher<Decl>(); |
339 | EXPECT_TRUE(matches("struct Foo { Foo() : foo(1) {} int foo; };" , CtorDecl)); |
340 | EXPECT_FALSE(matches("struct Foo { Foo() {} int foo; };" , CtorDecl)); |
341 | EXPECT_FALSE(matches("struct Foo { Foo() : bar(1) {} int bar; };" , CtorDecl)); |
342 | } |
343 | |
344 | TEST_F(RegistryTest, Adaptative) { |
345 | Matcher<Decl> D = constructMatcher( |
346 | MatcherName: "recordDecl" , |
347 | Arg1: constructMatcher( |
348 | MatcherName: "has" , |
349 | Arg1: constructMatcher(MatcherName: "recordDecl" , |
350 | Arg1: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("X" ))))) |
351 | .getTypedMatcher<Decl>(); |
352 | EXPECT_TRUE(matches("class X {};" , D)); |
353 | EXPECT_TRUE(matches("class Y { class X {}; };" , D)); |
354 | EXPECT_FALSE(matches("class Y { class Z {}; };" , D)); |
355 | |
356 | Matcher<Stmt> S = constructMatcher( |
357 | MatcherName: "forStmt" , |
358 | Arg1: constructMatcher( |
359 | MatcherName: "hasDescendant" , |
360 | Arg1: constructMatcher(MatcherName: "varDecl" , |
361 | Arg1: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("X" ))))) |
362 | .getTypedMatcher<Stmt>(); |
363 | EXPECT_TRUE(matches("void foo() { for(int X;;); }" , S)); |
364 | EXPECT_TRUE(matches("void foo() { for(;;) { int X; } }" , S)); |
365 | EXPECT_FALSE(matches("void foo() { for(;;); }" , S)); |
366 | EXPECT_FALSE(matches("void foo() { if (int X = 0){} }" , S)); |
367 | |
368 | S = constructMatcher( |
369 | MatcherName: "compoundStmt" , Arg1: constructMatcher(MatcherName: "hasParent" , Arg1: constructMatcher(MatcherName: "ifStmt" ))) |
370 | .getTypedMatcher<Stmt>(); |
371 | EXPECT_TRUE(matches("void foo() { if (true) { int x = 42; } }" , S)); |
372 | EXPECT_FALSE(matches("void foo() { if (true) return; }" , S)); |
373 | } |
374 | |
375 | TEST_F(RegistryTest, VariadicOp) { |
376 | Matcher<Decl> D = constructMatcher( |
377 | MatcherName: "anyOf" , |
378 | Arg1: constructMatcher(MatcherName: "recordDecl" , |
379 | Arg1: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("Foo" ))), |
380 | Arg2: constructMatcher(MatcherName: "functionDecl" , |
381 | Arg1: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("foo" )))) |
382 | .getTypedMatcher<Decl>(); |
383 | |
384 | EXPECT_TRUE(matches("void foo(){}" , D)); |
385 | EXPECT_TRUE(matches("struct Foo{};" , D)); |
386 | EXPECT_FALSE(matches("int i = 0;" , D)); |
387 | |
388 | D = constructMatcher( |
389 | MatcherName: "allOf" , Arg1: constructMatcher(MatcherName: "recordDecl" ), |
390 | Arg2: constructMatcher( |
391 | MatcherName: "namedDecl" , |
392 | Arg1: constructMatcher(MatcherName: "anyOf" , |
393 | Arg1: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("Foo" )), |
394 | Arg2: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("Bar" ))))) |
395 | .getTypedMatcher<Decl>(); |
396 | |
397 | EXPECT_FALSE(matches("void foo(){}" , D)); |
398 | EXPECT_TRUE(matches("struct Foo{};" , D)); |
399 | EXPECT_FALSE(matches("int i = 0;" , D)); |
400 | EXPECT_TRUE(matches("class Bar{};" , D)); |
401 | EXPECT_FALSE(matches("class OtherBar{};" , D)); |
402 | |
403 | D = recordDecl( |
404 | has(fieldDecl(hasName(Name: "Foo" ))), |
405 | constructMatcher( |
406 | MatcherName: "unless" , |
407 | Arg1: constructMatcher(MatcherName: "namedDecl" , |
408 | Arg1: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("Bar" )))) |
409 | .getTypedMatcher<Decl>()); |
410 | |
411 | EXPECT_FALSE(matches("class Bar{ int Foo; };" , D)); |
412 | EXPECT_TRUE(matches("class OtherBar{ int Foo; };" , D)); |
413 | |
414 | D = constructMatcher( |
415 | MatcherName: "namedDecl" , Arg1: constructMatcher(MatcherName: "hasName" , Arg1: StringRef("Foo" )), |
416 | Arg2: constructMatcher(MatcherName: "unless" , Arg1: constructMatcher(MatcherName: "recordDecl" ))) |
417 | .getTypedMatcher<Decl>(); |
418 | EXPECT_TRUE(matches("void Foo(){}" , D)); |
419 | EXPECT_TRUE(notMatches("struct Foo {};" , D)); |
420 | } |
421 | |
422 | TEST_F(RegistryTest, Errors) { |
423 | // Incorrect argument count. |
424 | std::unique_ptr<Diagnostics> Error(new Diagnostics()); |
425 | EXPECT_TRUE(constructMatcher("hasInitializer" , Error.get()).isNull()); |
426 | EXPECT_EQ("Incorrect argument count. (Expected = 1) != (Actual = 0)" , |
427 | Error->toString()); |
428 | Error.reset(p: new Diagnostics()); |
429 | EXPECT_TRUE(constructMatcher("isArrow" , StringRef(), Error.get()).isNull()); |
430 | EXPECT_EQ("Incorrect argument count. (Expected = 0) != (Actual = 1)" , |
431 | Error->toString()); |
432 | Error.reset(p: new Diagnostics()); |
433 | EXPECT_TRUE(constructMatcher("anyOf" , Error.get()).isNull()); |
434 | EXPECT_EQ("Incorrect argument count. (Expected = (2, )) != (Actual = 0)" , |
435 | Error->toString()); |
436 | Error.reset(p: new Diagnostics()); |
437 | EXPECT_TRUE(constructMatcher("unless" , StringRef(), StringRef(), |
438 | Error.get()).isNull()); |
439 | EXPECT_EQ("Incorrect argument count. (Expected = (1, 1)) != (Actual = 2)" , |
440 | Error->toString()); |
441 | |
442 | // Bad argument type |
443 | Error.reset(p: new Diagnostics()); |
444 | EXPECT_TRUE(constructMatcher("ofClass" , StringRef(), Error.get()).isNull()); |
445 | EXPECT_EQ("Incorrect type for arg 1. (Expected = Matcher<CXXRecordDecl>) != " |
446 | "(Actual = String)" , |
447 | Error->toString()); |
448 | Error.reset(p: new Diagnostics()); |
449 | EXPECT_TRUE( |
450 | constructMatcher("cxxRecordDecl" , constructMatcher("cxxRecordDecl" ), |
451 | constructMatcher("parameterCountIs" , 3), Error.get()) |
452 | .isNull()); |
453 | EXPECT_EQ("Incorrect type for arg 2. (Expected = Matcher<CXXRecordDecl>) != " |
454 | "(Actual = Matcher<FunctionDecl|FunctionProtoType>)" , |
455 | Error->toString()); |
456 | |
457 | // Bad argument type with variadic. |
458 | Error.reset(p: new Diagnostics()); |
459 | EXPECT_TRUE(constructMatcher("anyOf" , StringRef(), StringRef(), |
460 | Error.get()).isNull()); |
461 | EXPECT_EQ( |
462 | "Incorrect type for arg 1. (Expected = Matcher<>) != (Actual = String)" , |
463 | Error->toString()); |
464 | Error.reset(p: new Diagnostics()); |
465 | EXPECT_TRUE(constructMatcher( |
466 | "cxxRecordDecl" , |
467 | constructMatcher("allOf" , |
468 | constructMatcher("isDerivedFrom" , StringRef("FOO" )), |
469 | constructMatcher("isArrow" )), |
470 | Error.get()).isNull()); |
471 | EXPECT_EQ("Incorrect type for arg 1. " |
472 | "(Expected = Matcher<CXXRecordDecl>) != " |
473 | "(Actual = Matcher<CXXRecordDecl|ObjCInterfaceDecl>&Matcher" |
474 | "<MemberExpr|UnresolvedMemberExpr|CXXDependentScopeMemberExpr>)" , |
475 | Error->toString()); |
476 | } |
477 | |
478 | TEST_F(RegistryTest, Completion) { |
479 | CompVector Comps = getCompletions(); |
480 | // Overloaded |
481 | EXPECT_TRUE(hasCompletion( |
482 | Comps, "hasParent(" , |
483 | "Matcher<NestedNameSpecifierLoc|TypeLoc|Decl|...> " |
484 | "hasParent(Matcher<NestedNameSpecifierLoc|TypeLoc|Decl|...>)" )); |
485 | // Variadic. |
486 | EXPECT_TRUE(hasCompletion(Comps, "whileStmt(" , |
487 | "Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)" )); |
488 | // Polymorphic. |
489 | EXPECT_TRUE(hasCompletion( |
490 | Comps, "hasDescendant(" , |
491 | "Matcher<NestedNameSpecifierLoc|QualType|TypeLoc|...> " |
492 | "hasDescendant(Matcher<NestedNameSpecifierLoc|QualType|TypeLoc|...>)" )); |
493 | |
494 | CompVector WhileComps = getCompletions(MatcherName1: "whileStmt" , ArgNo1: 0); |
495 | |
496 | EXPECT_TRUE(hasCompletion(WhileComps, "hasBody(" , |
497 | "Matcher<WhileStmt> hasBody(Matcher<Stmt>)" )); |
498 | EXPECT_TRUE(hasCompletion( |
499 | WhileComps, "hasParent(" , |
500 | "Matcher<Stmt> " |
501 | "hasParent(Matcher<NestedNameSpecifierLoc|TypeLoc|Decl|...>)" )); |
502 | EXPECT_TRUE( |
503 | hasCompletion(WhileComps, "allOf(" , "Matcher<T> allOf(Matcher<T>...)" )); |
504 | |
505 | EXPECT_FALSE(hasCompletion(WhileComps, "whileStmt(" )); |
506 | EXPECT_FALSE(hasCompletion(WhileComps, "ifStmt(" )); |
507 | |
508 | CompVector AllOfWhileComps = |
509 | getCompletions(MatcherName1: "allOf" , ArgNo1: 0, MatcherName2: "whileStmt" , ArgNo2: 0); |
510 | ASSERT_EQ(AllOfWhileComps.size(), WhileComps.size()); |
511 | EXPECT_TRUE(std::equal(WhileComps.begin(), WhileComps.end(), |
512 | AllOfWhileComps.begin())); |
513 | |
514 | CompVector DeclWhileComps = |
515 | getCompletions(MatcherName1: "decl" , ArgNo1: 0, MatcherName2: "whileStmt" , ArgNo2: 0); |
516 | EXPECT_EQ(0u, DeclWhileComps.size()); |
517 | |
518 | CompVector NamedDeclComps = getCompletions(MatcherName1: "namedDecl" , ArgNo1: 0); |
519 | EXPECT_TRUE( |
520 | hasCompletion(NamedDeclComps, "isPublic()" , "Matcher<Decl> isPublic()" )); |
521 | EXPECT_TRUE(hasCompletion(NamedDeclComps, "hasName(\"" , |
522 | "Matcher<NamedDecl> hasName(string)" )); |
523 | |
524 | // Heterogeneous overloads. |
525 | Comps = getCompletions(MatcherName1: "classTemplateSpecializationDecl" , ArgNo1: 0); |
526 | EXPECT_TRUE(hasCompletion( |
527 | Comps, "isSameOrDerivedFrom(" , |
528 | "Matcher<CXXRecordDecl> isSameOrDerivedFrom(string|Matcher<NamedDecl>)" )); |
529 | } |
530 | |
531 | TEST_F(RegistryTest, MatcherBuilder) { |
532 | auto Ctor = *lookupMatcherCtor(MatcherName: "mapAnyOf" ); |
533 | EXPECT_TRUE(Registry::isBuilderMatcher(Ctor)); |
534 | auto BuiltCtor = Registry::buildMatcherCtor(Ctor, NameRange: {}, Args: Args(Arg1: ASTNodeKind::getFromNodeKind<WhileStmt>(), Arg2: ASTNodeKind::getFromNodeKind<ForStmt>()), Error: nullptr); |
535 | EXPECT_TRUE(BuiltCtor.get()); |
536 | auto LoopMatcher = Registry::constructMatcher(Ctor: BuiltCtor.get(), NameRange: SourceRange(), Args: Args(), Error: nullptr).getTypedMatcher<Stmt>(); |
537 | EXPECT_TRUE(matches("void f() { for (;;) {} }" , LoopMatcher)); |
538 | EXPECT_TRUE(matches("void f() { while (true) {} }" , LoopMatcher)); |
539 | EXPECT_FALSE(matches("void f() { if (true) {} }" , LoopMatcher)); |
540 | |
541 | auto NotBuiltCtor = Registry::buildMatcherCtor(Ctor, NameRange: {}, Args: Args(Arg1: ASTNodeKind::getFromNodeKind<FunctionDecl>(), Arg2: ASTNodeKind::getFromNodeKind<ForStmt>()), Error: nullptr); |
542 | EXPECT_FALSE(NotBuiltCtor.get()); |
543 | } |
544 | |
545 | TEST_F(RegistryTest, NodeType) { |
546 | EXPECT_TRUE(Registry::nodeMatcherType(*lookupMatcherCtor("callExpr" )).isSame(ASTNodeKind::getFromNodeKind<CallExpr>())); |
547 | EXPECT_TRUE(Registry::nodeMatcherType(*lookupMatcherCtor("has" )).isNone()); |
548 | EXPECT_TRUE(Registry::nodeMatcherType(*lookupMatcherCtor("allOf" )).isNone()); |
549 | } |
550 | |
551 | TEST_F(RegistryTest, HasArgs) { |
552 | Matcher<Decl> Value = constructMatcher( |
553 | MatcherName: "decl" , Arg1: constructMatcher(MatcherName: "hasAttr" , Arg1: StringRef("attr::WarnUnused" ))) |
554 | .getTypedMatcher<Decl>(); |
555 | EXPECT_TRUE(matches("struct __attribute__((warn_unused)) X {};" , Value)); |
556 | EXPECT_FALSE(matches("struct X {};" , Value)); |
557 | } |
558 | |
559 | TEST_F(RegistryTest, ParenExpr) { |
560 | Matcher<Stmt> Value = constructMatcher(MatcherName: "parenExpr" ).getTypedMatcher<Stmt>(); |
561 | EXPECT_TRUE(matches("int i = (1);" , traverse(TK_AsIs, Value))); |
562 | EXPECT_FALSE(matches("int i = 1;" , traverse(TK_AsIs, Value))); |
563 | } |
564 | |
565 | TEST_F(RegistryTest, EqualsMatcher) { |
566 | Matcher<Stmt> BooleanStmt = constructMatcher( |
567 | MatcherName: "cxxBoolLiteral" , Arg1: constructMatcher(MatcherName: "equals" , Arg1: VariantValue(true))) |
568 | .getTypedMatcher<Stmt>(); |
569 | EXPECT_TRUE(matches("bool x = true;" , BooleanStmt)); |
570 | EXPECT_FALSE(matches("bool x = false;" , BooleanStmt)); |
571 | EXPECT_FALSE(matches("bool x = 0;" , BooleanStmt)); |
572 | |
573 | BooleanStmt = constructMatcher( |
574 | MatcherName: "cxxBoolLiteral" , Arg1: constructMatcher(MatcherName: "equals" , Arg1: VariantValue(0))) |
575 | .getTypedMatcher<Stmt>(); |
576 | EXPECT_TRUE(matches("bool x = false;" , BooleanStmt)); |
577 | EXPECT_FALSE(matches("bool x = true;" , BooleanStmt)); |
578 | EXPECT_FALSE(matches("bool x = 0;" , BooleanStmt)); |
579 | |
580 | Matcher<Stmt> DoubleStmt = constructMatcher( |
581 | MatcherName: "floatLiteral" , Arg1: constructMatcher(MatcherName: "equals" , Arg1: VariantValue(1.2))) |
582 | .getTypedMatcher<Stmt>(); |
583 | EXPECT_TRUE(matches("double x = 1.2;" , DoubleStmt)); |
584 | #if 0 |
585 | // FIXME floatLiteral matching should work regardless of suffix. |
586 | EXPECT_TRUE(matches("double x = 1.2f;" , DoubleStmt)); |
587 | EXPECT_TRUE(matches("double x = 1.2l;" , DoubleStmt)); |
588 | #endif |
589 | EXPECT_TRUE(matches("double x = 12e-1;" , DoubleStmt)); |
590 | EXPECT_FALSE(matches("double x = 1.23;" , DoubleStmt)); |
591 | |
592 | Matcher<Stmt> IntegerStmt = constructMatcher( |
593 | MatcherName: "integerLiteral" , Arg1: constructMatcher(MatcherName: "equals" , Arg1: VariantValue(42))) |
594 | .getTypedMatcher<Stmt>(); |
595 | EXPECT_TRUE(matches("int x = 42;" , IntegerStmt)); |
596 | EXPECT_FALSE(matches("int x = 1;" , IntegerStmt)); |
597 | |
598 | Matcher<Stmt> CharStmt = constructMatcher( |
599 | MatcherName: "characterLiteral" , Arg1: constructMatcher(MatcherName: "equals" , Arg1: VariantValue('x'))) |
600 | .getTypedMatcher<Stmt>(); |
601 | EXPECT_TRUE(matches("int x = 'x';" , CharStmt)); |
602 | EXPECT_TRUE(matches("int x = L'x';" , CharStmt)); |
603 | EXPECT_TRUE(matches("int x = u'x';" , CharStmt)); |
604 | EXPECT_TRUE(matches("int x = U'x';" , CharStmt)); |
605 | EXPECT_FALSE(matches("int x = 120;" , CharStmt)); |
606 | } |
607 | |
608 | } // end anonymous namespace |
609 | } // end namespace dynamic |
610 | } // end namespace ast_matchers |
611 | } // end namespace clang |
612 | |