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