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 "llvm/Testing/Support/SupportHelpers.h"
17 #include "gtest/gtest.h"
18 
19 namespace clang {
20 namespace ast_matchers {
21 using internal::DynTypedMatcher;
22 
23 #if GTEST_HAS_DEATH_TEST
TEST(HasNameDeathTest,DiesOnEmptyName)24 TEST(HasNameDeathTest, DiesOnEmptyName) {
25   ASSERT_DEBUG_DEATH({
26     DeclarationMatcher HasEmptyName = recordDecl(hasName(""));
27     EXPECT_TRUE(notMatches("class X {};", HasEmptyName));
28   }, "");
29 }
30 
TEST(HasNameDeathTest,DiesOnEmptyPattern)31 TEST(HasNameDeathTest, DiesOnEmptyPattern) {
32   ASSERT_DEBUG_DEATH({
33       DeclarationMatcher HasEmptyName = recordDecl(matchesName(""));
34       EXPECT_TRUE(notMatches("class X {};", HasEmptyName));
35     }, "");
36 }
37 #endif
38 
TEST(ConstructVariadic,MismatchedTypes_Regression)39 TEST(ConstructVariadic, MismatchedTypes_Regression) {
40   EXPECT_TRUE(
41       matches("const int a = 0;", internal::DynTypedMatcher::constructVariadic(
42                                       internal::DynTypedMatcher::VO_AnyOf,
43                                       ASTNodeKind::getFromNodeKind<QualType>(),
44                                       {isConstQualified(), arrayType()})
45                                       .convertTo<QualType>()));
46 }
47 
48 // For testing AST_MATCHER_P().
AST_MATCHER_P(Decl,just,internal::Matcher<Decl>,AMatcher)49 AST_MATCHER_P(Decl, just, internal::Matcher<Decl>, AMatcher) {
50   // Make sure all special variables are used: node, match_finder,
51   // bound_nodes_builder, and the parameter named 'AMatcher'.
52   return AMatcher.matches(Node, Finder, Builder);
53 }
54 
TEST(AstMatcherPMacro,Works)55 TEST(AstMatcherPMacro, Works) {
56   DeclarationMatcher HasClassB = just(has(recordDecl(hasName("B")).bind("b")));
57 
58   EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
59       HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("b")));
60 
61   EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
62       HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("a")));
63 
64   EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
65       HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("b")));
66 }
67 
AST_POLYMORPHIC_MATCHER_P(polymorphicHas,AST_POLYMORPHIC_SUPPORTED_TYPES (Decl,Stmt),internal::Matcher<Decl>,AMatcher)68 AST_POLYMORPHIC_MATCHER_P(polymorphicHas,
69                           AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt),
70                           internal::Matcher<Decl>, AMatcher) {
71   return Finder->matchesChildOf(
72       Node, AMatcher, Builder,
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(DynTypedMatcherTest,TraversalKindForwardsToImpl)175 TEST(DynTypedMatcherTest, TraversalKindForwardsToImpl) {
176   auto M = DynTypedMatcher(decl());
177   EXPECT_FALSE(M.getTraversalKind().hasValue());
178 
179   M = DynTypedMatcher(traverse(TK_AsIs, decl()));
180   EXPECT_THAT(M.getTraversalKind(), llvm::ValueIs(TK_AsIs));
181 }
182 
TEST(DynTypedMatcherTest,ConstructWithTraversalKindSetsTK)183 TEST(DynTypedMatcherTest, ConstructWithTraversalKindSetsTK) {
184   auto M = DynTypedMatcher(decl()).withTraversalKind(TK_AsIs);
185   EXPECT_THAT(M.getTraversalKind(), llvm::ValueIs(TK_AsIs));
186 }
187 
TEST(DynTypedMatcherTest,ConstructWithTraversalKindOverridesNestedTK)188 TEST(DynTypedMatcherTest, ConstructWithTraversalKindOverridesNestedTK) {
189   auto M = DynTypedMatcher(decl()).withTraversalKind(TK_AsIs).withTraversalKind(
190       TK_IgnoreUnlessSpelledInSource);
191   EXPECT_THAT(M.getTraversalKind(),
192               llvm::ValueIs(TK_IgnoreUnlessSpelledInSource));
193 }
194 
TEST(IsInlineMatcher,IsInline)195 TEST(IsInlineMatcher, IsInline) {
196   EXPECT_TRUE(matches("void g(); inline void f();",
197                       functionDecl(isInline(), hasName("f"))));
198   EXPECT_TRUE(matches("namespace n { inline namespace m {} }",
199                       namespaceDecl(isInline(), hasName("m"))));
200 }
201 
202 // FIXME: Figure out how to specify paths so the following tests pass on
203 // Windows.
204 #ifndef _WIN32
205 
TEST(Matcher,IsExpansionInMainFileMatcher)206 TEST(Matcher, IsExpansionInMainFileMatcher) {
207   EXPECT_TRUE(matches("class X {};",
208                       recordDecl(hasName("X"), isExpansionInMainFile())));
209   EXPECT_TRUE(notMatches("", recordDecl(isExpansionInMainFile())));
210   FileContentMappings M;
211   M.push_back(std::make_pair("/other", "class X {};"));
212   EXPECT_TRUE(matchesConditionally("#include <other>\n",
213                                    recordDecl(isExpansionInMainFile()), false,
214                                    {"-isystem/"}, M));
215 }
216 
TEST(Matcher,IsExpansionInSystemHeader)217 TEST(Matcher, IsExpansionInSystemHeader) {
218   FileContentMappings M;
219   M.push_back(std::make_pair("/other", "class X {};"));
220   EXPECT_TRUE(matchesConditionally("#include \"other\"\n",
221                                    recordDecl(isExpansionInSystemHeader()),
222                                    true, {"-isystem/"}, M));
223   EXPECT_TRUE(matchesConditionally("#include \"other\"\n",
224                                    recordDecl(isExpansionInSystemHeader()),
225                                    false, {"-I/"}, M));
226   EXPECT_TRUE(notMatches("class X {};",
227                          recordDecl(isExpansionInSystemHeader())));
228   EXPECT_TRUE(notMatches("", recordDecl(isExpansionInSystemHeader())));
229 }
230 
TEST(Matcher,IsExpansionInFileMatching)231 TEST(Matcher, IsExpansionInFileMatching) {
232   FileContentMappings M;
233   M.push_back(std::make_pair("/foo", "class A {};"));
234   M.push_back(std::make_pair("/bar", "class B {};"));
235   EXPECT_TRUE(matchesConditionally(
236       "#include <foo>\n"
237       "#include <bar>\n"
238       "class X {};",
239       recordDecl(isExpansionInFileMatching("b.*"), hasName("B")), true,
240       {"-isystem/"}, M));
241   EXPECT_TRUE(matchesConditionally(
242       "#include <foo>\n"
243       "#include <bar>\n"
244       "class X {};",
245       recordDecl(isExpansionInFileMatching("f.*"), hasName("X")), false,
246       {"-isystem/"}, M));
247 }
248 
249 #endif // _WIN32
250 
251 } // end namespace ast_matchers
252 } // end namespace clang
253