diff --git a/clang-tools-extra/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.cpp --- a/clang-tools-extra/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BoolPointerImplicitConversionCheck.cpp @@ -59,9 +59,9 @@ findAll(callExpr(hasAnyArgument(ignoringParenImpCasts(RefMatcher)))), *If, *Result.Context) .empty() || - !match( - findAll(cxxDeleteExpr(has(ignoringParenImpCasts(expr(RefMatcher))))), - *If, *Result.Context) + !match(findAll(cxxDeleteExpr( + deletes(ignoringParenImpCasts(expr(RefMatcher))))), + *If, *Result.Context) .empty()) return; diff --git a/clang-tools-extra/clang-tidy/readability/DeleteNullPointerCheck.cpp b/clang-tools-extra/clang-tidy/readability/DeleteNullPointerCheck.cpp --- a/clang-tools-extra/clang-tidy/readability/DeleteNullPointerCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/DeleteNullPointerCheck.cpp @@ -21,11 +21,11 @@ void DeleteNullPointerCheck::registerMatchers(MatchFinder *Finder) { const auto DeleteExpr = cxxDeleteExpr( - has(declRefExpr(to(decl(equalsBoundNode("deletedPointer")))))) + deletes(declRefExpr(to(decl(equalsBoundNode("deletedPointer")))))) .bind("deleteExpr"); const auto DeleteMemberExpr = - cxxDeleteExpr(has(memberExpr(hasDeclaration( + cxxDeleteExpr(deletes(memberExpr(hasDeclaration( fieldDecl(equalsBoundNode("deletedMemberPointer")))))) .bind("deleteMemberExpr"); diff --git a/clang-tools-extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp b/clang-tools-extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp --- a/clang-tools-extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/UniqueptrDeleteReleaseCheck.cpp @@ -27,7 +27,7 @@ hasName("std::default_delete"))))))); Finder->addMatcher( - cxxDeleteExpr(has(ignoringParenImpCasts(cxxMemberCallExpr( + cxxDeleteExpr(deletes(ignoringParenImpCasts(cxxMemberCallExpr( on(expr(hasType(UniquePtrWithDefaultDelete), unless(hasType(IsSusbstituted))) .bind("uptr")), diff --git a/clang/docs/LibASTMatchersReference.html b/clang/docs/LibASTMatchersReference.html --- a/clang/docs/LibASTMatchersReference.html +++ b/clang/docs/LibASTMatchersReference.html @@ -3075,6 +3075,19 @@ +
Matches array new and delete expressions. + +Given: + MyClass *p1 = new MyClass[10]; + delete[] p1; +cxxNewExpr(isArray()) + matches the expression 'new MyClass[10]'. +cxxDeleteExpr(isArray()) + matches the expression 'delete[] p1'. +
Matches template-dependent, but known, member names. @@ -3296,12 +3309,15 @@- Matcher<CXXNewExpr> isArray + Matches array new expressions. +@@ -6303,6 +6319,17 @@ Matches array new and delete expressions. Given: MyClass *p1 = new MyClass[10]; + delete[] p1; cxxNewExpr(isArray()) matches the expression 'new MyClass[10]'. +cxxDeleteExpr(isArray()) + matches the expression 'delete[] p1'.+ Matcher<CXXDeleteExpr> deletes Matcher<Expr> InnerMatcher + + Matchers the expression being deleted. + +Given: + int *Ptr = nullptr; + delete Ptr; +cxxDeleteExpr(deletes(declRefExpr(ignoringImpCasts(to(varDecl(hasName("Ptr"))))))) + matches the expression 'delete Ptr'. +Matcher<CXXDependentScopeMemberExpr> hasObjectExpression Matcher<Expr> InnerMatcher Matches a member expression where the object expression is matched by a given matcher. Implicit object expressions are included; that is, it matches 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 @@ -7550,16 +7550,21 @@ return Node.hasDefaultArg(); } -/// Matches array new expressions. +/// Matches array new and delete expressions. /// /// Given: /// \code /// MyClass *p1 = new MyClass[10]; +/// delete[] p1; /// \endcode /// cxxNewExpr(isArray()) /// matches the expression 'new MyClass[10]'. -AST_MATCHER(CXXNewExpr, isArray) { - return Node.isArray(); +/// cxxDeleteExpr(isArray()) +/// matches the expression 'delete[] p1'. +AST_POLYMORPHIC_MATCHER(isArray, + AST_POLYMORPHIC_SUPPORTED_TYPES(CXXNewExpr, + CXXDeleteExpr)) { + return isArrayOperator(Node); } /// Matches placement new expression arguments. @@ -7604,6 +7609,20 @@ InnerMatcher.matches(**Node.getArraySize(), Finder, Builder); } +/// Matchers the expression being deleted. +/// +/// Given: +/// \code +/// int *Ptr = nullptr; +/// delete Ptr; +/// \endcode +/// cxxDeleteExpr(deletes(declRefExpr(ignoringImpCasts(to(varDecl(hasName("Ptr"))))))) +/// matches the expression 'delete Ptr'. +AST_MATCHER_P_OVERLOAD(CXXDeleteExpr, deletes, internal::Matcher, + InnerMatcher, 1) { + return InnerMatcher.matches(*Node.getArgument(), Finder, Builder); +} + /// Matches a class declaration that is defined. /// /// Example matches x (matcher = cxxRecordDecl(hasDefinition())) 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 @@ -155,6 +155,14 @@ return Node.getAccessSpecifier(); } +/// Unifies obtaining the whether as new or delete operator operates on an +/// array. +inline bool isArrayOperator(const CXXNewExpr &Node) { return Node.isArray(); } + +inline bool isArrayOperator(const CXXDeleteExpr &Node) { + return Node.isArrayFormAsWritten(); +} + /// Internal version of BoundNodes. Holds all the bound nodes. class BoundNodesMap { public: diff --git a/clang/lib/ASTMatchers/Dynamic/Registry.cpp b/clang/lib/ASTMatchers/Dynamic/Registry.cpp --- a/clang/lib/ASTMatchers/Dynamic/Registry.cpp +++ b/clang/lib/ASTMatchers/Dynamic/Registry.cpp @@ -214,6 +214,7 @@ REGISTER_MATCHER(decltypeType); REGISTER_MATCHER(deducedTemplateSpecializationType); REGISTER_MATCHER(defaultStmt); + REGISTER_MATCHER(deletes); REGISTER_MATCHER(dependentSizedArrayType); REGISTER_MATCHER(designatedInitExpr); REGISTER_MATCHER(designatorCountIs); @@ -266,7 +267,6 @@ REGISTER_MATCHER(hasAnySubstatement); REGISTER_MATCHER(hasAnyTemplateArgument); REGISTER_MATCHER(hasAnyUsingShadowDecl); - REGISTER_MATCHER(hasArgument); REGISTER_MATCHER(hasArgumentOfType); REGISTER_MATCHER(hasArraySize); REGISTER_MATCHER(hasAttr); diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -3779,6 +3779,12 @@ EXPECT_TRUE(matches("struct MyClass {}; MyClass *p1 = new MyClass[10];", cxxNewExpr(isArray()))); + + EXPECT_TRUE( + matches("void foo(int* p) { delete[] p; }", cxxDeleteExpr(isArray()))); + + EXPECT_TRUE( + notMatches("void foo(int* p) { delete p; }", cxxDeleteExpr(isArray()))); } TEST_P(ASTMatchersTest, HasArraySize) { 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 @@ -5466,5 +5466,31 @@ IsPlacementNew)); } +TEST(CXXDeleteExpr, Deletes) { + StatementMatcher IsDeletingPtrVar = cxxDeleteExpr( + deletes(ignoringImpCasts(declRefExpr(to(varDecl(hasName("Ptr"))))))); + + EXPECT_TRUE(matches("void foo(int* Ptr) { delete Ptr; }", IsDeletingPtrVar)); + EXPECT_TRUE(notMatches(R"( + struct Foo{ + int* Address; + }; + void Bar(Foo& Ptr) { + delete Ptr.Address; + } + )", + IsDeletingPtrVar)); + + EXPECT_TRUE(notMatches(R"( + struct Foo{ + int* Ptr; + }; + void Bar(Foo& Ptr) { + delete Ptr.Ptr; + } + )", + IsDeletingPtrVar)); +} + } // namespace ast_matchers } // namespace clang