#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 {
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
};
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 {
return std::make_unique<FindConsumer>(Visitor);
}
protected:
TestVisitor *Visitor;
};
ASTContext *Context;
};
template <typename T, template <typename> class Visitor = TestVisitor>
class ExpectedLocationVisitor : public Visitor<T> {
public:
void DisallowMatch(Twine Match, unsigned Line, unsigned Column) {
DisallowedMatches.push_back(MatchCandidate(Match, Line, Column));
}
void ExpectMatch(Twine Match, unsigned Line, unsigned Column,
unsigned Times = 1) {
ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column, Times));
}
~ExpectedLocationVisitor() override {
for (typename std::vector<ExpectedMatch>::const_iterator
It = ExpectedMatches.begin(), End = ExpectedMatches.end();
It != End; ++It) {
It->ExpectFound();
}
}
protected:
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