blob: 1e5a0d256ebcc8009cb63ffe87d1d93377d912b0 [file] [log] [blame]
Manuel Klimekaa58b972013-01-31 13:10:40 +00001//===- 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#include "clang/AST/ASTContext.h"
20#include "clang/ASTMatchers/ASTMatchFinder.h"
21#include "clang/ASTMatchers/ASTMatchers.h"
22#include "clang/Tooling/Tooling.h"
23#include "gtest/gtest.h"
24
25namespace clang {
26namespace ast_matchers {
27
Fariborz Jahaniana1db7df2014-07-31 17:39:50 +000028enum Language {
29 Lang_C,
30 Lang_C89,
31 Lang_CXX,
32 Lang_CXX11,
33 Lang_OpenCL,
34 Lang_OBJCXX
35};
Manuel Klimekaa58b972013-01-31 13:10:40 +000036
37/// \brief Base class for verifying some property of nodes found by a matcher.
38template <typename NodeType>
39class MatchVerifier : public MatchFinder::MatchCallback {
40public:
41 template <typename MatcherType>
42 testing::AssertionResult match(const std::string &Code,
43 const MatcherType &AMatcher) {
Enea Zaffanella4c409492013-07-08 14:50:30 +000044 std::vector<std::string> Args;
45 return match(Code, AMatcher, Args, Lang_CXX);
Manuel Klimekaa58b972013-01-31 13:10:40 +000046 }
47
48 template <typename MatcherType>
49 testing::AssertionResult match(const std::string &Code,
Enea Zaffanella4c409492013-07-08 14:50:30 +000050 const MatcherType &AMatcher,
51 Language L) {
52 std::vector<std::string> Args;
53 return match(Code, AMatcher, Args, L);
54 }
55
56 template <typename MatcherType>
57 testing::AssertionResult match(const std::string &Code,
58 const MatcherType &AMatcher,
59 std::vector<std::string>& Args,
60 Language L);
Manuel Klimekaa58b972013-01-31 13:10:40 +000061
62protected:
63 virtual void run(const MatchFinder::MatchResult &Result);
64 virtual void verify(const MatchFinder::MatchResult &Result,
Manuel Klimekd4be4082013-02-28 13:21:39 +000065 const NodeType &Node) {}
Manuel Klimekaa58b972013-01-31 13:10:40 +000066
67 void setFailure(const Twine &Result) {
68 Verified = false;
69 VerifyResult = Result.str();
70 }
71
72 void setSuccess() {
73 Verified = true;
74 }
75
76private:
77 bool Verified;
78 std::string VerifyResult;
79};
80
81/// \brief Runs a matcher over some code, and returns the result of the
82/// verifier for the matched node.
83template <typename NodeType> template <typename MatcherType>
84testing::AssertionResult MatchVerifier<NodeType>::match(
Enea Zaffanella4c409492013-07-08 14:50:30 +000085 const std::string &Code, const MatcherType &AMatcher,
86 std::vector<std::string>& Args, Language L) {
Manuel Klimekaa58b972013-01-31 13:10:40 +000087 MatchFinder Finder;
88 Finder.addMatcher(AMatcher.bind(""), this);
Ahmed Charlesb8984322014-03-07 20:03:18 +000089 std::unique_ptr<tooling::FrontendActionFactory> Factory(
Manuel Klimekaa58b972013-01-31 13:10:40 +000090 tooling::newFrontendActionFactory(&Finder));
91
Manuel Klimekaa58b972013-01-31 13:10:40 +000092 StringRef FileName;
93 switch (L) {
94 case Lang_C:
95 Args.push_back("-std=c99");
96 FileName = "input.c";
97 break;
98 case Lang_C89:
99 Args.push_back("-std=c89");
100 FileName = "input.c";
101 break;
102 case Lang_CXX:
103 Args.push_back("-std=c++98");
104 FileName = "input.cc";
105 break;
Enea Zaffanella8421a062013-07-07 06:41:54 +0000106 case Lang_CXX11:
107 Args.push_back("-std=c++11");
108 FileName = "input.cc";
109 break;
Richard Smith9ca91012013-02-05 05:55:57 +0000110 case Lang_OpenCL:
111 FileName = "input.cl";
Fariborz Jahaniana1db7df2014-07-31 17:39:50 +0000112 break;
113 case Lang_OBJCXX:
114 FileName = "input.mm";
115 break;
Manuel Klimekaa58b972013-01-31 13:10:40 +0000116 }
117
118 // Default to failure in case callback is never called
119 setFailure("Could not find match");
120 if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName))
121 return testing::AssertionFailure() << "Parsing error";
122 if (!Verified)
123 return testing::AssertionFailure() << VerifyResult;
124 return testing::AssertionSuccess();
125}
126
127template <typename NodeType>
128void MatchVerifier<NodeType>::run(const MatchFinder::MatchResult &Result) {
129 const NodeType *Node = Result.Nodes.getNodeAs<NodeType>("");
130 if (!Node) {
131 setFailure("Matched node has wrong type");
132 } else {
133 // Callback has been called, default to success.
134 setSuccess();
135 verify(Result, *Node);
136 }
137}
138
Peter Collingbourneb23b39d2013-11-06 00:27:12 +0000139template <>
140inline void MatchVerifier<ast_type_traits::DynTypedNode>::run(
141 const MatchFinder::MatchResult &Result) {
142 BoundNodes::IDToNodeMap M = Result.Nodes.getMap();
143 BoundNodes::IDToNodeMap::const_iterator I = M.find("");
144 if (I == M.end()) {
145 setFailure("Node was not bound");
146 } else {
147 // Callback has been called, default to success.
148 setSuccess();
149 verify(Result, I->second);
150 }
151}
152
Manuel Klimekaa58b972013-01-31 13:10:40 +0000153/// \brief Verify whether a node has the correct source location.
154///
155/// By default, Node.getSourceLocation() is checked. This can be changed
156/// by overriding getLocation().
157template <typename NodeType>
158class LocationVerifier : public MatchVerifier<NodeType> {
159public:
160 void expectLocation(unsigned Line, unsigned Column) {
161 ExpectLine = Line;
162 ExpectColumn = Column;
163 }
164
165protected:
166 void verify(const MatchFinder::MatchResult &Result, const NodeType &Node) {
167 SourceLocation Loc = getLocation(Node);
168 unsigned Line = Result.SourceManager->getSpellingLineNumber(Loc);
169 unsigned Column = Result.SourceManager->getSpellingColumnNumber(Loc);
170 if (Line != ExpectLine || Column != ExpectColumn) {
171 std::string MsgStr;
172 llvm::raw_string_ostream Msg(MsgStr);
173 Msg << "Expected location <" << ExpectLine << ":" << ExpectColumn
174 << ">, found <";
175 Loc.print(Msg, *Result.SourceManager);
176 Msg << '>';
177 this->setFailure(Msg.str());
178 }
179 }
180
181 virtual SourceLocation getLocation(const NodeType &Node) {
182 return Node.getLocation();
183 }
184
185private:
186 unsigned ExpectLine, ExpectColumn;
187};
188
189/// \brief Verify whether a node has the correct source range.
190///
191/// By default, Node.getSourceRange() is checked. This can be changed
192/// by overriding getRange().
193template <typename NodeType>
194class RangeVerifier : public MatchVerifier<NodeType> {
195public:
196 void expectRange(unsigned BeginLine, unsigned BeginColumn,
197 unsigned EndLine, unsigned EndColumn) {
198 ExpectBeginLine = BeginLine;
199 ExpectBeginColumn = BeginColumn;
200 ExpectEndLine = EndLine;
201 ExpectEndColumn = EndColumn;
202 }
203
204protected:
205 void verify(const MatchFinder::MatchResult &Result, const NodeType &Node) {
206 SourceRange R = getRange(Node);
207 SourceLocation Begin = R.getBegin();
208 SourceLocation End = R.getEnd();
209 unsigned BeginLine = Result.SourceManager->getSpellingLineNumber(Begin);
210 unsigned BeginColumn = Result.SourceManager->getSpellingColumnNumber(Begin);
211 unsigned EndLine = Result.SourceManager->getSpellingLineNumber(End);
212 unsigned EndColumn = Result.SourceManager->getSpellingColumnNumber(End);
213 if (BeginLine != ExpectBeginLine || BeginColumn != ExpectBeginColumn ||
214 EndLine != ExpectEndLine || EndColumn != ExpectEndColumn) {
215 std::string MsgStr;
216 llvm::raw_string_ostream Msg(MsgStr);
217 Msg << "Expected range <" << ExpectBeginLine << ":" << ExpectBeginColumn
218 << '-' << ExpectEndLine << ":" << ExpectEndColumn << ">, found <";
219 Begin.print(Msg, *Result.SourceManager);
220 Msg << '-';
221 End.print(Msg, *Result.SourceManager);
222 Msg << '>';
223 this->setFailure(Msg.str());
224 }
225 }
226
227 virtual SourceRange getRange(const NodeType &Node) {
228 return Node.getSourceRange();
229 }
230
231private:
232 unsigned ExpectBeginLine, ExpectBeginColumn, ExpectEndLine, ExpectEndColumn;
233};
234
Peter Collingbourneb23b39d2013-11-06 00:27:12 +0000235/// \brief Verify whether a node's dump contains a given substring.
236class DumpVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
237public:
238 void expectSubstring(const std::string &Str) {
239 ExpectSubstring = Str;
240 }
241
242protected:
243 void verify(const MatchFinder::MatchResult &Result,
244 const ast_type_traits::DynTypedNode &Node) {
245 std::string DumpStr;
246 llvm::raw_string_ostream Dump(DumpStr);
247 Node.dump(Dump, *Result.SourceManager);
248
249 if (Dump.str().find(ExpectSubstring) == std::string::npos) {
250 std::string MsgStr;
251 llvm::raw_string_ostream Msg(MsgStr);
252 Msg << "Expected dump substring <" << ExpectSubstring << ">, found <"
253 << Dump.str() << '>';
254 this->setFailure(Msg.str());
255 }
256 }
257
258private:
259 std::string ExpectSubstring;
260};
261
262/// \brief Verify whether a node's pretty print matches a given string.
263class PrintVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
264public:
265 void expectString(const std::string &Str) {
266 ExpectString = Str;
267 }
268
269protected:
270 void verify(const MatchFinder::MatchResult &Result,
271 const ast_type_traits::DynTypedNode &Node) {
272 std::string PrintStr;
273 llvm::raw_string_ostream Print(PrintStr);
274 Node.print(Print, Result.Context->getPrintingPolicy());
275
276 if (Print.str() != ExpectString) {
277 std::string MsgStr;
278 llvm::raw_string_ostream Msg(MsgStr);
279 Msg << "Expected pretty print <" << ExpectString << ">, found <"
280 << Print.str() << '>';
281 this->setFailure(Msg.str());
282 }
283 }
284
285private:
286 std::string ExpectString;
287};
288
Manuel Klimekaa58b972013-01-31 13:10:40 +0000289} // end namespace ast_matchers
290} // end namespace clang