blob: 63dbd1475454fb70b8d4ffba2e992a31146edca4 [file] [log] [blame]
//===- unittest/Tooling/RecursiveASTVisitorTest.cpp -----------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestVisitor.h"
#include <stack>
using namespace clang;
namespace {
class LambdaExprVisitor : public ExpectedLocationVisitor<LambdaExprVisitor> {
public:
bool VisitLambdaExpr(LambdaExpr *Lambda) {
PendingBodies.push(Lambda);
Match("", Lambda->getIntroducerRange().getBegin());
return true;
}
/// For each call to VisitLambdaExpr, we expect a subsequent call (with
/// proper nesting) to TraverseLambdaBody.
bool TraverseLambdaBody(LambdaExpr *Lambda) {
EXPECT_FALSE(PendingBodies.empty());
EXPECT_EQ(PendingBodies.top(), Lambda);
PendingBodies.pop();
return TraverseStmt(Lambda->getBody());
}
/// Determine whether TraverseLambdaBody has been called for every call to
/// VisitLambdaExpr.
bool allBodiesHaveBeenTraversed() const {
return PendingBodies.empty();
}
private:
std::stack<LambdaExpr *> PendingBodies;
};
TEST(RecursiveASTVisitor, VisitsLambdaExpr) {
LambdaExprVisitor Visitor;
Visitor.ExpectMatch("", 1, 12);
EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }",
LambdaExprVisitor::Lang_CXX11));
}
TEST(RecursiveASTVisitor, TraverseLambdaBodyCanBeOverridden) {
LambdaExprVisitor Visitor;
EXPECT_TRUE(Visitor.runOver("void f() { []{ return; }(); }",
LambdaExprVisitor::Lang_CXX11));
EXPECT_TRUE(Visitor.allBodiesHaveBeenTraversed());
}
TEST(RecursiveASTVisitor, VisitsAttributedLambdaExpr) {
LambdaExprVisitor Visitor;
Visitor.ExpectMatch("", 1, 12);
EXPECT_TRUE(Visitor.runOver(
"void f() { [] () __attribute__ (( fastcall )) { return; }(); }",
LambdaExprVisitor::Lang_CXX14));
}
// Matches the (optional) capture-default of a lambda-introducer.
class LambdaDefaultCaptureVisitor
: public ExpectedLocationVisitor<LambdaDefaultCaptureVisitor> {
public:
bool VisitLambdaExpr(LambdaExpr *Lambda) {
if (Lambda->getCaptureDefault() != LCD_None) {
Match("", Lambda->getCaptureDefaultLoc());
}
return true;
}
};
TEST(RecursiveASTVisitor, HasCaptureDefaultLoc) {
LambdaDefaultCaptureVisitor Visitor;
Visitor.ExpectMatch("", 1, 20);
EXPECT_TRUE(Visitor.runOver("void f() { int a; [=]{a;}; }",
LambdaDefaultCaptureVisitor::Lang_CXX11));
}
// Checks for lambda classes that are not marked as implicitly-generated.
// (There should be none.)
class ClassVisitor : public ExpectedLocationVisitor<ClassVisitor> {
public:
ClassVisitor() : SawNonImplicitLambdaClass(false) {}
bool VisitCXXRecordDecl(CXXRecordDecl* record) {
if (record->isLambda() && !record->isImplicit())
SawNonImplicitLambdaClass = true;
return true;
}
bool sawOnlyImplicitLambdaClasses() const {
return !SawNonImplicitLambdaClass;
}
private:
bool SawNonImplicitLambdaClass;
};
TEST(RecursiveASTVisitor, LambdaClosureTypesAreImplicit) {
ClassVisitor Visitor;
EXPECT_TRUE(Visitor.runOver("auto lambda = []{};", ClassVisitor::Lang_CXX11));
EXPECT_TRUE(Visitor.sawOnlyImplicitLambdaClasses());
}
// Check to ensure that attributes and expressions within them are being
// visited.
class AttrVisitor : public ExpectedLocationVisitor<AttrVisitor> {
public:
bool VisitMemberExpr(MemberExpr *ME) {
Match(ME->getMemberDecl()->getNameAsString(), ME->getLocStart());
return true;
}
bool VisitAttr(Attr *A) {
Match("Attr", A->getLocation());
return true;
}
bool VisitGuardedByAttr(GuardedByAttr *A) {
Match("guarded_by", A->getLocation());
return true;
}
};
TEST(RecursiveASTVisitor, AttributesAreVisited) {
AttrVisitor Visitor;
Visitor.ExpectMatch("Attr", 4, 24);
Visitor.ExpectMatch("guarded_by", 4, 24);
Visitor.ExpectMatch("mu1", 4, 35);
Visitor.ExpectMatch("Attr", 5, 29);
Visitor.ExpectMatch("mu1", 5, 54);
Visitor.ExpectMatch("mu2", 5, 59);
EXPECT_TRUE(Visitor.runOver(
"class Foo {\n"
" int mu1;\n"
" int mu2;\n"
" int a __attribute__((guarded_by(mu1)));\n"
" void bar() __attribute__((exclusive_locks_required(mu1, mu2)));\n"
"};\n"));
}
// Check to ensure that implicit default argument expressions are visited.
class IntegerLiteralVisitor
: public ExpectedLocationVisitor<IntegerLiteralVisitor> {
public:
bool VisitIntegerLiteral(const IntegerLiteral *IL) {
Match("literal", IL->getLocation());
return true;
}
};
TEST(RecursiveASTVisitor, DefaultArgumentsAreVisited) {
IntegerLiteralVisitor Visitor;
Visitor.ExpectMatch("literal", 1, 15, 2);
EXPECT_TRUE(Visitor.runOver("int f(int i = 1);\n"
"static int k = f();\n"));
}
// Check to ensure that InitListExpr is visited twice, once each for the
// syntactic and semantic form.
class InitListExprPreOrderVisitor
: public ExpectedLocationVisitor<InitListExprPreOrderVisitor> {
public:
bool VisitInitListExpr(InitListExpr *ILE) {
Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getLocStart());
return true;
}
};
class InitListExprPostOrderVisitor
: public ExpectedLocationVisitor<InitListExprPostOrderVisitor> {
public:
bool shouldTraversePostOrder() const { return true; }
bool VisitInitListExpr(InitListExpr *ILE) {
Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getLocStart());
return true;
}
};
class InitListExprPreOrderNoQueueVisitor
: public ExpectedLocationVisitor<InitListExprPreOrderNoQueueVisitor> {
public:
bool TraverseInitListExpr(InitListExpr *ILE) {
return ExpectedLocationVisitor::TraverseInitListExpr(ILE);
}
bool VisitInitListExpr(InitListExpr *ILE) {
Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getLocStart());
return true;
}
};
class InitListExprPostOrderNoQueueVisitor
: public ExpectedLocationVisitor<InitListExprPostOrderNoQueueVisitor> {
public:
bool shouldTraversePostOrder() const { return true; }
bool TraverseInitListExpr(InitListExpr *ILE) {
return ExpectedLocationVisitor::TraverseInitListExpr(ILE);
}
bool VisitInitListExpr(InitListExpr *ILE) {
Match(ILE->isSemanticForm() ? "semantic" : "syntactic", ILE->getLocStart());
return true;
}
};
TEST(RecursiveASTVisitor, InitListExprIsPreOrderVisitedTwice) {
InitListExprPreOrderVisitor Visitor;
Visitor.ExpectMatch("syntactic", 2, 21);
Visitor.ExpectMatch("semantic", 2, 21);
EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
"static struct S s = {.x = 0};\n",
InitListExprPreOrderVisitor::Lang_C));
}
TEST(RecursiveASTVisitor, InitListExprIsPostOrderVisitedTwice) {
InitListExprPostOrderVisitor Visitor;
Visitor.ExpectMatch("syntactic", 2, 21);
Visitor.ExpectMatch("semantic", 2, 21);
EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
"static struct S s = {.x = 0};\n",
InitListExprPostOrderVisitor::Lang_C));
}
TEST(RecursiveASTVisitor, InitListExprIsPreOrderNoQueueVisitedTwice) {
InitListExprPreOrderNoQueueVisitor Visitor;
Visitor.ExpectMatch("syntactic", 2, 21);
Visitor.ExpectMatch("semantic", 2, 21);
EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
"static struct S s = {.x = 0};\n",
InitListExprPreOrderNoQueueVisitor::Lang_C));
}
TEST(RecursiveASTVisitor, InitListExprIsPostOrderNoQueueVisitedTwice) {
InitListExprPostOrderNoQueueVisitor Visitor;
Visitor.ExpectMatch("syntactic", 2, 21);
Visitor.ExpectMatch("semantic", 2, 21);
EXPECT_TRUE(Visitor.runOver("struct S { int x; };\n"
"static struct S s = {.x = 0};\n",
InitListExprPostOrderNoQueueVisitor::Lang_C));
}
// Check to ensure that nested name specifiers are visited.
class NestedNameSpecifiersVisitor
: public ExpectedLocationVisitor<NestedNameSpecifiersVisitor> {
public:
bool VisitRecordTypeLoc(RecordTypeLoc RTL) {
if (!RTL)
return true;
Match(RTL.getDecl()->getName(), RTL.getNameLoc());
return true;
}
bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNS) {
if (!NNS)
return true;
if (const NamespaceDecl *ND =
NNS.getNestedNameSpecifier()->getAsNamespace())
Match(ND->getName(), NNS.getLocalBeginLoc());
return ExpectedLocationVisitor::TraverseNestedNameSpecifierLoc(NNS);
}
};
TEST(RecursiveASTVisitor,
NestedNameSpecifiersForTemplateSpecializationsAreVisited) {
StringRef Source = R"(
namespace ns {
struct Outer {
template<typename T, typename U>
struct Nested { };
template<typename T>
static T x;
};
}
template<>
struct ns::Outer::Nested<int, int>;
template<>
struct ns::Outer::Nested<int, int> { };
template<typename T>
struct ns::Outer::Nested<int, T> { };
template<>
int ns::Outer::x<int> = 0;
)";
NestedNameSpecifiersVisitor Visitor;
Visitor.ExpectMatch("ns", 13, 8);
Visitor.ExpectMatch("ns", 16, 8);
Visitor.ExpectMatch("ns", 19, 8);
Visitor.ExpectMatch("ns", 22, 5);
Visitor.ExpectMatch("Outer", 13, 12);
Visitor.ExpectMatch("Outer", 16, 12);
Visitor.ExpectMatch("Outer", 19, 12);
Visitor.ExpectMatch("Outer", 22, 9);
EXPECT_TRUE(Visitor.runOver(Source, NestedNameSpecifiersVisitor::Lang_CXX14));
}
} // end anonymous namespace