#ifndef LLVM_CLANG_TEST_VISITOR_H
#define LLVM_CLANG_TEST_VISITOR_H
#include <vector>
#include "clang/AST/ASTContext.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"
namespace clang {
template <typename T>
class TestVisitor : public RecursiveASTVisitor<T> {
public:
TestVisitor() { }
virtual ~TestVisitor() { }
enum Language { Lang_C, Lang_CXX };
bool runOver(StringRef Code, Language L = Lang_CXX) {
std::vector<std::string> Args;
switch (L) {
case Lang_C: Args.push_back("-std=c99"); break;
case Lang_CXX: Args.push_back("-std=c++98"); break;
}
return tooling::runToolOnCodeWithArgs(CreateTestAction(), Code, Args);
}
bool shouldVisitTemplateInstantiations() const {
return true;
}
protected:
virtual ASTFrontendAction* CreateTestAction() {
return new TestAction(this);
}
class FindConsumer : public ASTConsumer {
public:
FindConsumer(TestVisitor *Visitor) : Visitor(Visitor) {}
virtual void HandleTranslationUnit(clang::ASTContext &Context) {
Visitor->Context = &Context;
Visitor->TraverseDecl(Context.getTranslationUnitDecl());
}
private:
TestVisitor *Visitor;
};
class TestAction : public ASTFrontendAction {
public:
TestAction(TestVisitor *Visitor) : Visitor(Visitor) {}
virtual clang::ASTConsumer* CreateASTConsumer(
CompilerInstance&, llvm::StringRef dummy) {
return new 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) {
ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column));
}
virtual ~ExpectedLocationVisitor() {
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)
: Candidate(Name, LineNumber, ColumnNumber), Found(false) {}
void UpdateFor(StringRef Name, FullSourceLoc Location, SourceManager &SM) {
if (Candidate.Matches(Name, Location)) {
EXPECT_TRUE(!Found);
Found = true;
} else if (!Found && Candidate.PartiallyMatches(Name, Location)) {
llvm::raw_string_ostream Stream(PartialMatches);
Stream << ", partial match: \"" << Name << "\" at ";
Location.print(Stream, SM);
}
}
void ExpectFound() const {
EXPECT_TRUE(Found)
<< "Expected \"" << Candidate.ExpectedName
<< "\" at " << Candidate.LineNumber
<< ":" << Candidate.ColumnNumber << PartialMatches;
}
MatchCandidate Candidate;
std::string PartialMatches;
bool Found;
};
std::vector<MatchCandidate> DisallowedMatches;
std::vector<ExpectedMatch> ExpectedMatches;
};
}
#endif