TestVisitor.h 8.0 KB

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