| 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 | |