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