<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">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>