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
This commit is contained in:
Martin Bohme 2016-08-17 14:59:53 +00:00
parent 7d7bacbafa
commit d5f94a6a59
4 changed files with 76 additions and 23 deletions

View File

@ -264,10 +264,12 @@ public:
/// \returns false if the visitation was terminated early, true otherwise.
bool TraverseConstructorInitializer(CXXCtorInitializer *Init);
/// \brief Recursively visit a lambda capture.
/// \brief Recursively visit a lambda capture. \c Init is the expression that
/// will be used to initialize the capture.
///
/// \returns false if the visitation was terminated early, true otherwise.
bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C);
bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
Expr *Init);
/// \brief Recursively visit the body of a lambda expression.
///
@ -885,9 +887,12 @@ bool RecursiveASTVisitor<Derived>::TraverseConstructorInitializer(
template <typename Derived>
bool
RecursiveASTVisitor<Derived>::TraverseLambdaCapture(LambdaExpr *LE,
const LambdaCapture *C) {
const LambdaCapture *C,
Expr *Init) {
if (LE->isInitCapture(C))
TRY_TO(TraverseDecl(C->getCapturedVar()));
else
TRY_TO(TraverseStmt(Init));
return true;
}
@ -2261,13 +2266,11 @@ DEF_TRAVERSE_STMT(CXXTemporaryObjectExpr, {
// Walk only the visible parts of lambda expressions.
DEF_TRAVERSE_STMT(LambdaExpr, {
for (LambdaExpr::capture_iterator C = S->explicit_capture_begin(),
CEnd = S->explicit_capture_end();
C != CEnd; ++C) {
TRY_TO(TraverseLambdaCapture(S, C));
}
for (Expr *Init : S->capture_inits()) {
TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(Init);
for (unsigned I = 0, N = S->capture_size(); I != N; ++I) {
const LambdaCapture *C = S->capture_begin() + I;
if (C->isExplicit() || getDerived().shouldVisitImplicitCode()) {
TRY_TO(TraverseLambdaCapture(S, C, S->capture_init_begin()[I]));
}
}
TypeLoc TL = S->getCallOperator()->getTypeSourceInfo()->getTypeLoc();

View File

@ -276,7 +276,8 @@ public:
return true;
}
bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C) {
bool TraverseLambdaCapture(LambdaExpr *LE, const LambdaCapture *C,
Expr *Init) {
if (C->capturesThis() || C->capturesVLAType())
return true;

View File

@ -161,10 +161,21 @@ TEST(RecursiveASTVisitor, CanSkipImplicitMemberInitializations) {
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) {
@ -191,14 +202,43 @@ TEST(RecursiveASTVisitor, VisitsCallExpr) {
"void x(); void y() { x(); }"));
}
TEST(RecursiveASTVisitor, VisitsLambdaCaptureInit) {
TEST(RecursiveASTVisitor, VisitsExplicitLambdaCaptureInit) {
DeclRefExprVisitor Visitor;
Visitor.ExpectMatch("i", 1, 20);
EXPECT_TRUE(Visitor.runOver(
"void f() { int i; [i]{}; };",
"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;

View File

@ -43,6 +43,7 @@ public:
Lang_C,
Lang_CXX98,
Lang_CXX11,
Lang_CXX14,
Lang_OBJC,
Lang_OBJCXX11,
Lang_CXX = Lang_CXX98
@ -55,6 +56,7 @@ public:
case Lang_C: Args.push_back("-std=c99"); 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_OBJC: Args.push_back("-ObjC"); break;
case Lang_OBJCXX11:
Args.push_back("-ObjC++");
@ -127,9 +129,12 @@ public:
/// \brief Expect 'Match' to occur at the given 'Line' and 'Column'.
///
/// Any number of expected matches can be set by calling this repeatedly.
/// Each is expected to be matched exactly once.
void ExpectMatch(Twine Match, unsigned Line, unsigned Column) {
ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column));
/// Each is expected to be matched 'Times' number of times. (This is useful in
/// cases in which different AST nodes can match at the same source code
/// location.)
void ExpectMatch(Twine Match, unsigned Line, unsigned Column,
unsigned Times = 1) {
ExpectedMatches.push_back(ExpectedMatch(Match, Line, Column, Times));
}
/// \brief Checks that all expected matches have been found.
@ -200,14 +205,17 @@ protected:
};
struct ExpectedMatch {
ExpectedMatch(Twine Name, unsigned LineNumber, unsigned ColumnNumber)
: Candidate(Name, LineNumber, ColumnNumber), Found(false) {}
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_TRUE(!Found);
Found = true;
} else if (!Found && Candidate.PartiallyMatches(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);
@ -215,7 +223,7 @@ protected:
}
void ExpectFound() const {
EXPECT_TRUE(Found)
EXPECT_EQ(TimesExpected, TimesSeen)
<< "Expected \"" << Candidate.ExpectedName
<< "\" at " << Candidate.LineNumber
<< ":" << Candidate.ColumnNumber << PartialMatches;
@ -223,7 +231,8 @@ protected:
MatchCandidate Candidate;
std::string PartialMatches;
bool Found;
unsigned TimesExpected;
unsigned TimesSeen;
};
std::vector<MatchCandidate> DisallowedMatches;