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

Reply via email to