https://github.com/serge-sans-paille updated https://github.com/llvm/llvm-project/pull/188866
>From da13e5808957a852a92435431e4a78541fbe912b Mon Sep 17 00:00:00 2001 From: serge-sans-paille <[email protected]> Date: Thu, 26 Mar 2026 22:40:37 +0100 Subject: [PATCH 1/3] [clang-tidy] Improve bugprone.use-after-move interaction with explicit destructor call. It is valid (although niche) to call an explicit destructor after moving the object. --- .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 9 ++++++--- clang-tools-extra/docs/ReleaseNotes.rst | 2 ++ .../docs/clang-tidy/checks/bugprone/use-after-move.rst | 2 +- .../clang-tidy/checkers/bugprone/use-after-move.cpp | 10 ++++++++++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp index 06b5940c648a3..d60098eab85ee 100644 --- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp @@ -401,9 +401,12 @@ void UseAfterMoveFinder::getDeclRefs( } }; - auto DeclRefMatcher = declRefExpr(hasDeclaration(equalsNode(MovedVariable)), - unless(inDecltypeOrTemplateArg())) - .bind("declref"); + auto DeclRefMatcher = + declRefExpr( + hasDeclaration(equalsNode(MovedVariable)), + unless(inDecltypeOrTemplateArg()), + unless(hasParent(memberExpr(hasDeclaration(cxxDestructorDecl()))))) + .bind("declref"); AddDeclRefs(match(traverse(TK_AsIs, findAll(DeclRefMatcher)), *S->getStmt(), *Context)); diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 49901f8a706c6..25cef90df1e07 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -258,6 +258,8 @@ Changes in existing checks - Add support for annotation of user-defined types as having the same moved-from semantics as standard smart pointers. + - Do not report explicit call to destructor after move as an invalid use. + - Improved :doc:`cppcoreguidelines-init-variables <clang-tidy/checks/cppcoreguidelines/init-variables>` check by ensuring that member pointers are correctly flagged as uninitialized. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst index a0e2cc767854d..da72f742b38d0 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst @@ -193,7 +193,7 @@ Use --- Any occurrence of the moved variable that is not a reinitialization (see below) -is considered to be a use. +or an explicit call to the variable destructor is considered to be a use. An exception to this are objects of type ``std::unique_ptr``, ``std::shared_ptr``, ``std::weak_ptr``, ``std::optional``, and ``std::any``, diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp index 94b950f855f72..dcab9c5e59665 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp @@ -89,6 +89,16 @@ void selfMove() { a.foo(); } +void * operator new(size_t, void *p); + +// Don't flag an explicit destructor call +void explicitDestrucotr() { + alignas(A) char storage[sizeof(A)]; + A& a = *new (storage) A(); + std::move(a); + a.~A(); // It's always valid to destruct a moved object. +} + // A warning should only be emitted for one use-after-move. void onlyFlagOneUseAfterMove() { A a; >From 6a8f95833b352cdefd698a080bbe0674d72f11bb Mon Sep 17 00:00:00 2001 From: serge-sans-paille <[email protected]> Date: Fri, 27 Mar 2026 23:37:28 +0100 Subject: [PATCH 2/3] fixup! [clang-tidy] Improve bugprone.use-after-move interaction with explicit destructor call. --- .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 23 +++++++++++++++---- .../checkers/bugprone/use-after-move.cpp | 16 ++++++++++++- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp index d60098eab85ee..9e9dfdc5de11e 100644 --- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp @@ -82,6 +82,21 @@ class UseAfterMoveFinder { llvm::SmallPtrSet<const CFGBlock *, 8> Visited; }; +AST_MATCHER_P(Expr, hasParentIgnoringParenImpCasts, + ast_matchers::internal::Matcher<Expr>, InnerMatcher) { + const Expr *E = &Node; + do { + const DynTypedNodeList Parents = Finder->getASTContext().getParents(*E); + if (Parents.size() != 1) + return false; + E = Parents[0].get<Expr>(); + if (!E) + return false; + } while (isa<ImplicitCastExpr, ParenExpr>(E)); + + return InnerMatcher.matches(*E, Finder, Builder); +} + } // namespace static auto getNameMatcher(llvm::ArrayRef<StringRef> InvalidationFunctions) { @@ -402,10 +417,10 @@ void UseAfterMoveFinder::getDeclRefs( }; auto DeclRefMatcher = - declRefExpr( - hasDeclaration(equalsNode(MovedVariable)), - unless(inDecltypeOrTemplateArg()), - unless(hasParent(memberExpr(hasDeclaration(cxxDestructorDecl()))))) + declRefExpr(hasDeclaration(equalsNode(MovedVariable)), + unless(inDecltypeOrTemplateArg()), + unless(hasParentIgnoringParenImpCasts( + memberExpr(hasDeclaration(cxxDestructorDecl()))))) .bind("declref"); AddDeclRefs(match(traverse(TK_AsIs, findAll(DeclRefMatcher)), *S->getStmt(), diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp index dcab9c5e59665..6e354c215fc63 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp @@ -92,11 +92,25 @@ void selfMove() { void * operator new(size_t, void *p); // Don't flag an explicit destructor call -void explicitDestrucotr() { +void explicitDestructor() { alignas(A) char storage[sizeof(A)]; A& a = *new (storage) A(); std::move(a); a.~A(); // It's always valid to destruct a moved object. + + A& b = *new (storage) A(); + std::move(b); + (b).~A(); // Parenthesis should not change the behavior. + b.foo(); // But destruction is not a reinitialization. + // CHECK-NOTES: [[@LINE-1]]:3: warning: 'b' used after it was moved + // CHECK-NOTES: [[@LINE-4]]:3: note: move occurred here + + A& c = *new (storage) A(); + std::move(c); + c.foo(); + // CHECK-NOTES: [[@LINE-1]]:3: warning: 'c' used after it was moved + // CHECK-NOTES: [[@LINE-3]]:3: note: move occurred here + c.~A(); } // A warning should only be emitted for one use-after-move. >From 412c6cb1bc3430100bcc1d18cbaa18172d100023 Mon Sep 17 00:00:00 2001 From: serge-sans-paille <[email protected]> Date: Fri, 3 Apr 2026 12:08:44 +0200 Subject: [PATCH 3/3] fixup! fixup! [clang-tidy] Improve bugprone.use-after-move interaction with explicit destructor call. --- .../test/clang-tidy/checkers/bugprone/use-after-move.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp index 6e354c215fc63..80df2b99eb874 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp @@ -98,6 +98,12 @@ void explicitDestructor() { std::move(a); a.~A(); // It's always valid to destruct a moved object. + using B = AnnotatedContainer<int>; + alignas(B) char other_storage[sizeof(B)]; + B& a_p = *new (other_storage) B(); + std::move(a_p); + a_p.~B(); // Same as above, but with a template class. + A& b = *new (storage) A(); std::move(b); (b).~A(); // Parenthesis should not change the behavior. _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
