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