1 //===- unittest/AST/MatchVerifier.h - AST unit test support ---------------===//
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
7 //===----------------------------------------------------------------------===//
9 // Provides MatchVerifier, a base class to implement gtest matchers that
10 // verify things that can be matched on the AST.
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.
16 //===----------------------------------------------------------------------===//
18 #ifndef LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H
19 #define LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H
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"
29 namespace ast_matchers
{
31 /// \brief Base class for verifying some property of nodes found by a matcher.
32 template <typename NodeType
>
33 class MatchVerifier
: public MatchFinder::MatchCallback
{
35 template <typename MatcherType
>
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
);
42 template <typename MatcherType
>
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
);
49 template <typename MatcherType
>
50 testing::AssertionResult
51 match(const std::string
&Code
, const MatcherType
&AMatcher
,
52 std::vector
<std::string
> &Args
, TestLanguage L
);
54 template <typename MatcherType
>
55 testing::AssertionResult
match(const Decl
*D
, const MatcherType
&AMatcher
);
58 void run(const MatchFinder::MatchResult
&Result
) override
;
59 virtual void verify(const MatchFinder::MatchResult
&Result
,
60 const NodeType
&Node
) {}
62 void setFailure(const Twine
&Result
) {
64 VerifyResult
= Result
.str();
73 std::string VerifyResult
;
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
81 MatchVerifier
<NodeType
>::match(const std::string
&Code
,
82 const MatcherType
&AMatcher
,
83 std::vector
<std::string
> &Args
, TestLanguage L
) {
85 Finder
.addMatcher(AMatcher
.bind(""), this);
86 std::unique_ptr
<tooling::FrontendActionFactory
> Factory(
87 tooling::newFrontendActionFactory(&Finder
));
92 Args
.push_back("-std=c89");
96 Args
.push_back("-std=c99");
100 Args
.push_back("-std=c++03");
101 FileName
= "input.cc";
104 Args
.push_back("-std=c++11");
105 FileName
= "input.cc";
108 Args
.push_back("-std=c++14");
109 FileName
= "input.cc";
112 Args
.push_back("-std=c++17");
113 FileName
= "input.cc";
116 Args
.push_back("-std=c++20");
117 FileName
= "input.cc";
120 Args
.push_back("-cl-no-stdinc");
121 FileName
= "input.cl";
124 Args
.push_back("-fobjc-nonfragile-abi");
125 FileName
= "input.m";
128 FileName
= "input.mm";
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";
137 return testing::AssertionFailure() << VerifyResult
;
138 return testing::AssertionSuccess();
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
>
144 testing::AssertionResult MatchVerifier
<NodeType
>::match(
145 const Decl
*D
, const MatcherType
&AMatcher
) {
147 Finder
.addMatcher(AMatcher
.bind(""), this);
149 setFailure("Could not find match");
150 Finder
.match(*D
, D
->getASTContext());
153 return testing::AssertionFailure() << VerifyResult
;
154 return testing::AssertionSuccess();
157 template <typename NodeType
>
158 void MatchVerifier
<NodeType
>::run(const MatchFinder::MatchResult
&Result
) {
159 const NodeType
*Node
= Result
.Nodes
.getNodeAs
<NodeType
>("");
161 setFailure("Matched node has wrong type");
163 // Callback has been called, default to success.
165 verify(Result
, *Node
);
171 MatchVerifier
<DynTypedNode
>::run(const MatchFinder::MatchResult
&Result
) {
172 BoundNodes::IDToNodeMap M
= Result
.Nodes
.getMap();
173 BoundNodes::IDToNodeMap::const_iterator I
= M
.find("");
175 setFailure("Node was not bound");
177 // Callback has been called, default to success.
179 verify(Result
, I
->second
);
183 /// \brief Verify whether a node has the correct source location.
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
> {
190 void expectLocation(unsigned Line
, unsigned Column
) {
192 ExpectColumn
= Column
;
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
) {
203 llvm::raw_string_ostream
Msg(MsgStr
);
204 Msg
<< "Expected location <" << ExpectLine
<< ":" << ExpectColumn
206 Loc
.print(Msg
, *Result
.SourceManager
);
208 this->setFailure(Msg
.str());
212 virtual SourceLocation
getLocation(const NodeType
&Node
) {
213 return Node
.getLocation();
217 unsigned ExpectLine
, ExpectColumn
;
220 /// \brief Verify whether a node has the correct source range.
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
> {
227 void expectRange(unsigned BeginLine
, unsigned BeginColumn
,
228 unsigned EndLine
, unsigned EndColumn
) {
229 ExpectBeginLine
= BeginLine
;
230 ExpectBeginColumn
= BeginColumn
;
231 ExpectEndLine
= EndLine
;
232 ExpectEndColumn
= EndColumn
;
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
) {
248 llvm::raw_string_ostream
Msg(MsgStr
);
249 Msg
<< "Expected range <" << ExpectBeginLine
<< ":" << ExpectBeginColumn
250 << '-' << ExpectEndLine
<< ":" << ExpectEndColumn
<< ">, found <";
251 Begin
.print(Msg
, *Result
.SourceManager
);
253 End
.print(Msg
, *Result
.SourceManager
);
255 this->setFailure(Msg
.str());
259 virtual SourceRange
getRange(const NodeType
&Node
) {
260 return Node
.getSourceRange();
264 unsigned ExpectBeginLine
, ExpectBeginColumn
, ExpectEndLine
, ExpectEndColumn
;
267 /// \brief Verify whether a node's dump contains a given substring.
268 class DumpVerifier
: public MatchVerifier
<DynTypedNode
> {
270 void expectSubstring(const std::string
&Str
) {
271 ExpectSubstring
= Str
;
275 void verify(const MatchFinder::MatchResult
&Result
,
276 const DynTypedNode
&Node
) override
{
278 llvm::raw_string_ostream
Dump(DumpStr
);
279 Node
.dump(Dump
, *Result
.Context
);
281 if (Dump
.str().find(ExpectSubstring
) == std::string::npos
) {
283 llvm::raw_string_ostream
Msg(MsgStr
);
284 Msg
<< "Expected dump substring <" << ExpectSubstring
<< ">, found <"
285 << Dump
.str() << '>';
286 this->setFailure(Msg
.str());
291 std::string ExpectSubstring
;
294 /// \brief Verify whether a node's pretty print matches a given string.
295 class PrintVerifier
: public MatchVerifier
<DynTypedNode
> {
297 void expectString(const std::string
&Str
) {
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());
308 if (Print
.str() != ExpectString
) {
310 llvm::raw_string_ostream
Msg(MsgStr
);
311 Msg
<< "Expected pretty print <" << ExpectString
<< ">, found <"
312 << Print
.str() << '>';
313 this->setFailure(Msg
.str());
318 std::string ExpectString
;
321 } // end namespace ast_matchers
322 } // end namespace clang