Clang Project

clang_source_code/unittests/ASTMatchers/ASTMatchersInternalTest.cpp
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
18namespace clang {
19namespace ast_matchers {
20
21#if GTEST_HAS_DEATH_TEST
22TEST(HasNameDeathTest, DiesOnEmptyName) {
23  ASSERT_DEBUG_DEATH({
24    DeclarationMatcher HasEmptyName = recordDecl(hasName(""));
25    EXPECT_TRUE(notMatches("class X {};", HasEmptyName));
26  }, "");
27}
28
29TEST(HasNameDeathTest, DiesOnEmptyPattern) {
30  ASSERT_DEBUG_DEATH({
31      DeclarationMatcher HasEmptyName = recordDecl(matchesName(""));
32      EXPECT_TRUE(notMatches("class X {};", HasEmptyName));
33    }, "");
34}
35
36TEST(IsDerivedFromDeathTest, DiesOnEmptyBaseName) {
37  ASSERT_DEBUG_DEATH({
38    DeclarationMatcher IsDerivedFromEmpty = cxxRecordDecl(isDerivedFrom(""));
39    EXPECT_TRUE(notMatches("class X {};", IsDerivedFromEmpty));
40  }, "");
41}
42#endif
43
44TEST(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().
55AST_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
61TEST(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
74AST_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
83TEST(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
102TEST(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<FrontendActionFactoryFactory(
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
121class VerifyStartOfTranslationUnit : public MatchFinder::MatchCallback {
122public:
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
131TEST(MatchFinder, InterceptsStartOfTranslationUnit) {
132  MatchFinder Finder;
133  VerifyStartOfTranslationUnit VerifyCallback;
134  Finder.addMatcher(decl(), &VerifyCallback);
135  std::unique_ptr<FrontendActionFactoryFactory(
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<ASTUnitAST(tooling::buildASTFromCode("int x;"));
142  ASSERT_TRUE(AST.get());
143  Finder.matchAST(AST->getASTContext());
144  EXPECT_TRUE(VerifyCallback.Called);
145}
146
147class VerifyEndOfTranslationUnit : public MatchFinder::MatchCallback {
148public:
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
157TEST(MatchFinder, InterceptsEndOfTranslationUnit) {
158  MatchFinder Finder;
159  VerifyEndOfTranslationUnit VerifyCallback;
160  Finder.addMatcher(decl(), &VerifyCallback);
161  std::unique_ptr<FrontendActionFactoryFactory(
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<ASTUnitAST(tooling::buildASTFromCode("int x;"));
168  ASSERT_TRUE(AST.get());
169  Finder.matchAST(AST->getASTContext());
170  EXPECT_TRUE(VerifyCallback.Called);
171}
172
173TEST(Matcher, matchOverEntireASTContext) {
174  std::unique_ptr<ASTUnitAST =
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
182TEST(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
193TEST(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
204TEST(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
218TEST(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
clang::ast_matchers::VerifyStartOfTranslationUnit::run
clang::ast_matchers::VerifyStartOfTranslationUnit::onStartOfTranslationUnit
clang::ast_matchers::VerifyEndOfTranslationUnit::run
clang::ast_matchers::VerifyEndOfTranslationUnit::onEndOfTranslationUnit