https://github.com/voyager-jhk updated https://github.com/llvm/llvm-project/pull/199905
>From 4757408305cf6ebdc6d4ca7de5c8451824744d6f Mon Sep 17 00:00:00 2001 From: voyager-jhk <[email protected]> Date: Wed, 27 May 2026 16:54:08 +0800 Subject: [PATCH] [clang-tidy] Fix false positive in bugprone-use-after-move with std::forward on derived classes The `bugprone-use-after-move` check correctly identified partial moves when using `std::move` by matching the `ImplicitCastExpr` (DerivedToBase) as the parent of the call. However, when using `std::forward<Base>`, the cast occurs inside the argument, causing the matcher to miss the cast and falsely report a use-after-move. This patch manually checks the first argument of the moving call for a `DerivedToBase` cast if the parent matcher fails, ensuring both `move` and `forward` are correctly identified as partial moves. Fixes #63202 --- .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 8 ++++ clang-tools-extra/docs/ReleaseNotes.rst | 3 ++ .../use-after-move-forward-derived.cpp | 42 +++++++++++++++++++ .../checkers/bugprone/use-after-move.cpp | 35 ++++++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move-forward-derived.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp index 399442f52bd33..1570c3a39ab07 100644 --- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp @@ -641,6 +641,14 @@ void UseAfterMoveCheck::check(const MatchFinder::MatchResult &Result) { const CXXRecordDecl *MovedAs = ParentCast ? ParentCast->getType()->getAsCXXRecordDecl() : nullptr; + if (!MovedAs && CallMove && CallMove->getNumArgs() > 0) { + if (const auto *ArgCast = + dyn_cast<ImplicitCastExpr>(CallMove->getArg(0)->IgnoreParens())) { + if (ArgCast->getCastKind() == CK_DerivedToBase) + MovedAs = ArgCast->getType()->getAsCXXRecordDecl(); + } + } + for (Stmt *CodeBlock : CodeBlocks) { UseAfterMoveFinder Finder(Result.Context, InvalidationFunctions, ReinitializationFunctions, MovedAs); diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index f1b49e2cb6056..dfbd7f7d7bdbc 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -441,6 +441,9 @@ Changes in existing checks - Avoid false positives when moving object is reinitialized via the base class's ``operator=``. + - Avoid false positives when forwarding derived objects to base class + initializers. + - 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/test/clang-tidy/checkers/bugprone/use-after-move-forward-derived.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move-forward-derived.cpp new file mode 100644 index 0000000000000..4322b94bdf00d --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move-forward-derived.cpp @@ -0,0 +1,42 @@ +// RUN: %check_clang_tidy -std=c++17-or-later %s bugprone-use-after-move %t + +#include <utility> + +struct Person { + Person() = default; + Person(Person&&) {} +}; + +struct SpecialPerson : Person { + int surname; + + // Valid partial move + SpecialPerson(SpecialPerson&& sp) + : Person(std::move(sp)), + surname(std::move(sp.surname)) + // CHECK-NOTES-NOT: [[@LINE-1]]:{{[0-9]+}}: warning: 'sp' used after it was moved + {} + + // Valid partial forward (The original false positive bug) + SpecialPerson(SpecialPerson&& sp, int) + : Person(std::forward<Person>(sp)), + surname(std::move(sp.surname)) + // CHECK-NOTES-NOT: [[@LINE-1]]:{{[0-9]+}}: warning: 'sp' used after it was forwarded + {} + + // Invalid full move (Must warn) + SpecialPerson(SpecialPerson&& sp, float) { + SpecialPerson other(std::move(sp)); + sp.surname = 1; + // CHECK-NOTES: [[@LINE-1]]:{{[0-9]+}}: warning: 'sp' used after it was moved [bugprone-use-after-move] + // CHECK-NOTES: [[@LINE-3]]:{{[0-9]+}}: note: move occurred here + } + + // Invalid full forward (Must warn) + SpecialPerson(SpecialPerson&& sp, double) { + SpecialPerson other(std::forward<SpecialPerson>(sp)); + sp.surname = 1; + // CHECK-NOTES: [[@LINE-1]]:{{[0-9]+}}: warning: 'sp' used after it was forwarded [bugprone-use-after-move] + // CHECK-NOTES: [[@LINE-3]]:{{[0-9]+}}: note: forward occurred here + } +}; 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 d4e78d359b654..6b9ac8c4a3861 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 @@ -1814,3 +1814,38 @@ namespace GH62206 { (d) = b; // Should not warn } } // namespace GH62206 + +namespace GH63202 { + +struct Person { + Person() = default; + Person(Person&&) = default; + Person& operator=(Person&&) = default; +}; + +struct SpecialPerson : Person { + int surname; + + // Valid: partial move + SpecialPerson(SpecialPerson&& sp) + : Person(std::move(sp)), surname(std::move(sp.surname)) {} + + // Valid: partial forward — case fixed by this patch + SpecialPerson(SpecialPerson&& sp, int) + : Person(std::forward<Person>(sp)), surname(std::move(sp.surname)) {} +}; + +void invalidFullMove(SpecialPerson&& sp) { + SpecialPerson other(std::move(sp)); + sp.surname = 1; + // CHECK-NOTES: [[@LINE-1]]:3: warning: 'sp' used after it was moved [bugprone-use-after-move] + // CHECK-NOTES: [[@LINE-3]]:17: note: move occurred here +} + +void invalidFullForward(SpecialPerson&& sp) { + SpecialPerson other(std::forward<SpecialPerson>(sp)); + sp.surname = 1; + // CHECK-NOTES: [[@LINE-1]]:3: warning: 'sp' used after it was forwarded [bugprone-use-after-move] + // CHECK-NOTES: [[@LINE-3]]:17: note: forward occurred here +} +} // namespace GH63202 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
