• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===--- 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 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// \brief Defines utility templates for RecursiveASTVisitor related tests.
12 ///
13 //===----------------------------------------------------------------------===//
14 
15 #ifndef LLVM_CLANG_TEST_VISITOR_H
16 #define LLVM_CLANG_TEST_VISITOR_H
17 
18 #include "clang/AST/ASTConsumer.h"
19 #include "clang/AST/ASTContext.h"
20 #include "clang/AST/RecursiveASTVisitor.h"
21 #include "clang/Frontend/CompilerInstance.h"
22 #include "clang/Frontend/FrontendAction.h"
23 #include "clang/Tooling/Tooling.h"
24 #include "gtest/gtest.h"
25 #include <vector>
26 
27 namespace clang {
28 
29 /// \brief Base class for simple RecursiveASTVisitor based tests.
30 ///
31 /// This is a drop-in replacement for RecursiveASTVisitor itself, with the
32 /// additional capability of running it over a snippet of code.
33 ///
34 /// Visits template instantiations (but not implicit code) by default.
35 template <typename T>
36 class TestVisitor : public RecursiveASTVisitor<T> {
37 public:
TestVisitor()38   TestVisitor() { }
39 
~TestVisitor()40   virtual ~TestVisitor() { }
41 
42   enum Language { Lang_C, Lang_CXX98, Lang_CXX11, Lang_CXX=Lang_CXX98 };
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: Args.push_back("-std=c99"); break;
49       case Lang_CXX98: Args.push_back("-std=c++98"); break;
50       case Lang_CXX11: Args.push_back("-std=c++11"); break;
51     }
52     return tooling::runToolOnCodeWithArgs(CreateTestAction(), Code, Args);
53   }
54 
shouldVisitTemplateInstantiations()55   bool shouldVisitTemplateInstantiations() const {
56     return true;
57   }
58 
59 protected:
CreateTestAction()60   virtual ASTFrontendAction* CreateTestAction() {
61     return new TestAction(this);
62   }
63 
64   class FindConsumer : public ASTConsumer {
65   public:
FindConsumer(TestVisitor * Visitor)66     FindConsumer(TestVisitor *Visitor) : Visitor(Visitor) {}
67 
HandleTranslationUnit(clang::ASTContext & Context)68     virtual void HandleTranslationUnit(clang::ASTContext &Context) {
69       Visitor->Context = &Context;
70       Visitor->TraverseDecl(Context.getTranslationUnitDecl());
71     }
72 
73   private:
74     TestVisitor *Visitor;
75   };
76 
77   class TestAction : public ASTFrontendAction {
78   public:
TestAction(TestVisitor * Visitor)79     TestAction(TestVisitor *Visitor) : Visitor(Visitor) {}
80 
CreateASTConsumer(CompilerInstance &,llvm::StringRef dummy)81     virtual clang::ASTConsumer* CreateASTConsumer(
82         CompilerInstance&, llvm::StringRef dummy) {
83       /// TestConsumer will be deleted by the framework calling us.
84       return new FindConsumer(Visitor);
85     }
86 
87   protected:
88     TestVisitor *Visitor;
89   };
90 
91   ASTContext *Context;
92 };
93 
94 /// \brief A RecursiveASTVisitor to check that certain matches are (or are
95 /// not) observed during visitation.
96 ///
97 /// This is a RecursiveASTVisitor for testing the RecursiveASTVisitor itself,
98 /// and allows simple creation of test visitors running matches on only a small
99 /// subset of the Visit* methods.
100 template <typename T, template <typename> class Visitor = TestVisitor>
101 class ExpectedLocationVisitor : public Visitor<T> {
102 public:
103   /// \brief Expect 'Match' *not* to occur at the given 'Line' and 'Column'.
104   ///
105   /// Any number of matches can be disallowed.
DisallowMatch(Twine Match,unsigned Line,unsigned Column)106   void DisallowMatch(Twine Match, unsigned Line, unsigned Column) {
107     DisallowedMatches.push_back(MatchCandidate(Match, Line, Column));
108   }
109 
110   /// \brief Expect 'Match' to occur at the given 'Line' and 'Column'.
111   ///
112   /// Any number of expected matches can be set by calling this repeatedly.
113   /// Each is expected to be matched exactly once.
ExpectMatch(Twine Match,unsigned Line,unsigned Column)114   void ExpectMatch(Twine Match, unsigned Line, unsigned Column) {
115     ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column));
116   }
117 
118   /// \brief Checks that all expected matches have been found.
~ExpectedLocationVisitor()119   virtual ~ExpectedLocationVisitor() {
120     for (typename std::vector<ExpectedMatch>::const_iterator
121              It = ExpectedMatches.begin(), End = ExpectedMatches.end();
122          It != End; ++It) {
123       It->ExpectFound();
124     }
125   }
126 
127 protected:
128   /// \brief Checks an actual match against expected and disallowed matches.
129   ///
130   /// Implementations are required to call this with appropriate values
131   /// for 'Name' during visitation.
Match(StringRef Name,SourceLocation Location)132   void Match(StringRef Name, SourceLocation Location) {
133     const FullSourceLoc FullLocation = this->Context->getFullLoc(Location);
134 
135     for (typename std::vector<MatchCandidate>::const_iterator
136              It = DisallowedMatches.begin(), End = DisallowedMatches.end();
137          It != End; ++It) {
138       EXPECT_FALSE(It->Matches(Name, FullLocation))
139           << "Matched disallowed " << *It;
140     }
141 
142     for (typename std::vector<ExpectedMatch>::iterator
143              It = ExpectedMatches.begin(), End = ExpectedMatches.end();
144          It != End; ++It) {
145       It->UpdateFor(Name, FullLocation, this->Context->getSourceManager());
146     }
147   }
148 
149  private:
150   struct MatchCandidate {
151     std::string ExpectedName;
152     unsigned LineNumber;
153     unsigned ColumnNumber;
154 
MatchCandidateMatchCandidate155     MatchCandidate(Twine Name, unsigned LineNumber, unsigned ColumnNumber)
156       : ExpectedName(Name.str()), LineNumber(LineNumber),
157         ColumnNumber(ColumnNumber) {
158     }
159 
MatchesMatchCandidate160     bool Matches(StringRef Name, FullSourceLoc const &Location) const {
161       return MatchesName(Name) && MatchesLocation(Location);
162     }
163 
PartiallyMatchesMatchCandidate164     bool PartiallyMatches(StringRef Name, FullSourceLoc const &Location) const {
165       return MatchesName(Name) || MatchesLocation(Location);
166     }
167 
MatchesNameMatchCandidate168     bool MatchesName(StringRef Name) const {
169       return Name == ExpectedName;
170     }
171 
MatchesLocationMatchCandidate172     bool MatchesLocation(FullSourceLoc const &Location) const {
173       return Location.isValid() &&
174           Location.getSpellingLineNumber() == LineNumber &&
175           Location.getSpellingColumnNumber() == ColumnNumber;
176     }
177 
178     friend std::ostream &operator<<(std::ostream &Stream,
179                                     MatchCandidate const &Match) {
180       return Stream << Match.ExpectedName
181                     << " at " << Match.LineNumber << ":" << Match.ColumnNumber;
182     }
183   };
184 
185   struct ExpectedMatch {
ExpectedMatchExpectedMatch186     ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber)
187       : Candidate(Name, LineNumber, ColumnNumber), Found(false) {}
188 
UpdateForExpectedMatch189     void UpdateFor(StringRef Name, FullSourceLoc Location, SourceManager &SM) {
190       if (Candidate.Matches(Name, Location)) {
191         EXPECT_TRUE(!Found);
192         Found = true;
193       } else if (!Found && Candidate.PartiallyMatches(Name, Location)) {
194         llvm::raw_string_ostream Stream(PartialMatches);
195         Stream << ", partial match: \"" << Name << "\" at ";
196         Location.print(Stream, SM);
197       }
198     }
199 
ExpectFoundExpectedMatch200     void ExpectFound() const {
201       EXPECT_TRUE(Found)
202           << "Expected \"" << Candidate.ExpectedName
203           << "\" at " << Candidate.LineNumber
204           << ":" << Candidate.ColumnNumber << PartialMatches;
205     }
206 
207     MatchCandidate Candidate;
208     std::string PartialMatches;
209     bool Found;
210   };
211 
212   std::vector<MatchCandidate> DisallowedMatches;
213   std::vector<ExpectedMatch> ExpectedMatches;
214 };
215 }
216 
217 #endif /* LLVM_CLANG_TEST_VISITOR_H */
218