blob: 3e94d539f76a5c49e90b4aa5991c423899f48f89 [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
Benjamin Kramer12152ab2014-08-08 13:24:19 +000019#ifndef LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H
20#define LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H
21
Manuel Klimekaa58b972013-01-31 13:10:40 +000022#include "clang/AST/ASTContext.h"
23#include "clang/ASTMatchers/ASTMatchFinder.h"
24#include "clang/ASTMatchers/ASTMatchers.h"
25#include "clang/Tooling/Tooling.h"
Gabor Marton1f667532018-05-24 08:41:07 +000026#include "Language.h"
Manuel Klimekaa58b972013-01-31 13:10:40 +000027#include "gtest/gtest.h"
28
29namespace clang {
30namespace ast_matchers {
31
Manuel Klimekaa58b972013-01-31 13:10:40 +000032/// \brief Base class for verifying some property of nodes found by a matcher.
33template <typename NodeType>
34class MatchVerifier : public MatchFinder::MatchCallback {
35public:
36 template <typename MatcherType>
37 testing::AssertionResult match(const std::string &Code,
38 const MatcherType &AMatcher) {
Enea Zaffanella4c409492013-07-08 14:50:30 +000039 std::vector<std::string> Args;
40 return match(Code, AMatcher, Args, Lang_CXX);
Manuel Klimekaa58b972013-01-31 13:10:40 +000041 }
42
43 template <typename MatcherType>
44 testing::AssertionResult match(const std::string &Code,
Enea Zaffanella4c409492013-07-08 14:50:30 +000045 const MatcherType &AMatcher,
46 Language L) {
47 std::vector<std::string> Args;
48 return match(Code, AMatcher, Args, L);
49 }
50
51 template <typename MatcherType>
52 testing::AssertionResult match(const std::string &Code,
53 const MatcherType &AMatcher,
54 std::vector<std::string>& Args,
55 Language L);
Manuel Klimekaa58b972013-01-31 13:10:40 +000056
Aaron Ballmana35b8fc2016-03-09 17:11:51 +000057 template <typename MatcherType>
58 testing::AssertionResult match(const Decl *D, const MatcherType &AMatcher);
59
Manuel Klimekaa58b972013-01-31 13:10:40 +000060protected:
Alexander Kornienko34eb2072015-04-11 02:00:23 +000061 void run(const MatchFinder::MatchResult &Result) override;
Manuel Klimekaa58b972013-01-31 13:10:40 +000062 virtual void verify(const MatchFinder::MatchResult &Result,
Manuel Klimekd4be4082013-02-28 13:21:39 +000063 const NodeType &Node) {}
Manuel Klimekaa58b972013-01-31 13:10:40 +000064
65 void setFailure(const Twine &Result) {
66 Verified = false;
67 VerifyResult = Result.str();
68 }
69
70 void setSuccess() {
71 Verified = true;
72 }
73
74private:
75 bool Verified;
76 std::string VerifyResult;
77};
78
79/// \brief Runs a matcher over some code, and returns the result of the
80/// verifier for the matched node.
81template <typename NodeType> template <typename MatcherType>
82testing::AssertionResult MatchVerifier<NodeType>::match(
Enea Zaffanella4c409492013-07-08 14:50:30 +000083 const std::string &Code, const MatcherType &AMatcher,
84 std::vector<std::string>& Args, Language L) {
Manuel Klimekaa58b972013-01-31 13:10:40 +000085 MatchFinder Finder;
86 Finder.addMatcher(AMatcher.bind(""), this);
Ahmed Charlesb8984322014-03-07 20:03:18 +000087 std::unique_ptr<tooling::FrontendActionFactory> Factory(
Manuel Klimekaa58b972013-01-31 13:10:40 +000088 tooling::newFrontendActionFactory(&Finder));
89
Manuel Klimekaa58b972013-01-31 13:10:40 +000090 StringRef FileName;
91 switch (L) {
92 case Lang_C:
93 Args.push_back("-std=c99");
94 FileName = "input.c";
95 break;
96 case Lang_C89:
97 Args.push_back("-std=c89");
98 FileName = "input.c";
99 break;
100 case Lang_CXX:
101 Args.push_back("-std=c++98");
102 FileName = "input.cc";
103 break;
Enea Zaffanella8421a062013-07-07 06:41:54 +0000104 case Lang_CXX11:
105 Args.push_back("-std=c++11");
106 FileName = "input.cc";
107 break;
Gabor Marton1f667532018-05-24 08:41:07 +0000108 case Lang_CXX14:
109 Args.push_back("-std=c++14");
110 FileName = "input.cc";
111 break;
Richard Smith9ca91012013-02-05 05:55:57 +0000112 case Lang_OpenCL:
113 FileName = "input.cl";
Fariborz Jahaniana1db7df2014-07-31 17:39:50 +0000114 break;
115 case Lang_OBJCXX:
116 FileName = "input.mm";
117 break;
Manuel Klimekaa58b972013-01-31 13:10:40 +0000118 }
119
120 // Default to failure in case callback is never called
121 setFailure("Could not find match");
122 if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName))
123 return testing::AssertionFailure() << "Parsing error";
124 if (!Verified)
125 return testing::AssertionFailure() << VerifyResult;
126 return testing::AssertionSuccess();
127}
128
Aaron Ballmana35b8fc2016-03-09 17:11:51 +0000129/// \brief Runs a matcher over some AST, and returns the result of the
130/// verifier for the matched node.
131template <typename NodeType> template <typename MatcherType>
132testing::AssertionResult MatchVerifier<NodeType>::match(
133 const Decl *D, const MatcherType &AMatcher) {
134 MatchFinder Finder;
135 Finder.addMatcher(AMatcher.bind(""), this);
136
137 setFailure("Could not find match");
138 Finder.match(*D, D->getASTContext());
139
140 if (!Verified)
141 return testing::AssertionFailure() << VerifyResult;
142 return testing::AssertionSuccess();
143}
144
Manuel Klimekaa58b972013-01-31 13:10:40 +0000145template <typename NodeType>
146void MatchVerifier<NodeType>::run(const MatchFinder::MatchResult &Result) {
147 const NodeType *Node = Result.Nodes.getNodeAs<NodeType>("");
148 if (!Node) {
149 setFailure("Matched node has wrong type");
150 } else {
151 // Callback has been called, default to success.
152 setSuccess();
153 verify(Result, *Node);
154 }
155}
156
Peter Collingbourneb23b39d2013-11-06 00:27:12 +0000157template <>
158inline void MatchVerifier<ast_type_traits::DynTypedNode>::run(
159 const MatchFinder::MatchResult &Result) {
160 BoundNodes::IDToNodeMap M = Result.Nodes.getMap();
161 BoundNodes::IDToNodeMap::const_iterator I = M.find("");
162 if (I == M.end()) {
163 setFailure("Node was not bound");
164 } else {
165 // Callback has been called, default to success.
166 setSuccess();
167 verify(Result, I->second);
168 }
169}
170
Manuel Klimekaa58b972013-01-31 13:10:40 +0000171/// \brief Verify whether a node has the correct source location.
172///
173/// By default, Node.getSourceLocation() is checked. This can be changed
174/// by overriding getLocation().
175template <typename NodeType>
176class LocationVerifier : public MatchVerifier<NodeType> {
177public:
178 void expectLocation(unsigned Line, unsigned Column) {
179 ExpectLine = Line;
180 ExpectColumn = Column;
181 }
182
183protected:
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000184 void verify(const MatchFinder::MatchResult &Result,
185 const NodeType &Node) override {
Manuel Klimekaa58b972013-01-31 13:10:40 +0000186 SourceLocation Loc = getLocation(Node);
187 unsigned Line = Result.SourceManager->getSpellingLineNumber(Loc);
188 unsigned Column = Result.SourceManager->getSpellingColumnNumber(Loc);
189 if (Line != ExpectLine || Column != ExpectColumn) {
190 std::string MsgStr;
191 llvm::raw_string_ostream Msg(MsgStr);
192 Msg << "Expected location <" << ExpectLine << ":" << ExpectColumn
193 << ">, found <";
194 Loc.print(Msg, *Result.SourceManager);
195 Msg << '>';
196 this->setFailure(Msg.str());
197 }
198 }
199
200 virtual SourceLocation getLocation(const NodeType &Node) {
201 return Node.getLocation();
202 }
203
204private:
205 unsigned ExpectLine, ExpectColumn;
206};
207
208/// \brief Verify whether a node has the correct source range.
209///
210/// By default, Node.getSourceRange() is checked. This can be changed
211/// by overriding getRange().
212template <typename NodeType>
213class RangeVerifier : public MatchVerifier<NodeType> {
214public:
215 void expectRange(unsigned BeginLine, unsigned BeginColumn,
216 unsigned EndLine, unsigned EndColumn) {
217 ExpectBeginLine = BeginLine;
218 ExpectBeginColumn = BeginColumn;
219 ExpectEndLine = EndLine;
220 ExpectEndColumn = EndColumn;
221 }
222
223protected:
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000224 void verify(const MatchFinder::MatchResult &Result,
225 const NodeType &Node) override {
Manuel Klimekaa58b972013-01-31 13:10:40 +0000226 SourceRange R = getRange(Node);
227 SourceLocation Begin = R.getBegin();
228 SourceLocation End = R.getEnd();
229 unsigned BeginLine = Result.SourceManager->getSpellingLineNumber(Begin);
230 unsigned BeginColumn = Result.SourceManager->getSpellingColumnNumber(Begin);
231 unsigned EndLine = Result.SourceManager->getSpellingLineNumber(End);
232 unsigned EndColumn = Result.SourceManager->getSpellingColumnNumber(End);
233 if (BeginLine != ExpectBeginLine || BeginColumn != ExpectBeginColumn ||
234 EndLine != ExpectEndLine || EndColumn != ExpectEndColumn) {
235 std::string MsgStr;
236 llvm::raw_string_ostream Msg(MsgStr);
237 Msg << "Expected range <" << ExpectBeginLine << ":" << ExpectBeginColumn
238 << '-' << ExpectEndLine << ":" << ExpectEndColumn << ">, found <";
239 Begin.print(Msg, *Result.SourceManager);
240 Msg << '-';
241 End.print(Msg, *Result.SourceManager);
242 Msg << '>';
243 this->setFailure(Msg.str());
244 }
245 }
246
247 virtual SourceRange getRange(const NodeType &Node) {
248 return Node.getSourceRange();
249 }
250
251private:
252 unsigned ExpectBeginLine, ExpectBeginColumn, ExpectEndLine, ExpectEndColumn;
253};
254
Peter Collingbourneb23b39d2013-11-06 00:27:12 +0000255/// \brief Verify whether a node's dump contains a given substring.
256class DumpVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
257public:
258 void expectSubstring(const std::string &Str) {
259 ExpectSubstring = Str;
260 }
261
262protected:
263 void verify(const MatchFinder::MatchResult &Result,
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000264 const ast_type_traits::DynTypedNode &Node) override {
Peter Collingbourneb23b39d2013-11-06 00:27:12 +0000265 std::string DumpStr;
266 llvm::raw_string_ostream Dump(DumpStr);
267 Node.dump(Dump, *Result.SourceManager);
268
269 if (Dump.str().find(ExpectSubstring) == std::string::npos) {
270 std::string MsgStr;
271 llvm::raw_string_ostream Msg(MsgStr);
272 Msg << "Expected dump substring <" << ExpectSubstring << ">, found <"
273 << Dump.str() << '>';
274 this->setFailure(Msg.str());
275 }
276 }
277
278private:
279 std::string ExpectSubstring;
280};
281
282/// \brief Verify whether a node's pretty print matches a given string.
283class PrintVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
284public:
285 void expectString(const std::string &Str) {
286 ExpectString = Str;
287 }
288
289protected:
290 void verify(const MatchFinder::MatchResult &Result,
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000291 const ast_type_traits::DynTypedNode &Node) override {
Peter Collingbourneb23b39d2013-11-06 00:27:12 +0000292 std::string PrintStr;
293 llvm::raw_string_ostream Print(PrintStr);
294 Node.print(Print, Result.Context->getPrintingPolicy());
295
296 if (Print.str() != ExpectString) {
297 std::string MsgStr;
298 llvm::raw_string_ostream Msg(MsgStr);
299 Msg << "Expected pretty print <" << ExpectString << ">, found <"
300 << Print.str() << '>';
301 this->setFailure(Msg.str());
302 }
303 }
304
305private:
306 std::string ExpectString;
307};
308
Manuel Klimekaa58b972013-01-31 13:10:40 +0000309} // end namespace ast_matchers
310} // end namespace clang
Benjamin Kramer12152ab2014-08-08 13:24:19 +0000311
312#endif