blob: 1d1bf57db1c8fde4ae25e2f9973f46fef91dc5d8 [file] [log] [blame]
Manuel Klimekaa58b972013-01-31 13:10:40 +00001//===- unittest/AST/MatchVerifier.h - AST unit test support ---------------===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Manuel Klimekaa58b972013-01-31 13:10:40 +00006//
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
Benjamin Kramer12152ab2014-08-08 13:24:19 +000018#ifndef LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H
19#define LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H
20
Manuel Klimekaa58b972013-01-31 13:10:40 +000021#include "clang/AST/ASTContext.h"
22#include "clang/ASTMatchers/ASTMatchFinder.h"
23#include "clang/ASTMatchers/ASTMatchers.h"
24#include "clang/Tooling/Tooling.h"
Gabor Marton1f667532018-05-24 08:41:07 +000025#include "Language.h"
Manuel Klimekaa58b972013-01-31 13:10:40 +000026#include "gtest/gtest.h"
27
28namespace clang {
29namespace ast_matchers {
30
Manuel Klimekaa58b972013-01-31 13:10:40 +000031/// \brief Base class for verifying some property of nodes found by a matcher.
32template <typename NodeType>
33class MatchVerifier : public MatchFinder::MatchCallback {
34public:
35 template <typename MatcherType>
36 testing::AssertionResult match(const std::string &Code,
37 const MatcherType &AMatcher) {
Enea Zaffanella4c409492013-07-08 14:50:30 +000038 std::vector<std::string> Args;
39 return match(Code, AMatcher, Args, Lang_CXX);
Manuel Klimekaa58b972013-01-31 13:10:40 +000040 }
41
42 template <typename MatcherType>
43 testing::AssertionResult match(const std::string &Code,
Enea Zaffanella4c409492013-07-08 14:50:30 +000044 const MatcherType &AMatcher,
45 Language L) {
46 std::vector<std::string> Args;
47 return match(Code, AMatcher, Args, L);
48 }
49
50 template <typename MatcherType>
51 testing::AssertionResult match(const std::string &Code,
52 const MatcherType &AMatcher,
53 std::vector<std::string>& Args,
54 Language L);
Manuel Klimekaa58b972013-01-31 13:10:40 +000055
Aaron Ballmana35b8fc2016-03-09 17:11:51 +000056 template <typename MatcherType>
57 testing::AssertionResult match(const Decl *D, const MatcherType &AMatcher);
58
Manuel Klimekaa58b972013-01-31 13:10:40 +000059protected:
Alexander Kornienko34eb2072015-04-11 02:00:23 +000060 void run(const MatchFinder::MatchResult &Result) override;
Manuel Klimekaa58b972013-01-31 13:10:40 +000061 virtual void verify(const MatchFinder::MatchResult &Result,
Manuel Klimekd4be4082013-02-28 13:21:39 +000062 const NodeType &Node) {}
Manuel Klimekaa58b972013-01-31 13:10:40 +000063
64 void setFailure(const Twine &Result) {
65 Verified = false;
66 VerifyResult = Result.str();
67 }
68
69 void setSuccess() {
70 Verified = true;
71 }
72
73private:
74 bool Verified;
75 std::string VerifyResult;
76};
77
78/// \brief Runs a matcher over some code, and returns the result of the
79/// verifier for the matched node.
80template <typename NodeType> template <typename MatcherType>
81testing::AssertionResult MatchVerifier<NodeType>::match(
Enea Zaffanella4c409492013-07-08 14:50:30 +000082 const std::string &Code, const MatcherType &AMatcher,
83 std::vector<std::string>& Args, Language L) {
Manuel Klimekaa58b972013-01-31 13:10:40 +000084 MatchFinder Finder;
85 Finder.addMatcher(AMatcher.bind(""), this);
Ahmed Charlesb8984322014-03-07 20:03:18 +000086 std::unique_ptr<tooling::FrontendActionFactory> Factory(
Manuel Klimekaa58b972013-01-31 13:10:40 +000087 tooling::newFrontendActionFactory(&Finder));
88
Manuel Klimekaa58b972013-01-31 13:10:40 +000089 StringRef FileName;
90 switch (L) {
91 case Lang_C:
92 Args.push_back("-std=c99");
93 FileName = "input.c";
94 break;
95 case Lang_C89:
96 Args.push_back("-std=c89");
97 FileName = "input.c";
98 break;
99 case Lang_CXX:
100 Args.push_back("-std=c++98");
101 FileName = "input.cc";
102 break;
Enea Zaffanella8421a062013-07-07 06:41:54 +0000103 case Lang_CXX11:
104 Args.push_back("-std=c++11");
105 FileName = "input.cc";
106 break;
Gabor Marton1f667532018-05-24 08:41:07 +0000107 case Lang_CXX14:
108 Args.push_back("-std=c++14");
109 FileName = "input.cc";
110 break;
Richard Smith9ca91012013-02-05 05:55:57 +0000111 case Lang_OpenCL:
112 FileName = "input.cl";
Fariborz Jahaniana1db7df2014-07-31 17:39:50 +0000113 break;
114 case Lang_OBJCXX:
115 FileName = "input.mm";
116 break;
Manuel Klimekaa58b972013-01-31 13:10:40 +0000117 }
118
119 // Default to failure in case callback is never called
120 setFailure("Could not find match");
121 if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName))
122 return testing::AssertionFailure() << "Parsing error";
123 if (!Verified)
124 return testing::AssertionFailure() << VerifyResult;
125 return testing::AssertionSuccess();
126}
127
Aaron Ballmana35b8fc2016-03-09 17:11:51 +0000128/// \brief Runs a matcher over some AST, and returns the result of the
129/// verifier for the matched node.
130template <typename NodeType> template <typename MatcherType>
131testing::AssertionResult MatchVerifier<NodeType>::match(
132 const Decl *D, const MatcherType &AMatcher) {
133 MatchFinder Finder;
134 Finder.addMatcher(AMatcher.bind(""), this);
135
136 setFailure("Could not find match");
137 Finder.match(*D, D->getASTContext());
138
139 if (!Verified)
140 return testing::AssertionFailure() << VerifyResult;
141 return testing::AssertionSuccess();
142}
143
Manuel Klimekaa58b972013-01-31 13:10:40 +0000144template <typename NodeType>
145void MatchVerifier<NodeType>::run(const MatchFinder::MatchResult &Result) {
146 const NodeType *Node = Result.Nodes.getNodeAs<NodeType>("");
147 if (!Node) {
148 setFailure("Matched node has wrong type");
149 } else {
150 // Callback has been called, default to success.
151 setSuccess();
152 verify(Result, *Node);
153 }
154}
155
Peter Collingbourneb23b39d2013-11-06 00:27:12 +0000156template <>
157inline void MatchVerifier<ast_type_traits::DynTypedNode>::run(
158 const MatchFinder::MatchResult &Result) {
159 BoundNodes::IDToNodeMap M = Result.Nodes.getMap();
160 BoundNodes::IDToNodeMap::const_iterator I = M.find("");
161 if (I == M.end()) {
162 setFailure("Node was not bound");
163 } else {
164 // Callback has been called, default to success.
165 setSuccess();
166 verify(Result, I->second);
167 }
168}
169
Manuel Klimekaa58b972013-01-31 13:10:40 +0000170/// \brief Verify whether a node has the correct source location.
171///
172/// By default, Node.getSourceLocation() is checked. This can be changed
173/// by overriding getLocation().
174template <typename NodeType>
175class LocationVerifier : public MatchVerifier<NodeType> {
176public:
177 void expectLocation(unsigned Line, unsigned Column) {
178 ExpectLine = Line;
179 ExpectColumn = Column;
180 }
181
182protected:
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000183 void verify(const MatchFinder::MatchResult &Result,
184 const NodeType &Node) override {
Manuel Klimekaa58b972013-01-31 13:10:40 +0000185 SourceLocation Loc = getLocation(Node);
186 unsigned Line = Result.SourceManager->getSpellingLineNumber(Loc);
187 unsigned Column = Result.SourceManager->getSpellingColumnNumber(Loc);
188 if (Line != ExpectLine || Column != ExpectColumn) {
189 std::string MsgStr;
190 llvm::raw_string_ostream Msg(MsgStr);
191 Msg << "Expected location <" << ExpectLine << ":" << ExpectColumn
192 << ">, found <";
193 Loc.print(Msg, *Result.SourceManager);
194 Msg << '>';
195 this->setFailure(Msg.str());
196 }
197 }
198
199 virtual SourceLocation getLocation(const NodeType &Node) {
200 return Node.getLocation();
201 }
202
203private:
204 unsigned ExpectLine, ExpectColumn;
205};
206
207/// \brief Verify whether a node has the correct source range.
208///
209/// By default, Node.getSourceRange() is checked. This can be changed
210/// by overriding getRange().
211template <typename NodeType>
212class RangeVerifier : public MatchVerifier<NodeType> {
213public:
214 void expectRange(unsigned BeginLine, unsigned BeginColumn,
215 unsigned EndLine, unsigned EndColumn) {
216 ExpectBeginLine = BeginLine;
217 ExpectBeginColumn = BeginColumn;
218 ExpectEndLine = EndLine;
219 ExpectEndColumn = EndColumn;
220 }
221
222protected:
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000223 void verify(const MatchFinder::MatchResult &Result,
224 const NodeType &Node) override {
Manuel Klimekaa58b972013-01-31 13:10:40 +0000225 SourceRange R = getRange(Node);
226 SourceLocation Begin = R.getBegin();
227 SourceLocation End = R.getEnd();
228 unsigned BeginLine = Result.SourceManager->getSpellingLineNumber(Begin);
229 unsigned BeginColumn = Result.SourceManager->getSpellingColumnNumber(Begin);
230 unsigned EndLine = Result.SourceManager->getSpellingLineNumber(End);
231 unsigned EndColumn = Result.SourceManager->getSpellingColumnNumber(End);
232 if (BeginLine != ExpectBeginLine || BeginColumn != ExpectBeginColumn ||
233 EndLine != ExpectEndLine || EndColumn != ExpectEndColumn) {
234 std::string MsgStr;
235 llvm::raw_string_ostream Msg(MsgStr);
236 Msg << "Expected range <" << ExpectBeginLine << ":" << ExpectBeginColumn
237 << '-' << ExpectEndLine << ":" << ExpectEndColumn << ">, found <";
238 Begin.print(Msg, *Result.SourceManager);
239 Msg << '-';
240 End.print(Msg, *Result.SourceManager);
241 Msg << '>';
242 this->setFailure(Msg.str());
243 }
244 }
245
246 virtual SourceRange getRange(const NodeType &Node) {
247 return Node.getSourceRange();
248 }
249
250private:
251 unsigned ExpectBeginLine, ExpectBeginColumn, ExpectEndLine, ExpectEndColumn;
252};
253
Peter Collingbourneb23b39d2013-11-06 00:27:12 +0000254/// \brief Verify whether a node's dump contains a given substring.
255class DumpVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
256public:
257 void expectSubstring(const std::string &Str) {
258 ExpectSubstring = Str;
259 }
260
261protected:
262 void verify(const MatchFinder::MatchResult &Result,
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000263 const ast_type_traits::DynTypedNode &Node) override {
Peter Collingbourneb23b39d2013-11-06 00:27:12 +0000264 std::string DumpStr;
265 llvm::raw_string_ostream Dump(DumpStr);
266 Node.dump(Dump, *Result.SourceManager);
267
268 if (Dump.str().find(ExpectSubstring) == std::string::npos) {
269 std::string MsgStr;
270 llvm::raw_string_ostream Msg(MsgStr);
271 Msg << "Expected dump substring <" << ExpectSubstring << ">, found <"
272 << Dump.str() << '>';
273 this->setFailure(Msg.str());
274 }
275 }
276
277private:
278 std::string ExpectSubstring;
279};
280
281/// \brief Verify whether a node's pretty print matches a given string.
282class PrintVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
283public:
284 void expectString(const std::string &Str) {
285 ExpectString = Str;
286 }
287
288protected:
289 void verify(const MatchFinder::MatchResult &Result,
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000290 const ast_type_traits::DynTypedNode &Node) override {
Peter Collingbourneb23b39d2013-11-06 00:27:12 +0000291 std::string PrintStr;
292 llvm::raw_string_ostream Print(PrintStr);
293 Node.print(Print, Result.Context->getPrintingPolicy());
294
295 if (Print.str() != ExpectString) {
296 std::string MsgStr;
297 llvm::raw_string_ostream Msg(MsgStr);
298 Msg << "Expected pretty print <" << ExpectString << ">, found <"
299 << Print.str() << '>';
300 this->setFailure(Msg.str());
301 }
302 }
303
304private:
305 std::string ExpectString;
306};
307
Manuel Klimekaa58b972013-01-31 13:10:40 +0000308} // end namespace ast_matchers
309} // end namespace clang
Benjamin Kramer12152ab2014-08-08 13:24:19 +0000310
311#endif