1//===--- TestVisitor.h ------------------------------------------*- C++ -*-===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// \brief Defines utility templates for RecursiveASTVisitor related tests.
11///
12//===----------------------------------------------------------------------===//
13
14#ifndef LLVM_CLANG_UNITTESTS_TOOLING_TESTVISITOR_H
15#define LLVM_CLANG_UNITTESTS_TOOLING_TESTVISITOR_H
16
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"
24#include <vector>
25
26namespace clang {
27namespace detail {
28// Use 'TestVisitor' or include 'CRTPTestVisitor.h' and use 'CRTPTestVisitor'
29// instead of using this directly.
30class TestVisitorHelper {
31public:
32 enum Language {
33 Lang_C,
34 Lang_CXX98,
35 Lang_CXX11,
36 Lang_CXX14,
37 Lang_CXX17,
38 Lang_CXX2a,
39 Lang_OBJC,
40 Lang_OBJCXX11,
41 Lang_CXX = Lang_CXX98
42 };
43
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;
47 switch (L) {
48 case Lang_C:
49 Args.push_back(x: "-x");
50 Args.push_back(x: "c");
51 break;
52 case Lang_CXX98:
53 Args.push_back(x: "-std=c++98");
54 break;
55 case Lang_CXX11:
56 Args.push_back(x: "-std=c++11");
57 break;
58 case Lang_CXX14:
59 Args.push_back(x: "-std=c++14");
60 break;
61 case Lang_CXX17:
62 Args.push_back(x: "-std=c++17");
63 break;
64 case Lang_CXX2a:
65 Args.push_back(x: "-std=c++2a");
66 break;
67 case Lang_OBJC:
68 Args.push_back(x: "-ObjC");
69 Args.push_back(x: "-fobjc-runtime=macosx-10.12.0");
70 break;
71 case Lang_OBJCXX11:
72 Args.push_back(x: "-ObjC++");
73 Args.push_back(x: "-std=c++11");
74 Args.push_back(x: "-fblocks");
75 break;
76 }
77 return tooling::runToolOnCodeWithArgs(ToolAction: CreateTestAction(), Code, Args);
78 }
79
80protected:
81 TestVisitorHelper() = default;
82 virtual ~TestVisitorHelper() = default;
83 virtual void InvokeTraverseDecl(TranslationUnitDecl *D) = 0;
84
85 virtual std::unique_ptr<ASTFrontendAction> CreateTestAction() {
86 return std::make_unique<TestAction>(args: this);
87 }
88
89 class FindConsumer : public ASTConsumer {
90 public:
91 FindConsumer(TestVisitorHelper *Visitor) : Visitor(Visitor) {}
92
93 void HandleTranslationUnit(clang::ASTContext &Context) override {
94 Visitor->Context = &Context;
95 Visitor->InvokeTraverseDecl(D: Context.getTranslationUnitDecl());
96 }
97
98 private:
99 TestVisitorHelper *Visitor;
100 };
101
102 class TestAction : public ASTFrontendAction {
103 public:
104 TestAction(TestVisitorHelper *Visitor) : Visitor(Visitor) {}
105
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>(args&: Visitor);
110 }
111
112 protected:
113 TestVisitorHelper *Visitor;
114 };
115
116 ASTContext *Context;
117};
118
119class ExpectedLocationVisitorHelper {
120public:
121 /// \brief Expect 'Match' *not* to occur at the given 'Line' and 'Column'.
122 ///
123 /// Any number of matches can be disallowed.
124 void DisallowMatch(Twine Match, unsigned Line, unsigned Column) {
125 DisallowedMatches.push_back(x: MatchCandidate(Match, Line, Column));
126 }
127
128 /// \brief Expect 'Match' to occur at the given 'Line' and 'Column'.
129 ///
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
133 /// location.)
134 void ExpectMatch(Twine Match, unsigned Line, unsigned Column,
135 unsigned Times = 1) {
136 ExpectedMatches.push_back(x: ExpectedMatch(Match, Line, Column, Times));
137 }
138
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();
145 It != End; ++It) {
146 It->ExpectFound();
147 }
148 }
149
150protected:
151 virtual ASTContext *getASTContext() = 0;
152
153 /// \brief Checks an actual match against expected and disallowed matches.
154 ///
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(Loc: Location);
159
160 // FIXME: Range-based for loop.
161 for (std::vector<MatchCandidate>::const_iterator
162 It = DisallowedMatches.begin(),
163 End = DisallowedMatches.end();
164 It != End; ++It) {
165 EXPECT_FALSE(It->Matches(Name, FullLocation))
166 << "Matched disallowed " << *It;
167 }
168
169 // FIXME: Range-based for loop.
170 for (std::vector<ExpectedMatch>::iterator It = ExpectedMatches.begin(),
171 End = ExpectedMatches.end();
172 It != End; ++It) {
173 It->UpdateFor(Name, Location: FullLocation, SM&: getASTContext()->getSourceManager());
174 }
175 }
176
177private:
178 struct MatchCandidate {
179 std::string ExpectedName;
180 unsigned LineNumber;
181 unsigned ColumnNumber;
182
183 MatchCandidate(Twine Name, unsigned LineNumber, unsigned ColumnNumber)
184 : ExpectedName(Name.str()), LineNumber(LineNumber),
185 ColumnNumber(ColumnNumber) {
186 }
187
188 bool Matches(StringRef Name, FullSourceLoc const &Location) const {
189 return MatchesName(Name) && MatchesLocation(Location);
190 }
191
192 bool PartiallyMatches(StringRef Name, FullSourceLoc const &Location) const {
193 return MatchesName(Name) || MatchesLocation(Location);
194 }
195
196 bool MatchesName(StringRef Name) const {
197 return Name == ExpectedName;
198 }
199
200 bool MatchesLocation(FullSourceLoc const &Location) const {
201 return Location.isValid() &&
202 Location.getSpellingLineNumber() == LineNumber &&
203 Location.getSpellingColumnNumber() == ColumnNumber;
204 }
205
206 friend std::ostream &operator<<(std::ostream &Stream,
207 MatchCandidate const &Match) {
208 return Stream << Match.ExpectedName
209 << " at " << Match.LineNumber << ":" << Match.ColumnNumber;
210 }
211 };
212
213 struct ExpectedMatch {
214 ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber,
215 unsigned Times)
216 : Candidate(Name, LineNumber, ColumnNumber), TimesExpected(Times),
217 TimesSeen(0) {}
218
219 void UpdateFor(StringRef Name, FullSourceLoc Location, SourceManager &SM) {
220 if (Candidate.Matches(Name, Location)) {
221 EXPECT_LT(TimesSeen, TimesExpected);
222 ++TimesSeen;
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(OS&: Stream, SM);
228 }
229 }
230
231 void ExpectFound() const {
232 EXPECT_EQ(TimesExpected, TimesSeen)
233 << "Expected \"" << Candidate.ExpectedName
234 << "\" at " << Candidate.LineNumber
235 << ":" << Candidate.ColumnNumber << PartialMatches;
236 }
237
238 MatchCandidate Candidate;
239 std::string PartialMatches;
240 unsigned TimesExpected;
241 unsigned TimesSeen;
242 };
243
244 std::vector<MatchCandidate> DisallowedMatches;
245 std::vector<ExpectedMatch> ExpectedMatches;
246};
247} // namespace detail
248
249/// \brief Base class for simple (Dynamic)RecursiveASTVisitor based tests.
250///
251/// This is a drop-in replacement for DynamicRecursiveASTVisitor itself, with
252/// the additional capability of running it over a snippet of code.
253///
254/// Visits template instantiations and implicit code by default.
255///
256/// For post-order traversal etc. use CTRPTestVisitor from
257/// CTRPTestVisitor.h instead.
258class TestVisitor : public DynamicRecursiveASTVisitor,
259 public detail::TestVisitorHelper {
260public:
261 TestVisitor() {
262 ShouldVisitTemplateInstantiations = true;
263 ShouldVisitImplicitCode = true;
264 }
265
266 void InvokeTraverseDecl(TranslationUnitDecl *D) override { TraverseDecl(D); }
267};
268
269/// \brief A RecursiveASTVisitor to check that certain matches are (or are
270/// not) observed during visitation.
271///
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.
275///
276/// For post-order traversal etc. use CTRPExpectedLocationVisitor from
277/// CTRPTestVisitor.h instead.
278class ExpectedLocationVisitor : public TestVisitor,
279 public detail::ExpectedLocationVisitorHelper {
280 ASTContext *getASTContext() override { return Context; }
281};
282} // namespace clang
283
284#endif
285

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang/unittests/Tooling/TestVisitor.h