This patch causes practically infinite traversal times on code that
contains deeply nested lambdas. Please fix or revert the commit.
There's a very simple test case (add more nesting, if it's still fast ;):
void f() {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
[] {
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}();
}
Three nested lambdas are enough to demonstrate the issue by looking at
the AST dump. The body of the innermost lambda (0x45593fda99a0) is
printed 8 times, and it will be traversed 8 times as well by AST matchers:
`-FunctionDecl 0x45593fda9198 </tmp/nested-lambdas.cc:1:1, line:8:1>
line:1:6 f 'void ()'
`-CompoundStmt 0x45593fdce970 <col:10, line:8:1>
`-ExprWithCleanups 0x45593fdce958 <line:2:3, line:7:5> 'void':'void'
`-CXXOperatorCallExpr 0x45593fdce928 <line:2:3, line:7:5>
'void':'void' '()'
|-ImplicitCastExpr 0x45593fdce8b0 <col:4, col:5> 'auto (*)()
const -> void' <FunctionToPointerDecay>
| `-DeclRefExpr 0x45593fdce890 <col:4, col:5> 'auto () const
-> void' lvalue CXXMethod 0x45593fda9490 'operator()' 'auto () const
-> void'
`-ImplicitCastExpr 0x45593fdce910 <line:2:3, line:7:3> 'const
(lambda at /tmp/nested-lambdas.cc:2:3)' lvalue <NoOp>
`-MaterializeTemporaryExpr 0x45593fdce8f8 <line:2:3,
line:7:3> '(lambda at /tmp/nested-lambdas.cc:2:3)' lvalue
`-LambdaExpr 0x45593fdce788 <line:2:3, line:7:3> '(lambda
at /tmp/nested-lambdas.cc:2:3)'
|-CXXRecordDecl 0x45593fda9350 <line:2:3> col:3 implicit
class definition
| |-DefinitionData lambda pass_in_registers empty
standard_layout trivially_copyable literal can_const_default_init
| | |-DefaultConstructor defaulted_is_constexpr
| | |-CopyConstructor simple trivial has_const_param
needs_implicit implicit_has_const_param
| | |-MoveConstructor exists simple trivial needs_implicit
| | |-CopyAssignment trivial has_const_param
needs_implicit implicit_has_const_param
| | |-MoveAssignment
| | `-Destructor simple irrelevant trivial
| |-CXXMethodDecl 0x45593fda9490 <col:4, line:7:3>
line:2:3 used constexpr operator() 'auto () const -> void' inline
| | `-CompoundStmt 0x45593fdce4e0 <col:6, line:7:3>
| | `-ExprWithCleanups 0x45593fdce4c8 <line:3:3,
line:6:5> 'void':'void'
| | `-CXXOperatorCallExpr 0x45593fdce498 <line:3:3,
line:6:5> 'void':'void' '()'
| | |-ImplicitCastExpr 0x45593fdce420 <col:4,
col:5> 'auto (*)() const -> void' <FunctionToPointerDecay>
| | | `-DeclRefExpr 0x45593fdce400 <col:4, col:5>
'auto () const -> void' lvalue CXXMethod 0x45593fda96c0 'operator()'
'auto () const -> void'
| | `-ImplicitCastExpr 0x45593fdce480 <line:3:3,
line:6:3> 'const (lambda at /tmp/nested-lambdas.cc:3:3)' lvalue <NoOp>
| | `-MaterializeTemporaryExpr 0x45593fdce468
<line:3:3, line:6:3> '(lambda at /tmp/nested-lambdas.cc:3:3)' lvalue
| | `-LambdaExpr 0x45593fdce2f0 <line:3:3,
line:6:3> '(lambda at /tmp/nested-lambdas.cc:3:3)'
| | |-CXXRecordDecl 0x45593fda9588
<line:3:3> col:3 implicit class definition
| | | |-DefinitionData lambda
pass_in_registers empty standard_layout trivially_copyable literal
can_const_default_init
| | | | |-DefaultConstructor
defaulted_is_constexpr
| | | | |-CopyConstructor simple trivial
has_const_param needs_implicit implicit_has_const_param
| | | | |-MoveConstructor exists simple
trivial needs_implicit
| | | | |-CopyAssignment trivial
has_const_param needs_implicit implicit_has_const_param
| | | | |-MoveAssignment
| | | | `-Destructor simple irrelevant trivial
| | | |-CXXMethodDecl 0x45593fda96c0 <col:4,
line:6:3> line:3:3 used constexpr operator() 'auto () const -> void'
inline
| | | | `-CompoundStmt 0x45593fdce048
<col:6, line:6:3>
| | | | `-ExprWithCleanups 0x45593fdce030
<line:4:3, line:5:5> 'void':'void'
| | | | `-CXXOperatorCallExpr 0x45593fdce000
<line:4:3, line:5:5> 'void':'void' '()'
| | | | |-ImplicitCastExpr
0x45593fda9f88 <col:4, col:5> 'auto (*)() const -> void'
<FunctionToPointerDecay>
| | | | | `-DeclRefExpr 0x45593fda9f08
<col:4, col:5> 'auto () const -> void' lvalue CXXMethod 0x45593fda98f0
'operator()' 'auto () const -> void'
| | | | `-ImplicitCastExpr
0x45593fda9fe0 <line:4:3, line:5:3> 'const (lambda at
/tmp/nested-lambdas.cc:4:3)' lvalue <NoOp>
| | | | `-MaterializeTemporaryExpr
0x45593fda9fc8 <line:4:3, line:5:3> '(lambda at
/tmp/nested-lambdas.cc:4:3)' lvalue
| | | | `-LambdaExpr
0x45593fda9dd0 <line:4:3, line:5:3> '(lambda at
/tmp/nested-lambdas.cc:4:3)'
| | | | |-CXXRecordDecl 0x45593fda97b8
<line:4:3> col:3 implicit class definition
| | | | | |-DefinitionData
lambda pass_in_registers empty standard_layout trivially_copyable
literal can_const_default_init
| | | | | | |-DefaultConstructor
defaulted_is_constexpr
| | | | | | |-CopyConstructor
simple trivial has_const_param needs_implicit implicit_has_const_param
| | | | | | |-MoveConstructor
exists simple trivial needs_implicit
| | | | | | |-CopyAssignment
trivial has_const_param needs_implicit implicit_has_const_param
| | | | | | |-MoveAssignment
| | | | | | `-Destructor simple
irrelevant trivial
| | | | | |-CXXMethodDecl
0x45593fda98f0 <col:4, line:5:3> line:4:3 used constexpr operator()
'auto () const -> void' inline
| | | | | | `-CompoundStmt
0x45593fda99a0 <col:6, line:5:3>
| | | | | |-CXXConversionDecl
0x45593fda9c68 <line:4:3, line:5:3> line:4:3 implicit constexpr
operator void (*)() 'auto (*() const noexcept)() -> void' inline
| | | | | |-CXXMethodDecl
0x45593fda9d18 <col:3, line:5:3> line:4:3 implicit __invoke 'auto ()
-> void' static inline
| | | | | `-CXXDestructorDecl
0x45593fda9df8 <col:3> col:3 implicit referenced ~ 'void () noexcept'
inline default trivial
| | | | `-CompoundStmt 0x45593fda99a0
<col:6, line:5:3>
| | | |-CXXConversionDecl 0x45593fdce188
<line:3:3, line:6:3> line:3:3 implicit constexpr operator void (*)()
'auto (*() const noexcept)() -> void' inline
| | | |-CXXMethodDecl 0x45593fdce238 <col:3,
line:6:3> line:3:3 implicit __invoke 'auto () -> void' static inline
| | | `-CXXDestructorDecl 0x45593fdce318
<col:3> col:3 implicit referenced ~ 'void () noexcept' inline default
trivial
| | `-CompoundStmt 0x45593fdce048 <col:6,
line:6:3>
| | `-ExprWithCleanups 0x45593fdce030
<line:4:3, line:5:5> 'void':'void'
| | `-CXXOperatorCallExpr 0x45593fdce000
<line:4:3, line:5:5> 'void':'void' '()'
| | |-ImplicitCastExpr 0x45593fda9f88
<col:4, col:5> 'auto (*)() const -> void' <FunctionToPointerDecay>
| | | `-DeclRefExpr 0x45593fda9f08
<col:4, col:5> 'auto () const -> void' lvalue CXXMethod 0x45593fda98f0
'operator()' 'auto () const -> void'
| | `-ImplicitCastExpr 0x45593fda9fe0
<line:4:3, line:5:3> 'const (lambda at /tmp/nested-lambdas.cc:4:3)'
lvalue <NoOp>
| | `-MaterializeTemporaryExpr 0x45593fda9fc8 <line:4:3,
line:5:3> '(lambda at /tmp/nested-lambdas.cc:4:3)' lvalue
| | `-LambdaExpr 0x45593fda9dd0
<line:4:3, line:5:3> '(lambda at /tmp/nested-lambdas.cc:4:3)'
| | |-CXXRecordDecl
0x45593fda97b8 <line:4:3> col:3 implicit class definition
| | | |-DefinitionData lambda
pass_in_registers empty standard_layout trivially_copyable literal
can_const_default_init
| | | | |-DefaultConstructor
defaulted_is_constexpr
| | | | |-CopyConstructor simple
trivial has_const_param needs_implicit implicit_has_const_param
| | | | |-MoveConstructor exists
simple trivial needs_implicit
| | | | |-CopyAssignment trivial
has_const_param needs_implicit implicit_has_const_param
| | | | |-MoveAssignment
| | | | `-Destructor simple
irrelevant trivial
| | | |-CXXMethodDecl
0x45593fda98f0 <col:4, line:5:3> line:4:3 used constexpr operator()
'auto () const -> void' inline
| | | | `-CompoundStmt
0x45593fda99a0 <col:6, line:5:3>
| | | |-CXXConversionDecl
0x45593fda9c68 <line:4:3, line:5:3> line:4:3 implicit constexpr
operator void (*)() 'auto (*() const noexcept)() -> void' inline
| | | |-CXXMethodDecl
0x45593fda9d18 <col:3, line:5:3> line:4:3 implicit __invoke 'auto ()
-> void' static inline
| | | `-CXXDestructorDecl
0x45593fda9df8 <col:3> col:3 implicit referenced ~ 'void () noexcept'
inline default trivial
| | `-CompoundStmt
0x45593fda99a0 <col:6, line:5:3>
| |-CXXConversionDecl 0x45593fdce620 <line:2:3,
line:7:3> line:2:3 implicit constexpr operator void (*)() 'auto (*()
const noexcept)() -> void' inline
| |-CXXMethodDecl 0x45593fdce6d0 <col:3, line:7:3>
line:2:3 implicit __invoke 'auto () -> void' static inline
| `-CXXDestructorDecl 0x45593fdce7b0 <col:3> col:3
implicit referenced ~ 'void () noexcept' inline default trivial
`-CompoundStmt 0x45593fdce4e0 <col:6, line:7:3>
`-ExprWithCleanups 0x45593fdce4c8 <line:3:3, line:6:5>
'void':'void'
`-CXXOperatorCallExpr 0x45593fdce498 <line:3:3,
line:6:5> 'void':'void' '()'
|-ImplicitCastExpr 0x45593fdce420 <col:4, col:5>
'auto (*)() const -> void' <FunctionToPointerDecay>
| `-DeclRefExpr 0x45593fdce400 <col:4, col:5>
'auto () const -> void' lvalue CXXMethod 0x45593fda96c0 'operator()'
'auto () const -> void'
`-ImplicitCastExpr 0x45593fdce480 <line:3:3,
line:6:3> 'const (lambda at /tmp/nested-lambdas.cc:3:3)' lvalue <NoOp>
`-MaterializeTemporaryExpr 0x45593fdce468
<line:3:3, line:6:3> '(lambda at /tmp/nested-lambdas.cc:3:3)' lvalue
`-LambdaExpr 0x45593fdce2f0 <line:3:3,
line:6:3> '(lambda at /tmp/nested-lambdas.cc:3:3)'
|-CXXRecordDecl 0x45593fda9588 <line:3:3>
col:3 implicit class definition
| |-DefinitionData lambda pass_in_registers
empty standard_layout trivially_copyable literal can_const_default_init
| | |-DefaultConstructor defaulted_is_constexpr
| | |-CopyConstructor simple trivial
has_const_param needs_implicit implicit_has_const_param
| | |-MoveConstructor exists simple trivial
needs_implicit
| | |-CopyAssignment trivial has_const_param
needs_implicit implicit_has_const_param
| | |-MoveAssignment
| | `-Destructor simple irrelevant trivial
| |-CXXMethodDecl 0x45593fda96c0 <col:4,
line:6:3> line:3:3 used constexpr operator() 'auto () const -> void'
inline
| | `-CompoundStmt 0x45593fdce048 <col:6,
line:6:3>
| | `-ExprWithCleanups 0x45593fdce030
<line:4:3, line:5:5> 'void':'void'
| | `-CXXOperatorCallExpr 0x45593fdce000
<line:4:3, line:5:5> 'void':'void' '()'
| | |-ImplicitCastExpr 0x45593fda9f88
<col:4, col:5> 'auto (*)() const -> void' <FunctionToPointerDecay>
| | | `-DeclRefExpr 0x45593fda9f08
<col:4, col:5> 'auto () const -> void' lvalue CXXMethod 0x45593fda98f0
'operator()' 'auto () const -> void'
| | `-ImplicitCastExpr 0x45593fda9fe0
<line:4:3, line:5:3> 'const (lambda at /tmp/nested-lambdas.cc:4:3)'
lvalue <NoOp>
| | `-MaterializeTemporaryExpr
0x45593fda9fc8 <line:4:3, line:5:3> '(lambda at
/tmp/nested-lambdas.cc:4:3)' lvalue
| | `-LambdaExpr 0x45593fda9dd0
<line:4:3, line:5:3> '(lambda at /tmp/nested-lambdas.cc:4:3)'
| | |-CXXRecordDecl
0x45593fda97b8 <line:4:3> col:3 implicit class definition
| | | |-DefinitionData lambda
pass_in_registers empty standard_layout trivially_copyable literal
can_const_default_init
| | | | |-DefaultConstructor
defaulted_is_constexpr
| | | | |-CopyConstructor simple
trivial has_const_param needs_implicit implicit_has_const_param
| | | | |-MoveConstructor exists
simple trivial needs_implicit
| | | | |-CopyAssignment trivial
has_const_param needs_implicit implicit_has_const_param
| | | | |-MoveAssignment
| | | | `-Destructor simple
irrelevant trivial
| | | |-CXXMethodDecl
0x45593fda98f0 <col:4, line:5:3> line:4:3 used constexpr operator()
'auto () const -> void' inline
| | | | `-CompoundStmt
0x45593fda99a0 <col:6, line:5:3>
| | | |-CXXConversionDecl
0x45593fda9c68 <line:4:3, line:5:3> line:4:3 implicit constexpr
operator void (*)() 'auto (*() const noexcept)() -> void' inline
| | | |-CXXMethodDecl
0x45593fda9d18 <col:3, line:5:3> line:4:3 implicit __invoke 'auto ()
-> void' static inline
| | | `-CXXDestructorDecl
0x45593fda9df8 <col:3> col:3 implicit referenced ~ 'void () noexcept'
inline default trivial
| | `-CompoundStmt
0x45593fda99a0 <col:6, line:5:3>
| |-CXXConversionDecl 0x45593fdce188
<line:3:3, line:6:3> line:3:3 implicit constexpr operator void (*)()
'auto (*() const noexcept)() -> void' inline
| |-CXXMethodDecl 0x45593fdce238 <col:3,
line:6:3> line:3:3 implicit __invoke 'auto () -> void' static inline
| `-CXXDestructorDecl 0x45593fdce318 <col:3>
col:3 implicit referenced ~ 'void () noexcept' inline default trivial
`-CompoundStmt 0x45593fdce048 <col:6, line:6:3>
`-ExprWithCleanups 0x45593fdce030
<line:4:3, line:5:5> 'void':'void'
`-CXXOperatorCallExpr 0x45593fdce000
<line:4:3, line:5:5> 'void':'void' '()'
|-ImplicitCastExpr 0x45593fda9f88
<col:4, col:5> 'auto (*)() const -> void' <FunctionToPointerDecay>
| `-DeclRefExpr 0x45593fda9f08 <col:4,
col:5> 'auto () const -> void' lvalue CXXMethod 0x45593fda98f0
'operator()' 'auto () const -> void'
`-ImplicitCastExpr 0x45593fda9fe0
<line:4:3, line:5:3> 'const (lambda at /tmp/nested-lambdas.cc:4:3)'
lvalue <NoOp>
`-MaterializeTemporaryExpr 0x45593fda9fc8 <line:4:3, line:5:3>
'(lambda at /tmp/nested-lambdas.cc:4:3)' lvalue
`-LambdaExpr 0x45593fda9dd0
<line:4:3, line:5:3> '(lambda at /tmp/nested-lambdas.cc:4:3)'
|-CXXRecordDecl 0x45593fda97b8
<line:4:3> col:3 implicit class definition
| |-DefinitionData lambda
pass_in_registers empty standard_layout trivially_copyable literal
can_const_default_init
| | |-DefaultConstructor
defaulted_is_constexpr
| | |-CopyConstructor simple
trivial has_const_param needs_implicit implicit_has_const_param
| | |-MoveConstructor exists
simple trivial needs_implicit
| | |-CopyAssignment trivial
has_const_param needs_implicit implicit_has_const_param
| | |-MoveAssignment
| | `-Destructor simple
irrelevant trivial
| |-CXXMethodDecl 0x45593fda98f0
<col:4, line:5:3> line:4:3 used constexpr operator() 'auto () const ->
void' inline
| | `-CompoundStmt
0x45593fda99a0 <col:6, line:5:3>
| |-CXXConversionDecl
0x45593fda9c68 <line:4:3, line:5:3> line:4:3 implicit constexpr
operator void (*)() 'auto (*() const noexcept)() -> void' inline
| |-CXXMethodDecl 0x45593fda9d18
<col:3, line:5:3> line:4:3 implicit __invoke 'auto () -> void' static
inline
| `-CXXDestructorDecl
0x45593fda9df8 <col:3> col:3 implicit referenced ~ 'void () noexcept'
inline default trivial
`-CompoundStmt 0x45593fda99a0
<col:6, line:5:3>
On Tue, Jan 5, 2021 at 3:45 PM Stephen Kelly via llvm-branch-commits
<llvm-branch-commits@lists.llvm.org
<mailto:llvm-branch-commits@lists.llvm.org>> wrote:
Author: Stephen Kelly
Date: 2021-01-05T14:39:46Z
New Revision: c3a21e5de3dc3f55e4d219afd55dec518159d356
URL:
https://github.com/llvm/llvm-project/commit/c3a21e5de3dc3f55e4d219afd55dec518159d356
DIFF:
https://github.com/llvm/llvm-project/commit/c3a21e5de3dc3f55e4d219afd55dec518159d356.diff
LOG: [ASTMatchers] Ensure that we can match inside lambdas
Because we don't know in ASTMatchFinder whether we're matching in AsIs
or IgnoreUnlessSpelledInSource mode, we need to traverse the lambda
twice, but store whether we're matching in nodes spelled in source or
not.
Differential Revision: https://reviews.llvm.org/D93688
Added:
Modified:
clang/include/clang/ASTMatchers/ASTMatchersInternal.h
clang/lib/ASTMatchers/ASTMatchFinder.cpp
clang/lib/ASTMatchers/ASTMatchersInternal.cpp
clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
Removed:
################################################################################
diff --git
a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
index 46de4093272d..f49728d1f50e 100644
--- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
+++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h
@@ -723,6 +723,8 @@ class ASTMatchFinder {
virtual bool IsMatchingInASTNodeNotSpelledInSource() const = 0;
+ virtual bool IsMatchingInASTNodeNotAsIs() const = 0;
+
bool isTraversalIgnoringImplicitNodes() const;
protected:
diff --git a/clang/lib/ASTMatchers/ASTMatchFinder.cpp
b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
index 762885fa0052..af21e2283d8b 100644
--- a/clang/lib/ASTMatchers/ASTMatchFinder.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchFinder.cpp
@@ -475,6 +475,55 @@ class MatchASTVisitor : public
RecursiveASTVisitor<MatchASTVisitor>,
}
}
return true;
+ } else if (auto *LE = dyn_cast<LambdaExpr>(S)) {
+ for (auto I : llvm::zip(LE->captures(), LE->capture_inits())) {
+ auto C = std::get<0>(I);
+ ASTNodeNotSpelledInSourceScope RAII(
+ this, TraversingASTNodeNotSpelledInSource ||
!C.isExplicit());
+ TraverseLambdaCapture(LE, &C, std::get<1>(I));
+ }
+
+ {
+ ASTNodeNotSpelledInSourceScope RAII(this, true);
+ TraverseDecl(LE->getLambdaClass());
The line above triggers an additional traversal of all nested lambdas,
leading to exponential time growth.
+ }
+ {
+ ASTNodeNotAsIsSourceScope RAII(this, true);
+
+ // We need to poke around to find the bits that might be
explicitly
+ // written.
+ TypeLoc TL =
LE->getCallOperator()->getTypeSourceInfo()->getTypeLoc();
+ FunctionProtoTypeLoc Proto =
TL.getAsAdjusted<FunctionProtoTypeLoc>();
+
+ if (auto *TPL = LE->getTemplateParameterList()) {
+ for (NamedDecl *D : *TPL) {
+ TraverseDecl(D);
+ }
+ if (Expr *RequiresClause = TPL->getRequiresClause()) {
+ TraverseStmt(RequiresClause);
+ }
+ }
+
+ if (LE->hasExplicitParameters()) {
+ // Visit parameters.
+ for (ParmVarDecl *Param : Proto.getParams())
+ TraverseDecl(Param);
+ }
+
+ const auto *T = Proto.getTypePtr();
+ for (const auto &E : T->exceptions())
+ TraverseType(E);
+
+ if (Expr *NE = T->getNoexceptExpr())
+ TraverseStmt(NE, Queue);
+
+ if (LE->hasExplicitResultType())
+ TraverseTypeLoc(Proto.getReturnLoc());
+ TraverseStmt(LE->getTrailingRequiresClause());
+
+ TraverseStmt(LE->getBody());
+ }
+ return true;
}
return
RecursiveASTVisitor<MatchASTVisitor>::dataTraverseNode(S, Queue);
}
@@ -617,6 +666,9 @@ class MatchASTVisitor : public
RecursiveASTVisitor<MatchASTVisitor>,
bool IsMatchingInASTNodeNotSpelledInSource() const override {
return TraversingASTNodeNotSpelledInSource;
}
+ bool IsMatchingInASTNodeNotAsIs() const override {
+ return TraversingASTNodeNotAsIs;
+ }
bool TraverseTemplateInstantiations(ClassTemplateDecl *D) {
ASTNodeNotSpelledInSourceScope RAII(this, true);
@@ -638,6 +690,7 @@ class MatchASTVisitor : public
RecursiveASTVisitor<MatchASTVisitor>,
private:
bool TraversingASTNodeNotSpelledInSource = false;
+ bool TraversingASTNodeNotAsIs = false;
bool TraversingASTChildrenNotSpelledInSource = false;
struct ASTNodeNotSpelledInSourceScope {
@@ -654,6 +707,18 @@ class MatchASTVisitor : public
RecursiveASTVisitor<MatchASTVisitor>,
bool MB;
};
+ struct ASTNodeNotAsIsSourceScope {
+ ASTNodeNotAsIsSourceScope(MatchASTVisitor *V, bool B)
+ : MV(V), MB(V->TraversingASTNodeNotAsIs) {
+ V->TraversingASTNodeNotAsIs = B;
+ }
+ ~ASTNodeNotAsIsSourceScope() { MV->TraversingASTNodeNotAsIs =
MB; }
+
+ private:
+ MatchASTVisitor *MV;
+ bool MB;
+ };
+
struct ASTChildrenNotSpelledInSource {
ASTChildrenNotSpelledInSource(MatchASTVisitor *V, bool B)
: MV(V), MB(V->TraversingASTChildrenNotSpelledInSource) {
diff --git a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
index 3c19bceb079e..eb0fffcc3c37 100644
--- a/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
+++ b/clang/lib/ASTMatchers/ASTMatchersInternal.cpp
@@ -293,6 +293,10 @@ bool DynTypedMatcher::matches(const
DynTypedNode &DynNode,
Finder->IsMatchingInASTNodeNotSpelledInSource())
return false;
+ if (!Finder->isTraversalIgnoringImplicitNodes() &&
+ Finder->IsMatchingInASTNodeNotAsIs())
+ return false;
+
auto N =
Finder->getASTContext().getParentMapContext().traverseIgnored(DynNode);
@@ -317,6 +321,10 @@ bool
DynTypedMatcher::matchesNoKindCheck(const DynTypedNode &DynNode,
Finder->IsMatchingInASTNodeNotSpelledInSource())
return false;
+ if (!Finder->isTraversalIgnoringImplicitNodes() &&
+ Finder->IsMatchingInASTNodeNotAsIs())
+ return false;
+
auto N =
Finder->getASTContext().getParentMapContext().traverseIgnored(DynNode);
diff --git
a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
index a3a3a911b85c..1dc5179ce857 100644
--- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
+++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp
@@ -3065,6 +3065,33 @@ void func14() {
traverse(TK_IgnoreUnlessSpelledInSource,
functionDecl(hasName("func14"),
hasDescendant(floatLiteral()))),
langCxx20OrLater()));
+
+ Code = R"cpp(
+void foo() {
+ int explicit_captured = 0;
+ int implicit_captured = 0;
+ auto l = [&, explicit_captured](int i) {
+ if (i || explicit_captured || implicit_captured) return;
+ };
+}
+)cpp";
+
+ EXPECT_TRUE(matches(Code, traverse(TK_AsIs, ifStmt())));
+ EXPECT_TRUE(
+ matches(Code, traverse(TK_IgnoreUnlessSpelledInSource,
ifStmt())));
+
+ auto lambdaExplicitCapture = declRefExpr(
+ to(varDecl(hasName("explicit_captured"))),
unless(hasAncestor(ifStmt())));
+ auto lambdaImplicitCapture = declRefExpr(
+ to(varDecl(hasName("implicit_captured"))),
unless(hasAncestor(ifStmt())));
+
+ EXPECT_TRUE(matches(Code, traverse(TK_AsIs,
lambdaExplicitCapture)));
+ EXPECT_TRUE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
lambdaExplicitCapture)));
+
+ EXPECT_TRUE(matches(Code, traverse(TK_AsIs,
lambdaImplicitCapture)));
+ EXPECT_FALSE(matches(
+ Code, traverse(TK_IgnoreUnlessSpelledInSource,
lambdaImplicitCapture)));
}
TEST(IgnoringImpCasts, MatchesImpCasts) {
_______________________________________________
llvm-branch-commits mailing list
llvm-branch-commits@lists.llvm.org
<mailto:llvm-branch-commits@lists.llvm.org>
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits