1 //===- unittest/ASTMatchers/Dynamic/RegistryTest.cpp - Registry unit tests -===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===-----------------------------------------------------------------------===//
9 
10 #include "../ASTMatchersTest.h"
11 #include "clang/ASTMatchers/Dynamic/Registry.h"
12 #include "gtest/gtest.h"
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:
Args()24   std::vector<ParserValue> Args() { return std::vector<ParserValue>(); }
Args(const VariantValue & Arg1)25   std::vector<ParserValue> Args(const VariantValue &Arg1) {
26     std::vector<ParserValue> Out(1);
27     Out[0].Value = Arg1;
28     return Out;
29   }
Args(const VariantValue & Arg1,const VariantValue & Arg2)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 
lookupMatcherCtor(StringRef MatcherName)38   llvm::Optional<MatcherCtor> lookupMatcherCtor(StringRef MatcherName) {
39     return Registry::lookupMatcherCtor(MatcherName);
40   }
41 
constructMatcher(StringRef MatcherName,Diagnostics * Error=nullptr)42   VariantMatcher constructMatcher(StringRef MatcherName,
43                                   Diagnostics *Error = nullptr) {
44     Diagnostics DummyError;
45     if (!Error) Error = &DummyError;
46     llvm::Optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName);
47     VariantMatcher Out;
48     if (Ctor)
49       Out = Registry::constructMatcher(*Ctor, SourceRange(), Args(), Error);
50     EXPECT_EQ("", DummyError.toStringFull());
51     return Out;
52   }
53 
constructMatcher(StringRef MatcherName,const VariantValue & Arg1,Diagnostics * Error=nullptr)54   VariantMatcher constructMatcher(StringRef MatcherName,
55                                   const VariantValue &Arg1,
56                                   Diagnostics *Error = nullptr) {
57     Diagnostics DummyError;
58     if (!Error) Error = &DummyError;
59     llvm::Optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName);
60     VariantMatcher Out;
61     if (Ctor)
62       Out = Registry::constructMatcher(*Ctor, SourceRange(), Args(Arg1), Error);
63     EXPECT_EQ("", DummyError.toStringFull()) << MatcherName;
64     return Out;
65   }
66 
constructMatcher(StringRef MatcherName,const VariantValue & Arg1,const VariantValue & Arg2,Diagnostics * Error=nullptr)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     llvm::Optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName);
74     VariantMatcher Out;
75     if (Ctor)
76       Out = Registry::constructMatcher(*Ctor, SourceRange(), Args(Arg1, Arg2),
77                                        Error);
78     EXPECT_EQ("", DummyError.toStringFull());
79     return Out;
80   }
81 
82   typedef std::vector<MatcherCompletion> CompVector;
83 
getCompletions()84   CompVector getCompletions() {
85     std::vector<std::pair<MatcherCtor, unsigned> > Context;
86     return Registry::getMatcherCompletions(
87         Registry::getAcceptedCompletionTypes(Context));
88   }
89 
getCompletions(StringRef MatcherName1,unsigned ArgNo1)90   CompVector getCompletions(StringRef MatcherName1, unsigned ArgNo1) {
91     std::vector<std::pair<MatcherCtor, unsigned> > Context;
92     llvm::Optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName1);
93     if (!Ctor)
94       return CompVector();
95     Context.push_back(std::make_pair(*Ctor, ArgNo1));
96     return Registry::getMatcherCompletions(
97         Registry::getAcceptedCompletionTypes(Context));
98   }
99 
getCompletions(StringRef MatcherName1,unsigned ArgNo1,StringRef MatcherName2,unsigned ArgNo2)100   CompVector getCompletions(StringRef MatcherName1, unsigned ArgNo1,
101                             StringRef MatcherName2, unsigned ArgNo2) {
102     std::vector<std::pair<MatcherCtor, unsigned> > Context;
103     llvm::Optional<MatcherCtor> Ctor = lookupMatcherCtor(MatcherName1);
104     if (!Ctor)
105       return CompVector();
106     Context.push_back(std::make_pair(*Ctor, ArgNo1));
107     Ctor = lookupMatcherCtor(MatcherName2);
108     if (!Ctor)
109       return CompVector();
110     Context.push_back(std::make_pair(*Ctor, ArgNo2));
111     return Registry::getMatcherCompletions(
112         Registry::getAcceptedCompletionTypes(Context));
113   }
114 
hasCompletion(const CompVector & Comps,StringRef TypedText,StringRef MatcherDecl=StringRef ())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 
TEST_F(RegistryTest,CanConstructNoArgs)128 TEST_F(RegistryTest, CanConstructNoArgs) {
129   Matcher<Stmt> IsArrowValue = constructMatcher(
130       "memberExpr", constructMatcher("isArrow")).getTypedMatcher<Stmt>();
131   Matcher<Stmt> BoolValue =
132       constructMatcher("boolLiteral").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 
TEST_F(RegistryTest,ConstructWithSimpleArgs)145 TEST_F(RegistryTest, ConstructWithSimpleArgs) {
146   Matcher<Decl> Value = constructMatcher(
147       "namedDecl", constructMatcher("hasName", std::string("X")))
148       .getTypedMatcher<Decl>();
149   EXPECT_TRUE(matches("class X {};", Value));
150   EXPECT_FALSE(matches("int x;", Value));
151 
152   Value = functionDecl(constructMatcher("parameterCountIs", 2)
153                            .getTypedMatcher<FunctionDecl>());
154   EXPECT_TRUE(matches("void foo(int,int);", Value));
155   EXPECT_FALSE(matches("void foo(int);", Value));
156 }
157 
TEST_F(RegistryTest,ConstructWithMatcherArgs)158 TEST_F(RegistryTest, ConstructWithMatcherArgs) {
159   Matcher<Decl> HasInitializerSimple = constructMatcher(
160       "varDecl", constructMatcher("hasInitializer", constructMatcher("stmt")))
161       .getTypedMatcher<Decl>();
162   Matcher<Decl> HasInitializerComplex = constructMatcher(
163       "varDecl",
164       constructMatcher("hasInitializer", constructMatcher("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           "hasParameter", 1, constructMatcher("hasName", std::string("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 
TEST_F(RegistryTest,OverloadedMatchers)187 TEST_F(RegistryTest, OverloadedMatchers) {
188   Matcher<Stmt> CallExpr0 = constructMatcher(
189       "callExpr",
190       constructMatcher("callee", constructMatcher("memberExpr",
191                                                   constructMatcher("isArrow"))))
192       .getTypedMatcher<Stmt>();
193 
194   Matcher<Stmt> CallExpr1 = constructMatcher(
195       "callExpr",
196       constructMatcher(
197           "callee",
198           constructMatcher("methodDecl",
199                            constructMatcher("hasName", std::string("x")))))
200       .getTypedMatcher<Stmt>();
201 
202   std::string Code = "class Y { public: void x(); }; void z() { Y y; y.x(); }";
203   EXPECT_FALSE(matches(Code, CallExpr0));
204   EXPECT_TRUE(matches(Code, CallExpr1));
205 
206   Code = "class Z { public: void z() { this->z(); } };";
207   EXPECT_TRUE(matches(Code, CallExpr0));
208   EXPECT_FALSE(matches(Code, CallExpr1));
209 
210   Matcher<Decl> DeclDecl = declaratorDecl(hasTypeLoc(
211       constructMatcher(
212           "loc", constructMatcher("asString", std::string("const double *")))
213           .getTypedMatcher<TypeLoc>()));
214 
215   Matcher<NestedNameSpecifierLoc> NNSL =
216       constructMatcher(
217           "loc", VariantMatcher::SingleMatcher(nestedNameSpecifier(
218                      specifiesType(hasDeclaration(recordDecl(hasName("A")))))))
219           .getTypedMatcher<NestedNameSpecifierLoc>();
220 
221   Code = "const double * x = 0;";
222   EXPECT_TRUE(matches(Code, DeclDecl));
223   EXPECT_FALSE(matches(Code, NNSL));
224 
225   Code = "struct A { struct B {}; }; A::B a_b;";
226   EXPECT_FALSE(matches(Code, DeclDecl));
227   EXPECT_TRUE(matches(Code, NNSL));
228 }
229 
TEST_F(RegistryTest,PolymorphicMatchers)230 TEST_F(RegistryTest, PolymorphicMatchers) {
231   const VariantMatcher IsDefinition = constructMatcher("isDefinition");
232   Matcher<Decl> Var =
233       constructMatcher("varDecl", IsDefinition).getTypedMatcher<Decl>();
234   Matcher<Decl> Class =
235       constructMatcher("recordDecl", IsDefinition).getTypedMatcher<Decl>();
236   Matcher<Decl> Func =
237       constructMatcher("functionDecl", IsDefinition).getTypedMatcher<Decl>();
238   EXPECT_TRUE(matches("int a;", Var));
239   EXPECT_FALSE(matches("extern int a;", Var));
240   EXPECT_TRUE(matches("class A {};", Class));
241   EXPECT_FALSE(matches("class A;", Class));
242   EXPECT_TRUE(matches("void f(){};", Func));
243   EXPECT_FALSE(matches("void f();", Func));
244 
245   Matcher<Decl> Anything = constructMatcher("anything").getTypedMatcher<Decl>();
246   Matcher<Decl> RecordDecl = constructMatcher(
247       "recordDecl", constructMatcher("hasName", std::string("Foo")),
248       VariantMatcher::SingleMatcher(Anything)).getTypedMatcher<Decl>();
249 
250   EXPECT_TRUE(matches("int Foo;", Anything));
251   EXPECT_TRUE(matches("class Foo {};", Anything));
252   EXPECT_TRUE(matches("void Foo(){};", Anything));
253   EXPECT_FALSE(matches("int Foo;", RecordDecl));
254   EXPECT_TRUE(matches("class Foo {};", RecordDecl));
255   EXPECT_FALSE(matches("void Foo(){};", RecordDecl));
256 
257   Matcher<Stmt> ConstructExpr = constructMatcher(
258       "constructExpr",
259       constructMatcher(
260           "hasDeclaration",
261           constructMatcher(
262               "methodDecl",
263               constructMatcher(
264                   "ofClass", constructMatcher("hasName", std::string("Foo"))))))
265                                     .getTypedMatcher<Stmt>();
266   EXPECT_FALSE(matches("class Foo { public: Foo(); };", ConstructExpr));
267   EXPECT_TRUE(
268       matches("class Foo { public: Foo(); }; Foo foo = Foo();", ConstructExpr));
269 }
270 
TEST_F(RegistryTest,TemplateArgument)271 TEST_F(RegistryTest, TemplateArgument) {
272   Matcher<Decl> HasTemplateArgument = constructMatcher(
273       "classTemplateSpecializationDecl",
274       constructMatcher(
275           "hasAnyTemplateArgument",
276           constructMatcher("refersToType",
277                            constructMatcher("asString", std::string("int")))))
278       .getTypedMatcher<Decl>();
279   EXPECT_TRUE(matches("template<typename T> class A {}; A<int> a;",
280                       HasTemplateArgument));
281   EXPECT_FALSE(matches("template<typename T> class A {}; A<char> a;",
282                        HasTemplateArgument));
283 }
284 
TEST_F(RegistryTest,TypeTraversal)285 TEST_F(RegistryTest, TypeTraversal) {
286   Matcher<Type> M = constructMatcher(
287       "pointerType",
288       constructMatcher("pointee", constructMatcher("isConstQualified"),
289                        constructMatcher("isInteger"))).getTypedMatcher<Type>();
290   EXPECT_FALSE(matches("int *a;", M));
291   EXPECT_TRUE(matches("int const *b;", M));
292 
293   M = constructMatcher(
294       "arrayType",
295       constructMatcher("hasElementType", constructMatcher("builtinType")))
296       .getTypedMatcher<Type>();
297   EXPECT_FALSE(matches("struct A{}; A a[7];;", M));
298   EXPECT_TRUE(matches("int b[7];", M));
299 }
300 
TEST_F(RegistryTest,CXXCtorInitializer)301 TEST_F(RegistryTest, CXXCtorInitializer) {
302   Matcher<Decl> CtorDecl = constructMatcher(
303       "constructorDecl",
304       constructMatcher(
305           "hasAnyConstructorInitializer",
306           constructMatcher("forField",
307                            constructMatcher("hasName", std::string("foo")))))
308       .getTypedMatcher<Decl>();
309   EXPECT_TRUE(matches("struct Foo { Foo() : foo(1) {} int foo; };", CtorDecl));
310   EXPECT_FALSE(matches("struct Foo { Foo() {} int foo; };", CtorDecl));
311   EXPECT_FALSE(matches("struct Foo { Foo() : bar(1) {} int bar; };", CtorDecl));
312 }
313 
TEST_F(RegistryTest,Adaptative)314 TEST_F(RegistryTest, Adaptative) {
315   Matcher<Decl> D = constructMatcher(
316       "recordDecl",
317       constructMatcher(
318           "has",
319           constructMatcher("recordDecl",
320                            constructMatcher("hasName", std::string("X")))))
321       .getTypedMatcher<Decl>();
322   EXPECT_TRUE(matches("class X {};", D));
323   EXPECT_TRUE(matches("class Y { class X {}; };", D));
324   EXPECT_FALSE(matches("class Y { class Z {}; };", D));
325 
326   Matcher<Stmt> S = constructMatcher(
327       "forStmt",
328       constructMatcher(
329           "hasDescendant",
330           constructMatcher("varDecl",
331                            constructMatcher("hasName", std::string("X")))))
332       .getTypedMatcher<Stmt>();
333   EXPECT_TRUE(matches("void foo() { for(int X;;); }", S));
334   EXPECT_TRUE(matches("void foo() { for(;;) { int X; } }", S));
335   EXPECT_FALSE(matches("void foo() { for(;;); }", S));
336   EXPECT_FALSE(matches("void foo() { if (int X = 0){} }", S));
337 
338   S = constructMatcher(
339       "compoundStmt", constructMatcher("hasParent", constructMatcher("ifStmt")))
340       .getTypedMatcher<Stmt>();
341   EXPECT_TRUE(matches("void foo() { if (true) { int x = 42; } }", S));
342   EXPECT_FALSE(matches("void foo() { if (true) return; }", S));
343 }
344 
TEST_F(RegistryTest,VariadicOp)345 TEST_F(RegistryTest, VariadicOp) {
346   Matcher<Decl> D = constructMatcher(
347       "anyOf",
348       constructMatcher("recordDecl",
349                        constructMatcher("hasName", std::string("Foo"))),
350       constructMatcher("functionDecl",
351                        constructMatcher("hasName", std::string("foo"))))
352       .getTypedMatcher<Decl>();
353 
354   EXPECT_TRUE(matches("void foo(){}", D));
355   EXPECT_TRUE(matches("struct Foo{};", D));
356   EXPECT_FALSE(matches("int i = 0;", D));
357 
358   D = constructMatcher(
359       "allOf", constructMatcher("recordDecl"),
360       constructMatcher(
361           "namedDecl",
362           constructMatcher("anyOf",
363                            constructMatcher("hasName", std::string("Foo")),
364                            constructMatcher("hasName", std::string("Bar")))))
365       .getTypedMatcher<Decl>();
366 
367   EXPECT_FALSE(matches("void foo(){}", D));
368   EXPECT_TRUE(matches("struct Foo{};", D));
369   EXPECT_FALSE(matches("int i = 0;", D));
370   EXPECT_TRUE(matches("class Bar{};", D));
371   EXPECT_FALSE(matches("class OtherBar{};", D));
372 
373   D = recordDecl(
374       has(fieldDecl(hasName("Foo"))),
375       constructMatcher(
376           "unless",
377           constructMatcher("namedDecl",
378                            constructMatcher("hasName", std::string("Bar"))))
379           .getTypedMatcher<Decl>());
380 
381   EXPECT_FALSE(matches("class Bar{ int Foo; };", D));
382   EXPECT_TRUE(matches("class OtherBar{ int Foo; };", D));
383 
384   D = constructMatcher(
385           "namedDecl", constructMatcher("hasName", std::string("Foo")),
386           constructMatcher("unless", constructMatcher("recordDecl")))
387           .getTypedMatcher<Decl>();
388   EXPECT_TRUE(matches("void Foo(){}", D));
389   EXPECT_TRUE(notMatches("struct Foo {};", D));
390 }
391 
TEST_F(RegistryTest,Errors)392 TEST_F(RegistryTest, Errors) {
393   // Incorrect argument count.
394   std::unique_ptr<Diagnostics> Error(new Diagnostics());
395   EXPECT_TRUE(constructMatcher("hasInitializer", Error.get()).isNull());
396   EXPECT_EQ("Incorrect argument count. (Expected = 1) != (Actual = 0)",
397             Error->toString());
398   Error.reset(new Diagnostics());
399   EXPECT_TRUE(constructMatcher("isArrow", std::string(), Error.get()).isNull());
400   EXPECT_EQ("Incorrect argument count. (Expected = 0) != (Actual = 1)",
401             Error->toString());
402   Error.reset(new Diagnostics());
403   EXPECT_TRUE(constructMatcher("anyOf", Error.get()).isNull());
404   EXPECT_EQ("Incorrect argument count. (Expected = (2, )) != (Actual = 0)",
405             Error->toString());
406   Error.reset(new Diagnostics());
407   EXPECT_TRUE(constructMatcher("unless", std::string(), std::string(),
408                                Error.get()).isNull());
409   EXPECT_EQ("Incorrect argument count. (Expected = (1, 1)) != (Actual = 2)",
410             Error->toString());
411 
412   // Bad argument type
413   Error.reset(new Diagnostics());
414   EXPECT_TRUE(constructMatcher("ofClass", std::string(), Error.get()).isNull());
415   EXPECT_EQ("Incorrect type for arg 1. (Expected = Matcher<CXXRecordDecl>) != "
416             "(Actual = String)",
417             Error->toString());
418   Error.reset(new Diagnostics());
419   EXPECT_TRUE(constructMatcher("recordDecl", constructMatcher("recordDecl"),
420                                constructMatcher("parameterCountIs", 3),
421                                Error.get()).isNull());
422   EXPECT_EQ("Incorrect type for arg 2. (Expected = Matcher<CXXRecordDecl>) != "
423             "(Actual = Matcher<FunctionDecl>)",
424             Error->toString());
425 
426   // Bad argument type with variadic.
427   Error.reset(new Diagnostics());
428   EXPECT_TRUE(constructMatcher("anyOf", std::string(), std::string(),
429                                Error.get()).isNull());
430   EXPECT_EQ(
431       "Incorrect type for arg 1. (Expected = Matcher<>) != (Actual = String)",
432       Error->toString());
433   Error.reset(new Diagnostics());
434   EXPECT_TRUE(constructMatcher(
435       "recordDecl",
436       constructMatcher("allOf",
437                        constructMatcher("isDerivedFrom", std::string("FOO")),
438                        constructMatcher("isArrow")),
439       Error.get()).isNull());
440   EXPECT_EQ("Incorrect type for arg 1. "
441             "(Expected = Matcher<CXXRecordDecl>) != "
442             "(Actual = Matcher<CXXRecordDecl>&Matcher<MemberExpr>)",
443             Error->toString());
444 }
445 
TEST_F(RegistryTest,Completion)446 TEST_F(RegistryTest, Completion) {
447   CompVector Comps = getCompletions();
448   // Overloaded
449   EXPECT_TRUE(hasCompletion(
450       Comps, "hasParent(", "Matcher<Decl|Stmt> hasParent(Matcher<Decl|Stmt>)"));
451   // Variadic.
452   EXPECT_TRUE(hasCompletion(Comps, "whileStmt(",
453                             "Matcher<Stmt> whileStmt(Matcher<WhileStmt>...)"));
454   // Polymorphic.
455   EXPECT_TRUE(hasCompletion(
456       Comps, "hasDescendant(",
457       "Matcher<NestedNameSpecifier|NestedNameSpecifierLoc|QualType|...> "
458       "hasDescendant(Matcher<CXXCtorInitializer|NestedNameSpecifier|"
459       "NestedNameSpecifierLoc|...>)"));
460 
461   CompVector WhileComps = getCompletions("whileStmt", 0);
462 
463   EXPECT_TRUE(hasCompletion(WhileComps, "hasBody(",
464                             "Matcher<WhileStmt> hasBody(Matcher<Stmt>)"));
465   EXPECT_TRUE(hasCompletion(WhileComps, "hasParent(",
466                             "Matcher<Stmt> hasParent(Matcher<Decl|Stmt>)"));
467   EXPECT_TRUE(
468       hasCompletion(WhileComps, "allOf(", "Matcher<T> allOf(Matcher<T>...)"));
469 
470   EXPECT_FALSE(hasCompletion(WhileComps, "whileStmt("));
471   EXPECT_FALSE(hasCompletion(WhileComps, "ifStmt("));
472 
473   CompVector AllOfWhileComps =
474       getCompletions("allOf", 0, "whileStmt", 0);
475   ASSERT_EQ(AllOfWhileComps.size(), WhileComps.size());
476   EXPECT_TRUE(std::equal(WhileComps.begin(), WhileComps.end(),
477                          AllOfWhileComps.begin()));
478 
479   CompVector DeclWhileComps =
480       getCompletions("decl", 0, "whileStmt", 0);
481   EXPECT_EQ(0u, DeclWhileComps.size());
482 
483   CompVector NamedDeclComps = getCompletions("namedDecl", 0);
484   EXPECT_TRUE(
485       hasCompletion(NamedDeclComps, "isPublic()", "Matcher<Decl> isPublic()"));
486   EXPECT_TRUE(hasCompletion(NamedDeclComps, "hasName(\"",
487                             "Matcher<NamedDecl> hasName(string)"));
488 
489   // Heterogeneous overloads.
490   Comps = getCompletions("classTemplateSpecializationDecl", 0);
491   EXPECT_TRUE(hasCompletion(
492       Comps, "isSameOrDerivedFrom(",
493       "Matcher<CXXRecordDecl> isSameOrDerivedFrom(string|Matcher<NamedDecl>)"));
494 }
495 
TEST_F(RegistryTest,HasArgs)496 TEST_F(RegistryTest, HasArgs) {
497   Matcher<Decl> Value = constructMatcher(
498       "decl", constructMatcher("hasAttr", std::string("attr::WarnUnused")))
499       .getTypedMatcher<Decl>();
500   EXPECT_TRUE(matches("struct __attribute__((warn_unused)) X {};", Value));
501   EXPECT_FALSE(matches("struct X {};", Value));
502 }
503 
504 } // end anonymous namespace
505 } // end namespace dynamic
506 } // end namespace ast_matchers
507 } // end namespace clang
508