123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- //===--- TestVisitor.h ------------------------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- ///
- /// \file
- /// \brief Defines utility templates for RecursiveASTVisitor related tests.
- ///
- //===----------------------------------------------------------------------===//
- #ifndef LLVM_CLANG_UNITTESTS_TOOLING_TESTVISITOR_H
- #define LLVM_CLANG_UNITTESTS_TOOLING_TESTVISITOR_H
- #include "clang/AST/ASTConsumer.h"
- #include "clang/AST/ASTContext.h"
- #include "clang/AST/RecursiveASTVisitor.h"
- #include "clang/Frontend/CompilerInstance.h"
- #include "clang/Frontend/FrontendAction.h"
- #include "clang/Tooling/Tooling.h"
- #include "gtest/gtest.h"
- #include <vector>
- namespace clang {
- /// \brief Base class for simple RecursiveASTVisitor based tests.
- ///
- /// This is a drop-in replacement for RecursiveASTVisitor itself, with the
- /// additional capability of running it over a snippet of code.
- ///
- /// Visits template instantiations and implicit code by default.
- template <typename T>
- class TestVisitor : public RecursiveASTVisitor<T> {
- public:
- TestVisitor() { }
- virtual ~TestVisitor() { }
- enum Language {
- Lang_C,
- Lang_CXX98,
- Lang_CXX11,
- Lang_CXX14,
- Lang_CXX17,
- Lang_CXX2a,
- Lang_OBJC,
- Lang_OBJCXX11,
- Lang_CXX = Lang_CXX98
- };
- /// \brief Runs the current AST visitor over the given code.
- bool runOver(StringRef Code, Language L = Lang_CXX) {
- std::vector<std::string> Args;
- switch (L) {
- case Lang_C:
- Args.push_back("-x");
- Args.push_back("c");
- break;
- case Lang_CXX98: Args.push_back("-std=c++98"); break;
- case Lang_CXX11: Args.push_back("-std=c++11"); break;
- case Lang_CXX14: Args.push_back("-std=c++14"); break;
- case Lang_CXX17: Args.push_back("-std=c++17"); break;
- case Lang_CXX2a: Args.push_back("-std=c++2a"); break;
- case Lang_OBJC:
- Args.push_back("-ObjC");
- Args.push_back("-fobjc-runtime=macosx-10.12.0");
- break;
- case Lang_OBJCXX11:
- Args.push_back("-ObjC++");
- Args.push_back("-std=c++11");
- Args.push_back("-fblocks");
- break;
- }
- return tooling::runToolOnCodeWithArgs(CreateTestAction(), Code, Args);
- }
- bool shouldVisitTemplateInstantiations() const {
- return true;
- }
- bool shouldVisitImplicitCode() const {
- return true;
- }
- protected:
- virtual std::unique_ptr<ASTFrontendAction> CreateTestAction() {
- return std::make_unique<TestAction>(this);
- }
- class FindConsumer : public ASTConsumer {
- public:
- FindConsumer(TestVisitor *Visitor) : Visitor(Visitor) {}
- void HandleTranslationUnit(clang::ASTContext &Context) override {
- Visitor->Context = &Context;
- Visitor->TraverseDecl(Context.getTranslationUnitDecl());
- }
- private:
- TestVisitor *Visitor;
- };
- class TestAction : public ASTFrontendAction {
- public:
- TestAction(TestVisitor *Visitor) : Visitor(Visitor) {}
- std::unique_ptr<clang::ASTConsumer>
- CreateASTConsumer(CompilerInstance &, llvm::StringRef dummy) override {
- /// TestConsumer will be deleted by the framework calling us.
- return std::make_unique<FindConsumer>(Visitor);
- }
- protected:
- TestVisitor *Visitor;
- };
- ASTContext *Context;
- };
- /// \brief A RecursiveASTVisitor to check that certain matches are (or are
- /// not) observed during visitation.
- ///
- /// This is a RecursiveASTVisitor for testing the RecursiveASTVisitor itself,
- /// and allows simple creation of test visitors running matches on only a small
- /// subset of the Visit* methods.
- template <typename T, template <typename> class Visitor = TestVisitor>
- class ExpectedLocationVisitor : public Visitor<T> {
- public:
- /// \brief Expect 'Match' *not* to occur at the given 'Line' and 'Column'.
- ///
- /// Any number of matches can be disallowed.
- void DisallowMatch(Twine Match, unsigned Line, unsigned Column) {
- DisallowedMatches.push_back(MatchCandidate(Match, Line, Column));
- }
- /// \brief Expect 'Match' to occur at the given 'Line' and 'Column'.
- ///
- /// Any number of expected matches can be set by calling this repeatedly.
- /// Each is expected to be matched 'Times' number of times. (This is useful in
- /// cases in which different AST nodes can match at the same source code
- /// location.)
- void ExpectMatch(Twine Match, unsigned Line, unsigned Column,
- unsigned Times = 1) {
- ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column, Times));
- }
- /// \brief Checks that all expected matches have been found.
- ~ExpectedLocationVisitor() override {
- for (typename std::vector<ExpectedMatch>::const_iterator
- It = ExpectedMatches.begin(), End = ExpectedMatches.end();
- It != End; ++It) {
- It->ExpectFound();
- }
- }
- protected:
- /// \brief Checks an actual match against expected and disallowed matches.
- ///
- /// Implementations are required to call this with appropriate values
- /// for 'Name' during visitation.
- void Match(StringRef Name, SourceLocation Location) {
- const FullSourceLoc FullLocation = this->Context->getFullLoc(Location);
- for (typename std::vector<MatchCandidate>::const_iterator
- It = DisallowedMatches.begin(), End = DisallowedMatches.end();
- It != End; ++It) {
- EXPECT_FALSE(It->Matches(Name, FullLocation))
- << "Matched disallowed " << *It;
- }
- for (typename std::vector<ExpectedMatch>::iterator
- It = ExpectedMatches.begin(), End = ExpectedMatches.end();
- It != End; ++It) {
- It->UpdateFor(Name, FullLocation, this->Context->getSourceManager());
- }
- }
- private:
- struct MatchCandidate {
- std::string ExpectedName;
- unsigned LineNumber;
- unsigned ColumnNumber;
- MatchCandidate(Twine Name, unsigned LineNumber, unsigned ColumnNumber)
- : ExpectedName(Name.str()), LineNumber(LineNumber),
- ColumnNumber(ColumnNumber) {
- }
- bool Matches(StringRef Name, FullSourceLoc const &Location) const {
- return MatchesName(Name) && MatchesLocation(Location);
- }
- bool PartiallyMatches(StringRef Name, FullSourceLoc const &Location) const {
- return MatchesName(Name) || MatchesLocation(Location);
- }
- bool MatchesName(StringRef Name) const {
- return Name == ExpectedName;
- }
- bool MatchesLocation(FullSourceLoc const &Location) const {
- return Location.isValid() &&
- Location.getSpellingLineNumber() == LineNumber &&
- Location.getSpellingColumnNumber() == ColumnNumber;
- }
- friend std::ostream &operator<<(std::ostream &Stream,
- MatchCandidate const &Match) {
- return Stream << Match.ExpectedName
- << " at " << Match.LineNumber << ":" << Match.ColumnNumber;
- }
- };
- struct ExpectedMatch {
- ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber,
- unsigned Times)
- : Candidate(Name, LineNumber, ColumnNumber), TimesExpected(Times),
- TimesSeen(0) {}
- void UpdateFor(StringRef Name, FullSourceLoc Location, SourceManager &SM) {
- if (Candidate.Matches(Name, Location)) {
- EXPECT_LT(TimesSeen, TimesExpected);
- ++TimesSeen;
- } else if (TimesSeen < TimesExpected &&
- Candidate.PartiallyMatches(Name, Location)) {
- llvm::raw_string_ostream Stream(PartialMatches);
- Stream << ", partial match: \"" << Name << "\" at ";
- Location.print(Stream, SM);
- }
- }
- void ExpectFound() const {
- EXPECT_EQ(TimesExpected, TimesSeen)
- << "Expected \"" << Candidate.ExpectedName
- << "\" at " << Candidate.LineNumber
- << ":" << Candidate.ColumnNumber << PartialMatches;
- }
- MatchCandidate Candidate;
- std::string PartialMatches;
- unsigned TimesExpected;
- unsigned TimesSeen;
- };
- std::vector<MatchCandidate> DisallowedMatches;
- std::vector<ExpectedMatch> ExpectedMatches;
- };
- }
- #endif
|