diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -3106,9 +3106,16 @@ /// \c A but not \c B. AST_MATCHER_P(CXXRecordDecl, hasMethod, internal::Matcher, InnerMatcher) { - return matchesFirstInPointerRange(InnerMatcher, Node.method_begin(), - Node.method_end(), Finder, - Builder) != Node.method_end(); + BoundNodesTreeBuilder Result(*Builder); + auto MatchIt = matchesFirstInPointerRange(InnerMatcher, Node.method_begin(), + Node.method_end(), Finder, &Result); + if (MatchIt == Node.method_end()) + return false; + + if (Finder->isTraversalIgnoringImplicitNodes() && (*MatchIt)->isImplicit()) + return false; + *Builder = std::move(Result); + return true; } /// Matches the generated class of lambda expressions. @@ -4044,7 +4051,15 @@ CallExpr, CXXConstructExpr, CXXUnresolvedConstructExpr, ObjCMessageExpr), unsigned, N) { - return Node.getNumArgs() == N; + unsigned NumArgs = Node.getNumArgs(); + if (!Finder->isTraversalIgnoringImplicitNodes()) + return NumArgs == N; + while (NumArgs) { + if (!isa(Node.getArg(NumArgs - 1))) + break; + --NumArgs; + } + return NumArgs == N; } /// Matches the n'th argument of a call expression or a constructor @@ -4060,9 +4075,12 @@ CallExpr, CXXConstructExpr, CXXUnresolvedConstructExpr, ObjCMessageExpr), unsigned, N, internal::Matcher, InnerMatcher) { - return (N < Node.getNumArgs() && - InnerMatcher.matches( - *Node.getArg(N)->IgnoreParenImpCasts(), Finder, Builder)); + if (N >= Node.getNumArgs()) + return false; + const Expr *Arg = Node.getArg(N); + if (Finder->isTraversalIgnoringImplicitNodes() && isa(Arg)) + return false; + return InnerMatcher.matches(*Arg->IgnoreParenImpCasts(), Finder, Builder); } /// Matches the n'th item of an initializer list expression. @@ -4154,9 +4172,11 @@ /// record matches Foo, hasAnyConstructorInitializer matches foo_(1) AST_MATCHER_P(CXXConstructorDecl, hasAnyConstructorInitializer, internal::Matcher, InnerMatcher) { - return matchesFirstInPointerRange(InnerMatcher, Node.init_begin(), - Node.init_end(), Finder, - Builder) != Node.init_end(); + auto MatchIt = matchesFirstInPointerRange(InnerMatcher, Node.init_begin(), + Node.init_end(), Finder, Builder); + if (MatchIt == Node.init_end()) + return false; + return (*MatchIt)->isWritten() || !Finder->isTraversalIgnoringImplicitNodes(); } /// Matches the field declaration of a constructor initializer. @@ -4281,6 +4301,9 @@ CXXUnresolvedConstructExpr, ObjCMessageExpr), internal::Matcher, InnerMatcher) { for (const Expr *Arg : Node.arguments()) { + if (Finder->isTraversalIgnoringImplicitNodes() && + isa(Arg)) + break; BoundNodesTreeBuilder Result(*Builder); if (InnerMatcher.matches(*Arg, Finder, &Result)) { *Builder = std::move(Result); @@ -4998,6 +5021,8 @@ CXXForRangeStmt, FunctionDecl), internal::Matcher, InnerMatcher) { + if (Finder->isTraversalIgnoringImplicitNodes() && isDefaultedHelper(&Node)) + return false; const Stmt *const Statement = internal::GetBodyMatcher::get(Node); return (Statement != nullptr && InnerMatcher.matches(*Statement, Finder, Builder)); @@ -6880,6 +6905,8 @@ BoundNodesTreeBuilder Result; bool Matched = false; for (const auto *I : Node.inits()) { + if (Finder->isTraversalIgnoringImplicitNodes() && !I->isWritten()) + continue; BoundNodesTreeBuilder InitBuilder(*Builder); if (InnerMatcher.matches(*I, Finder, &InitBuilder)) { Matched = true; diff --git a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h --- a/clang/include/clang/ASTMatchers/ASTMatchersInternal.h +++ b/clang/include/clang/ASTMatchers/ASTMatchersInternal.h @@ -619,6 +619,130 @@ return Matcher(Implementation); } +/// Interface that allows matchers to traverse the AST. +/// FIXME: Find a better name. +/// +/// This provides three entry methods for each base node type in the AST: +/// - \c matchesChildOf: +/// Matches a matcher on every child node of the given node. Returns true +/// if at least one child node could be matched. +/// - \c matchesDescendantOf: +/// Matches a matcher on all descendant nodes of the given node. Returns true +/// if at least one descendant matched. +/// - \c matchesAncestorOf: +/// Matches a matcher on all ancestors of the given node. Returns true if +/// at least one ancestor matched. +/// +/// FIXME: Currently we only allow Stmt and Decl nodes to start a traversal. +/// In the future, we want to implement this for all nodes for which it makes +/// sense. In the case of matchesAncestorOf, we'll want to implement it for +/// all nodes, as all nodes have ancestors. +class ASTMatchFinder { +public: + /// Defines how bindings are processed on recursive matches. + enum BindKind { + /// Stop at the first match and only bind the first match. + BK_First, + + /// Create results for all combinations of bindings that match. + BK_All + }; + + /// Defines which ancestors are considered for a match. + enum AncestorMatchMode { + /// All ancestors. + AMM_All, + + /// Direct parent only. + AMM_ParentOnly + }; + + virtual ~ASTMatchFinder() = default; + + /// Returns true if the given C++ class is directly or indirectly derived + /// from a base type matching \c base. + /// + /// A class is not considered to be derived from itself. + virtual bool classIsDerivedFrom(const CXXRecordDecl *Declaration, + const Matcher &Base, + BoundNodesTreeBuilder *Builder, + bool Directly) = 0; + + /// Returns true if the given Objective-C class is directly or indirectly + /// derived from a base class matching \c base. + /// + /// A class is not considered to be derived from itself. + virtual bool objcClassIsDerivedFrom(const ObjCInterfaceDecl *Declaration, + const Matcher &Base, + BoundNodesTreeBuilder *Builder, + bool Directly) = 0; + + template + bool matchesChildOf(const T &Node, const DynTypedMatcher &Matcher, + BoundNodesTreeBuilder *Builder, TraversalKind Traverse, + BindKind Bind) { + static_assert(std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value, + "unsupported type for recursive matching"); + return matchesChildOf(DynTypedNode::create(Node), getASTContext(), Matcher, + Builder, Traverse, Bind); + } + + template + bool matchesDescendantOf(const T &Node, const DynTypedMatcher &Matcher, + BoundNodesTreeBuilder *Builder, BindKind Bind) { + static_assert(std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value, + "unsupported type for recursive matching"); + return matchesDescendantOf(DynTypedNode::create(Node), getASTContext(), + Matcher, Builder, Bind); + } + + // FIXME: Implement support for BindKind. + template + bool matchesAncestorOf(const T &Node, const DynTypedMatcher &Matcher, + BoundNodesTreeBuilder *Builder, + AncestorMatchMode MatchMode) { + static_assert(std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value || + std::is_base_of::value, + "type not allowed for recursive matching"); + return matchesAncestorOf(DynTypedNode::create(Node), getASTContext(), + Matcher, Builder, MatchMode); + } + + virtual ASTContext &getASTContext() const = 0; + + virtual bool IsMatchingInASTNodeNotSpelledInSource() const = 0; + + bool isTraversalIgnoringImplicitNodes() const; + +protected: + virtual bool matchesChildOf(const DynTypedNode &Node, ASTContext &Ctx, + const DynTypedMatcher &Matcher, + BoundNodesTreeBuilder *Builder, + TraversalKind Traverse, BindKind Bind) = 0; + + virtual bool matchesDescendantOf(const DynTypedNode &Node, ASTContext &Ctx, + const DynTypedMatcher &Matcher, + BoundNodesTreeBuilder *Builder, + BindKind Bind) = 0; + + virtual bool matchesAncestorOf(const DynTypedNode &Node, ASTContext &Ctx, + const DynTypedMatcher &Matcher, + BoundNodesTreeBuilder *Builder, + AncestorMatchMode MatchMode) = 0; +}; + /// Specialization of the conversion functions for QualType. /// /// This specialization provides the Matcher->Matcher @@ -665,6 +789,15 @@ return End; } +template ::value> + * = nullptr> +inline bool isDefaultedHelper(const T *) { + return false; +} +inline bool isDefaultedHelper(const FunctionDecl *FD) { + return FD->isDefaulted(); +} + // Metafunction to determine if type T has a member called getDecl. template class has_getDecl { @@ -932,6 +1065,8 @@ /// is \c NULL. bool matchesDecl(const Decl *Node, ASTMatchFinder *Finder, BoundNodesTreeBuilder *Builder) const { + if (Finder->isTraversalIgnoringImplicitNodes() && Node->isImplicit()) + return false; return Node != nullptr && this->InnerMatcher.matches( DynTypedNode::create(*Node), Finder, Builder); } @@ -953,134 +1088,6 @@ template const bool IsBaseType::value; -/// Interface that allows matchers to traverse the AST. -/// FIXME: Find a better name. -/// -/// This provides three entry methods for each base node type in the AST: -/// - \c matchesChildOf: -/// Matches a matcher on every child node of the given node. Returns true -/// if at least one child node could be matched. -/// - \c matchesDescendantOf: -/// Matches a matcher on all descendant nodes of the given node. Returns true -/// if at least one descendant matched. -/// - \c matchesAncestorOf: -/// Matches a matcher on all ancestors of the given node. Returns true if -/// at least one ancestor matched. -/// -/// FIXME: Currently we only allow Stmt and Decl nodes to start a traversal. -/// In the future, we want to implement this for all nodes for which it makes -/// sense. In the case of matchesAncestorOf, we'll want to implement it for -/// all nodes, as all nodes have ancestors. -class ASTMatchFinder { -public: - - /// Defines how bindings are processed on recursive matches. - enum BindKind { - /// Stop at the first match and only bind the first match. - BK_First, - - /// Create results for all combinations of bindings that match. - BK_All - }; - - /// Defines which ancestors are considered for a match. - enum AncestorMatchMode { - /// All ancestors. - AMM_All, - - /// Direct parent only. - AMM_ParentOnly - }; - - virtual ~ASTMatchFinder() = default; - - /// Returns true if the given C++ class is directly or indirectly derived - /// from a base type matching \c base. - /// - /// A class is not considered to be derived from itself. - virtual bool classIsDerivedFrom(const CXXRecordDecl *Declaration, - const Matcher &Base, - BoundNodesTreeBuilder *Builder, - bool Directly) = 0; - - /// Returns true if the given Objective-C class is directly or indirectly - /// derived from a base class matching \c base. - /// - /// A class is not considered to be derived from itself. - virtual bool objcClassIsDerivedFrom(const ObjCInterfaceDecl *Declaration, - const Matcher &Base, - BoundNodesTreeBuilder *Builder, - bool Directly) = 0; - - template - bool matchesChildOf(const T &Node, const DynTypedMatcher &Matcher, - BoundNodesTreeBuilder *Builder, TraversalKind Traverse, - BindKind Bind) { - static_assert(std::is_base_of::value || - std::is_base_of::value || - std::is_base_of::value || - std::is_base_of::value || - std::is_base_of::value || - std::is_base_of::value, - "unsupported type for recursive matching"); - return matchesChildOf(DynTypedNode::create(Node), getASTContext(), Matcher, - Builder, Traverse, Bind); - } - - template - bool matchesDescendantOf(const T &Node, - const DynTypedMatcher &Matcher, - BoundNodesTreeBuilder *Builder, - BindKind Bind) { - static_assert(std::is_base_of::value || - std::is_base_of::value || - std::is_base_of::value || - std::is_base_of::value || - std::is_base_of::value || - std::is_base_of::value, - "unsupported type for recursive matching"); - return matchesDescendantOf(DynTypedNode::create(Node), getASTContext(), - Matcher, Builder, Bind); - } - - // FIXME: Implement support for BindKind. - template - bool matchesAncestorOf(const T &Node, - const DynTypedMatcher &Matcher, - BoundNodesTreeBuilder *Builder, - AncestorMatchMode MatchMode) { - static_assert(std::is_base_of::value || - std::is_base_of::value || - std::is_base_of::value || - std::is_base_of::value, - "type not allowed for recursive matching"); - return matchesAncestorOf(DynTypedNode::create(Node), getASTContext(), - Matcher, Builder, MatchMode); - } - - virtual ASTContext &getASTContext() const = 0; - - virtual bool IsMatchingInASTNodeNotSpelledInSource() const = 0; - - bool isTraversalIgnoringImplicitNodes() const; - -protected: - virtual bool matchesChildOf(const DynTypedNode &Node, ASTContext &Ctx, - const DynTypedMatcher &Matcher, - BoundNodesTreeBuilder *Builder, - TraversalKind Traverse, BindKind Bind) = 0; - - virtual bool matchesDescendantOf(const DynTypedNode &Node, ASTContext &Ctx, - const DynTypedMatcher &Matcher, - BoundNodesTreeBuilder *Builder, - BindKind Bind) = 0; - - virtual bool matchesAncestorOf(const DynTypedNode &Node, ASTContext &Ctx, - const DynTypedMatcher &Matcher, - BoundNodesTreeBuilder *Builder, - AncestorMatchMode MatchMode) = 0; -}; - /// A type-list implementation. /// /// A "linked list" of types, accessible by using the ::head and ::tail diff --git a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersTraversalTest.cpp @@ -2335,8 +2335,8 @@ StringRef Code = R"cpp( struct NonTrivial { NonTrivial() {} - NonTrivial(NonTrivial&) {} - NonTrivial& operator=(NonTrivial&) { return *this; } + NonTrivial(const NonTrivial &) {} + NonTrivial& operator=(const NonTrivial &) { return *this; } ~NonTrivial() {} }; @@ -2347,7 +2347,7 @@ struct ContainsArray { NonTrivial arr[2]; - ContainsArray& operator=(ContainsArray &other) = default; + ContainsArray& operator=(const ContainsArray &other) = default; }; void copyIt() @@ -2368,6 +2368,13 @@ HasCtorInits() : NoSpecialMethods(), m_i(42) {} }; +struct CtorInitsNonTrivial : NonTrivial +{ + int m_i; + NonTrivial m_nt; + CtorInitsNonTrivial() : NonTrivial(), m_i(42) {} +}; + )cpp"; { auto M = cxxRecordDecl(hasName("NoSpecialMethods"), @@ -2393,6 +2400,35 @@ M = cxxRecordDecl(hasName("NoSpecialMethods"), has(cxxDestructorDecl())); EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + + M = cxxRecordDecl(hasName("NoSpecialMethods"), + hasMethod(cxxConstructorDecl(isCopyConstructor()))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + + M = cxxRecordDecl(hasName("NoSpecialMethods"), + hasMethod(cxxMethodDecl(isCopyAssignmentOperator()))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + + M = cxxRecordDecl(hasName("NoSpecialMethods"), + hasMethod(cxxConstructorDecl(isDefaultConstructor()))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + + M = cxxRecordDecl(hasName("NoSpecialMethods"), + hasMethod(cxxDestructorDecl())); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } + { + // Because the copy-assignment operator is not spelled in the + // source (ie, isImplicit()), we don't match it + auto M = + cxxOperatorCallExpr(hasType(cxxRecordDecl(hasName("NoSpecialMethods"))), + callee(cxxMethodDecl(isCopyAssignmentOperator()))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); } { // Compiler generates a forStmt to copy the array @@ -2414,6 +2450,24 @@ EXPECT_TRUE(matches(Code, traverse(TK_AsIs, MDef))); EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, MDef))); + auto MBody = cxxMethodDecl(MDecl, hasBody(compoundStmt())); + + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, MBody))); + EXPECT_FALSE( + matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, MBody))); + + auto MIsDefn = cxxMethodDecl(MDecl, isDefinition()); + + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, MIsDefn))); + EXPECT_TRUE( + matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, MIsDefn))); + + auto MIsInline = cxxMethodDecl(MDecl, isInline()); + + EXPECT_FALSE(matches(Code, traverse(TK_AsIs, MIsInline))); + EXPECT_FALSE( + matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, MIsInline))); + // The parameter of the defaulted method can still be matched. auto MParm = cxxMethodDecl(MDecl, hasParameter(0, parmVarDecl(hasName("other")))); @@ -2435,19 +2489,34 @@ EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); } + { + auto M = cxxConstructorDecl(hasName("HasCtorInits"), + hasAnyConstructorInitializer(cxxCtorInitializer( + forField(hasName("m_nt"))))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } + { + auto M = + cxxConstructorDecl(hasName("HasCtorInits"), + forEachConstructorInitializer( + cxxCtorInitializer(forField(hasName("m_nt"))))); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } { auto M = cxxConstructorDecl( - hasName("HasCtorInits"), - has(cxxCtorInitializer(withInitializer(cxxConstructExpr(hasDeclaration( - cxxConstructorDecl(hasName("NoSpecialMethods")))))))); + hasName("CtorInitsNonTrivial"), + has(cxxCtorInitializer(withInitializer(cxxConstructExpr( + hasDeclaration(cxxConstructorDecl(hasName("NonTrivial")))))))); EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); } { auto M = cxxConstructorDecl( hasName("HasCtorInits"), - has(cxxCtorInitializer(withInitializer(cxxConstructExpr( - hasDeclaration(cxxConstructorDecl(hasName("NonTrivial")))))))); + has(cxxCtorInitializer(withInitializer(cxxConstructExpr(hasDeclaration( + cxxConstructorDecl(hasName("NoSpecialMethods")))))))); EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); } @@ -2573,13 +2642,37 @@ hasDefaultArg(42); } )cpp"; + auto hasDefaultArgCall = [](auto InnerMatcher) { + return callExpr(callee(functionDecl(hasName("hasDefaultArg"))), + InnerMatcher); + }; { - auto M = callExpr(has(integerLiteral(equals(42)))); + auto M = hasDefaultArgCall(has(integerLiteral(equals(42)))); EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); } { - auto M = callExpr(has(cxxDefaultArgExpr())); + auto M = hasDefaultArgCall(has(cxxDefaultArgExpr())); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } + { + auto M = hasDefaultArgCall(argumentCountIs(2)); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } + { + auto M = hasDefaultArgCall(argumentCountIs(1)); + EXPECT_FALSE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_TRUE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } + { + auto M = hasDefaultArgCall(hasArgument(1, cxxDefaultArgExpr())); + EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); + EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); + } + { + auto M = hasDefaultArgCall(hasAnyArgument(cxxDefaultArgExpr())); EXPECT_TRUE(matches(Code, traverse(TK_AsIs, M))); EXPECT_FALSE(matches(Code, traverse(TK_IgnoreUnlessSpelledInSource, M))); } diff --git a/clang/unittests/Tooling/TransformerTest.cpp b/clang/unittests/Tooling/TransformerTest.cpp --- a/clang/unittests/Tooling/TransformerTest.cpp +++ b/clang/unittests/Tooling/TransformerTest.cpp @@ -1245,6 +1245,32 @@ testRuleFailure(makeRule(traverse(TK_AsIs, MatchedLoop), RewriteRule), RewriteInput); } + { + auto MatchedLoop = forStmt( + hasLoopInit(declStmt( + hasSingleDecl(varDecl(hasInitializer(integerLiteral(equals(0)))) + .bind("loopVar")))), + hasCondition(binaryOperator(hasOperatorName("!="), + hasLHS(ignoringImplicit(declRefExpr(to( + varDecl(equalsBoundNode("loopVar")))))), + hasRHS(expr().bind("upperBoundExpr")))), + hasIncrement(unaryOperator(hasOperatorName("++"), + hasUnaryOperand(declRefExpr(to( + varDecl(equalsBoundNode("loopVar")))))) + .bind("incrementOp"))); + + auto RewriteRule = + changeTo(transformer::enclose(node("loopVar"), node("incrementOp")), + cat("auto ", name("loopVar"), " : boost::irange(", + node("upperBoundExpr"), ")")); + + testRule(makeRule(traverse(TK_IgnoreUnlessSpelledInSource, MatchedLoop), + RewriteRule), + RewriteInput, RewriteOutput); + + testRuleFailure(makeRule(traverse(TK_AsIs, MatchedLoop), RewriteRule), + RewriteInput); + } } TEST_F(TransformerTest, TemplateInstantiation) {