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> 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 > https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits >
_______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits