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