forked from OSchip/llvm-project
306 lines
9.4 KiB
C++
306 lines
9.4 KiB
C++
//===- 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
|