blob: 2e64032cf4068e7ac07d065a20ebcd4f9f3fe5b9 [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
15#ifndef LLVM_CLANG_TEST_VISITOR_H
16#define LLVM_CLANG_TEST_VISITOR_H
17
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:
38 TestVisitor() { }
39
Matt Beaumont-Gaya0e2c042012-06-25 18:27:11 +000040 virtual ~TestVisitor() { }
41
Alp Toker62438da2014-06-06 15:05:09 +000042 enum Language { Lang_C, Lang_CXX98, Lang_CXX11, Lang_OBJC, Lang_CXX=Lang_CXX98 };
Richard Smith87deab32012-08-17 21:23:17 +000043
Richard Smith87d8fb92012-06-24 23:56:26 +000044 /// \brief Runs the current AST visitor over the given code.
Richard Smith87deab32012-08-17 21:23:17 +000045 bool runOver(StringRef Code, Language L = Lang_CXX) {
Nico Weber077a53e2012-08-30 02:02:19 +000046 std::vector<std::string> Args;
47 switch (L) {
48 case Lang_C: Args.push_back("-std=c99"); break;
James Dennetteb576c02013-06-30 03:05:49 +000049 case Lang_CXX98: Args.push_back("-std=c++98"); break;
50 case Lang_CXX11: Args.push_back("-std=c++11"); break;
Alp Toker62438da2014-06-06 15:05:09 +000051 case Lang_OBJC: Args.push_back("-ObjC"); break;
Nico Weber077a53e2012-08-30 02:02:19 +000052 }
53 return tooling::runToolOnCodeWithArgs(CreateTestAction(), Code, Args);
Richard Smith87d8fb92012-06-24 23:56:26 +000054 }
55
56 bool shouldVisitTemplateInstantiations() const {
57 return true;
58 }
59
Michael Hanc90d12d2013-09-11 15:53:29 +000060 bool shouldVisitImplicitCode() const {
61 return true;
62 }
63
Richard Smith87d8fb92012-06-24 23:56:26 +000064protected:
65 virtual ASTFrontendAction* CreateTestAction() {
66 return new TestAction(this);
67 }
68
69 class FindConsumer : public ASTConsumer {
70 public:
71 FindConsumer(TestVisitor *Visitor) : Visitor(Visitor) {}
72
73 virtual void HandleTranslationUnit(clang::ASTContext &Context) {
Manuel Klimek5da9dcb2012-07-05 18:13:01 +000074 Visitor->Context = &Context;
Richard Smith87d8fb92012-06-24 23:56:26 +000075 Visitor->TraverseDecl(Context.getTranslationUnitDecl());
76 }
77
78 private:
79 TestVisitor *Visitor;
80 };
81
82 class TestAction : public ASTFrontendAction {
83 public:
84 TestAction(TestVisitor *Visitor) : Visitor(Visitor) {}
85
86 virtual clang::ASTConsumer* CreateASTConsumer(
Manuel Klimek5da9dcb2012-07-05 18:13:01 +000087 CompilerInstance&, llvm::StringRef dummy) {
Richard Smith87d8fb92012-06-24 23:56:26 +000088 /// TestConsumer will be deleted by the framework calling us.
89 return new FindConsumer(Visitor);
90 }
91
92 protected:
93 TestVisitor *Visitor;
94 };
95
96 ASTContext *Context;
97};
98
James Dennett75c100b2012-08-24 06:59:51 +000099/// \brief A RecursiveASTVisitor to check that certain matches are (or are
100/// not) observed during visitation.
Richard Smith87d8fb92012-06-24 23:56:26 +0000101///
James Dennett75c100b2012-08-24 06:59:51 +0000102/// This is a RecursiveASTVisitor for testing the RecursiveASTVisitor itself,
103/// and allows simple creation of test visitors running matches on only a small
Richard Smith87d8fb92012-06-24 23:56:26 +0000104/// subset of the Visit* methods.
105template <typename T, template <typename> class Visitor = TestVisitor>
106class ExpectedLocationVisitor : public Visitor<T> {
107public:
James Dennett75c100b2012-08-24 06:59:51 +0000108 /// \brief Expect 'Match' *not* to occur at the given 'Line' and 'Column'.
109 ///
110 /// Any number of matches can be disallowed.
111 void DisallowMatch(Twine Match, unsigned Line, unsigned Column) {
112 DisallowedMatches.push_back(MatchCandidate(Match, Line, Column));
Richard Smith87d8fb92012-06-24 23:56:26 +0000113 }
114
115 /// \brief Expect 'Match' to occur at the given 'Line' and 'Column'.
James Dennett75c100b2012-08-24 06:59:51 +0000116 ///
117 /// Any number of expected matches can be set by calling this repeatedly.
118 /// Each is expected to be matched exactly once.
Richard Smith87d8fb92012-06-24 23:56:26 +0000119 void ExpectMatch(Twine Match, unsigned Line, unsigned Column) {
James Dennett75c100b2012-08-24 06:59:51 +0000120 ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column));
121 }
122
123 /// \brief Checks that all expected matches have been found.
124 virtual ~ExpectedLocationVisitor() {
125 for (typename std::vector<ExpectedMatch>::const_iterator
126 It = ExpectedMatches.begin(), End = ExpectedMatches.end();
127 It != End; ++It) {
128 It->ExpectFound();
129 }
Richard Smith87d8fb92012-06-24 23:56:26 +0000130 }
131
132protected:
James Dennett75c100b2012-08-24 06:59:51 +0000133 /// \brief Checks an actual match against expected and disallowed matches.
Richard Smith87d8fb92012-06-24 23:56:26 +0000134 ///
135 /// Implementations are required to call this with appropriate values
136 /// for 'Name' during visitation.
137 void Match(StringRef Name, SourceLocation Location) {
James Dennett75c100b2012-08-24 06:59:51 +0000138 const FullSourceLoc FullLocation = this->Context->getFullLoc(Location);
139
140 for (typename std::vector<MatchCandidate>::const_iterator
141 It = DisallowedMatches.begin(), End = DisallowedMatches.end();
142 It != End; ++It) {
143 EXPECT_FALSE(It->Matches(Name, FullLocation))
144 << "Matched disallowed " << *It;
145 }
146
147 for (typename std::vector<ExpectedMatch>::iterator
148 It = ExpectedMatches.begin(), End = ExpectedMatches.end();
149 It != End; ++It) {
150 It->UpdateFor(Name, FullLocation, this->Context->getSourceManager());
Richard Smith87d8fb92012-06-24 23:56:26 +0000151 }
152 }
153
James Dennett75c100b2012-08-24 06:59:51 +0000154 private:
155 struct MatchCandidate {
156 std::string ExpectedName;
157 unsigned LineNumber;
158 unsigned ColumnNumber;
159
160 MatchCandidate(Twine Name, unsigned LineNumber, unsigned ColumnNumber)
161 : ExpectedName(Name.str()), LineNumber(LineNumber),
162 ColumnNumber(ColumnNumber) {
163 }
164
165 bool Matches(StringRef Name, FullSourceLoc const &Location) const {
166 return MatchesName(Name) && MatchesLocation(Location);
167 }
168
169 bool PartiallyMatches(StringRef Name, FullSourceLoc const &Location) const {
170 return MatchesName(Name) || MatchesLocation(Location);
171 }
172
173 bool MatchesName(StringRef Name) const {
174 return Name == ExpectedName;
175 }
176
177 bool MatchesLocation(FullSourceLoc const &Location) const {
178 return Location.isValid() &&
179 Location.getSpellingLineNumber() == LineNumber &&
180 Location.getSpellingColumnNumber() == ColumnNumber;
181 }
182
183 friend std::ostream &operator<<(std::ostream &Stream,
184 MatchCandidate const &Match) {
185 return Stream << Match.ExpectedName
186 << " at " << Match.LineNumber << ":" << Match.ColumnNumber;
187 }
188 };
189
190 struct ExpectedMatch {
191 ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber)
192 : Candidate(Name, LineNumber, ColumnNumber), Found(false) {}
193
194 void UpdateFor(StringRef Name, FullSourceLoc Location, SourceManager &SM) {
195 if (Candidate.Matches(Name, Location)) {
196 EXPECT_TRUE(!Found);
197 Found = true;
198 } else if (!Found && Candidate.PartiallyMatches(Name, Location)) {
199 llvm::raw_string_ostream Stream(PartialMatches);
200 Stream << ", partial match: \"" << Name << "\" at ";
201 Location.print(Stream, SM);
202 }
203 }
204
205 void ExpectFound() const {
206 EXPECT_TRUE(Found)
207 << "Expected \"" << Candidate.ExpectedName
208 << "\" at " << Candidate.LineNumber
209 << ":" << Candidate.ColumnNumber << PartialMatches;
210 }
211
212 MatchCandidate Candidate;
213 std::string PartialMatches;
214 bool Found;
215 };
216
217 std::vector<MatchCandidate> DisallowedMatches;
218 std::vector<ExpectedMatch> ExpectedMatches;
Richard Smith87d8fb92012-06-24 23:56:26 +0000219};
220}
221
222#endif /* LLVM_CLANG_TEST_VISITOR_H */