• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===- unittest/Tooling/ASTMatchersTest.h - Matcher tests helpers ------===//
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 #ifndef LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H
11 #define LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H
12 
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/Frontend/ASTUnit.h"
15 #include "clang/Tooling/Tooling.h"
16 #include "gtest/gtest.h"
17 
18 namespace clang {
19 namespace ast_matchers {
20 
21 using clang::tooling::buildASTFromCodeWithArgs;
22 using clang::tooling::newFrontendActionFactory;
23 using clang::tooling::runToolOnCodeWithArgs;
24 using clang::tooling::FrontendActionFactory;
25 
26 class BoundNodesCallback {
27 public:
~BoundNodesCallback()28   virtual ~BoundNodesCallback() {}
29   virtual bool run(const BoundNodes *BoundNodes) = 0;
30   virtual bool run(const BoundNodes *BoundNodes, ASTContext *Context) = 0;
onEndOfTranslationUnit()31   virtual void onEndOfTranslationUnit() {}
32 };
33 
34 // If 'FindResultVerifier' is not NULL, sets *Verified to the result of
35 // running 'FindResultVerifier' with the bound nodes as argument.
36 // If 'FindResultVerifier' is NULL, sets *Verified to true when Run is called.
37 class VerifyMatch : public MatchFinder::MatchCallback {
38 public:
VerifyMatch(BoundNodesCallback * FindResultVerifier,bool * Verified)39   VerifyMatch(BoundNodesCallback *FindResultVerifier, bool *Verified)
40       : Verified(Verified), FindResultReviewer(FindResultVerifier) {}
41 
run(const MatchFinder::MatchResult & Result)42   virtual void run(const MatchFinder::MatchResult &Result) {
43     if (FindResultReviewer != nullptr) {
44       *Verified |= FindResultReviewer->run(&Result.Nodes, Result.Context);
45     } else {
46       *Verified = true;
47     }
48   }
49 
onEndOfTranslationUnit()50   void onEndOfTranslationUnit() override {
51     if (FindResultReviewer)
52       FindResultReviewer->onEndOfTranslationUnit();
53   }
54 
55 private:
56   bool *const Verified;
57   BoundNodesCallback *const FindResultReviewer;
58 };
59 
60 template <typename T>
matchesConditionally(const std::string & Code,const T & AMatcher,bool ExpectMatch,llvm::StringRef CompileArg)61 testing::AssertionResult matchesConditionally(const std::string &Code,
62                                               const T &AMatcher,
63                                               bool ExpectMatch,
64                                               llvm::StringRef CompileArg) {
65   bool Found = false, DynamicFound = false;
66   MatchFinder Finder;
67   VerifyMatch VerifyFound(nullptr, &Found);
68   Finder.addMatcher(AMatcher, &VerifyFound);
69   VerifyMatch VerifyDynamicFound(nullptr, &DynamicFound);
70   if (!Finder.addDynamicMatcher(AMatcher, &VerifyDynamicFound))
71     return testing::AssertionFailure() << "Could not add dynamic matcher";
72   std::unique_ptr<FrontendActionFactory> Factory(
73       newFrontendActionFactory(&Finder));
74   // Some tests use typeof, which is a gnu extension.
75   std::vector<std::string> Args(1, CompileArg);
76   if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) {
77     return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
78   }
79   if (Found != DynamicFound) {
80     return testing::AssertionFailure() << "Dynamic match result ("
81                                        << DynamicFound
82                                        << ") does not match static result ("
83                                        << Found << ")";
84   }
85   if (!Found && ExpectMatch) {
86     return testing::AssertionFailure()
87       << "Could not find match in \"" << Code << "\"";
88   } else if (Found && !ExpectMatch) {
89     return testing::AssertionFailure()
90       << "Found unexpected match in \"" << Code << "\"";
91   }
92   return testing::AssertionSuccess();
93 }
94 
95 template <typename T>
matches(const std::string & Code,const T & AMatcher)96 testing::AssertionResult matches(const std::string &Code, const T &AMatcher) {
97   return matchesConditionally(Code, AMatcher, true, "-std=c++11");
98 }
99 
100 template <typename T>
notMatches(const std::string & Code,const T & AMatcher)101 testing::AssertionResult notMatches(const std::string &Code,
102                                     const T &AMatcher) {
103   return matchesConditionally(Code, AMatcher, false, "-std=c++11");
104 }
105 
106 template <typename T>
107 testing::AssertionResult
matchAndVerifyResultConditionally(const std::string & Code,const T & AMatcher,BoundNodesCallback * FindResultVerifier,bool ExpectResult)108 matchAndVerifyResultConditionally(const std::string &Code, const T &AMatcher,
109                                   BoundNodesCallback *FindResultVerifier,
110                                   bool ExpectResult) {
111   std::unique_ptr<BoundNodesCallback> ScopedVerifier(FindResultVerifier);
112   bool VerifiedResult = false;
113   MatchFinder Finder;
114   VerifyMatch VerifyVerifiedResult(FindResultVerifier, &VerifiedResult);
115   Finder.addMatcher(AMatcher, &VerifyVerifiedResult);
116   std::unique_ptr<FrontendActionFactory> Factory(
117       newFrontendActionFactory(&Finder));
118   // Some tests use typeof, which is a gnu extension.
119   std::vector<std::string> Args(1, "-std=gnu++98");
120   if (!runToolOnCodeWithArgs(Factory->create(), Code, Args)) {
121     return testing::AssertionFailure() << "Parsing error in \"" << Code << "\"";
122   }
123   if (!VerifiedResult && ExpectResult) {
124     return testing::AssertionFailure()
125       << "Could not verify result in \"" << Code << "\"";
126   } else if (VerifiedResult && !ExpectResult) {
127     return testing::AssertionFailure()
128       << "Verified unexpected result in \"" << Code << "\"";
129   }
130 
131   VerifiedResult = false;
132   std::unique_ptr<ASTUnit> AST(buildASTFromCodeWithArgs(Code, Args));
133   if (!AST.get())
134     return testing::AssertionFailure() << "Parsing error in \"" << Code
135                                        << "\" while building AST";
136   Finder.matchAST(AST->getASTContext());
137   if (!VerifiedResult && ExpectResult) {
138     return testing::AssertionFailure()
139       << "Could not verify result in \"" << Code << "\" with AST";
140   } else if (VerifiedResult && !ExpectResult) {
141     return testing::AssertionFailure()
142       << "Verified unexpected result in \"" << Code << "\" with AST";
143   }
144 
145   return testing::AssertionSuccess();
146 }
147 
148 // FIXME: Find better names for these functions (or document what they
149 // do more precisely).
150 template <typename T>
151 testing::AssertionResult
matchAndVerifyResultTrue(const std::string & Code,const T & AMatcher,BoundNodesCallback * FindResultVerifier)152 matchAndVerifyResultTrue(const std::string &Code, const T &AMatcher,
153                          BoundNodesCallback *FindResultVerifier) {
154   return matchAndVerifyResultConditionally(
155       Code, AMatcher, FindResultVerifier, true);
156 }
157 
158 template <typename T>
159 testing::AssertionResult
matchAndVerifyResultFalse(const std::string & Code,const T & AMatcher,BoundNodesCallback * FindResultVerifier)160 matchAndVerifyResultFalse(const std::string &Code, const T &AMatcher,
161                           BoundNodesCallback *FindResultVerifier) {
162   return matchAndVerifyResultConditionally(
163       Code, AMatcher, FindResultVerifier, false);
164 }
165 
166 } // end namespace ast_matchers
167 } // end namespace clang
168 
169 #endif  // LLVM_CLANG_UNITTESTS_AST_MATCHERS_AST_MATCHERS_TEST_H
170