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