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