1 // unittests/ASTMatchers/ASTMatchersInternalTest.cpp - AST matcher 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/AST/PrettyPrinter.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/ASTMatchers/ASTMatchers.h" 13 #include "clang/Tooling/Tooling.h" 14 #include "llvm/ADT/Triple.h" 15 #include "llvm/Config/config.h" 16 #include "llvm/Support/Host.h" 17 #include "llvm/Testing/Support/SupportHelpers.h" 18 #include "gtest/gtest.h" 19 20 namespace clang { 21 namespace ast_matchers { 22 using internal::DynTypedMatcher; 23 24 #if GTEST_HAS_DEATH_TEST 25 TEST(HasNameDeathTest, DiesOnEmptyName) { 26 ASSERT_DEBUG_DEATH({ 27 DeclarationMatcher HasEmptyName = recordDecl(hasName("")); 28 EXPECT_TRUE(notMatches("class X {};", HasEmptyName)); 29 }, ""); 30 } 31 32 TEST(HasNameDeathTest, DiesOnEmptyPattern) { 33 ASSERT_DEBUG_DEATH({ 34 DeclarationMatcher HasEmptyName = recordDecl(matchesName("")); 35 EXPECT_TRUE(notMatches("class X {};", HasEmptyName)); 36 }, ""); 37 } 38 39 // FIXME: Figure out why back traces aren't being generated on clang builds on 40 // windows. 41 #if ENABLE_BACKTRACES && (!defined(_MSC_VER) || !defined(__clang__)) 42 43 AST_MATCHER(Decl, causeCrash) { 44 abort(); 45 return true; 46 } 47 48 TEST(MatcherCrashDeathTest, CrashOnMatcherDump) { 49 llvm::EnablePrettyStackTrace(); 50 auto Matcher = testing::HasSubstr( 51 "ASTMatcher: Matching '<unknown>' against:\n\tFunctionDecl foo : " 52 "<input.cc:1:1, col:10>"); 53 ASSERT_DEATH(matches("void foo();", functionDecl(causeCrash())), Matcher); 54 } 55 56 template <typename MatcherT> 57 static void crashTestNodeDump(MatcherT Matcher, 58 ArrayRef<StringRef> MatchedNodes, 59 StringRef Against, StringRef Code) { 60 llvm::EnablePrettyStackTrace(); 61 MatchFinder Finder; 62 63 struct CrashCallback : public MatchFinder::MatchCallback { 64 void run(const MatchFinder::MatchResult &Result) override { abort(); } 65 llvm::Optional<TraversalKind> getCheckTraversalKind() const override { 66 return TK_IgnoreUnlessSpelledInSource; 67 } 68 StringRef getID() const override { return "CrashTester"; } 69 } Callback; 70 Finder.addMatcher(std::move(Matcher), &Callback); 71 if (MatchedNodes.empty()) { 72 ASSERT_DEATH(tooling::runToolOnCode( 73 newFrontendActionFactory(&Finder)->create(), Code), 74 testing::HasSubstr( 75 ("ASTMatcher: Processing 'CrashTester' against:\n\t" + 76 Against + "\nNo bound nodes") 77 .str())); 78 } else { 79 std::vector<testing::PolymorphicMatcher< 80 testing::internal::HasSubstrMatcher<std::string>>> 81 Matchers; 82 Matchers.reserve(MatchedNodes.size()); 83 for (auto Node : MatchedNodes) { 84 Matchers.push_back(testing::HasSubstr(Node.str())); 85 } 86 auto CrashMatcher = testing::AllOf( 87 testing::HasSubstr( 88 ("ASTMatcher: Processing 'CrashTester' against:\n\t" + Against + 89 "\n--- Bound Nodes Begin ---") 90 .str()), 91 testing::HasSubstr("--- Bound Nodes End ---"), 92 testing::AllOfArray(Matchers)); 93 94 ASSERT_DEATH(tooling::runToolOnCode( 95 newFrontendActionFactory(&Finder)->create(), Code), 96 CrashMatcher); 97 } 98 } 99 TEST(MatcherCrashDeathTest, CrashOnCallbackDump) { 100 crashTestNodeDump(forStmt(), {}, "ForStmt : <input.cc:1:14, col:21>", 101 "void foo() { for(;;); }"); 102 crashTestNodeDump( 103 forStmt(hasLoopInit(declStmt(hasSingleDecl( 104 varDecl(hasType(qualType().bind("QT")), 105 hasType(type().bind("T")), 106 hasInitializer( 107 integerLiteral().bind("IL"))) 108 .bind("VD"))) 109 .bind("DS"))) 110 .bind("FS"), 111 {"FS - { ForStmt : <input.cc:3:5, line:4:5> }", 112 "DS - { DeclStmt : <input.cc:3:10, col:19> }", 113 "IL - { IntegerLiteral : <input.cc:3:18> }", "QT - { QualType : int }", 114 "T - { BuiltinType : int }", 115 "VD - { VarDecl I : <input.cc:3:10, col:18> }"}, 116 "ForStmt : <input.cc:3:5, line:4:5>", 117 R"cpp( 118 void foo() { 119 for (int I = 0; I < 5; ++I) { 120 } 121 } 122 )cpp"); 123 crashTestNodeDump( 124 cxxRecordDecl(hasMethod(cxxMethodDecl(hasName("operator+")).bind("Op+"))) 125 .bind("Unnamed"), 126 {"Unnamed - { CXXRecordDecl (anonymous) : <input.cc:1:1, col:36> }", 127 "Op+ - { CXXMethodDecl (anonymous struct)::operator+ : <input.cc:1:10, " 128 "col:29> }"}, 129 "CXXRecordDecl (anonymous) : <input.cc:1:1, col:36>", 130 "struct { int operator+(int) const; } Unnamed;"); 131 crashTestNodeDump( 132 cxxRecordDecl(hasMethod(cxxConstructorDecl(isDefaulted()).bind("Ctor")), 133 hasMethod(cxxDestructorDecl(isDefaulted()).bind("Dtor"))), 134 {"Ctor - { CXXConstructorDecl Foo::Foo : <input.cc:1:14, col:28> }", 135 "Dtor - { CXXDestructorDecl Foo::~Foo : <input.cc:1:31, col:46> }"}, 136 "CXXRecordDecl Foo : <input.cc:1:1, col:49>", 137 "struct Foo { Foo() = default; ~Foo() = default; };"); 138 } 139 #endif // ENABLE_BACKTRACES 140 #endif 141 142 TEST(ConstructVariadic, MismatchedTypes_Regression) { 143 EXPECT_TRUE( 144 matches("const int a = 0;", internal::DynTypedMatcher::constructVariadic( 145 internal::DynTypedMatcher::VO_AnyOf, 146 ASTNodeKind::getFromNodeKind<QualType>(), 147 {isConstQualified(), arrayType()}) 148 .convertTo<QualType>())); 149 } 150 151 // For testing AST_MATCHER_P(). 152 AST_MATCHER_P(Decl, just, internal::Matcher<Decl>, AMatcher) { 153 // Make sure all special variables are used: node, match_finder, 154 // bound_nodes_builder, and the parameter named 'AMatcher'. 155 return AMatcher.matches(Node, Finder, Builder); 156 } 157 158 TEST(AstMatcherPMacro, Works) { 159 DeclarationMatcher HasClassB = just(has(recordDecl(hasName("B")).bind("b"))); 160 161 EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };", 162 HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("b"))); 163 164 EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };", 165 HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("a"))); 166 167 EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };", 168 HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("b"))); 169 } 170 171 AST_POLYMORPHIC_MATCHER_P(polymorphicHas, 172 AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt), 173 internal::Matcher<Decl>, AMatcher) { 174 return Finder->matchesChildOf( 175 Node, AMatcher, Builder, 176 ASTMatchFinder::BK_First); 177 } 178 179 TEST(AstPolymorphicMatcherPMacro, Works) { 180 DeclarationMatcher HasClassB = 181 polymorphicHas(recordDecl(hasName("B")).bind("b")); 182 183 EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };", 184 HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("b"))); 185 186 EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };", 187 HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("a"))); 188 189 EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };", 190 HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("b"))); 191 192 StatementMatcher StatementHasClassB = 193 polymorphicHas(recordDecl(hasName("B"))); 194 195 EXPECT_TRUE(matches("void x() { class B {}; }", StatementHasClassB)); 196 } 197 198 TEST(MatchFinder, CheckProfiling) { 199 MatchFinder::MatchFinderOptions Options; 200 llvm::StringMap<llvm::TimeRecord> Records; 201 Options.CheckProfiling.emplace(Records); 202 MatchFinder Finder(std::move(Options)); 203 204 struct NamedCallback : public MatchFinder::MatchCallback { 205 void run(const MatchFinder::MatchResult &Result) override {} 206 StringRef getID() const override { return "MyID"; } 207 } Callback; 208 Finder.addMatcher(decl(), &Callback); 209 std::unique_ptr<FrontendActionFactory> Factory( 210 newFrontendActionFactory(&Finder)); 211 ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;")); 212 213 EXPECT_EQ(1u, Records.size()); 214 EXPECT_EQ("MyID", Records.begin()->getKey()); 215 } 216 217 class VerifyStartOfTranslationUnit : public MatchFinder::MatchCallback { 218 public: 219 VerifyStartOfTranslationUnit() : Called(false) {} 220 void run(const MatchFinder::MatchResult &Result) override { 221 EXPECT_TRUE(Called); 222 } 223 void onStartOfTranslationUnit() override { Called = true; } 224 bool Called; 225 }; 226 227 TEST(MatchFinder, InterceptsStartOfTranslationUnit) { 228 MatchFinder Finder; 229 VerifyStartOfTranslationUnit VerifyCallback; 230 Finder.addMatcher(decl(), &VerifyCallback); 231 std::unique_ptr<FrontendActionFactory> Factory( 232 newFrontendActionFactory(&Finder)); 233 ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;")); 234 EXPECT_TRUE(VerifyCallback.Called); 235 236 VerifyCallback.Called = false; 237 std::unique_ptr<ASTUnit> AST(tooling::buildASTFromCode("int x;")); 238 ASSERT_TRUE(AST.get()); 239 Finder.matchAST(AST->getASTContext()); 240 EXPECT_TRUE(VerifyCallback.Called); 241 } 242 243 class VerifyEndOfTranslationUnit : public MatchFinder::MatchCallback { 244 public: 245 VerifyEndOfTranslationUnit() : Called(false) {} 246 void run(const MatchFinder::MatchResult &Result) override { 247 EXPECT_FALSE(Called); 248 } 249 void onEndOfTranslationUnit() override { Called = true; } 250 bool Called; 251 }; 252 253 TEST(MatchFinder, InterceptsEndOfTranslationUnit) { 254 MatchFinder Finder; 255 VerifyEndOfTranslationUnit VerifyCallback; 256 Finder.addMatcher(decl(), &VerifyCallback); 257 std::unique_ptr<FrontendActionFactory> Factory( 258 newFrontendActionFactory(&Finder)); 259 ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;")); 260 EXPECT_TRUE(VerifyCallback.Called); 261 262 VerifyCallback.Called = false; 263 std::unique_ptr<ASTUnit> AST(tooling::buildASTFromCode("int x;")); 264 ASSERT_TRUE(AST.get()); 265 Finder.matchAST(AST->getASTContext()); 266 EXPECT_TRUE(VerifyCallback.Called); 267 } 268 269 TEST(Matcher, matchOverEntireASTContext) { 270 std::unique_ptr<ASTUnit> AST = 271 clang::tooling::buildASTFromCode("struct { int *foo; };"); 272 ASSERT_TRUE(AST.get()); 273 auto PT = selectFirst<PointerType>( 274 "x", match(pointerType().bind("x"), AST->getASTContext())); 275 EXPECT_NE(nullptr, PT); 276 } 277 278 TEST(DynTypedMatcherTest, TraversalKindForwardsToImpl) { 279 auto M = DynTypedMatcher(decl()); 280 EXPECT_FALSE(M.getTraversalKind()); 281 282 M = DynTypedMatcher(traverse(TK_AsIs, decl())); 283 EXPECT_THAT(M.getTraversalKind(), llvm::ValueIs(TK_AsIs)); 284 } 285 286 TEST(DynTypedMatcherTest, ConstructWithTraversalKindSetsTK) { 287 auto M = DynTypedMatcher(decl()).withTraversalKind(TK_AsIs); 288 EXPECT_THAT(M.getTraversalKind(), llvm::ValueIs(TK_AsIs)); 289 } 290 291 TEST(DynTypedMatcherTest, ConstructWithTraversalKindOverridesNestedTK) { 292 auto M = DynTypedMatcher(decl()).withTraversalKind(TK_AsIs).withTraversalKind( 293 TK_IgnoreUnlessSpelledInSource); 294 EXPECT_THAT(M.getTraversalKind(), 295 llvm::ValueIs(TK_IgnoreUnlessSpelledInSource)); 296 } 297 298 TEST(IsInlineMatcher, IsInline) { 299 EXPECT_TRUE(matches("void g(); inline void f();", 300 functionDecl(isInline(), hasName("f")))); 301 EXPECT_TRUE(matches("namespace n { inline namespace m {} }", 302 namespaceDecl(isInline(), hasName("m")))); 303 EXPECT_TRUE(matches("inline int Foo = 5;", 304 varDecl(isInline(), hasName("Foo")), {Lang_CXX17})); 305 } 306 307 // FIXME: Figure out how to specify paths so the following tests pass on 308 // Windows. 309 #ifndef _WIN32 310 311 TEST(Matcher, IsExpansionInMainFileMatcher) { 312 EXPECT_TRUE(matches("class X {};", 313 recordDecl(hasName("X"), isExpansionInMainFile()))); 314 EXPECT_TRUE(notMatches("", recordDecl(isExpansionInMainFile()))); 315 FileContentMappings M; 316 M.push_back(std::make_pair("/other", "class X {};")); 317 EXPECT_TRUE(matchesConditionally("#include <other>\n", 318 recordDecl(isExpansionInMainFile()), false, 319 {"-isystem/"}, M)); 320 } 321 322 TEST(Matcher, IsExpansionInSystemHeader) { 323 FileContentMappings M; 324 M.push_back(std::make_pair("/other", "class X {};")); 325 EXPECT_TRUE(matchesConditionally("#include \"other\"\n", 326 recordDecl(isExpansionInSystemHeader()), 327 true, {"-isystem/"}, M)); 328 EXPECT_TRUE(matchesConditionally("#include \"other\"\n", 329 recordDecl(isExpansionInSystemHeader()), 330 false, {"-I/"}, M)); 331 EXPECT_TRUE(notMatches("class X {};", 332 recordDecl(isExpansionInSystemHeader()))); 333 EXPECT_TRUE(notMatches("", recordDecl(isExpansionInSystemHeader()))); 334 } 335 336 TEST(Matcher, IsExpansionInFileMatching) { 337 FileContentMappings M; 338 M.push_back(std::make_pair("/foo", "class A {};")); 339 M.push_back(std::make_pair("/bar", "class B {};")); 340 EXPECT_TRUE(matchesConditionally( 341 "#include <foo>\n" 342 "#include <bar>\n" 343 "class X {};", 344 recordDecl(isExpansionInFileMatching("b.*"), hasName("B")), true, 345 {"-isystem/"}, M)); 346 EXPECT_TRUE(matchesConditionally( 347 "#include <foo>\n" 348 "#include <bar>\n" 349 "class X {};", 350 recordDecl(isExpansionInFileMatching("f.*"), hasName("X")), false, 351 {"-isystem/"}, M)); 352 } 353 354 #endif // _WIN32 355 356 } // end namespace ast_matchers 357 } // end namespace clang 358