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 protected:
66 virtual void run(const MatchFinder::MatchResult &Result);
verify(const MatchFinder::MatchResult & Result,const NodeType & Node)67 virtual void verify(const MatchFinder::MatchResult &Result,
68 const NodeType &Node) {}
69
setFailure(const Twine & Result)70 void setFailure(const Twine &Result) {
71 Verified = false;
72 VerifyResult = Result.str();
73 }
74
setSuccess()75 void setSuccess() {
76 Verified = true;
77 }
78
79 private:
80 bool Verified;
81 std::string VerifyResult;
82 };
83
84 /// \brief Runs a matcher over some code, and returns the result of the
85 /// verifier for the matched node.
86 template <typename NodeType> template <typename MatcherType>
match(const std::string & Code,const MatcherType & AMatcher,std::vector<std::string> & Args,Language L)87 testing::AssertionResult MatchVerifier<NodeType>::match(
88 const std::string &Code, const MatcherType &AMatcher,
89 std::vector<std::string>& Args, Language L) {
90 MatchFinder Finder;
91 Finder.addMatcher(AMatcher.bind(""), this);
92 std::unique_ptr<tooling::FrontendActionFactory> Factory(
93 tooling::newFrontendActionFactory(&Finder));
94
95 StringRef FileName;
96 switch (L) {
97 case Lang_C:
98 Args.push_back("-std=c99");
99 FileName = "input.c";
100 break;
101 case Lang_C89:
102 Args.push_back("-std=c89");
103 FileName = "input.c";
104 break;
105 case Lang_CXX:
106 Args.push_back("-std=c++98");
107 FileName = "input.cc";
108 break;
109 case Lang_CXX11:
110 Args.push_back("-std=c++11");
111 FileName = "input.cc";
112 break;
113 case Lang_OpenCL:
114 FileName = "input.cl";
115 break;
116 case Lang_OBJCXX:
117 FileName = "input.mm";
118 break;
119 }
120
121 // Default to failure in case callback is never called
122 setFailure("Could not find match");
123 if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName))
124 return testing::AssertionFailure() << "Parsing error";
125 if (!Verified)
126 return testing::AssertionFailure() << VerifyResult;
127 return testing::AssertionSuccess();
128 }
129
130 template <typename NodeType>
run(const MatchFinder::MatchResult & Result)131 void MatchVerifier<NodeType>::run(const MatchFinder::MatchResult &Result) {
132 const NodeType *Node = Result.Nodes.getNodeAs<NodeType>("");
133 if (!Node) {
134 setFailure("Matched node has wrong type");
135 } else {
136 // Callback has been called, default to success.
137 setSuccess();
138 verify(Result, *Node);
139 }
140 }
141
142 template <>
run(const MatchFinder::MatchResult & Result)143 inline void MatchVerifier<ast_type_traits::DynTypedNode>::run(
144 const MatchFinder::MatchResult &Result) {
145 BoundNodes::IDToNodeMap M = Result.Nodes.getMap();
146 BoundNodes::IDToNodeMap::const_iterator I = M.find("");
147 if (I == M.end()) {
148 setFailure("Node was not bound");
149 } else {
150 // Callback has been called, default to success.
151 setSuccess();
152 verify(Result, I->second);
153 }
154 }
155
156 /// \brief Verify whether a node has the correct source location.
157 ///
158 /// By default, Node.getSourceLocation() is checked. This can be changed
159 /// by overriding getLocation().
160 template <typename NodeType>
161 class LocationVerifier : public MatchVerifier<NodeType> {
162 public:
expectLocation(unsigned Line,unsigned Column)163 void expectLocation(unsigned Line, unsigned Column) {
164 ExpectLine = Line;
165 ExpectColumn = Column;
166 }
167
168 protected:
verify(const MatchFinder::MatchResult & Result,const NodeType & Node)169 void verify(const MatchFinder::MatchResult &Result, const NodeType &Node) {
170 SourceLocation Loc = getLocation(Node);
171 unsigned Line = Result.SourceManager->getSpellingLineNumber(Loc);
172 unsigned Column = Result.SourceManager->getSpellingColumnNumber(Loc);
173 if (Line != ExpectLine || Column != ExpectColumn) {
174 std::string MsgStr;
175 llvm::raw_string_ostream Msg(MsgStr);
176 Msg << "Expected location <" << ExpectLine << ":" << ExpectColumn
177 << ">, found <";
178 Loc.print(Msg, *Result.SourceManager);
179 Msg << '>';
180 this->setFailure(Msg.str());
181 }
182 }
183
getLocation(const NodeType & Node)184 virtual SourceLocation getLocation(const NodeType &Node) {
185 return Node.getLocation();
186 }
187
188 private:
189 unsigned ExpectLine, ExpectColumn;
190 };
191
192 /// \brief Verify whether a node has the correct source range.
193 ///
194 /// By default, Node.getSourceRange() is checked. This can be changed
195 /// by overriding getRange().
196 template <typename NodeType>
197 class RangeVerifier : public MatchVerifier<NodeType> {
198 public:
expectRange(unsigned BeginLine,unsigned BeginColumn,unsigned EndLine,unsigned EndColumn)199 void expectRange(unsigned BeginLine, unsigned BeginColumn,
200 unsigned EndLine, unsigned EndColumn) {
201 ExpectBeginLine = BeginLine;
202 ExpectBeginColumn = BeginColumn;
203 ExpectEndLine = EndLine;
204 ExpectEndColumn = EndColumn;
205 }
206
207 protected:
verify(const MatchFinder::MatchResult & Result,const NodeType & Node)208 void verify(const MatchFinder::MatchResult &Result, const NodeType &Node) {
209 SourceRange R = getRange(Node);
210 SourceLocation Begin = R.getBegin();
211 SourceLocation End = R.getEnd();
212 unsigned BeginLine = Result.SourceManager->getSpellingLineNumber(Begin);
213 unsigned BeginColumn = Result.SourceManager->getSpellingColumnNumber(Begin);
214 unsigned EndLine = Result.SourceManager->getSpellingLineNumber(End);
215 unsigned EndColumn = Result.SourceManager->getSpellingColumnNumber(End);
216 if (BeginLine != ExpectBeginLine || BeginColumn != ExpectBeginColumn ||
217 EndLine != ExpectEndLine || EndColumn != ExpectEndColumn) {
218 std::string MsgStr;
219 llvm::raw_string_ostream Msg(MsgStr);
220 Msg << "Expected range <" << ExpectBeginLine << ":" << ExpectBeginColumn
221 << '-' << ExpectEndLine << ":" << ExpectEndColumn << ">, found <";
222 Begin.print(Msg, *Result.SourceManager);
223 Msg << '-';
224 End.print(Msg, *Result.SourceManager);
225 Msg << '>';
226 this->setFailure(Msg.str());
227 }
228 }
229
getRange(const NodeType & Node)230 virtual SourceRange getRange(const NodeType &Node) {
231 return Node.getSourceRange();
232 }
233
234 private:
235 unsigned ExpectBeginLine, ExpectBeginColumn, ExpectEndLine, ExpectEndColumn;
236 };
237
238 /// \brief Verify whether a node's dump contains a given substring.
239 class DumpVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
240 public:
expectSubstring(const std::string & Str)241 void expectSubstring(const std::string &Str) {
242 ExpectSubstring = Str;
243 }
244
245 protected:
verify(const MatchFinder::MatchResult & Result,const ast_type_traits::DynTypedNode & Node)246 void verify(const MatchFinder::MatchResult &Result,
247 const ast_type_traits::DynTypedNode &Node) {
248 std::string DumpStr;
249 llvm::raw_string_ostream Dump(DumpStr);
250 Node.dump(Dump, *Result.SourceManager);
251
252 if (Dump.str().find(ExpectSubstring) == std::string::npos) {
253 std::string MsgStr;
254 llvm::raw_string_ostream Msg(MsgStr);
255 Msg << "Expected dump substring <" << ExpectSubstring << ">, found <"
256 << Dump.str() << '>';
257 this->setFailure(Msg.str());
258 }
259 }
260
261 private:
262 std::string ExpectSubstring;
263 };
264
265 /// \brief Verify whether a node's pretty print matches a given string.
266 class PrintVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
267 public:
expectString(const std::string & Str)268 void expectString(const std::string &Str) {
269 ExpectString = Str;
270 }
271
272 protected:
verify(const MatchFinder::MatchResult & Result,const ast_type_traits::DynTypedNode & Node)273 void verify(const MatchFinder::MatchResult &Result,
274 const ast_type_traits::DynTypedNode &Node) {
275 std::string PrintStr;
276 llvm::raw_string_ostream Print(PrintStr);
277 Node.print(Print, Result.Context->getPrintingPolicy());
278
279 if (Print.str() != ExpectString) {
280 std::string MsgStr;
281 llvm::raw_string_ostream Msg(MsgStr);
282 Msg << "Expected pretty print <" << ExpectString << ">, found <"
283 << Print.str() << '>';
284 this->setFailure(Msg.str());
285 }
286 }
287
288 private:
289 std::string ExpectString;
290 };
291
292 } // end namespace ast_matchers
293 } // end namespace clang
294
295 #endif
296