brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.9 KiB · fdf57a9 Raw
285 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// \brief Defines utility templates for RecursiveASTVisitor related tests.11///12//===----------------------------------------------------------------------===//13 14#ifndef LLVM_CLANG_UNITTESTS_TOOLING_TESTVISITOR_H15#define LLVM_CLANG_UNITTESTS_TOOLING_TESTVISITOR_H16 17#include "clang/AST/ASTConsumer.h"18#include "clang/AST/ASTContext.h"19#include "clang/AST/DynamicRecursiveASTVisitor.h"20#include "clang/Frontend/CompilerInstance.h"21#include "clang/Frontend/FrontendAction.h"22#include "clang/Tooling/Tooling.h"23#include "gtest/gtest.h"24#include <vector>25 26namespace clang {27namespace detail {28// Use 'TestVisitor' or include 'CRTPTestVisitor.h' and use 'CRTPTestVisitor'29// instead of using this directly.30class TestVisitorHelper {31public:32  enum Language {33    Lang_C,34    Lang_CXX98,35    Lang_CXX11,36    Lang_CXX14,37    Lang_CXX17,38    Lang_CXX2a,39    Lang_OBJC,40    Lang_OBJCXX11,41    Lang_CXX = Lang_CXX9842  };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:49      Args.push_back("-x");50      Args.push_back("c");51      break;52    case Lang_CXX98:53      Args.push_back("-std=c++98");54      break;55    case Lang_CXX11:56      Args.push_back("-std=c++11");57      break;58    case Lang_CXX14:59      Args.push_back("-std=c++14");60      break;61    case Lang_CXX17:62      Args.push_back("-std=c++17");63      break;64    case Lang_CXX2a:65      Args.push_back("-std=c++2a");66      break;67    case Lang_OBJC:68      Args.push_back("-ObjC");69      Args.push_back("-fobjc-runtime=macosx-10.12.0");70      break;71    case Lang_OBJCXX11:72      Args.push_back("-ObjC++");73      Args.push_back("-std=c++11");74      Args.push_back("-fblocks");75      break;76    }77    return tooling::runToolOnCodeWithArgs(CreateTestAction(), Code, Args);78  }79 80protected:81  TestVisitorHelper() = default;82  virtual ~TestVisitorHelper() = default;83  virtual void InvokeTraverseDecl(TranslationUnitDecl *D) = 0;84 85  virtual std::unique_ptr<ASTFrontendAction> CreateTestAction() {86    return std::make_unique<TestAction>(this);87  }88 89  class FindConsumer : public ASTConsumer {90  public:91    FindConsumer(TestVisitorHelper *Visitor) : Visitor(Visitor) {}92 93    void HandleTranslationUnit(clang::ASTContext &Context) override {94      Visitor->Context = &Context;95      Visitor->InvokeTraverseDecl(Context.getTranslationUnitDecl());96    }97 98  private:99    TestVisitorHelper *Visitor;100  };101 102  class TestAction : public ASTFrontendAction {103  public:104    TestAction(TestVisitorHelper *Visitor) : Visitor(Visitor) {}105 106    std::unique_ptr<clang::ASTConsumer>107    CreateASTConsumer(CompilerInstance &, llvm::StringRef dummy) override {108      /// TestConsumer will be deleted by the framework calling us.109      return std::make_unique<FindConsumer>(Visitor);110    }111 112  protected:113    TestVisitorHelper *Visitor;114  };115 116  ASTContext *Context;117};118 119class ExpectedLocationVisitorHelper {120public:121  /// \brief Expect 'Match' *not* to occur at the given 'Line' and 'Column'.122  ///123  /// Any number of matches can be disallowed.124  void DisallowMatch(Twine Match, unsigned Line, unsigned Column) {125    DisallowedMatches.push_back(MatchCandidate(Match, Line, Column));126  }127 128  /// \brief Expect 'Match' to occur at the given 'Line' and 'Column'.129  ///130  /// Any number of expected matches can be set by calling this repeatedly.131  /// Each is expected to be matched 'Times' number of times. (This is useful in132  /// cases in which different AST nodes can match at the same source code133  /// location.)134  void ExpectMatch(Twine Match, unsigned Line, unsigned Column,135                   unsigned Times = 1) {136    ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column, Times));137  }138 139  /// \brief Checks that all expected matches have been found.140  virtual ~ExpectedLocationVisitorHelper() {141    // FIXME: Range-based for loop.142    for (std::vector<ExpectedMatch>::const_iterator143             It = ExpectedMatches.begin(),144             End = ExpectedMatches.end();145         It != End; ++It) {146      It->ExpectFound();147    }148  }149 150protected:151  virtual ASTContext *getASTContext() = 0;152 153  /// \brief Checks an actual match against expected and disallowed matches.154  ///155  /// Implementations are required to call this with appropriate values156  /// for 'Name' during visitation.157  void Match(StringRef Name, SourceLocation Location) {158    const FullSourceLoc FullLocation = getASTContext()->getFullLoc(Location);159 160    // FIXME: Range-based for loop.161    for (std::vector<MatchCandidate>::const_iterator162             It = DisallowedMatches.begin(),163             End = DisallowedMatches.end();164         It != End; ++It) {165      EXPECT_FALSE(It->Matches(Name, FullLocation))166          << "Matched disallowed " << *It;167    }168 169    // FIXME: Range-based for loop.170    for (std::vector<ExpectedMatch>::iterator It = ExpectedMatches.begin(),171                                              End = ExpectedMatches.end();172         It != End; ++It) {173      It->UpdateFor(Name, FullLocation, getASTContext()->getSourceManager());174    }175  }176 177private:178  struct MatchCandidate {179    std::string ExpectedName;180    unsigned LineNumber;181    unsigned ColumnNumber;182 183    MatchCandidate(Twine Name, unsigned LineNumber, unsigned ColumnNumber)184      : ExpectedName(Name.str()), LineNumber(LineNumber),185        ColumnNumber(ColumnNumber) {186    }187 188    bool Matches(StringRef Name, FullSourceLoc const &Location) const {189      return MatchesName(Name) && MatchesLocation(Location);190    }191 192    bool PartiallyMatches(StringRef Name, FullSourceLoc const &Location) const {193      return MatchesName(Name) || MatchesLocation(Location);194    }195 196    bool MatchesName(StringRef Name) const {197      return Name == ExpectedName;198    }199 200    bool MatchesLocation(FullSourceLoc const &Location) const {201      return Location.isValid() &&202          Location.getSpellingLineNumber() == LineNumber &&203          Location.getSpellingColumnNumber() == ColumnNumber;204    }205 206    friend std::ostream &operator<<(std::ostream &Stream,207                                    MatchCandidate const &Match) {208      return Stream << Match.ExpectedName209                    << " at " << Match.LineNumber << ":" << Match.ColumnNumber;210    }211  };212 213  struct ExpectedMatch {214    ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber,215                  unsigned Times)216        : Candidate(Name, LineNumber, ColumnNumber), TimesExpected(Times),217          TimesSeen(0) {}218 219    void UpdateFor(StringRef Name, FullSourceLoc Location, SourceManager &SM) {220      if (Candidate.Matches(Name, Location)) {221        EXPECT_LT(TimesSeen, TimesExpected);222        ++TimesSeen;223      } else if (TimesSeen < TimesExpected &&224                 Candidate.PartiallyMatches(Name, Location)) {225        llvm::raw_string_ostream Stream(PartialMatches);226        Stream << ", partial match: \"" << Name << "\" at ";227        Location.print(Stream, SM);228      }229    }230 231    void ExpectFound() const {232      EXPECT_EQ(TimesExpected, TimesSeen)233          << "Expected \"" << Candidate.ExpectedName234          << "\" at " << Candidate.LineNumber235          << ":" << Candidate.ColumnNumber << PartialMatches;236    }237 238    MatchCandidate Candidate;239    std::string PartialMatches;240    unsigned TimesExpected;241    unsigned TimesSeen;242  };243 244  std::vector<MatchCandidate> DisallowedMatches;245  std::vector<ExpectedMatch> ExpectedMatches;246};247} // namespace detail248 249/// \brief Base class for simple (Dynamic)RecursiveASTVisitor based tests.250///251/// This is a drop-in replacement for DynamicRecursiveASTVisitor itself, with252/// the additional capability of running it over a snippet of code.253///254/// Visits template instantiations and implicit code by default.255///256/// For post-order traversal etc. use CTRPTestVisitor from257/// CTRPTestVisitor.h instead.258class TestVisitor : public DynamicRecursiveASTVisitor,259                    public detail::TestVisitorHelper {260public:261  TestVisitor() {262    ShouldVisitTemplateInstantiations = true;263    ShouldVisitImplicitCode = true;264  }265 266  void InvokeTraverseDecl(TranslationUnitDecl *D) override { TraverseDecl(D); }267};268 269/// \brief A RecursiveASTVisitor to check that certain matches are (or are270/// not) observed during visitation.271///272/// This is a RecursiveASTVisitor for testing the RecursiveASTVisitor itself,273/// and allows simple creation of test visitors running matches on only a small274/// subset of the Visit* methods.275///276/// For post-order traversal etc. use CTRPExpectedLocationVisitor from277/// CTRPTestVisitor.h instead.278class ExpectedLocationVisitor : public TestVisitor,279                                public detail::ExpectedLocationVisitorHelper {280  ASTContext *getASTContext() override { return Context; }281};282} // namespace clang283 284#endif285