• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===- unittest/AST/MatchVerifier.h - AST unit test support ---------------===//
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 //  Provides MatchVerifier, a base class to implement gtest matchers that
11 //  verify things that can be matched on the AST.
12 //
13 //  Also implements matchers based on MatchVerifier:
14 //  LocationVerifier and RangeVerifier to verify whether a matched node has
15 //  the expected source location or source range.
16 //
17 //===----------------------------------------------------------------------===//
18 
19 #ifndef LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H
20 #define LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H
21 
22 #include "clang/AST/ASTContext.h"
23 #include "clang/ASTMatchers/ASTMatchFinder.h"
24 #include "clang/ASTMatchers/ASTMatchers.h"
25 #include "clang/Tooling/Tooling.h"
26 #include "gtest/gtest.h"
27 
28 namespace clang {
29 namespace ast_matchers {
30 
31 enum Language {
32     Lang_C,
33     Lang_C89,
34     Lang_CXX,
35     Lang_CXX11,
36     Lang_OpenCL,
37     Lang_OBJCXX
38 };
39 
40 /// \brief Base class for verifying some property of nodes found by a matcher.
41 template <typename NodeType>
42 class MatchVerifier : public MatchFinder::MatchCallback {
43 public:
44   template <typename MatcherType>
match(const std::string & Code,const MatcherType & AMatcher)45   testing::AssertionResult match(const std::string &Code,
46                                  const MatcherType &AMatcher) {
47     std::vector<std::string> Args;
48     return match(Code, AMatcher, Args, Lang_CXX);
49   }
50 
51   template <typename MatcherType>
match(const std::string & Code,const MatcherType & AMatcher,Language L)52   testing::AssertionResult match(const std::string &Code,
53                                  const MatcherType &AMatcher,
54                                  Language L) {
55     std::vector<std::string> Args;
56     return match(Code, AMatcher, Args, L);
57   }
58 
59   template <typename MatcherType>
60   testing::AssertionResult match(const std::string &Code,
61                                  const MatcherType &AMatcher,
62                                  std::vector<std::string>& Args,
63                                  Language L);
64 
65   template <typename MatcherType>
66   testing::AssertionResult match(const Decl *D, const MatcherType &AMatcher);
67 
68 protected:
69   void run(const MatchFinder::MatchResult &Result) override;
verify(const MatchFinder::MatchResult & Result,const NodeType & Node)70   virtual void verify(const MatchFinder::MatchResult &Result,
71                       const NodeType &Node) {}
72 
setFailure(const Twine & Result)73   void setFailure(const Twine &Result) {
74     Verified = false;
75     VerifyResult = Result.str();
76   }
77 
setSuccess()78   void setSuccess() {
79     Verified = true;
80   }
81 
82 private:
83   bool Verified;
84   std::string VerifyResult;
85 };
86 
87 /// \brief Runs a matcher over some code, and returns the result of the
88 /// verifier for the matched node.
89 template <typename NodeType> template <typename MatcherType>
match(const std::string & Code,const MatcherType & AMatcher,std::vector<std::string> & Args,Language L)90 testing::AssertionResult MatchVerifier<NodeType>::match(
91     const std::string &Code, const MatcherType &AMatcher,
92     std::vector<std::string>& Args, Language L) {
93   MatchFinder Finder;
94   Finder.addMatcher(AMatcher.bind(""), this);
95   std::unique_ptr<tooling::FrontendActionFactory> Factory(
96       tooling::newFrontendActionFactory(&Finder));
97 
98   StringRef FileName;
99   switch (L) {
100   case Lang_C:
101     Args.push_back("-std=c99");
102     FileName = "input.c";
103     break;
104   case Lang_C89:
105     Args.push_back("-std=c89");
106     FileName = "input.c";
107     break;
108   case Lang_CXX:
109     Args.push_back("-std=c++98");
110     FileName = "input.cc";
111     break;
112   case Lang_CXX11:
113     Args.push_back("-std=c++11");
114     FileName = "input.cc";
115     break;
116   case Lang_OpenCL:
117     FileName = "input.cl";
118     break;
119   case Lang_OBJCXX:
120     FileName = "input.mm";
121     break;
122   }
123 
124   // Default to failure in case callback is never called
125   setFailure("Could not find match");
126   if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName))
127     return testing::AssertionFailure() << "Parsing error";
128   if (!Verified)
129     return testing::AssertionFailure() << VerifyResult;
130   return testing::AssertionSuccess();
131 }
132 
133 /// \brief Runs a matcher over some AST, and returns the result of the
134 /// verifier for the matched node.
135 template <typename NodeType> template <typename MatcherType>
match(const Decl * D,const MatcherType & AMatcher)136 testing::AssertionResult MatchVerifier<NodeType>::match(
137     const Decl *D, const MatcherType &AMatcher) {
138   MatchFinder Finder;
139   Finder.addMatcher(AMatcher.bind(""), this);
140 
141   setFailure("Could not find match");
142   Finder.match(*D, D->getASTContext());
143 
144   if (!Verified)
145     return testing::AssertionFailure() << VerifyResult;
146   return testing::AssertionSuccess();
147 }
148 
149 template <typename NodeType>
run(const MatchFinder::MatchResult & Result)150 void MatchVerifier<NodeType>::run(const MatchFinder::MatchResult &Result) {
151   const NodeType *Node = Result.Nodes.getNodeAs<NodeType>("");
152   if (!Node) {
153     setFailure("Matched node has wrong type");
154   } else {
155     // Callback has been called, default to success.
156     setSuccess();
157     verify(Result, *Node);
158   }
159 }
160 
161 template <>
run(const MatchFinder::MatchResult & Result)162 inline void MatchVerifier<ast_type_traits::DynTypedNode>::run(
163     const MatchFinder::MatchResult &Result) {
164   BoundNodes::IDToNodeMap M = Result.Nodes.getMap();
165   BoundNodes::IDToNodeMap::const_iterator I = M.find("");
166   if (I == M.end()) {
167     setFailure("Node was not bound");
168   } else {
169     // Callback has been called, default to success.
170     setSuccess();
171     verify(Result, I->second);
172   }
173 }
174 
175 /// \brief Verify whether a node has the correct source location.
176 ///
177 /// By default, Node.getSourceLocation() is checked. This can be changed
178 /// by overriding getLocation().
179 template <typename NodeType>
180 class LocationVerifier : public MatchVerifier<NodeType> {
181 public:
expectLocation(unsigned Line,unsigned Column)182   void expectLocation(unsigned Line, unsigned Column) {
183     ExpectLine = Line;
184     ExpectColumn = Column;
185   }
186 
187 protected:
verify(const MatchFinder::MatchResult & Result,const NodeType & Node)188   void verify(const MatchFinder::MatchResult &Result,
189               const NodeType &Node) override {
190     SourceLocation Loc = getLocation(Node);
191     unsigned Line = Result.SourceManager->getSpellingLineNumber(Loc);
192     unsigned Column = Result.SourceManager->getSpellingColumnNumber(Loc);
193     if (Line != ExpectLine || Column != ExpectColumn) {
194       std::string MsgStr;
195       llvm::raw_string_ostream Msg(MsgStr);
196       Msg << "Expected location <" << ExpectLine << ":" << ExpectColumn
197           << ">, found <";
198       Loc.print(Msg, *Result.SourceManager);
199       Msg << '>';
200       this->setFailure(Msg.str());
201     }
202   }
203 
getLocation(const NodeType & Node)204   virtual SourceLocation getLocation(const NodeType &Node) {
205     return Node.getLocation();
206   }
207 
208 private:
209   unsigned ExpectLine, ExpectColumn;
210 };
211 
212 /// \brief Verify whether a node has the correct source range.
213 ///
214 /// By default, Node.getSourceRange() is checked. This can be changed
215 /// by overriding getRange().
216 template <typename NodeType>
217 class RangeVerifier : public MatchVerifier<NodeType> {
218 public:
expectRange(unsigned BeginLine,unsigned BeginColumn,unsigned EndLine,unsigned EndColumn)219   void expectRange(unsigned BeginLine, unsigned BeginColumn,
220                    unsigned EndLine, unsigned EndColumn) {
221     ExpectBeginLine = BeginLine;
222     ExpectBeginColumn = BeginColumn;
223     ExpectEndLine = EndLine;
224     ExpectEndColumn = EndColumn;
225   }
226 
227 protected:
verify(const MatchFinder::MatchResult & Result,const NodeType & Node)228   void verify(const MatchFinder::MatchResult &Result,
229               const NodeType &Node) override {
230     SourceRange R = getRange(Node);
231     SourceLocation Begin = R.getBegin();
232     SourceLocation End = R.getEnd();
233     unsigned BeginLine = Result.SourceManager->getSpellingLineNumber(Begin);
234     unsigned BeginColumn = Result.SourceManager->getSpellingColumnNumber(Begin);
235     unsigned EndLine = Result.SourceManager->getSpellingLineNumber(End);
236     unsigned EndColumn = Result.SourceManager->getSpellingColumnNumber(End);
237     if (BeginLine != ExpectBeginLine || BeginColumn != ExpectBeginColumn ||
238         EndLine != ExpectEndLine || EndColumn != ExpectEndColumn) {
239       std::string MsgStr;
240       llvm::raw_string_ostream Msg(MsgStr);
241       Msg << "Expected range <" << ExpectBeginLine << ":" << ExpectBeginColumn
242           << '-' << ExpectEndLine << ":" << ExpectEndColumn << ">, found <";
243       Begin.print(Msg, *Result.SourceManager);
244       Msg << '-';
245       End.print(Msg, *Result.SourceManager);
246       Msg << '>';
247       this->setFailure(Msg.str());
248     }
249   }
250 
getRange(const NodeType & Node)251   virtual SourceRange getRange(const NodeType &Node) {
252     return Node.getSourceRange();
253   }
254 
255 private:
256   unsigned ExpectBeginLine, ExpectBeginColumn, ExpectEndLine, ExpectEndColumn;
257 };
258 
259 /// \brief Verify whether a node's dump contains a given substring.
260 class DumpVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
261 public:
expectSubstring(const std::string & Str)262   void expectSubstring(const std::string &Str) {
263     ExpectSubstring = Str;
264   }
265 
266 protected:
verify(const MatchFinder::MatchResult & Result,const ast_type_traits::DynTypedNode & Node)267   void verify(const MatchFinder::MatchResult &Result,
268               const ast_type_traits::DynTypedNode &Node) override {
269     std::string DumpStr;
270     llvm::raw_string_ostream Dump(DumpStr);
271     Node.dump(Dump, *Result.SourceManager);
272 
273     if (Dump.str().find(ExpectSubstring) == std::string::npos) {
274       std::string MsgStr;
275       llvm::raw_string_ostream Msg(MsgStr);
276       Msg << "Expected dump substring <" << ExpectSubstring << ">, found <"
277           << Dump.str() << '>';
278       this->setFailure(Msg.str());
279     }
280   }
281 
282 private:
283   std::string ExpectSubstring;
284 };
285 
286 /// \brief Verify whether a node's pretty print matches a given string.
287 class PrintVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
288 public:
expectString(const std::string & Str)289   void expectString(const std::string &Str) {
290     ExpectString = Str;
291   }
292 
293 protected:
verify(const MatchFinder::MatchResult & Result,const ast_type_traits::DynTypedNode & Node)294   void verify(const MatchFinder::MatchResult &Result,
295               const ast_type_traits::DynTypedNode &Node) override {
296     std::string PrintStr;
297     llvm::raw_string_ostream Print(PrintStr);
298     Node.print(Print, Result.Context->getPrintingPolicy());
299 
300     if (Print.str() != ExpectString) {
301       std::string MsgStr;
302       llvm::raw_string_ostream Msg(MsgStr);
303       Msg << "Expected pretty print <" << ExpectString << ">, found <"
304           << Print.str() << '>';
305       this->setFailure(Msg.str());
306     }
307   }
308 
309 private:
310   std::string ExpectString;
311 };
312 
313 } // end namespace ast_matchers
314 } // end namespace clang
315 
316 #endif
317