blob: a762ec8b1453052d82e6d67cd6f5d0a6bfe0c89e [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) {
56 case Lang_C: Args.push_back("-std=c99"); break;
James Dennetteb576c02013-06-30 03:05:49 +000057 case Lang_CXX98: Args.push_back("-std=c++98"); break;
58 case Lang_CXX11: Args.push_back("-std=c++11"); break;
Martin Bohmed5f94a62016-08-17 14:59:53 +000059 case Lang_CXX14: Args.push_back("-std=c++14"); break;
Alp Toker62438da2014-06-06 15:05:09 +000060 case Lang_OBJC: Args.push_back("-ObjC"); break;
Alp Tokere3ad5312014-06-26 01:42:24 +000061 case Lang_OBJCXX11:
62 Args.push_back("-ObjC++");
63 Args.push_back("-std=c++11");
Alp Toker0843bff2014-06-26 02:07:06 +000064 Args.push_back("-fblocks");
Alp Tokere3ad5312014-06-26 01:42:24 +000065 break;
Nico Weber077a53e2012-08-30 02:02:19 +000066 }
67 return tooling::runToolOnCodeWithArgs(CreateTestAction(), Code, Args);
Richard Smith87d8fb92012-06-24 23:56:26 +000068 }
69
70 bool shouldVisitTemplateInstantiations() const {
71 return true;
72 }
73
Michael Hanc90d12d2013-09-11 15:53:29 +000074 bool shouldVisitImplicitCode() const {
75 return true;
76 }
77
Richard Smith87d8fb92012-06-24 23:56:26 +000078protected:
79 virtual ASTFrontendAction* CreateTestAction() {
80 return new TestAction(this);
81 }
82
83 class FindConsumer : public ASTConsumer {
84 public:
85 FindConsumer(TestVisitor *Visitor) : Visitor(Visitor) {}
86
Alexander Kornienko34eb2072015-04-11 02:00:23 +000087 void HandleTranslationUnit(clang::ASTContext &Context) override {
Manuel Klimek5da9dcb2012-07-05 18:13:01 +000088 Visitor->Context = &Context;
Richard Smith87d8fb92012-06-24 23:56:26 +000089 Visitor->TraverseDecl(Context.getTranslationUnitDecl());
90 }
91
92 private:
93 TestVisitor *Visitor;
94 };
95
96 class TestAction : public ASTFrontendAction {
97 public:
98 TestAction(TestVisitor *Visitor) : Visitor(Visitor) {}
99
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000100 std::unique_ptr<clang::ASTConsumer>
101 CreateASTConsumer(CompilerInstance &, llvm::StringRef dummy) override {
Richard Smith87d8fb92012-06-24 23:56:26 +0000102 /// TestConsumer will be deleted by the framework calling us.
David Blaikie6beb6aa2014-08-10 19:56:51 +0000103 return llvm::make_unique<FindConsumer>(Visitor);
Richard Smith87d8fb92012-06-24 23:56:26 +0000104 }
105
106 protected:
107 TestVisitor *Visitor;
108 };
109
110 ASTContext *Context;
111};
112
James Dennett75c100b2012-08-24 06:59:51 +0000113/// \brief A RecursiveASTVisitor to check that certain matches are (or are
114/// not) observed during visitation.
Richard Smith87d8fb92012-06-24 23:56:26 +0000115///
James Dennett75c100b2012-08-24 06:59:51 +0000116/// This is a RecursiveASTVisitor for testing the RecursiveASTVisitor itself,
117/// and allows simple creation of test visitors running matches on only a small
Richard Smith87d8fb92012-06-24 23:56:26 +0000118/// subset of the Visit* methods.
119template <typename T, template <typename> class Visitor = TestVisitor>
120class ExpectedLocationVisitor : public Visitor<T> {
121public:
James Dennett75c100b2012-08-24 06:59:51 +0000122 /// \brief Expect 'Match' *not* to occur at the given 'Line' and 'Column'.
123 ///
124 /// Any number of matches can be disallowed.
125 void DisallowMatch(Twine Match, unsigned Line, unsigned Column) {
126 DisallowedMatches.push_back(MatchCandidate(Match, Line, Column));
Richard Smith87d8fb92012-06-24 23:56:26 +0000127 }
128
129 /// \brief Expect 'Match' to occur at the given 'Line' and 'Column'.
James Dennett75c100b2012-08-24 06:59:51 +0000130 ///
131 /// Any number of expected matches can be set by calling this repeatedly.
Martin Bohmed5f94a62016-08-17 14:59:53 +0000132 /// Each is expected to be matched 'Times' number of times. (This is useful in
133 /// cases in which different AST nodes can match at the same source code
134 /// location.)
135 void ExpectMatch(Twine Match, unsigned Line, unsigned Column,
136 unsigned Times = 1) {
137 ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column, Times));
James Dennett75c100b2012-08-24 06:59:51 +0000138 }
139
140 /// \brief Checks that all expected matches have been found.
Alexander Kornienko34eb2072015-04-11 02:00:23 +0000141 ~ExpectedLocationVisitor() override {
James Dennett75c100b2012-08-24 06:59:51 +0000142 for (typename std::vector<ExpectedMatch>::const_iterator
143 It = ExpectedMatches.begin(), End = ExpectedMatches.end();
144 It != End; ++It) {
145 It->ExpectFound();
146 }
Richard Smith87d8fb92012-06-24 23:56:26 +0000147 }
148
149protected:
James Dennett75c100b2012-08-24 06:59:51 +0000150 /// \brief Checks an actual match against expected and disallowed matches.
Richard Smith87d8fb92012-06-24 23:56:26 +0000151 ///
152 /// Implementations are required to call this with appropriate values
153 /// for 'Name' during visitation.
154 void Match(StringRef Name, SourceLocation Location) {
James Dennett75c100b2012-08-24 06:59:51 +0000155 const FullSourceLoc FullLocation = this->Context->getFullLoc(Location);
156
157 for (typename std::vector<MatchCandidate>::const_iterator
158 It = DisallowedMatches.begin(), End = DisallowedMatches.end();
159 It != End; ++It) {
160 EXPECT_FALSE(It->Matches(Name, FullLocation))
161 << "Matched disallowed " << *It;
162 }
163
164 for (typename std::vector<ExpectedMatch>::iterator
165 It = ExpectedMatches.begin(), End = ExpectedMatches.end();
166 It != End; ++It) {
167 It->UpdateFor(Name, FullLocation, this->Context->getSourceManager());
Richard Smith87d8fb92012-06-24 23:56:26 +0000168 }
169 }
170
James Dennett75c100b2012-08-24 06:59:51 +0000171 private:
172 struct MatchCandidate {
173 std::string ExpectedName;
174 unsigned LineNumber;
175 unsigned ColumnNumber;
176
177 MatchCandidate(Twine Name, unsigned LineNumber, unsigned ColumnNumber)
178 : ExpectedName(Name.str()), LineNumber(LineNumber),
179 ColumnNumber(ColumnNumber) {
180 }
181
182 bool Matches(StringRef Name, FullSourceLoc const &Location) const {
183 return MatchesName(Name) && MatchesLocation(Location);
184 }
185
186 bool PartiallyMatches(StringRef Name, FullSourceLoc const &Location) const {
187 return MatchesName(Name) || MatchesLocation(Location);
188 }
189
190 bool MatchesName(StringRef Name) const {
191 return Name == ExpectedName;
192 }
193
194 bool MatchesLocation(FullSourceLoc const &Location) const {
195 return Location.isValid() &&
196 Location.getSpellingLineNumber() == LineNumber &&
197 Location.getSpellingColumnNumber() == ColumnNumber;
198 }
199
200 friend std::ostream &operator<<(std::ostream &Stream,
201 MatchCandidate const &Match) {
202 return Stream << Match.ExpectedName
203 << " at " << Match.LineNumber << ":" << Match.ColumnNumber;
204 }
205 };
206
207 struct ExpectedMatch {
Martin Bohmed5f94a62016-08-17 14:59:53 +0000208 ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber,
209 unsigned Times)
210 : Candidate(Name, LineNumber, ColumnNumber), TimesExpected(Times),
211 TimesSeen(0) {}
James Dennett75c100b2012-08-24 06:59:51 +0000212
213 void UpdateFor(StringRef Name, FullSourceLoc Location, SourceManager &SM) {
214 if (Candidate.Matches(Name, Location)) {
Martin Bohmed5f94a62016-08-17 14:59:53 +0000215 EXPECT_LT(TimesSeen, TimesExpected);
216 ++TimesSeen;
217 } else if (TimesSeen < TimesExpected &&
218 Candidate.PartiallyMatches(Name, Location)) {
James Dennett75c100b2012-08-24 06:59:51 +0000219 llvm::raw_string_ostream Stream(PartialMatches);
220 Stream << ", partial match: \"" << Name << "\" at ";
221 Location.print(Stream, SM);
222 }
223 }
224
225 void ExpectFound() const {
Martin Bohmed5f94a62016-08-17 14:59:53 +0000226 EXPECT_EQ(TimesExpected, TimesSeen)
James Dennett75c100b2012-08-24 06:59:51 +0000227 << "Expected \"" << Candidate.ExpectedName
228 << "\" at " << Candidate.LineNumber
229 << ":" << Candidate.ColumnNumber << PartialMatches;
230 }
231
232 MatchCandidate Candidate;
233 std::string PartialMatches;
Martin Bohmed5f94a62016-08-17 14:59:53 +0000234 unsigned TimesExpected;
235 unsigned TimesSeen;
James Dennett75c100b2012-08-24 06:59:51 +0000236 };
237
238 std::vector<MatchCandidate> DisallowedMatches;
239 std::vector<ExpectedMatch> ExpectedMatches;
Richard Smith87d8fb92012-06-24 23:56:26 +0000240};
Alexander Kornienkoab9db512015-06-22 23:07:51 +0000241}
Richard Smith87d8fb92012-06-24 23:56:26 +0000242
Manuel Klimek20c1c892014-10-09 15:02:06 +0000243#endif