llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Arendelle (SekaiArendelle) <details> <summary>Changes</summary> Fixes #<!-- -->218227 --- Full diff: https://github.com/llvm/llvm-project/pull/218303.diff 2 Files Affected: - (modified) clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp (+36-3) - (added) clang/test/Analysis/use-after-move-cxx23.cpp (+47) ``````````diff diff --git a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp index 9c616a2d17783..47e808afca3a0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp @@ -473,7 +473,12 @@ void MoveChecker::checkPostCall(const CallEvent &Call, if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator()) return; - const auto ArgRegion = AFC->getArgSVal(0).getAsRegion(); + // For an explicit-object member function, the object parameter is part of + // the function's parameter list. In that case, the object being moved from + // is the second argument rather than the first one. + const unsigned MoveArgIndex = + MethodDecl->isExplicitObjectMemberFunction() ? 1 : 0; + const auto ArgRegion = AFC->getArgSVal(MoveArgIndex).getAsRegion(); if (!ArgRegion) return; @@ -482,14 +487,17 @@ void MoveChecker::checkPostCall(const CallEvent &Call, if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion) return; - if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC)) + if (MethodDecl->isExplicitObjectMemberFunction()) { + if (AFC->getArgSVal(0).getAsRegion() == ArgRegion) + return; + } else if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC)) if (IC->getCXXThisVal().getAsRegion() == ArgRegion) return; const MemRegion *BaseRegion = ArgRegion->getBaseRegion(); // Skip temp objects because of their short lifetime. if (BaseRegion->getAs<CXXTempObjectRegion>() || - AFC->getArgExpr(0)->isPRValue()) + AFC->getArgExpr(MoveArgIndex)->isPRValue()) return; // If it has already been reported do not need to modify the state. @@ -705,6 +713,31 @@ void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { } } + // Calls to explicit-object member functions are represented as ordinary + // function calls because they have no implicit 'this' argument. Model an + // explicit-object assignment here before handling instance calls below. + const auto *ExplicitObjectMethod = + dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); + if (ExplicitObjectMethod && + ExplicitObjectMethod->isExplicitObjectMemberFunction() && + ExplicitObjectMethod->getOverloadedOperator() == OO_Equal) { + const MemRegion *ThisRegion = Call.getArgSVal(0).getAsRegion(); + State = removeFromState(State, ThisRegion); + + if (ExplicitObjectMethod->isCopyAssignmentOperator() || + ExplicitObjectMethod->isMoveAssignmentOperator()) { + const MemRegion *ArgRegion = Call.getArgSVal(1).getAsRegion(); + const CXXRecordDecl *RD = ExplicitObjectMethod->getParent(); + MisuseKind MK = ExplicitObjectMethod->isMoveAssignmentOperator() + ? MK_Move + : MK_Copy; + modelUse(State, ArgRegion, RD, MK, C); + return; + } + C.addTransition(State); + return; + } + const auto IC = dyn_cast<CXXInstanceCall>(&Call); if (!IC) return; diff --git a/clang/test/Analysis/use-after-move-cxx23.cpp b/clang/test/Analysis/use-after-move-cxx23.cpp new file mode 100644 index 0000000000000..ff989f43d17d3 --- /dev/null +++ b/clang/test/Analysis/use-after-move-cxx23.cpp @@ -0,0 +1,47 @@ +// RUN: %clang_analyze_cc1 -std=c++23 -analyzer-checker=cplusplus.Move \ +// RUN: -analyzer-output=text -verify %s + +#include "Inputs/system-header-simulator-cxx.h" + +struct Owner { + Owner() = default; + Owner(Owner &&) {} + + Owner &operator=(this Owner &self, Owner &&other) { + return self; + } + + void use() const {} +}; + +void moveAssignmentMarksTheSource() { + Owner target; + Owner source; + target = std::move(source); // expected-note {{Object 'source' is moved}} + target.use(); + source.use(); // expected-warning {{Method called on moved-from object 'source'}} + // expected-note@-1 {{Method called on moved-from object 'source'}} +} + +void moveAssignmentResetsTheTarget() { + Owner movedFrom; + Owner target = std::move(movedFrom); + Owner source; + target = std::move(source); + target.use(); +} + +void movingFromTheSourceTwiceWarns() { + Owner firstTarget; + Owner secondTarget; + Owner source; + firstTarget = std::move(source); // expected-note {{Object 'source' is moved}} + secondTarget = std::move(source); // expected-warning {{Moved-from object 'source' is moved}} + // expected-note@-1 {{Moved-from object 'source' is moved}} +} + +void selfMoveAssignmentDoesNotMarkTheObject() { + Owner object; + object = std::move(object); + object.use(); +} `````````` </details> https://github.com/llvm/llvm-project/pull/218303 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
