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