Author: serge-sans-paille Date: 2026-04-05T10:06:46Z New Revision: df2de0a26d187d42f55f2d340309c552b1bb0186
URL: https://github.com/llvm/llvm-project/commit/df2de0a26d187d42f55f2d340309c552b1bb0186 DIFF: https://github.com/llvm/llvm-project/commit/df2de0a26d187d42f55f2d340309c552b1bb0186.diff LOG: [clang-tidy] Improve bugprone.use-after-move interaction with explicit destructor call. (#188866) It is valid (although niche) to call an explicit destructor after moving the object. Added: Modified: clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp clang-tools-extra/docs/ReleaseNotes.rst clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp Removed: ################################################################################ diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp index 06b5940c648a3..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) { @@ -401,9 +416,12 @@ void UseAfterMoveFinder::getDeclRefs( } }; - auto DeclRefMatcher = declRefExpr(hasDeclaration(equalsNode(MovedVariable)), - unless(inDecltypeOrTemplateArg())) - .bind("declref"); + auto DeclRefMatcher = + declRefExpr(hasDeclaration(equalsNode(MovedVariable)), + unless(inDecltypeOrTemplateArg()), + unless(hasParentIgnoringParenImpCasts( + 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 36e311341f336..ad2ee2ae4b35e 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -275,6 +275,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-avoid-capturing-lambda-coroutines <clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines>` check by adding the `AllowExplicitObjectParameters` option. When enabled, 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..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 @@ -89,6 +89,36 @@ void selfMove() { a.foo(); } +void * operator new(size_t, void *p); + +// Don't flag an explicit destructor call +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. + + 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. + 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. void onlyFlagOneUseAfterMove() { A a; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
