[clang-tools-extra] [clang-tidy] Improve bugprone.use-after-move interaction with explicit destructor call. (PR #188866)
via cfe-commits
cfe-commits at lists.llvm.org
Sat Apr 4 05:47:11 PDT 2026
https://github.com/serge-sans-paille updated https://github.com/llvm/llvm-project/pull/188866
>From e85099de7fec4c9ad6e808a86528dc9ddf685ecb Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
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 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..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 6bf698e42406155697ad39d906bde26a84461eee Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
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 9479b0cc70b1511ab36f02ef50e9e75a6d14234d Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
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.
More information about the cfe-commits
mailing list