blob: adfd3ef60f50066618cb49d75c3a166bb4b917c5 [file] [log] [blame]
Richard Smith87d8fb92012-06-24 23:56:26 +00001//===--- TestVisitor.h ------------------------------------------*- C++ -*-===//
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//===----------------------------------------------------------------------===//
James Dennett75c100b2012-08-24 06:59:51 +00009///
10/// \file
11/// \brief Defines utility templates for RecursiveASTVisitor related tests.
12///
Richard Smith87d8fb92012-06-24 23:56:26 +000013//===----------------------------------------------------------------------===//
14
Benjamin Kramer2f5db8b2014-08-13 16:25:19 +000015#ifndef LLVM_CLANG_UNITTESTS_TOOLING_TESTVISITOR_H
16#define LLVM_CLANG_UNITTESTS_TOOLING_TESTVISITOR_H
Richard Smith87d8fb92012-06-24 23:56:26 +000017
18#include "clang/AST/ASTConsumer.h"
Chandler Carruth320d9662012-12-04 09:45:34 +000019#include "clang/AST/ASTContext.h"
Richard Smith87d8fb92012-06-24 23:56:26 +000020#include "clang/AST/RecursiveASTVisitor.h"
Richard Smith87d8fb92012-06-24 23:56:26 +000021#include "clang/Frontend/CompilerInstance.h"
Chandler Carruth320d9662012-12-04 09:45:34 +000022#include "clang/Frontend/FrontendAction.h"
Richard Smith87d8fb92012-06-24 23:56:26 +000023#include "clang/Tooling/Tooling.h"
24#include "gtest/gtest.h"
Chandler Carruth320d9662012-12-04 09:45:34 +000025#include <vector>
Richard Smith87d8fb92012-06-24 23:56:26 +000026
27namespace clang {
28
Matt Beaumont-Gaya0e2c042012-06-25 18:27:11 +000029/// \brief Base class for simple RecursiveASTVisitor based tests.
Richard Smith87d8fb92012-06-24 23:56:26 +000030///
31/// This is a drop-in replacement for RecursiveASTVisitor itself, with the
32/// additional capability of running it over a snippet of code.
33///
Michael Hanc90d12d2013-09-11 15:53:29 +000034/// Visits template instantiations and implicit code by default.
Richard Smith87d8fb92012-06-24 23:56:26 +000035template <typename T>
36class TestVisitor : public RecursiveASTVisitor<T> {
37public:
Angel Garcia Gomez637d1e62015-10-20 13:23:58 +000038 TestVisitor() { }
Richard Smith87d8fb92012-06-24 23:56:26 +000039
Angel Garcia Gomez637d1e62015-10-20 13:23:58 +000040 virtual ~TestVisitor() { }
Matt Beaumont-Gaya0e2c042012-06-25 18:27:11 +000041
Alp Tokere3ad5312014-06-26 01:42:24 +000042 enum Language {
43 Lang_C,
44 Lang_CXX98,
45 Lang_CXX11,
Martin Bohmed5f94a62016-08-17 14:59:53 +000046 Lang_CXX14,
Alp Tokere3ad5312014-06-26 01:42:24 +000047 Lang_OBJC,
48 Lang_OBJCXX11,
49 Lang_CXX = Lang_CXX98
50 };
Richard Smith87deab32012-08-17 21:23:17 +000051
Richard Smith87d8fb92012-06-24 23:56:26 +000052 /// \brief Runs the current AST visitor over the given code.
Richard Smith87deab32012-08-17 21:23:17 +000053 bool runOver(StringRef Code, Language L = Lang_CXX) {
Nico Weber077a53e2012-08-30 02:02:19 +000054 std::vector<std::string> Args;
55 switch (L) {
Stephan Bergmann83e6a822017-06-27 07:59:56 +000056 case Lang_C:
57 Args.push_back("-x");
58 Args.push_back("c");
59 break;
James Dennetteb576c02013-06-30 03:05:49 +000060 case Lang_CXX98: Args.push_back("-std=c++98"); break;
61 case Lang_CXX11: Args.push_back("-std=c++11"); break;
Martin Bohmed5f94a62016-08-17 14:59:53 +000062 case Lang_CXX14: Args.push_back("-std=c++14"); break;
Alp Toker62438da2014-06-06 15:05:09 +000063 case Lang_OBJC: Args.push_back("-ObjC"); break;
Alp Tokere3ad5312014-06-26 01:42:24 +000064 case Lang_OBJCXX11:
65 Args.push_back("-ObjC++");
66 Args.push_back("-std=c++11");
Alp Toker0843bff2014-06-26 02:07:06 +000067 Args.push_back("-fblocks");
Alp Tokere3ad5312014-06-26 01:42:24 +000068 break;
Nico Weber077a53e2012-08-30 02:02:19 +000069 }
70 return tooling::runToolOnCodeWithArgs(CreateTestAction(), Code, Args);
Richard Smith87d8fb92012-06-24 23:56:26 +000071 }
72
73 bool shouldVisitTemplateInstantiations() const {
74 return true;
75 }
76
Michael Hanc90d12d2013-09-11 15:53:29 +000077 bool shouldVisitImplicitCode() const {
78 return true;
79 }
80
Richard Smith87d8fb92012-06-24 23:56:26 +000081protected:
82 virtual ASTFrontendAction* CreateTestAction() {
83 return new TestAction(this);
84 }
85
86 class FindConsumer : public ASTConsumer {
87 public:
88 FindConsumer(TestVisitor *Visitor) : Visitor(Visitor) {}
89
Alexander Kornienko34eb2072015-04-11 02:00:23 +000090 void HandleTranslationUnit(clang::ASTContext &Context) override {
Manuel Klimek5da9dcb2012-07-05 18:13:01 +000091 Visitor->Context = &Context;
Richard Smith87d8fb92012-06-24 23:56:26 +000092 Visitor->TraverseDecl(Context.getTranslationUnitDecl());
93 }
94
95 private:
96 TestVisitor *Visitor;
97 };
98
99 class TestAction : public ASTFrontendAction {
100 public:
101 TestAction(TestVisitor *Visitor) : Visitor(Visitor) {}
102
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000103 std::unique_ptr<clang::ASTConsumer>
104 CreateASTConsumer(CompilerInstance &, llvm::StringRef dummy) override {
Richard Smith87d8fb92012-06-24 23:56:26 +0000105 /// TestConsumer will be deleted by the framework calling us.
David Blaikie6beb6aa2014-08-10 19:56:51 +0000106 return llvm::make_unique<FindConsumer>(Visitor);
Richard Smith87d8fb92012-06-24 23:56:26 +0000107 }
108
109 protected:
110 TestVisitor *Visitor;
111 };
112
113 ASTContext *Context;
114};
115
James Dennett75c100b2012-08-24 06:59:51 +0000116/// \brief A RecursiveASTVisitor to check that certain matches are (or are
117/// not) observed during visitation.
Richard Smith87d8fb92012-06-24 23:56:26 +0000118///
James Dennett75c100b2012-08-24 06:59:51 +0000119/// This is a RecursiveASTVisitor for testing the RecursiveASTVisitor itself,
120/// and allows simple creation of test visitors running matches on only a small
Richard Smith87d8fb92012-06-24 23:56:26 +0000121/// subset of the Visit* methods.
122template <typename T, template <typename> class Visitor = TestVisitor>
123class ExpectedLocationVisitor : public Visitor<T> {
124public:
James Dennett75c100b2012-08-24 06:59:51 +0000125 /// \brief Expect 'Match' *not* to occur at the given 'Line' and 'Column'.
126 ///
127 /// Any number of matches can be disallowed.
128 void DisallowMatch(Twine Match, unsigned Line, unsigned Column) {
129 DisallowedMatches.push_back(MatchCandidate(Match, Line, Column));
Richard Smith87d8fb92012-06-24 23:56:26 +0000130 }
131
132 /// \brief Expect 'Match' to occur at the given 'Line' and 'Column'.
James Dennett75c100b2012-08-24 06:59:51 +0000133 ///
134 /// Any number of expected matches can be set by calling this repeatedly.
Martin Bohmed5f94a62016-08-17 14:59:53 +0000135 /// Each is expected to be matched 'Times' number of times. (This is useful in
136 /// cases in which different AST nodes can match at the same source code
137 /// location.)
138 void ExpectMatch(Twine Match, unsigned Line, unsigned Column,
139 unsigned Times = 1) {
140 ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column, Times));
James Dennett75c100b2012-08-24 06:59:51 +0000141 }
142
143 /// \brief Checks that all expected matches have been found.
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000144 ~ExpectedLocationVisitor() override {
James Dennett75c100b2012-08-24 06:59:51 +0000145 for (typename std::vector<ExpectedMatch>::const_iterator
146 It = ExpectedMatches.begin(), End = ExpectedMatches.end();
147 It != End; ++It) {
148 It->ExpectFound();
149 }
Richard Smith87d8fb92012-06-24 23:56:26 +0000150 }
151
152protected:
James Dennett75c100b2012-08-24 06:59:51 +0000153 /// \brief Checks an actual match against expected and disallowed matches.
Richard Smith87d8fb92012-06-24 23:56:26 +0000154 ///
155 /// Implementations are required to call this with appropriate values
156 /// for 'Name' during visitation.
157 void Match(StringRef Name, SourceLocation Location) {
James Dennett75c100b2012-08-24 06:59:51 +0000158 const FullSourceLoc FullLocation = this->Context->getFullLoc(Location);
159
160 for (typename std::vector<MatchCandidate>::const_iterator
161 It = DisallowedMatches.begin(), End = DisallowedMatches.end();
162 It != End; ++It) {
163 EXPECT_FALSE(It->Matches(Name, FullLocation))
164 << "Matched disallowed " << *It;
165 }
166
167 for (typename std::vector<ExpectedMatch>::iterator
168 It = ExpectedMatches.begin(), End = ExpectedMatches.end();
169 It != End; ++It) {
170 It->UpdateFor(Name, FullLocation, this->Context->getSourceManager());
Richard Smith87d8fb92012-06-24 23:56:26 +0000171 }
172 }
173
James Dennett75c100b2012-08-24 06:59:51 +0000174 private:
175 struct MatchCandidate {
176 std::string ExpectedName;
177 unsigned LineNumber;
178 unsigned ColumnNumber;
179
180 MatchCandidate(Twine Name, unsigned LineNumber, unsigned ColumnNumber)
181 : ExpectedName(Name.str()), LineNumber(LineNumber),
182 ColumnNumber(ColumnNumber) {
183 }
184
185 bool Matches(StringRef Name, FullSourceLoc const &Location) const {
186 return MatchesName(Name) && MatchesLocation(Location);
187 }
188
189 bool PartiallyMatches(StringRef Name, FullSourceLoc const &Location) const {
190 return MatchesName(Name) || MatchesLocation(Location);
191 }
192
193 bool MatchesName(StringRef Name) const {
194 return Name == ExpectedName;
195 }
196
197 bool MatchesLocation(FullSourceLoc const &Location) const {
198 return Location.isValid() &&
199 Location.getSpellingLineNumber() == LineNumber &&
200 Location.getSpellingColumnNumber() == ColumnNumber;
201 }
202
203 friend std::ostream &operator<<(std::ostream &Stream,
204 MatchCandidate const &Match) {
205 return Stream << Match.ExpectedName
206 << " at " << Match.LineNumber << ":" << Match.ColumnNumber;
207 }
208 };
209
210 struct ExpectedMatch {
Martin Bohmed5f94a62016-08-17 14:59:53 +0000211 ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber,
212 unsigned Times)
213 : Candidate(Name, LineNumber, ColumnNumber), TimesExpected(Times),
214 TimesSeen(0) {}
James Dennett75c100b2012-08-24 06:59:51 +0000215
216 void UpdateFor(StringRef Name, FullSourceLoc Location, SourceManager &SM) {
217 if (Candidate.Matches(Name, Location)) {
Martin Bohmed5f94a62016-08-17 14:59:53 +0000218 EXPECT_LT(TimesSeen, TimesExpected);
219 ++TimesSeen;
220 } else if (TimesSeen < TimesExpected &&
221 Candidate.PartiallyMatches(Name, Location)) {
James Dennett75c100b2012-08-24 06:59:51 +0000222 llvm::raw_string_ostream Stream(PartialMatches);
223 Stream << ", partial match: \"" << Name << "\" at ";
224 Location.print(Stream, SM);
225 }
226 }
227
228 void ExpectFound() const {
Martin Bohmed5f94a62016-08-17 14:59:53 +0000229 EXPECT_EQ(TimesExpected, TimesSeen)
James Dennett75c100b2012-08-24 06:59:51 +0000230 << "Expected \"" << Candidate.ExpectedName
231 << "\" at " << Candidate.LineNumber
232 << ":" << Candidate.ColumnNumber << PartialMatches;
233 }
234
235 MatchCandidate Candidate;
236 std::string PartialMatches;
Martin Bohmed5f94a62016-08-17 14:59:53 +0000237 unsigned TimesExpected;
238 unsigned TimesSeen;
James Dennett75c100b2012-08-24 06:59:51 +0000239 };
240
241 std::vector<MatchCandidate> DisallowedMatches;
242 std::vector<ExpectedMatch> ExpectedMatches;
Richard Smith87d8fb92012-06-24 23:56:26 +0000243};
Alexander Kornienkoab9db512015-06-22 23:07:51 +0000244}
Richard Smith87d8fb92012-06-24 23:56:26 +0000245
Manuel Klimek20c1c892014-10-09 15:02:06 +0000246#endif