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/Support/Host.h"
16 #include "gtest/gtest.h"
17 
18 namespace clang {
19 namespace ast_matchers {
20 
21 #if GTEST_HAS_DEATH_TEST
TEST(HasNameDeathTest,DiesOnEmptyName)22 TEST(HasNameDeathTest, DiesOnEmptyName) {
23   ASSERT_DEBUG_DEATH({
24     DeclarationMatcher HasEmptyName = recordDecl(hasName(""));
25     EXPECT_TRUE(notMatches("class X {};", HasEmptyName));
26   }, "");
27 }
28 
TEST(HasNameDeathTest,DiesOnEmptyPattern)29 TEST(HasNameDeathTest, DiesOnEmptyPattern) {
30   ASSERT_DEBUG_DEATH({
31       DeclarationMatcher HasEmptyName = recordDecl(matchesName(""));
32       EXPECT_TRUE(notMatches("class X {};", HasEmptyName));
33     }, "");
34 }
35 #endif
36 
TEST(ConstructVariadic,MismatchedTypes_Regression)37 TEST(ConstructVariadic, MismatchedTypes_Regression) {
38   EXPECT_TRUE(
39       matches("const int a = 0;",
40               internal::DynTypedMatcher::constructVariadic(
41                   internal::DynTypedMatcher::VO_AnyOf,
42                   ast_type_traits::ASTNodeKind::getFromNodeKind<QualType>(),
43                   {isConstQualified(), arrayType()})
44                   .convertTo<QualType>()));
45 }
46 
47 // For testing AST_MATCHER_P().
AST_MATCHER_P(Decl,just,internal::Matcher<Decl>,AMatcher)48 AST_MATCHER_P(Decl, just, internal::Matcher<Decl>, AMatcher) {
49   // Make sure all special variables are used: node, match_finder,
50   // bound_nodes_builder, and the parameter named 'AMatcher'.
51   return AMatcher.matches(Node, Finder, Builder);
52 }
53 
TEST(AstMatcherPMacro,Works)54 TEST(AstMatcherPMacro, Works) {
55   DeclarationMatcher HasClassB = just(has(recordDecl(hasName("B")).bind("b")));
56 
57   EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
58       HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("b")));
59 
60   EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
61       HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("a")));
62 
63   EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
64       HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("b")));
65 }
66 
AST_POLYMORPHIC_MATCHER_P(polymorphicHas,AST_POLYMORPHIC_SUPPORTED_TYPES (Decl,Stmt),internal::Matcher<Decl>,AMatcher)67 AST_POLYMORPHIC_MATCHER_P(polymorphicHas,
68                           AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt),
69                           internal::Matcher<Decl>, AMatcher) {
70   return Finder->matchesChildOf(
71       Node, AMatcher, Builder,
72       ast_type_traits::TraversalKind::TK_IgnoreImplicitCastsAndParentheses,
73       ASTMatchFinder::BK_First);
74 }
75 
TEST(AstPolymorphicMatcherPMacro,Works)76 TEST(AstPolymorphicMatcherPMacro, Works) {
77   DeclarationMatcher HasClassB =
78       polymorphicHas(recordDecl(hasName("B")).bind("b"));
79 
80   EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
81       HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("b")));
82 
83   EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
84       HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("a")));
85 
86   EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
87       HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("b")));
88 
89   StatementMatcher StatementHasClassB =
90       polymorphicHas(recordDecl(hasName("B")));
91 
92   EXPECT_TRUE(matches("void x() { class B {}; }", StatementHasClassB));
93 }
94 
TEST(MatchFinder,CheckProfiling)95 TEST(MatchFinder, CheckProfiling) {
96   MatchFinder::MatchFinderOptions Options;
97   llvm::StringMap<llvm::TimeRecord> Records;
98   Options.CheckProfiling.emplace(Records);
99   MatchFinder Finder(std::move(Options));
100 
101   struct NamedCallback : public MatchFinder::MatchCallback {
102     void run(const MatchFinder::MatchResult &Result) override {}
103     StringRef getID() const override { return "MyID"; }
104   } Callback;
105   Finder.addMatcher(decl(), &Callback);
106   std::unique_ptr<FrontendActionFactory> Factory(
107       newFrontendActionFactory(&Finder));
108   ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;"));
109 
110   EXPECT_EQ(1u, Records.size());
111   EXPECT_EQ("MyID", Records.begin()->getKey());
112 }
113 
114 class VerifyStartOfTranslationUnit : public MatchFinder::MatchCallback {
115 public:
VerifyStartOfTranslationUnit()116   VerifyStartOfTranslationUnit() : Called(false) {}
run(const MatchFinder::MatchResult & Result)117   void run(const MatchFinder::MatchResult &Result) override {
118     EXPECT_TRUE(Called);
119   }
onStartOfTranslationUnit()120   void onStartOfTranslationUnit() override { Called = true; }
121   bool Called;
122 };
123 
TEST(MatchFinder,InterceptsStartOfTranslationUnit)124 TEST(MatchFinder, InterceptsStartOfTranslationUnit) {
125   MatchFinder Finder;
126   VerifyStartOfTranslationUnit VerifyCallback;
127   Finder.addMatcher(decl(), &VerifyCallback);
128   std::unique_ptr<FrontendActionFactory> Factory(
129       newFrontendActionFactory(&Finder));
130   ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;"));
131   EXPECT_TRUE(VerifyCallback.Called);
132 
133   VerifyCallback.Called = false;
134   std::unique_ptr<ASTUnit> AST(tooling::buildASTFromCode("int x;"));
135   ASSERT_TRUE(AST.get());
136   Finder.matchAST(AST->getASTContext());
137   EXPECT_TRUE(VerifyCallback.Called);
138 }
139 
140 class VerifyEndOfTranslationUnit : public MatchFinder::MatchCallback {
141 public:
VerifyEndOfTranslationUnit()142   VerifyEndOfTranslationUnit() : Called(false) {}
run(const MatchFinder::MatchResult & Result)143   void run(const MatchFinder::MatchResult &Result) override {
144     EXPECT_FALSE(Called);
145   }
onEndOfTranslationUnit()146   void onEndOfTranslationUnit() override { Called = true; }
147   bool Called;
148 };
149 
TEST(MatchFinder,InterceptsEndOfTranslationUnit)150 TEST(MatchFinder, InterceptsEndOfTranslationUnit) {
151   MatchFinder Finder;
152   VerifyEndOfTranslationUnit VerifyCallback;
153   Finder.addMatcher(decl(), &VerifyCallback);
154   std::unique_ptr<FrontendActionFactory> Factory(
155       newFrontendActionFactory(&Finder));
156   ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;"));
157   EXPECT_TRUE(VerifyCallback.Called);
158 
159   VerifyCallback.Called = false;
160   std::unique_ptr<ASTUnit> AST(tooling::buildASTFromCode("int x;"));
161   ASSERT_TRUE(AST.get());
162   Finder.matchAST(AST->getASTContext());
163   EXPECT_TRUE(VerifyCallback.Called);
164 }
165 
TEST(Matcher,matchOverEntireASTContext)166 TEST(Matcher, matchOverEntireASTContext) {
167   std::unique_ptr<ASTUnit> AST =
168       clang::tooling::buildASTFromCode("struct { int *foo; };");
169   ASSERT_TRUE(AST.get());
170   auto PT = selectFirst<PointerType>(
171       "x", match(pointerType().bind("x"), AST->getASTContext()));
172   EXPECT_NE(nullptr, PT);
173 }
174 
TEST(IsInlineMatcher,IsInline)175 TEST(IsInlineMatcher, IsInline) {
176   EXPECT_TRUE(matches("void g(); inline void f();",
177                       functionDecl(isInline(), hasName("f"))));
178   EXPECT_TRUE(matches("namespace n { inline namespace m {} }",
179                       namespaceDecl(isInline(), hasName("m"))));
180 }
181 
182 // FIXME: Figure out how to specify paths so the following tests pass on
183 // Windows.
184 #ifndef _WIN32
185 
TEST(Matcher,IsExpansionInMainFileMatcher)186 TEST(Matcher, IsExpansionInMainFileMatcher) {
187   EXPECT_TRUE(matches("class X {};",
188                       recordDecl(hasName("X"), isExpansionInMainFile())));
189   EXPECT_TRUE(notMatches("", recordDecl(isExpansionInMainFile())));
190   FileContentMappings M;
191   M.push_back(std::make_pair("/other", "class X {};"));
192   EXPECT_TRUE(matchesConditionally("#include <other>\n",
193                                    recordDecl(isExpansionInMainFile()), false,
194                                    "-isystem/", M));
195 }
196 
TEST(Matcher,IsExpansionInSystemHeader)197 TEST(Matcher, IsExpansionInSystemHeader) {
198   FileContentMappings M;
199   M.push_back(std::make_pair("/other", "class X {};"));
200   EXPECT_TRUE(matchesConditionally(
201       "#include \"other\"\n", recordDecl(isExpansionInSystemHeader()), true,
202       "-isystem/", M));
203   EXPECT_TRUE(matchesConditionally("#include \"other\"\n",
204                                    recordDecl(isExpansionInSystemHeader()),
205                                    false, "-I/", M));
206   EXPECT_TRUE(notMatches("class X {};",
207                          recordDecl(isExpansionInSystemHeader())));
208   EXPECT_TRUE(notMatches("", recordDecl(isExpansionInSystemHeader())));
209 }
210 
TEST(Matcher,IsExpansionInFileMatching)211 TEST(Matcher, IsExpansionInFileMatching) {
212   FileContentMappings M;
213   M.push_back(std::make_pair("/foo", "class A {};"));
214   M.push_back(std::make_pair("/bar", "class B {};"));
215   EXPECT_TRUE(matchesConditionally(
216       "#include <foo>\n"
217       "#include <bar>\n"
218       "class X {};",
219       recordDecl(isExpansionInFileMatching("b.*"), hasName("B")), true,
220       "-isystem/", M));
221   EXPECT_TRUE(matchesConditionally(
222       "#include <foo>\n"
223       "#include <bar>\n"
224       "class X {};",
225       recordDecl(isExpansionInFileMatching("f.*"), hasName("X")), false,
226       "-isystem/", M));
227 }
228 
229 #endif // _WIN32
230 
231 } // end namespace ast_matchers
232 } // end namespace clang
233