llvm-project/clang/unittests/Tooling/RecursiveASTVisitorTestExprVisitor.cpp
Martin Bohme d5f94a6a59 Visit lambda capture inits from RecursiveASTVisitor::TraverseLambdaCapture().
Summary:
rL277342 made RecursiveASTVisitor visit lambda capture initialization
expressions (these are the Exprs in LambdaExpr::capture_inits()).

jdennett identified two issues with rL277342 (see comments there for details):

- It visits initialization expressions for implicit lambda captures, even if
  shouldVisitImplicitCode() returns false.

- It visits initialization expressions for init captures twice (because these
  were already traveresed in TraverseLambdaCapture() before rL277342)

This patch fixes these issues and moves the code for traversing initialization
expressions into TraverseLambdaCapture().

This patch also makes two changes required for the tests:

- It adds Lang_CXX14 to the Language enum in TestVisitor.

- It adds a parameter to ExpectedLocationVisitor::ExpectMatch() that specifies
  the number of times a match is expected to be seen.

Reviewers: klimek, jdennett, alexfh

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D23204

llvm-svn: 278933
2016-08-17 14:59:53 +00:00

272 lines
8.8 KiB
C++

//===- unittest/Tooling/RecursiveASTVisitorTestExprVisitor.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"
using namespace clang;
namespace {
class ParenExprVisitor : public ExpectedLocationVisitor<ParenExprVisitor> {
public:
bool VisitParenExpr(ParenExpr *Parens) {
Match("", Parens->getExprLoc());
return true;
}
};
TEST(RecursiveASTVisitor, VisitsParensDuringDataRecursion) {
ParenExprVisitor Visitor;
Visitor.ExpectMatch("", 1, 9);
EXPECT_TRUE(Visitor.runOver("int k = (4) + 9;\n"));
}
class TemplateArgumentLocTraverser
: public ExpectedLocationVisitor<TemplateArgumentLocTraverser> {
public:
bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &ArgLoc) {
std::string ArgStr;
llvm::raw_string_ostream Stream(ArgStr);
const TemplateArgument &Arg = ArgLoc.getArgument();
Arg.print(Context->getPrintingPolicy(), Stream);
Match(Stream.str(), ArgLoc.getLocation());
return ExpectedLocationVisitor<TemplateArgumentLocTraverser>::
TraverseTemplateArgumentLoc(ArgLoc);
}
};
TEST(RecursiveASTVisitor, VisitsClassTemplateTemplateParmDefaultArgument) {
TemplateArgumentLocTraverser Visitor;
Visitor.ExpectMatch("X", 2, 40);
EXPECT_TRUE(Visitor.runOver(
"template<typename T> class X;\n"
"template<template <typename> class T = X> class Y;\n"
"template<template <typename> class T> class Y {};\n"));
}
class CXXBoolLiteralExprVisitor
: public ExpectedLocationVisitor<CXXBoolLiteralExprVisitor> {
public:
bool VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *BE) {
if (BE->getValue())
Match("true", BE->getLocation());
else
Match("false", BE->getLocation());
return true;
}
};
TEST(RecursiveASTVisitor, VisitsClassTemplateNonTypeParmDefaultArgument) {
CXXBoolLiteralExprVisitor Visitor;
Visitor.ExpectMatch("true", 2, 19);
EXPECT_TRUE(Visitor.runOver(
"template<bool B> class X;\n"
"template<bool B = true> class Y;\n"
"template<bool B> class Y {};\n"));
}
// A visitor that visits implicit declarations and matches constructors.
class ImplicitCtorVisitor
: public ExpectedLocationVisitor<ImplicitCtorVisitor> {
public:
bool shouldVisitImplicitCode() const { return true; }
bool VisitCXXConstructorDecl(CXXConstructorDecl* Ctor) {
if (Ctor->isImplicit()) { // Was not written in source code
if (const CXXRecordDecl* Class = Ctor->getParent()) {
Match(Class->getName(), Ctor->getLocation());
}
}
return true;
}
};
TEST(RecursiveASTVisitor, VisitsImplicitCopyConstructors) {
ImplicitCtorVisitor Visitor;
Visitor.ExpectMatch("Simple", 2, 8);
// Note: Clang lazily instantiates implicit declarations, so we need
// to use them in order to force them to appear in the AST.
EXPECT_TRUE(Visitor.runOver(
"struct WithCtor { WithCtor(); }; \n"
"struct Simple { Simple(); WithCtor w; }; \n"
"int main() { Simple s; Simple t(s); }\n"));
}
/// \brief A visitor that optionally includes implicit code and matches
/// CXXConstructExpr.
///
/// The name recorded for the match is the name of the class whose constructor
/// is invoked by the CXXConstructExpr, not the name of the class whose
/// constructor the CXXConstructExpr is contained in.
class ConstructExprVisitor
: public ExpectedLocationVisitor<ConstructExprVisitor> {
public:
ConstructExprVisitor() : ShouldVisitImplicitCode(false) {}
bool shouldVisitImplicitCode() const { return ShouldVisitImplicitCode; }
void setShouldVisitImplicitCode(bool NewValue) {
ShouldVisitImplicitCode = NewValue;
}
bool VisitCXXConstructExpr(CXXConstructExpr* Expr) {
if (const CXXConstructorDecl* Ctor = Expr->getConstructor()) {
if (const CXXRecordDecl* Class = Ctor->getParent()) {
Match(Class->getName(), Expr->getLocation());
}
}
return true;
}
private:
bool ShouldVisitImplicitCode;
};
TEST(RecursiveASTVisitor, CanVisitImplicitMemberInitializations) {
ConstructExprVisitor Visitor;
Visitor.setShouldVisitImplicitCode(true);
Visitor.ExpectMatch("WithCtor", 2, 8);
// Simple has a constructor that implicitly initializes 'w'. Test
// that a visitor that visits implicit code visits that initialization.
// Note: Clang lazily instantiates implicit declarations, so we need
// to use them in order to force them to appear in the AST.
EXPECT_TRUE(Visitor.runOver(
"struct WithCtor { WithCtor(); }; \n"
"struct Simple { WithCtor w; }; \n"
"int main() { Simple s; }\n"));
}
// The same as CanVisitImplicitMemberInitializations, but checking that the
// visits are omitted when the visitor does not include implicit code.
TEST(RecursiveASTVisitor, CanSkipImplicitMemberInitializations) {
ConstructExprVisitor Visitor;
Visitor.setShouldVisitImplicitCode(false);
Visitor.DisallowMatch("WithCtor", 2, 8);
// Simple has a constructor that implicitly initializes 'w'. Test
// that a visitor that skips implicit code skips that initialization.
// Note: Clang lazily instantiates implicit declarations, so we need
// to use them in order to force them to appear in the AST.
EXPECT_TRUE(Visitor.runOver(
"struct WithCtor { WithCtor(); }; \n"
"struct Simple { WithCtor w; }; \n"
"int main() { Simple s; }\n"));
}
class DeclRefExprVisitor : public ExpectedLocationVisitor<DeclRefExprVisitor> {
public:
DeclRefExprVisitor() : ShouldVisitImplicitCode(false) {}
bool shouldVisitImplicitCode() const { return ShouldVisitImplicitCode; }
void setShouldVisitImplicitCode(bool NewValue) {
ShouldVisitImplicitCode = NewValue;
}
bool VisitDeclRefExpr(DeclRefExpr *Reference) {
Match(Reference->getNameInfo().getAsString(), Reference->getLocation());
return true;
}
private:
bool ShouldVisitImplicitCode;
};
TEST(RecursiveASTVisitor, VisitsBaseClassTemplateArguments) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("x", 2, 3);
EXPECT_TRUE(Visitor.runOver(
"void x(); template <void (*T)()> class X {};\nX<x> y;"));
}
TEST(RecursiveASTVisitor, VisitsCXXForRangeStmtRange) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("x", 2, 25);
Visitor.ExpectMatch("x", 2, 30);
EXPECT_TRUE(Visitor.runOver(
"int x[5];\n"
"void f() { for (int i : x) { x[0] = 1; } }",
DeclRefExprVisitor::Lang_CXX11));
}
TEST(RecursiveASTVisitor, VisitsCallExpr) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("x", 1, 22);
EXPECT_TRUE(Visitor.runOver(
"void x(); void y() { x(); }"));
}
TEST(RecursiveASTVisitor, VisitsExplicitLambdaCaptureInit) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("i", 1, 20);
EXPECT_TRUE(Visitor.runOver(
"void f() { int i; [i]{}; }",
DeclRefExprVisitor::Lang_CXX11));
}
TEST(RecursiveASTVisitor, VisitsUseOfImplicitLambdaCapture) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("i", 1, 24);
EXPECT_TRUE(Visitor.runOver(
"void f() { int i; [=]{ i; }; }",
DeclRefExprVisitor::Lang_CXX11));
}
TEST(RecursiveASTVisitor, VisitsImplicitLambdaCaptureInit) {
DeclRefExprVisitor Visitor;
Visitor.setShouldVisitImplicitCode(true);
// We're expecting the "i" in the lambda to be visited twice:
// - Once for the DeclRefExpr in the lambda capture initialization (whose
// source code location is set to the first use of the variable).
// - Once for the DeclRefExpr for the use of "i" inside the lambda.
Visitor.ExpectMatch("i", 1, 24, /*Times=*/2);
EXPECT_TRUE(Visitor.runOver(
"void f() { int i; [=]{ i; }; }",
DeclRefExprVisitor::Lang_CXX11));
}
TEST(RecursiveASTVisitor, VisitsLambdaInitCaptureInit) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("i", 1, 24);
EXPECT_TRUE(Visitor.runOver(
"void f() { int i; [a = i + 1]{}; }",
DeclRefExprVisitor::Lang_CXX14));
}
/* FIXME: According to Richard Smith this is a bug in the AST.
TEST(RecursiveASTVisitor, VisitsBaseClassTemplateArgumentsInInstantiation) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("x", 3, 43);
EXPECT_TRUE(Visitor.runOver(
"template <typename T> void x();\n"
"template <void (*T)()> class X {};\n"
"template <typename T> class Y : public X< x<T> > {};\n"
"Y<int> y;"));
}
*/
TEST(RecursiveASTVisitor, VisitsExtension) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("s", 1, 24);
EXPECT_TRUE(Visitor.runOver(
"int s = __extension__ (s);\n"));
}
TEST(RecursiveASTVisitor, VisitsCopyExprOfBlockDeclCapture) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("x", 3, 24);
EXPECT_TRUE(Visitor.runOver("void f(int(^)(int)); \n"
"void g() { \n"
" f([&](int x){ return x; }); \n"
"}",
DeclRefExprVisitor::Lang_OBJCXX11));
}
} // end anonymous namespace