https://github.com/AMS21 updated https://github.com/llvm/llvm-project/pull/82673
>From 84b816ae115808c9144f4a0808849844a6fcab00 Mon Sep 17 00:00:00 2001 From: AMS21 <ams21.git...@gmail.com> Date: Thu, 22 Feb 2024 19:24:43 +0100 Subject: [PATCH] [clang-tidy] Let `bugprone-use-after-move` also handle calls to `std::forward` Fixes #82023 --- .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 89 +++++++++++++------ clang-tools-extra/docs/ReleaseNotes.rst | 4 + .../checkers/bugprone/use-after-move.cpp | 37 ++++++++ 3 files changed, 103 insertions(+), 27 deletions(-) diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp index c5b6b541096ca9..760053b1b64983 100644 --- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp @@ -330,7 +330,8 @@ void UseAfterMoveFinder::getReinits( traverse(TK_AsIs, DeclRefMatcher), unless(parmVarDecl(hasType( references(qualType(isConstQualified())))))), - unless(callee(functionDecl(hasName("::std::move"))))))) + unless(callee(functionDecl( + hasAnyName("::std::move", "::std::forward"))))))) .bind("reinit"); Stmts->clear(); @@ -359,24 +360,55 @@ void UseAfterMoveFinder::getReinits( } } +enum class MoveType +{ + Move, // std::move + Forward, // std::forward +}; + +static MoveType determineMoveType(const FunctionDecl* FuncDecl) +{ + if (FuncDecl->getName() == "move") + return MoveType::Move; + if (FuncDecl->getName() == "forward") + return MoveType::Forward; + + assert(false && "Invalid move type"); +} + static void emitDiagnostic(const Expr *MovingCall, const DeclRefExpr *MoveArg, const UseAfterMove &Use, ClangTidyCheck *Check, - ASTContext *Context) { + ASTContext *Context, MoveType Type) { SourceLocation UseLoc = Use.DeclRef->getExprLoc(); SourceLocation MoveLoc = MovingCall->getExprLoc(); - Check->diag(UseLoc, "'%0' used after it was moved") - << MoveArg->getDecl()->getName(); - Check->diag(MoveLoc, "move occurred here", DiagnosticIDs::Note); + StringRef ActionType; + StringRef ActionTypePastTense; + switch (Type) { + case MoveType::Move: + ActionType = "move"; + ActionTypePastTense = "moved"; + break; + case MoveType::Forward: + ActionType = "forward"; + ActionTypePastTense = "forwarded"; + break; + } + + Check->diag(UseLoc, "'%0' used after it was %1") + << MoveArg->getDecl()->getName() << ActionTypePastTense; + Check->diag(MoveLoc, "%0 occurred here", DiagnosticIDs::Note) + << ActionType; if (Use.EvaluationOrderUndefined) { Check->diag(UseLoc, - "the use and move are unsequenced, i.e. there is no guarantee " + "the use and %0 are unsequenced, i.e. there is no guarantee " "about the order in which they are evaluated", - DiagnosticIDs::Note); + DiagnosticIDs::Note) + << ActionType; } else if (UseLoc < MoveLoc || Use.DeclRef == MoveArg) { - Check->diag(UseLoc, - "the use happens in a later loop iteration than the move", - DiagnosticIDs::Note); + Check->diag(UseLoc, "the use happens in a later loop iteration than the %0", + DiagnosticIDs::Note) + << ActionType; } } @@ -387,22 +419,23 @@ void UseAfterMoveCheck::registerMatchers(MatchFinder *Finder) { // the bool. auto TryEmplaceMatcher = cxxMemberCallExpr(callee(cxxMethodDecl(hasName("try_emplace")))); - auto CallMoveMatcher = - callExpr(argumentCountIs(1), callee(functionDecl(hasName("::std::move"))), - hasArgument(0, declRefExpr().bind("arg")), - unless(inDecltypeOrTemplateArg()), - unless(hasParent(TryEmplaceMatcher)), expr().bind("call-move"), - anyOf(hasAncestor(compoundStmt( - hasParent(lambdaExpr().bind("containing-lambda")))), - hasAncestor(functionDecl(anyOf( - cxxConstructorDecl( - hasAnyConstructorInitializer(withInitializer( - expr(anyOf(equalsBoundNode("call-move"), - hasDescendant(expr( - equalsBoundNode("call-move"))))) - .bind("containing-ctor-init")))) - .bind("containing-ctor"), - functionDecl().bind("containing-func")))))); + auto CallMoveMatcher = callExpr( + argumentCountIs(1), + callee(functionDecl(hasAnyName("::std::move", "::std::forward")).bind("move-decl")), + hasArgument(0, declRefExpr().bind("arg")), + unless(inDecltypeOrTemplateArg()), unless(hasParent(TryEmplaceMatcher)), + expr().bind("call-move"), + anyOf(hasAncestor(compoundStmt( + hasParent(lambdaExpr().bind("containing-lambda")))), + hasAncestor(functionDecl( + anyOf(cxxConstructorDecl( + hasAnyConstructorInitializer(withInitializer( + expr(anyOf(equalsBoundNode("call-move"), + hasDescendant(expr( + equalsBoundNode("call-move"))))) + .bind("containing-ctor-init")))) + .bind("containing-ctor"), + functionDecl().bind("containing-func")))))); Finder->addMatcher( traverse( @@ -436,6 +469,8 @@ void UseAfterMoveCheck::check(const MatchFinder::MatchResult &Result) { const auto *CallMove = Result.Nodes.getNodeAs<CallExpr>("call-move"); const auto *MovingCall = Result.Nodes.getNodeAs<Expr>("moving-call"); const auto *Arg = Result.Nodes.getNodeAs<DeclRefExpr>("arg"); + const auto *MoveDecl = + Result.Nodes.getNodeAs<FunctionDecl>("move-decl"); if (!MovingCall || !MovingCall->getExprLoc().isValid()) MovingCall = CallMove; @@ -470,7 +505,7 @@ void UseAfterMoveCheck::check(const MatchFinder::MatchResult &Result) { UseAfterMoveFinder Finder(Result.Context); UseAfterMove Use; if (Finder.find(CodeBlock, MovingCall, Arg->getDecl(), &Use)) - emitDiagnostic(MovingCall, Arg, Use, this, Result.Context); + emitDiagnostic(MovingCall, Arg, Use, this, Result.Context, determineMoveType(MoveDecl)); } } diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index a0b9fcfe0d7774..55c47c7617ce2a 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -130,6 +130,10 @@ Changes in existing checks <clang-tidy/checks/bugprone/unused-local-non-trivial-variable>` check by ignoring local variable with ``[maybe_unused]`` attribute. +- Improved :doc:`bugprone-use-after-move + <clang-tidy/checks/bugprone/use-after-move>` check to also handle + calls to ``std::forward``. + - Cleaned up :doc:`cppcoreguidelines-prefer-member-initializer <clang-tidy/checks/cppcoreguidelines/prefer-member-initializer>` by removing enforcement of rule `C.48 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 00b1da1e727e4f..7d9f63479a1b4e 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 @@ -111,6 +111,18 @@ constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) noexcept { return static_cast<typename remove_reference<_Tp>::type &&>(__t); } +template <class _Tp> +constexpr _Tp&& +forward(typename std::remove_reference<_Tp>::type& __t) noexcept { + return static_cast<_Tp&&>(__t); +} + +template <class _Tp> +constexpr _Tp&& +forward(typename std::remove_reference<_Tp>::type&& __t) noexcept { + return static_cast<_Tp&&>(__t); +} + } // namespace std class A { @@ -1525,3 +1537,28 @@ class PR38187 { private: std::string val_; }; + +namespace issue82023 +{ + +struct S { + S(); + S(S&&); +}; + +void consume(S s); + +template <typename T> +void forward(T&& t) { + consume(std::forward<T>(t)); + consume(std::forward<T>(t)); + // CHECK-NOTES: [[@LINE-1]]:27: warning: 't' used after it was forwarded + // CHECK-NOTES: [[@LINE-3]]:11: note: forward occurred here +} + +void create() { + S s; + forward(std::move(s)); +} + +} // namespace issue82023 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits