[clang-tools-extra] Extend bugprone-use-after-move check to handle std::optional::reset() and std::any::reset() (PR #114255)

via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 30 09:18:50 PDT 2024


https://github.com/higher-performance updated https://github.com/llvm/llvm-project/pull/114255

>From 49d09616824672243a9deb4c07c2b746b974a2e3 Mon Sep 17 00:00:00 2001
From: higher-performance <higher.performance.github at gmail.com>
Date: Wed, 30 Oct 2024 12:01:00 -0400
Subject: [PATCH] Extend bugprone-use-after-move check to handle
 std::optional::reset() and std::any::reset() similarly to smart pointers

---
 .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 16 +++++-----
 .../checkers/bugprone/use-after-move.cpp      | 29 +++++++++++++++++--
 2 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index 8f4b5e8092ddaa..0848b8c4e64733 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -242,7 +242,7 @@ void UseAfterMoveFinder::getUsesAndReinits(
   });
 }
 
-bool isStandardSmartPointer(const ValueDecl *VD) {
+bool isStandardResettableOwner(const ValueDecl *VD) {
   const Type *TheType = VD->getType().getNonReferenceType().getTypePtrOrNull();
   if (!TheType)
     return false;
@@ -256,7 +256,8 @@ bool isStandardSmartPointer(const ValueDecl *VD) {
     return false;
 
   StringRef Name = ID->getName();
-  if (Name != "unique_ptr" && Name != "shared_ptr" && Name != "weak_ptr")
+  if (Name != "unique_ptr" && Name != "shared_ptr" && Name != "weak_ptr" &&
+      Name != "optional" && Name != "any")
     return false;
 
   return RecordDecl->getDeclContext()->isStdNamespace();
@@ -279,7 +280,7 @@ void UseAfterMoveFinder::getDeclRefs(
         if (DeclRef && BlockMap->blockContainingStmt(DeclRef) == Block) {
           // Ignore uses of a standard smart pointer that don't dereference the
           // pointer.
-          if (Operator || !isStandardSmartPointer(DeclRef->getDecl())) {
+          if (Operator || !isStandardResettableOwner(DeclRef->getDecl())) {
             DeclRefs->insert(DeclRef);
           }
         }
@@ -315,9 +316,10 @@ void UseAfterMoveFinder::getReinits(
           "::std::unordered_map", "::std::unordered_multiset",
           "::std::unordered_multimap"))))));
 
-  auto StandardSmartPointerTypeMatcher = hasType(hasUnqualifiedDesugaredType(
-      recordType(hasDeclaration(cxxRecordDecl(hasAnyName(
-          "::std::unique_ptr", "::std::shared_ptr", "::std::weak_ptr"))))));
+  auto StandardResettableOwnerTypeMatcher = hasType(
+      hasUnqualifiedDesugaredType(recordType(hasDeclaration(cxxRecordDecl(
+          hasAnyName("::std::unique_ptr", "::std::shared_ptr",
+                     "::std::weak_ptr", "::std::optional", "::std::any"))))));
 
   // Matches different types of reinitialization.
   auto ReinitMatcher =
@@ -340,7 +342,7 @@ void UseAfterMoveFinder::getReinits(
                    callee(cxxMethodDecl(hasAnyName("clear", "assign")))),
                // reset() on standard smart pointers.
                cxxMemberCallExpr(
-                   on(expr(DeclRefMatcher, StandardSmartPointerTypeMatcher)),
+                   on(expr(DeclRefMatcher, StandardResettableOwnerTypeMatcher)),
                    callee(cxxMethodDecl(hasName("reset")))),
                // Methods that have the [[clang::reinitializes]] attribute.
                cxxMemberCallExpr(
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 6a4e3990e36dc5..49306e1e2107c1 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
@@ -33,6 +33,17 @@ struct weak_ptr {
   bool expired() const;
 };
 
+template <typename T>
+struct optional {
+  optional();
+  void reset();
+};
+
+struct any {
+  any();
+  void reset();
+};
+
 template <typename T1, typename T2>
 struct pair {};
 
@@ -994,10 +1005,10 @@ void standardContainerAssignIsReinit() {
   }
 }
 
-// Resetting the standard smart pointer types using reset() is treated as a
+// Resetting the standard smart owning types using reset() is treated as a
 // re-initialization. (We don't test std::weak_ptr<> because it can't be
 // dereferenced directly.)
-void standardSmartPointerResetIsReinit() {
+void resetIsReinit() {
   {
     std::unique_ptr<A> ptr;
     std::move(ptr);
@@ -1010,6 +1021,20 @@ void standardSmartPointerResetIsReinit() {
     ptr.reset(new A);
     *ptr;
   }
+  {
+    std::optional<A> opt;
+    std::move(opt);
+    opt.reset();
+    std::optional<A> opt2 = opt;
+    (void)opt2;
+  }
+  {
+    std::any a;
+    std::move(a);
+    a.reset();
+    std::any a2 = a;
+    (void)a2;
+  }
 }
 
 void reinitAnnotation() {



More information about the cfe-commits mailing list