[clang-tools-extra] [clang-tidy] Add [[clang::usable_on_move]] to inform bugprone-use-aft… (PR #216121)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 14 02:30:44 PDT 2026
https://github.com/serge-sans-paille updated https://github.com/llvm/llvm-project/pull/216121
>From a49ec7448b98c9262363e8400133c7a7a7ee07f8 Mon Sep 17 00:00:00 2001
From: serge-sans-paille <sguelton at mozilla.com>
Date: Thu, 13 Aug 2026 16:54:31 +0200
Subject: [PATCH] [clang-tidy] Add [[clang::annotate("clang-tidy",
"bugprone-use-after-move", "specified_after_move")]] to inform
bugprone-use-after-move that a call usage is safe after a move
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
À la std::unique_ptr where it's ok to test for nullability a moved-from
instance, several user-class may allow some method call on a moved-from
object, typically checking if a moved-from container is empty, etc.
---
.../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 50 ++++++++++++++-----
clang-tools-extra/docs/ReleaseNotes.md | 4 ++
.../checkers/bugprone/use-after-move.cpp | 12 +++++
3 files changed, 53 insertions(+), 13 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index b13367e25dcb2..a184e616e62a1 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -30,6 +30,14 @@ namespace clang::tidy::bugprone {
using matchers::hasUnevaluatedContext;
+static std::optional<StringRef> getStringLiteral(const Expr *E) {
+ assert(E);
+ if (const auto *SL = dyn_cast<StringLiteral>(E->IgnoreParenImpCasts()))
+ return SL->getString();
+ return std::nullopt;
+}
+
+
namespace {
AST_MATCHER_P(Expr, hasParentIgnoringParenImpCasts,
ast_matchers::internal::Matcher<Expr>, InnerMatcher) {
@@ -46,6 +54,28 @@ AST_MATCHER_P(Expr, hasParentIgnoringParenImpCasts,
return InnerMatcher.matches(*E, Finder, Builder);
}
+// Methods can be decorated with [[clang::annotate]] to specify it has
+// a user-defined behavior when called on a moved-from.
+AST_MATCHER(CXXMethodDecl, hasSpecifiedAfterMoveAnnotation) {
+ for(const AnnotateAttr *Attr : Node.specific_attrs<AnnotateAttr>()) {
+ if (Attr->getAnnotation() != "clang-tidy")
+ continue;
+
+ if (Attr->args_size() != 2)
+ continue;
+
+ std::optional<StringRef> Plugin = getStringLiteral(Attr->args_begin()[0]);
+ std::optional<StringRef> Annotation =
+ getStringLiteral(Attr->args_begin()[1]);
+
+ if (Plugin && Annotation && *Plugin == "bugprone-use-after-move" &&
+ *Annotation == "specified_after_move")
+ return true;
+ }
+ return false;
+ }
+
+
/// Contains information about a use-after-move.
struct UseAfterMove {
// The DeclRefExpr that constituted the use of the object.
@@ -351,13 +381,6 @@ void UseAfterMoveFinder::getUsesAndReinits(
});
}
-static std::optional<StringRef> getStringLiteral(const Expr *E) {
- assert(E);
- if (const auto *SL = dyn_cast<StringLiteral>(E->IgnoreParenImpCasts()))
- return SL->getString();
- return std::nullopt;
-}
-
// User defined types can use [[clang::annotate]] to mark smart-pointer-like
// types with a specified move from state that matches the standard smart
// pointer's moved-from state (nullptr).
@@ -434,12 +457,13 @@ void UseAfterMoveFinder::getDeclRefs(
};
const auto DeclRefMatcher =
- declRefExpr(hasDeclaration(equalsNode(MovedVariable)),
- unless(inDecltypeOrTemplateArg()),
- unless(hasParentIgnoringParenImpCasts(
- memberExpr(hasDeclaration(cxxDestructorDecl())))),
- optionally(hasParentIgnoringParenImpCasts(
- memberExpr().bind("member-expr"))))
+ declRefExpr(
+ hasDeclaration(equalsNode(MovedVariable)),
+ unless(inDecltypeOrTemplateArg()),
+ unless(hasParentIgnoringParenImpCasts(memberExpr(hasDeclaration(
+ anyOf(cxxDestructorDecl(), cxxMethodDecl(hasSpecifiedAfterMoveAnnotation())))))),
+ optionally(hasParentIgnoringParenImpCasts(
+ memberExpr().bind("member-expr"))))
.bind("declref");
AddDeclRefs(match(traverse(TK_AsIs, findAll(DeclRefMatcher)), *S->getStmt(),
diff --git a/clang-tools-extra/docs/ReleaseNotes.md b/clang-tools-extra/docs/ReleaseNotes.md
index ef3e6c49ce172..0fd8398b86839 100644
--- a/clang-tools-extra/docs/ReleaseNotes.md
+++ b/clang-tools-extra/docs/ReleaseNotes.md
@@ -118,6 +118,10 @@ infrastructure are described first, followed by tool-specific sections.
<clang-tidy/checks/bugprone/std-namespace-modification>` when checking
lambda closure types used as template arguments.
+- Improved {doc}`bugprone-use-after-move
+ <clang-tidy/checks/bugprone/use-after-move>` check to honor new
+ `[[clang::annotate("clang-tidy", "bugprone-use-after-move", "specified_after_move")]]` attribute.
+
- Improved {doc}`cppcoreguidelines-pro-type-member-init
<clang-tidy/checks/cppcoreguidelines/pro-type-member-init>` check by treating
`std::array` the same as built-in arrays when `IgnoreArrays` option is enabled.
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 1dd67941bd84c..d6a68f30e3c0d 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
@@ -68,6 +68,9 @@ class AnnotatedContainer {
void foo() const;
[[clang::reinitializes]] void clear();
+ [[clang::annotate("clang-tidy",
+ "bugprone-use-after-move",
+ "specified_after_move")]] bool empty() const;
};
////////////////////////////////////////////////////////////////////////////////
@@ -1007,6 +1010,15 @@ void reinitAnnotation() {
}
}
+void usableOnMovedAnnotation() {
+ {
+ AnnotatedContainer<int> obj;
+ std::move(obj);
+ obj.empty();
+ // No warning expected, empty is flagged as usable_on_moved
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
// Tests for annotations on smart-pointer-like types
More information about the cfe-commits
mailing list