1 // unittests/ASTMatchers/ASTMatchersInternalTest.cpp - AST matcher unit tests //
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
7 //===----------------------------------------------------------------------===//
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/TargetParser/Host.h"
15 #include "llvm/TargetParser/Triple.h"
16 #include "llvm/Testing/Support/SupportHelpers.h"
17 #include "gtest/gtest.h"
20 namespace ast_matchers
{
21 using internal::DynTypedMatcher
;
23 #if GTEST_HAS_DEATH_TEST
24 TEST(HasNameDeathTest
, DiesOnEmptyName
) {
26 DeclarationMatcher HasEmptyName
= recordDecl(hasName(""));
27 EXPECT_TRUE(notMatches("class X {};", HasEmptyName
));
31 TEST(HasNameDeathTest
, DiesOnEmptyPattern
) {
33 DeclarationMatcher HasEmptyName
= recordDecl(matchesName(""));
34 EXPECT_TRUE(notMatches("class X {};", HasEmptyName
));
38 // FIXME Re-enable these tests without breaking standalone builds.
40 // FIXME: Figure out why back traces aren't being generated on clang builds on
42 #if ENABLE_BACKTRACES && (!defined(_MSC_VER) || !defined(__clang__))
44 AST_MATCHER(Decl
, causeCrash
) {
49 TEST(MatcherCrashDeathTest
, CrashOnMatcherDump
) {
50 llvm::EnablePrettyStackTrace();
51 auto Matcher
= testing::HasSubstr(
52 "ASTMatcher: Matching '<unknown>' against:\n\tFunctionDecl foo : "
53 "<input.cc:1:1, col:10>");
54 ASSERT_DEATH(matches("void foo();", functionDecl(causeCrash())), Matcher
);
57 template <typename MatcherT
>
58 static void crashTestNodeDump(MatcherT Matcher
,
59 ArrayRef
<StringRef
> MatchedNodes
,
60 StringRef Against
, StringRef Code
) {
61 llvm::EnablePrettyStackTrace();
64 struct CrashCallback
: public MatchFinder::MatchCallback
{
65 void run(const MatchFinder::MatchResult
&Result
) override
{ abort(); }
66 std::optional
<TraversalKind
> getCheckTraversalKind() const override
{
67 return TK_IgnoreUnlessSpelledInSource
;
69 StringRef
getID() const override
{ return "CrashTester"; }
71 Finder
.addMatcher(std::move(Matcher
), &Callback
);
72 if (MatchedNodes
.empty()) {
73 ASSERT_DEATH(tooling::runToolOnCode(
74 newFrontendActionFactory(&Finder
)->create(), Code
),
76 ("ASTMatcher: Processing 'CrashTester' against:\n\t" +
77 Against
+ "\nNo bound nodes")
80 std::vector
<testing::PolymorphicMatcher
<
81 testing::internal::HasSubstrMatcher
<std::string
>>>
83 Matchers
.reserve(MatchedNodes
.size());
84 for (auto Node
: MatchedNodes
) {
85 Matchers
.push_back(testing::HasSubstr(Node
.str()));
87 auto CrashMatcher
= testing::AllOf(
89 ("ASTMatcher: Processing 'CrashTester' against:\n\t" + Against
+
90 "\n--- Bound Nodes Begin ---")
92 testing::HasSubstr("--- Bound Nodes End ---"),
93 testing::AllOfArray(Matchers
));
95 ASSERT_DEATH(tooling::runToolOnCode(
96 newFrontendActionFactory(&Finder
)->create(), Code
),
100 TEST(MatcherCrashDeathTest
, CrashOnCallbackDump
) {
101 crashTestNodeDump(forStmt(), {}, "ForStmt : <input.cc:1:14, col:21>",
102 "void foo() { for(;;); }");
104 forStmt(hasLoopInit(declStmt(hasSingleDecl(
105 varDecl(hasType(qualType().bind("QT")),
106 hasType(type().bind("T")),
108 integerLiteral().bind("IL")))
112 {"FS - { ForStmt : <input.cc:3:5, line:4:5> }",
113 "DS - { DeclStmt : <input.cc:3:10, col:19> }",
114 "IL - { IntegerLiteral : <input.cc:3:18> }", "QT - { QualType : int }",
115 "T - { BuiltinType : int }",
116 "VD - { VarDecl I : <input.cc:3:10, col:18> }"},
117 "ForStmt : <input.cc:3:5, line:4:5>",
120 for (int I = 0; I < 5; ++I) {
125 cxxRecordDecl(hasMethod(cxxMethodDecl(hasName("operator+")).bind("Op+")))
127 {"Unnamed - { CXXRecordDecl (anonymous) : <input.cc:1:1, col:36> }",
128 "Op+ - { CXXMethodDecl (anonymous struct)::operator+ : <input.cc:1:10, "
130 "CXXRecordDecl (anonymous) : <input.cc:1:1, col:36>",
131 "struct { int operator+(int) const; } Unnamed;");
133 cxxRecordDecl(hasMethod(cxxConstructorDecl(isDefaulted()).bind("Ctor")),
134 hasMethod(cxxDestructorDecl(isDefaulted()).bind("Dtor"))),
135 {"Ctor - { CXXConstructorDecl Foo::Foo : <input.cc:1:14, col:28> }",
136 "Dtor - { CXXDestructorDecl Foo::~Foo : <input.cc:1:31, col:46> }"},
137 "CXXRecordDecl Foo : <input.cc:1:1, col:49>",
138 "struct Foo { Foo() = default; ~Foo() = default; };");
140 #endif // ENABLE_BACKTRACES
144 TEST(ConstructVariadic
, MismatchedTypes_Regression
) {
146 matches("const int a = 0;", internal::DynTypedMatcher::constructVariadic(
147 internal::DynTypedMatcher::VO_AnyOf
,
148 ASTNodeKind::getFromNodeKind
<QualType
>(),
149 {isConstQualified(), arrayType()})
150 .convertTo
<QualType
>()));
153 // For testing AST_MATCHER_P().
154 AST_MATCHER_P(Decl
, just
, internal::Matcher
<Decl
>, AMatcher
) {
155 // Make sure all special variables are used: node, match_finder,
156 // bound_nodes_builder, and the parameter named 'AMatcher'.
157 return AMatcher
.matches(Node
, Finder
, Builder
);
160 TEST(AstMatcherPMacro
, Works
) {
161 DeclarationMatcher HasClassB
= just(has(recordDecl(hasName("B")).bind("b")));
163 EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
164 HasClassB
, std::make_unique
<VerifyIdIsBoundTo
<Decl
>>("b")));
166 EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
167 HasClassB
, std::make_unique
<VerifyIdIsBoundTo
<Decl
>>("a")));
169 EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
170 HasClassB
, std::make_unique
<VerifyIdIsBoundTo
<Decl
>>("b")));
173 AST_POLYMORPHIC_MATCHER_P(polymorphicHas
,
174 AST_POLYMORPHIC_SUPPORTED_TYPES(Decl
, Stmt
),
175 internal::Matcher
<Decl
>, AMatcher
) {
176 return Finder
->matchesChildOf(
177 Node
, AMatcher
, Builder
,
178 ASTMatchFinder::BK_First
);
181 TEST(AstPolymorphicMatcherPMacro
, Works
) {
182 DeclarationMatcher HasClassB
=
183 polymorphicHas(recordDecl(hasName("B")).bind("b"));
185 EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",
186 HasClassB
, std::make_unique
<VerifyIdIsBoundTo
<Decl
>>("b")));
188 EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",
189 HasClassB
, std::make_unique
<VerifyIdIsBoundTo
<Decl
>>("a")));
191 EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",
192 HasClassB
, std::make_unique
<VerifyIdIsBoundTo
<Decl
>>("b")));
194 StatementMatcher StatementHasClassB
=
195 polymorphicHas(recordDecl(hasName("B")));
197 EXPECT_TRUE(matches("void x() { class B {}; }", StatementHasClassB
));
200 TEST(MatchFinder
, CheckProfiling
) {
201 MatchFinder::MatchFinderOptions Options
;
202 llvm::StringMap
<llvm::TimeRecord
> Records
;
203 Options
.CheckProfiling
.emplace(Records
);
204 MatchFinder
Finder(std::move(Options
));
206 struct NamedCallback
: public MatchFinder::MatchCallback
{
207 void run(const MatchFinder::MatchResult
&Result
) override
{}
208 StringRef
getID() const override
{ return "MyID"; }
210 Finder
.addMatcher(decl(), &Callback
);
211 std::unique_ptr
<FrontendActionFactory
> Factory(
212 newFrontendActionFactory(&Finder
));
213 ASSERT_TRUE(tooling::runToolOnCode(Factory
->create(), "int x;"));
215 EXPECT_EQ(1u, Records
.size());
216 EXPECT_EQ("MyID", Records
.begin()->getKey());
219 class VerifyStartOfTranslationUnit
: public MatchFinder::MatchCallback
{
221 VerifyStartOfTranslationUnit() : Called(false) {}
222 void run(const MatchFinder::MatchResult
&Result
) override
{
225 void onStartOfTranslationUnit() override
{ Called
= true; }
229 TEST(MatchFinder
, InterceptsStartOfTranslationUnit
) {
231 VerifyStartOfTranslationUnit VerifyCallback
;
232 Finder
.addMatcher(decl(), &VerifyCallback
);
233 std::unique_ptr
<FrontendActionFactory
> Factory(
234 newFrontendActionFactory(&Finder
));
235 ASSERT_TRUE(tooling::runToolOnCode(Factory
->create(), "int x;"));
236 EXPECT_TRUE(VerifyCallback
.Called
);
238 VerifyCallback
.Called
= false;
239 std::unique_ptr
<ASTUnit
> AST(tooling::buildASTFromCode("int x;"));
240 ASSERT_TRUE(AST
.get());
241 Finder
.matchAST(AST
->getASTContext());
242 EXPECT_TRUE(VerifyCallback
.Called
);
245 class VerifyEndOfTranslationUnit
: public MatchFinder::MatchCallback
{
247 VerifyEndOfTranslationUnit() : Called(false) {}
248 void run(const MatchFinder::MatchResult
&Result
) override
{
249 EXPECT_FALSE(Called
);
251 void onEndOfTranslationUnit() override
{ Called
= true; }
255 TEST(MatchFinder
, InterceptsEndOfTranslationUnit
) {
257 VerifyEndOfTranslationUnit VerifyCallback
;
258 Finder
.addMatcher(decl(), &VerifyCallback
);
259 std::unique_ptr
<FrontendActionFactory
> Factory(
260 newFrontendActionFactory(&Finder
));
261 ASSERT_TRUE(tooling::runToolOnCode(Factory
->create(), "int x;"));
262 EXPECT_TRUE(VerifyCallback
.Called
);
264 VerifyCallback
.Called
= false;
265 std::unique_ptr
<ASTUnit
> AST(tooling::buildASTFromCode("int x;"));
266 ASSERT_TRUE(AST
.get());
267 Finder
.matchAST(AST
->getASTContext());
268 EXPECT_TRUE(VerifyCallback
.Called
);
271 TEST(Matcher
, matchOverEntireASTContext
) {
272 std::unique_ptr
<ASTUnit
> AST
=
273 clang::tooling::buildASTFromCode("struct { int *foo; };");
274 ASSERT_TRUE(AST
.get());
275 auto PT
= selectFirst
<PointerType
>(
276 "x", match(pointerType().bind("x"), AST
->getASTContext()));
277 EXPECT_NE(nullptr, PT
);
280 TEST(DynTypedMatcherTest
, TraversalKindForwardsToImpl
) {
281 auto M
= DynTypedMatcher(decl());
282 EXPECT_FALSE(M
.getTraversalKind());
284 M
= DynTypedMatcher(traverse(TK_AsIs
, decl()));
285 EXPECT_THAT(M
.getTraversalKind(), llvm::ValueIs(TK_AsIs
));
288 TEST(DynTypedMatcherTest
, ConstructWithTraversalKindSetsTK
) {
289 auto M
= DynTypedMatcher(decl()).withTraversalKind(TK_AsIs
);
290 EXPECT_THAT(M
.getTraversalKind(), llvm::ValueIs(TK_AsIs
));
293 TEST(DynTypedMatcherTest
, ConstructWithTraversalKindOverridesNestedTK
) {
294 auto M
= DynTypedMatcher(decl()).withTraversalKind(TK_AsIs
).withTraversalKind(
295 TK_IgnoreUnlessSpelledInSource
);
296 EXPECT_THAT(M
.getTraversalKind(),
297 llvm::ValueIs(TK_IgnoreUnlessSpelledInSource
));
300 TEST(IsInlineMatcher
, IsInline
) {
301 EXPECT_TRUE(matches("void g(); inline void f();",
302 functionDecl(isInline(), hasName("f"))));
303 EXPECT_TRUE(matches("namespace n { inline namespace m {} }",
304 namespaceDecl(isInline(), hasName("m"))));
305 EXPECT_TRUE(matches("inline int Foo = 5;",
306 varDecl(isInline(), hasName("Foo")), {Lang_CXX17
}));
309 // FIXME: Figure out how to specify paths so the following tests pass on
313 TEST(Matcher
, IsExpansionInMainFileMatcher
) {
314 EXPECT_TRUE(matches("class X {};",
315 recordDecl(hasName("X"), isExpansionInMainFile())));
316 EXPECT_TRUE(notMatches("", recordDecl(isExpansionInMainFile())));
317 FileContentMappings M
;
318 M
.push_back(std::make_pair("/other", "class X {};"));
319 EXPECT_TRUE(matchesConditionally("#include <other>\n",
320 recordDecl(isExpansionInMainFile()), false,
324 TEST(Matcher
, IsExpansionInSystemHeader
) {
325 FileContentMappings M
;
326 M
.push_back(std::make_pair("/other", "class X {};"));
327 EXPECT_TRUE(matchesConditionally("#include \"other\"\n",
328 recordDecl(isExpansionInSystemHeader()),
329 true, {"-isystem/"}, M
));
330 EXPECT_TRUE(matchesConditionally("#include \"other\"\n",
331 recordDecl(isExpansionInSystemHeader()),
333 EXPECT_TRUE(notMatches("class X {};",
334 recordDecl(isExpansionInSystemHeader())));
335 EXPECT_TRUE(notMatches("", recordDecl(isExpansionInSystemHeader())));
338 TEST(Matcher
, IsExpansionInFileMatching
) {
339 FileContentMappings M
;
340 M
.push_back(std::make_pair("/foo", "class A {};"));
341 M
.push_back(std::make_pair("/bar", "class B {};"));
342 EXPECT_TRUE(matchesConditionally(
346 recordDecl(isExpansionInFileMatching("b.*"), hasName("B")), true,
348 EXPECT_TRUE(matchesConditionally(
352 recordDecl(isExpansionInFileMatching("f.*"), hasName("X")), false,
358 } // end namespace ast_matchers
359 } // end namespace clang