1 | |
2 | |
3 | |
4 | |
5 | |
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 |
22 | TEST(HasNameDeathTest, DiesOnEmptyName) { |
23 | ASSERT_DEBUG_DEATH({ |
24 | DeclarationMatcher HasEmptyName = recordDecl(hasName("")); |
25 | EXPECT_TRUE(notMatches("class X {};", HasEmptyName)); |
26 | }, ""); |
27 | } |
28 | |
29 | TEST(HasNameDeathTest, DiesOnEmptyPattern) { |
30 | ASSERT_DEBUG_DEATH({ |
31 | DeclarationMatcher HasEmptyName = recordDecl(matchesName("")); |
32 | EXPECT_TRUE(notMatches("class X {};", HasEmptyName)); |
33 | }, ""); |
34 | } |
35 | |
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 | |
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 | |
55 | AST_MATCHER_P(Decl, just, internal::Matcher<Decl>, AMatcher) { |
56 | |
57 | |
58 | return AMatcher.matches(Node, Finder, Builder); |
59 | } |
60 | |
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 | |
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 | ASTMatchFinder::TK_IgnoreImplicitCastsAndParentheses, |
80 | ASTMatchFinder::BK_First); |
81 | } |
82 | |
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 | |
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: |
123 | VerifyStartOfTranslationUnit() : Called(false) {} |
124 | void run(const MatchFinder::MatchResult &Result) override { |
125 | EXPECT_TRUE(Called); |
126 | } |
127 | void onStartOfTranslationUnit() override { Called = true; } |
128 | bool Called; |
129 | }; |
130 | |
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: |
149 | VerifyEndOfTranslationUnit() : Called(false) {} |
150 | void run(const MatchFinder::MatchResult &Result) override { |
151 | EXPECT_FALSE(Called); |
152 | } |
153 | void onEndOfTranslationUnit() override { Called = true; } |
154 | bool Called; |
155 | }; |
156 | |
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 | |
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 | |
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 | |
190 | |
191 | #ifndef _WIN32 |
192 | |
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 | |
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 | |
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 |
237 | |
238 | } |
239 | } |
240 | |