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 <[email protected]> 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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
