1 //===--- TestVisitor.h ------------------------------------------*- C++ -*-===//
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 //===----------------------------------------------------------------------===//
10 /// \brief Defines utility templates for RecursiveASTVisitor related tests.
12 //===----------------------------------------------------------------------===//
14 #ifndef LLVM_CLANG_UNITTESTS_TOOLING_TESTVISITOR_H
15 #define LLVM_CLANG_UNITTESTS_TOOLING_TESTVISITOR_H
17 #include "clang/AST/ASTConsumer.h"
18 #include "clang/AST/ASTContext.h"
19 #include "clang/AST/DynamicRecursiveASTVisitor.h"
20 #include "clang/Frontend/CompilerInstance.h"
21 #include "clang/Frontend/FrontendAction.h"
22 #include "clang/Tooling/Tooling.h"
23 #include "gtest/gtest.h"
28 // Use 'TestVisitor' or include 'CRTPTestVisitor.h' and use 'CRTPTestVisitor'
29 // instead of using this directly.
30 class TestVisitorHelper
{
44 /// \brief Runs the current AST visitor over the given code.
45 bool runOver(StringRef Code
, Language L
= Lang_CXX
) {
46 std::vector
<std::string
> Args
;
53 Args
.push_back("-std=c++98");
56 Args
.push_back("-std=c++11");
59 Args
.push_back("-std=c++14");
62 Args
.push_back("-std=c++17");
65 Args
.push_back("-std=c++2a");
68 Args
.push_back("-ObjC");
69 Args
.push_back("-fobjc-runtime=macosx-10.12.0");
72 Args
.push_back("-ObjC++");
73 Args
.push_back("-std=c++11");
74 Args
.push_back("-fblocks");
77 return tooling::runToolOnCodeWithArgs(CreateTestAction(), Code
, Args
);
81 TestVisitorHelper() = default;
82 virtual ~TestVisitorHelper() = default;
83 virtual void InvokeTraverseDecl(TranslationUnitDecl
*D
) = 0;
85 virtual std::unique_ptr
<ASTFrontendAction
> CreateTestAction() {
86 return std::make_unique
<TestAction
>(this);
89 class FindConsumer
: public ASTConsumer
{
91 FindConsumer(TestVisitorHelper
*Visitor
) : Visitor(Visitor
) {}
93 void HandleTranslationUnit(clang::ASTContext
&Context
) override
{
94 Visitor
->Context
= &Context
;
95 Visitor
->InvokeTraverseDecl(Context
.getTranslationUnitDecl());
99 TestVisitorHelper
*Visitor
;
102 class TestAction
: public ASTFrontendAction
{
104 TestAction(TestVisitorHelper
*Visitor
) : Visitor(Visitor
) {}
106 std::unique_ptr
<clang::ASTConsumer
>
107 CreateASTConsumer(CompilerInstance
&, llvm::StringRef dummy
) override
{
108 /// TestConsumer will be deleted by the framework calling us.
109 return std::make_unique
<FindConsumer
>(Visitor
);
113 TestVisitorHelper
*Visitor
;
119 class ExpectedLocationVisitorHelper
{
121 /// \brief Expect 'Match' *not* to occur at the given 'Line' and 'Column'.
123 /// Any number of matches can be disallowed.
124 void DisallowMatch(Twine Match
, unsigned Line
, unsigned Column
) {
125 DisallowedMatches
.push_back(MatchCandidate(Match
, Line
, Column
));
128 /// \brief Expect 'Match' to occur at the given 'Line' and 'Column'.
130 /// Any number of expected matches can be set by calling this repeatedly.
131 /// Each is expected to be matched 'Times' number of times. (This is useful in
132 /// cases in which different AST nodes can match at the same source code
134 void ExpectMatch(Twine Match
, unsigned Line
, unsigned Column
,
135 unsigned Times
= 1) {
136 ExpectedMatches
.push_back(ExpectedMatch(Match
, Line
, Column
, Times
));
139 /// \brief Checks that all expected matches have been found.
140 virtual ~ExpectedLocationVisitorHelper() {
141 // FIXME: Range-based for loop.
142 for (std::vector
<ExpectedMatch
>::const_iterator
143 It
= ExpectedMatches
.begin(),
144 End
= ExpectedMatches
.end();
151 virtual ASTContext
*getASTContext() = 0;
153 /// \brief Checks an actual match against expected and disallowed matches.
155 /// Implementations are required to call this with appropriate values
156 /// for 'Name' during visitation.
157 void Match(StringRef Name
, SourceLocation Location
) {
158 const FullSourceLoc FullLocation
= getASTContext()->getFullLoc(Location
);
160 // FIXME: Range-based for loop.
161 for (std::vector
<MatchCandidate
>::const_iterator
162 It
= DisallowedMatches
.begin(),
163 End
= DisallowedMatches
.end();
165 EXPECT_FALSE(It
->Matches(Name
, FullLocation
))
166 << "Matched disallowed " << *It
;
169 // FIXME: Range-based for loop.
170 for (std::vector
<ExpectedMatch
>::iterator It
= ExpectedMatches
.begin(),
171 End
= ExpectedMatches
.end();
173 It
->UpdateFor(Name
, FullLocation
, getASTContext()->getSourceManager());
178 struct MatchCandidate
{
179 std::string ExpectedName
;
181 unsigned ColumnNumber
;
183 MatchCandidate(Twine Name
, unsigned LineNumber
, unsigned ColumnNumber
)
184 : ExpectedName(Name
.str()), LineNumber(LineNumber
),
185 ColumnNumber(ColumnNumber
) {
188 bool Matches(StringRef Name
, FullSourceLoc
const &Location
) const {
189 return MatchesName(Name
) && MatchesLocation(Location
);
192 bool PartiallyMatches(StringRef Name
, FullSourceLoc
const &Location
) const {
193 return MatchesName(Name
) || MatchesLocation(Location
);
196 bool MatchesName(StringRef Name
) const {
197 return Name
== ExpectedName
;
200 bool MatchesLocation(FullSourceLoc
const &Location
) const {
201 return Location
.isValid() &&
202 Location
.getSpellingLineNumber() == LineNumber
&&
203 Location
.getSpellingColumnNumber() == ColumnNumber
;
206 friend std::ostream
&operator<<(std::ostream
&Stream
,
207 MatchCandidate
const &Match
) {
208 return Stream
<< Match
.ExpectedName
209 << " at " << Match
.LineNumber
<< ":" << Match
.ColumnNumber
;
213 struct ExpectedMatch
{
214 ExpectedMatch(Twine Name
, unsigned LineNumber
, unsigned ColumnNumber
,
216 : Candidate(Name
, LineNumber
, ColumnNumber
), TimesExpected(Times
),
219 void UpdateFor(StringRef Name
, FullSourceLoc Location
, SourceManager
&SM
) {
220 if (Candidate
.Matches(Name
, Location
)) {
221 EXPECT_LT(TimesSeen
, TimesExpected
);
223 } else if (TimesSeen
< TimesExpected
&&
224 Candidate
.PartiallyMatches(Name
, Location
)) {
225 llvm::raw_string_ostream
Stream(PartialMatches
);
226 Stream
<< ", partial match: \"" << Name
<< "\" at ";
227 Location
.print(Stream
, SM
);
231 void ExpectFound() const {
232 EXPECT_EQ(TimesExpected
, TimesSeen
)
233 << "Expected \"" << Candidate
.ExpectedName
234 << "\" at " << Candidate
.LineNumber
235 << ":" << Candidate
.ColumnNumber
<< PartialMatches
;
238 MatchCandidate Candidate
;
239 std::string PartialMatches
;
240 unsigned TimesExpected
;
244 std::vector
<MatchCandidate
> DisallowedMatches
;
245 std::vector
<ExpectedMatch
> ExpectedMatches
;
247 } // namespace detail
249 /// \brief Base class for simple (Dynamic)RecursiveASTVisitor based tests.
251 /// This is a drop-in replacement for DynamicRecursiveASTVisitor itself, with
252 /// the additional capability of running it over a snippet of code.
254 /// Visits template instantiations and implicit code by default.
256 /// For post-order traversal etc. use CTRPTestVisitor from
257 /// CTRPTestVisitor.h instead.
258 class TestVisitor
: public DynamicRecursiveASTVisitor
,
259 public detail::TestVisitorHelper
{
262 ShouldVisitTemplateInstantiations
= true;
263 ShouldVisitImplicitCode
= true;
266 void InvokeTraverseDecl(TranslationUnitDecl
*D
) override
{ TraverseDecl(D
); }
269 /// \brief A RecursiveASTVisitor to check that certain matches are (or are
270 /// not) observed during visitation.
272 /// This is a RecursiveASTVisitor for testing the RecursiveASTVisitor itself,
273 /// and allows simple creation of test visitors running matches on only a small
274 /// subset of the Visit* methods.
276 /// For post-order traversal etc. use CTRPExpectedLocationVisitor from
277 /// CTRPTestVisitor.h instead.
278 class ExpectedLocationVisitor
: public TestVisitor
,
279 public detail::ExpectedLocationVisitorHelper
{
280 ASTContext
*getASTContext() override
{ return Context
; }