Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[clang-tidy] Fix smart pointers handling in bugprone-use-after-move #94869

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

PiotrZSL
Copy link
Member

@PiotrZSL PiotrZSL commented Jun 8, 2024

Removed custom handling of smart pointers and added option IgnoreNonDerefSmartPtrs to restore
previous behavior if needed.

Closes #90174

@llvmbot
Copy link
Collaborator

llvmbot commented Jun 8, 2024

@llvm/pr-subscribers-clang-tools-extra

@llvm/pr-subscribers-clang-tidy

Author: Piotr Zegar (PiotrZSL)

Changes
  • Removed custom smart pointers handling (were hiding issues)
  • Changed 'move occurred here' note location to always point to 'std::move'

Closes #90174


Full diff: https://github.com/llvm/llvm-project/pull/94869.diff

4 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp (+3-34)
  • (modified) clang-tools-extra/docs/ReleaseNotes.rst (+2-1)
  • (modified) clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst (-7)
  • (modified) clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move.cpp (+91-23)
diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
index b91ad0f182295..4f1ea32da20f4 100644
--- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp
@@ -215,26 +215,6 @@ void UseAfterMoveFinder::getUsesAndReinits(
   });
 }
 
-bool isStandardSmartPointer(const ValueDecl *VD) {
-  const Type *TheType = VD->getType().getNonReferenceType().getTypePtrOrNull();
-  if (!TheType)
-    return false;
-
-  const CXXRecordDecl *RecordDecl = TheType->getAsCXXRecordDecl();
-  if (!RecordDecl)
-    return false;
-
-  const IdentifierInfo *ID = RecordDecl->getIdentifier();
-  if (!ID)
-    return false;
-
-  StringRef Name = ID->getName();
-  if (Name != "unique_ptr" && Name != "shared_ptr" && Name != "weak_ptr")
-    return false;
-
-  return RecordDecl->getDeclContext()->isStdNamespace();
-}
-
 void UseAfterMoveFinder::getDeclRefs(
     const CFGBlock *Block, const Decl *MovedVariable,
     llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs) {
@@ -248,13 +228,8 @@ void UseAfterMoveFinder::getDeclRefs(
                         DeclRefs](const ArrayRef<BoundNodes> Matches) {
       for (const auto &Match : Matches) {
         const auto *DeclRef = Match.getNodeAs<DeclRefExpr>("declref");
-        const auto *Operator = Match.getNodeAs<CXXOperatorCallExpr>("operator");
         if (DeclRef && BlockMap->blockContainingStmt(DeclRef) == Block) {
-          // Ignore uses of a standard smart pointer that don't dereference the
-          // pointer.
-          if (Operator || !isStandardSmartPointer(DeclRef->getDecl())) {
-            DeclRefs->insert(DeclRef);
-          }
+          DeclRefs->insert(DeclRef);
         }
       }
     };
@@ -265,11 +240,6 @@ void UseAfterMoveFinder::getDeclRefs(
 
     AddDeclRefs(match(traverse(TK_AsIs, findAll(DeclRefMatcher)), *S->getStmt(),
                       *Context));
-    AddDeclRefs(match(findAll(cxxOperatorCallExpr(
-                                  hasAnyOverloadedOperatorName("*", "->", "[]"),
-                                  hasArgument(0, DeclRefMatcher))
-                                  .bind("operator")),
-                      *S->getStmt(), *Context));
   }
 }
 
@@ -411,10 +381,9 @@ void UseAfterMoveCheck::registerMatchers(MatchFinder *Finder) {
   auto TryEmplaceMatcher =
       cxxMemberCallExpr(callee(cxxMethodDecl(hasName("try_emplace"))));
   auto CallMoveMatcher =
-      callExpr(argumentCountIs(1),
+      callExpr(argumentCountIs(1), hasArgument(0, declRefExpr().bind("arg")),
                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(
@@ -496,7 +465,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(CallMove, Arg, Use, this, Result.Context,
                      determineMoveType(MoveDecl));
   }
 }
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 277a6e75da2ac..f9b84a6df532e 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -254,7 +254,8 @@ Changes in existing checks
 
 - Improved :doc:`bugprone-use-after-move
   <clang-tidy/checks/bugprone/use-after-move>` check to also handle
-  calls to ``std::forward``.
+  calls to ``std::forward``. Smart pointers are now handled like any other
+  objects allowing to detect more cases.
 
 - Improved :doc:`cppcoreguidelines-missing-std-forward
   <clang-tidy/checks/cppcoreguidelines/missing-std-forward>` check by no longer
diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst
index 08bb5374bab1f..faf9e4dddc12c 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/use-after-move.rst
@@ -195,13 +195,6 @@ Use
 Any occurrence of the moved variable that is not a reinitialization (see below)
 is considered to be a use.
 
-An exception to this are objects of type ``std::unique_ptr``,
-``std::shared_ptr`` and ``std::weak_ptr``, which have defined move behavior
-(objects of these classes are guaranteed to be empty after they have been moved
-from). Therefore, an object of these classes will only be considered to be used
-if it is dereferenced, i.e. if ``operator*``, ``operator->`` or ``operator[]``
-(in the case of ``std::unique_ptr<T []>``) is called on it.
-
 If multiple uses occur after a move, only the first of these is flagged.
 
 Reinitialization
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 7d9f63479a1b4..3d06dc008d353 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
@@ -8,6 +8,7 @@ typedef unsigned size_t;
 template <typename T>
 struct unique_ptr {
   unique_ptr();
+  unique_ptr(T* ptr);
   T *get() const;
   explicit operator bool() const;
   void reset(T *ptr);
@@ -123,6 +124,10 @@ forward(typename std::remove_reference<_Tp>::type&& __t) noexcept {
   return static_cast<_Tp&&>(__t);
 }
 
+template <typename T, typename... Args> auto make_unique(Args &&...args) {
+  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
 } // namespace std
 
 class A {
@@ -220,10 +225,8 @@ void standardSmartPtr() {
     std::unique_ptr<A> ptr;
     std::move(ptr);
     ptr.get();
-    static_cast<bool>(ptr);
-    *ptr;
-    // CHECK-NOTES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved
-    // CHECK-NOTES: [[@LINE-5]]:5: note: move occurred here
+    // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
+    // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
   }
   {
     std::unique_ptr<A> ptr;
@@ -243,10 +246,8 @@ void standardSmartPtr() {
     std::shared_ptr<A> ptr;
     std::move(ptr);
     ptr.get();
-    static_cast<bool>(ptr);
-    *ptr;
-    // CHECK-NOTES: [[@LINE-1]]:6: warning: 'ptr' used after it was moved
-    // CHECK-NOTES: [[@LINE-5]]:5: note: move occurred here
+    // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
+    // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
   }
   {
     std::shared_ptr<A> ptr;
@@ -261,6 +262,8 @@ void standardSmartPtr() {
     std::weak_ptr<A> ptr;
     std::move(ptr);
     ptr.expired();
+    // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
+    // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
   }
   // Make sure we recognize std::unique_ptr<> or std::shared_ptr<> if they're
   // wrapped in a typedef.
@@ -269,12 +272,16 @@ void standardSmartPtr() {
     PtrToA ptr;
     std::move(ptr);
     ptr.get();
+    // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
+    // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
   }
   {
     typedef std::shared_ptr<A> PtrToA;
     PtrToA ptr;
     std::move(ptr);
     ptr.get();
+    // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
+    // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
   }
   // And we don't get confused if the template argument is a little more
   // involved.
@@ -285,6 +292,8 @@ void standardSmartPtr() {
     std::unique_ptr<B::AnotherNameForA> ptr;
     std::move(ptr);
     ptr.get();
+    // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ptr' used after it was moved
+    // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
   }
   // Make sure we treat references to smart pointers correctly.
   {
@@ -292,12 +301,16 @@ void standardSmartPtr() {
     std::unique_ptr<A>& ref_to_ptr = ptr;
     std::move(ref_to_ptr);
     ref_to_ptr.get();
+    // CHECK-NOTES: [[@LINE-1]]:5: warning: 'ref_to_ptr' used after it was moved
+    // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
   }
   {
     std::unique_ptr<A> ptr;
     std::unique_ptr<A>&& rvalue_ref_to_ptr = std::move(ptr);
     std::move(rvalue_ref_to_ptr);
     rvalue_ref_to_ptr.get();
+    // CHECK-NOTES: [[@LINE-1]]:5: warning: 'rvalue_ref_to_ptr' used after it was moved
+    // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
   }
   // We don't give any special treatment to types that are called "unique_ptr"
   // or "shared_ptr" but are not in the "::std" namespace.
@@ -329,7 +342,7 @@ void moveInDeclaration() {
   A another_a(std::move(a));
   a.foo();
   // CHECK-NOTES: [[@LINE-1]]:3: warning: 'a' used after it was moved
-  // CHECK-NOTES: [[@LINE-3]]:5: note: move occurred here
+  // CHECK-NOTES: [[@LINE-3]]:15: note: move occurred here
 }
 
 // We see the std::move if it's inside an initializer list. Initializer lists
@@ -1068,10 +1081,10 @@ void sequencingOfMoveAndUse() {
     A a;
     g(g(a, std::move(a)), g(a, std::move(a)));
     // CHECK-NOTES: [[@LINE-1]]:9: warning: 'a' used after it was moved
-    // CHECK-NOTES: [[@LINE-2]]:27: note: move occurred here
+    // CHECK-NOTES: [[@LINE-2]]:32: note: move occurred here
     // CHECK-NOTES: [[@LINE-3]]:9: note: the use and move are unsequenced
     // CHECK-NOTES: [[@LINE-4]]:29: warning: 'a' used after it was moved
-    // CHECK-NOTES: [[@LINE-5]]:7: note: move occurred here
+    // CHECK-NOTES: [[@LINE-5]]:12: note: move occurred here
     // CHECK-NOTES: [[@LINE-6]]:29: note: the use and move are unsequenced
   }
   // This case is fine because the actual move only happens inside the call to
@@ -1088,7 +1101,7 @@ void sequencingOfMoveAndUse() {
     int v[3];
     v[a.getInt()] = intFromA(std::move(a));
     // CHECK-NOTES: [[@LINE-1]]:7: warning: 'a' used after it was moved
-    // CHECK-NOTES: [[@LINE-2]]:21: note: move occurred here
+    // CHECK-NOTES: [[@LINE-2]]:30: note: move occurred here
     // CHECK-NOTES: [[@LINE-3]]:7: note: the use and move are unsequenced
   }
   {
@@ -1096,7 +1109,7 @@ void sequencingOfMoveAndUse() {
     int v[3];
     v[intFromA(std::move(a))] = intFromInt(a.i);
     // CHECK-NOTES: [[@LINE-1]]:44: warning: 'a' used after it was moved
-    // CHECK-NOTES: [[@LINE-2]]:7: note: move occurred here
+    // CHECK-NOTES: [[@LINE-2]]:16: note: move occurred here
     // CHECK-NOTES: [[@LINE-3]]:44: note: the use and move are unsequenced
   }
 }
@@ -1168,7 +1181,7 @@ void commaOperatorSequences() {
     (a = A()), A(std::move(a));
     a.foo();
     // CHECK-NOTES: [[@LINE-1]]:5: warning: 'a' used after it was moved
-    // CHECK-NOTES: [[@LINE-3]]:16: note: move occurred here
+    // CHECK-NOTES: [[@LINE-3]]:18: note: move occurred here
   }
 }
 
@@ -1210,7 +1223,7 @@ void initializerListSequences() {
     A a;
     S2 s2{.a = std::move(a), .i = a.getInt()};
     // CHECK-NOTES: [[@LINE-1]]:35: warning: 'a' used after it was moved
-    // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here
+    // CHECK-NOTES: [[@LINE-2]]:16: note: move occurred here
   }
   {
     // Check the case where the constructed type has a constructor and the
@@ -1264,7 +1277,7 @@ void logicalOperatorsSequence() {
     A a;
     if (A(std::move(a)).getInt() > 0 && a.getInt() > 0) {
       // CHECK-NOTES: [[@LINE-1]]:41: warning: 'a' used after it was moved
-      // CHECK-NOTES: [[@LINE-2]]:9: note: move occurred here
+      // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here
       A().foo();
     }
   }
@@ -1278,7 +1291,7 @@ void logicalOperatorsSequence() {
     A a;
     if (A(std::move(a)).getInt() > 0 || a.getInt() > 0) {
       // CHECK-NOTES: [[@LINE-1]]:41: warning: 'a' used after it was moved
-      // CHECK-NOTES: [[@LINE-2]]:9: note: move occurred here
+      // CHECK-NOTES: [[@LINE-2]]:11: note: move occurred here
       A().foo();
     }
   }
@@ -1324,7 +1337,7 @@ void ifWhileAndSwitchSequenceInitDeclAndCondition() {
   for (int i = 0; i < 10; ++i) {
     if (A a1; A(std::move(a1)).getInt() > a1.getInt()) {}
     // CHECK-NOTES: [[@LINE-1]]:43: warning: 'a1' used after it was moved
-    // CHECK-NOTES: [[@LINE-2]]:15: note: move occurred here
+    // CHECK-NOTES: [[@LINE-2]]:17: note: move occurred here
     // CHECK-NOTES: [[@LINE-3]]:43: note: the use and move are unsequenced
   }
   for (int i = 0; i < 10; ++i) {
@@ -1419,7 +1432,7 @@ class CtorInit {
         s{std::move(val)},
         b{val.empty()}
   // CHECK-NOTES: [[@LINE-1]]:11: warning: 'val' used after it was moved
-  // CHECK-NOTES: [[@LINE-3]]:9: note: move occurred here
+  // CHECK-NOTES: [[@LINE-3]]:11: note: move occurred here
   {}
 
 private:
@@ -1435,7 +1448,7 @@ class CtorInitLambda {
         s{std::move(val)},
         b{[&] { return val.empty(); }()},
         // CHECK-NOTES: [[@LINE-1]]:12: warning: 'val' used after it was moved
-        // CHECK-NOTES: [[@LINE-3]]:9: note: move occurred here
+        // CHECK-NOTES: [[@LINE-3]]:11: note: move occurred here
         c{[] {
           std::string str{};
           std::move(str);
@@ -1445,7 +1458,7 @@ class CtorInitLambda {
         }()} {
     std::move(val);
     // CHECK-NOTES: [[@LINE-1]]:15: warning: 'val' used after it was moved
-    // CHECK-NOTES: [[@LINE-13]]:9: note: move occurred here
+    // CHECK-NOTES: [[@LINE-13]]:11: note: move occurred here
     std::string val2{};
     std::move(val2);
     val2.empty();
@@ -1468,7 +1481,7 @@ class CtorInitOrder {
         b{val.empty()},
         // CHECK-NOTES: [[@LINE-1]]:11: warning: 'val' used after it was moved
         s{std::move(val)} {} // wrong order
-  // CHECK-NOTES: [[@LINE-1]]:9: note: move occurred here
+  // CHECK-NOTES: [[@LINE-1]]:11: note: move occurred here
   // CHECK-NOTES: [[@LINE-4]]:11: note: the use happens in a later loop iteration than the move
 
 private:
@@ -1531,7 +1544,7 @@ class PR38187 {
   PR38187(std::string val) : val_(std::move(val)) {
     val.empty();
     // CHECK-NOTES: [[@LINE-1]]:5: warning: 'val' used after it was moved
-    // CHECK-NOTES: [[@LINE-3]]:30: note: move occurred here
+    // CHECK-NOTES: [[@LINE-3]]:35: note: move occurred here
   }
 
 private:
@@ -1562,3 +1575,58 @@ void create() {
 }
 
 } // namespace issue82023
+
+namespace PR90174 {
+
+struct A {};
+
+struct SinkA {
+  SinkA(std::unique_ptr<A>);
+};
+
+class ClassB {
+  ClassB(std::unique_ptr<A> aaa) : aa(std::move(aaa)) {
+    a = std::make_unique<SinkA>(std::move(aaa));
+    // CHECK-NOTES: [[@LINE-1]]:43: warning: 'aaa' used after it was moved
+    // CHECK-NOTES: [[@LINE-3]]:39: note: move occurred here
+  }
+  std::unique_ptr<A> aa;
+  std::unique_ptr<SinkA> a;
+};
+
+void s(const std::unique_ptr<A> &);
+
+template <typename T, typename... Args> auto my_make_unique(Args &&...args) {
+  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
+void natively(std::unique_ptr<A> x) {
+  std::unique_ptr<A> tmp = std::move(x);
+  std::unique_ptr<SinkA> y2{new SinkA(std::move(x))};
+  // CHECK-NOTES: [[@LINE-1]]:49: warning: 'x' used after it was moved
+  // CHECK-NOTES: [[@LINE-3]]:28: note: move occurred here
+}
+
+void viaStdMakeUnique(std::unique_ptr<A> x) {
+  std::unique_ptr<A> tmp = std::move(x);
+  std::unique_ptr<SinkA> y2 =
+      std::make_unique<SinkA>(std::move(x));
+  // CHECK-NOTES: [[@LINE-1]]:41: warning: 'x' used after it was moved
+  // CHECK-NOTES: [[@LINE-4]]:28: note: move occurred here
+}
+
+void viaMyMakeUnique(std::unique_ptr<A> x) {
+  std::unique_ptr<A> tmp = std::move(x);
+  std::unique_ptr<SinkA> y2 = my_make_unique<SinkA>(std::move(x));
+  // CHECK-NOTES: [[@LINE-1]]:63: warning: 'x' used after it was moved
+  // CHECK-NOTES: [[@LINE-3]]:28: note: move occurred here
+}
+
+void viaMyMakeUnique2(std::unique_ptr<A> x) {
+  std::unique_ptr<A> tmp = std::move(x);
+  s(x);
+  // CHECK-NOTES: [[@LINE-1]]:5: warning: 'x' used after it was moved
+  // CHECK-NOTES: [[@LINE-3]]:28: note: move occurred here
+}
+
+}

@martinboehme
Copy link
Contributor

More extensive comments on #90174 (let's continue the discussion there), but briefly:

  • Removed custom smart pointers handling (were hiding issues)

The standard smart pointers have a well-defined moved-from state (they are defined to be null after a move). Programmers may intentionally rely on this defined behavior. Warning on a (non-dereferencing) use-after-move for smart pointers as we do for other types would therefore result in false positives.

  • Changed 'move occurred here' note location to always point to 'std::move'

As discussed in more detail on #90174, the move doesn't actually occur in the std::move() (which is just a cast), and this can be important when sequencing rules come into play.

@PiotrZSL PiotrZSL force-pushed the 90174-clang-tidy-bugprone-use-after-move-false-negative branch from dfad1e2 to ceeb400 Compare July 16, 2024 18:35
Removed custom handling of smart pointers and
added option IgnoreNonDerefSmartPtrs to restore
previous behavior if needed.
@PiotrZSL PiotrZSL requested a review from njames93 August 1, 2024 18:24
@PiotrZSL PiotrZSL force-pushed the 90174-clang-tidy-bugprone-use-after-move-false-negative branch from ceeb400 to 51e3a4b Compare August 1, 2024 18:24
@PiotrZSL
Copy link
Member Author

PiotrZSL commented Aug 1, 2024

Rebase, bump

Copy link
Contributor

@martinboehme martinboehme left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry - I only noticed now after your rebase that this had been sitting in my review queue for quite a while.

  • I would suggest rewording the option name. The "ignore" (IgnoreNonDerefSmartPtrs) reads like a negation, which makes it harder to reason about what the option does. How about AllowMovedSmartPtrUse? (I think maybe the "non-deref" can be omitted from the option name -- I think it's sufficient that it's explained in the documentation).

  • In [clang-tidy] bugprone-use-after-move - false-negative #90174, I suggested that if we wanted to change the default behavior, we should survey users to see what their opinions / preferences are. Have you done this? If not, I think the default behavior (when the option is not set) should remain unchanged.

@PiotrZSL
Copy link
Member Author

PiotrZSL commented Aug 8, 2024

@martinboehme
"I suggested that if we wanted to change the default behavior, we should survey users to see what their opinions / preferences are. Have you done this? If not, I think the default behavior (when the option is not set) should remain unchanged."

Nope, there is no reliable way to "survey users". Default behavior of checks changes every release. Some checks are getting limited, by excluding some use cases, some checks are getting extended. In this case situation is easy. Users will update Clang-tidy, and if check will report "new issues", they have an option, to fix them or look into release notes to find out what changed in check and then change configuration to revert back to previous behavior. This is how things were handled in past for other checks.

@@ -200,7 +200,8 @@ An exception to this are objects of type ``std::unique_ptr``,
(objects of these classes are guaranteed to be empty after they have been moved
from). Therefore, an object of these classes will only be considered to be used
if it is dereferenced, i.e. if ``operator*``, ``operator->`` or ``operator[]``
(in the case of ``std::unique_ptr<T []>``) is called on it.
(in the case of ``std::unique_ptr<T []>``) is called on it. This behavior can be
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of the documentation still reads as if this is the default behavior, but in fact it isn't. Can you rephrase this to make this clear that this is optional (non-default) behavior that is activated by setting the AllowMovedSmartPtrUse option?

If this option is set to `true`, the check will not warn about uses of
``std::unique_ptr``, ``std::shared_ptr`` that are not dereferences. This
can be useful if you are using these smart pointers in a way that is not
idiomatic, but that you know is safe. Default is `false`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"This can be useful if you are using these smart pointers in a way that is not idiomatic, but that you know is safe."

I think "not idiomatic" is too strong -- I would say this is a question of style. Maybe just leave out this entire sentence (i.e. describe only the behavior)?


void natively(std::unique_ptr<A> x) {
std::unique_ptr<A> tmp = std::move(x);
std::unique_ptr<SinkA> y2{new SinkA(std::move(x))};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason (here and possibly also below) that SinkA needs to be referenced via a unique_ptr instead of simply making it a local variable of SinkA?

I.e. why not simply do

SinkA sink(std::move(x));

since the behavior we're interested in is what happens when we move x into the argument for the SinkA constructor, and not what happens with the SinkA itself.

(Also nit: I'm not clear on the reason for the y2 variable name -- a) there is no y1, and b) maybe just sink to emphasize the type?)

// CHECK-MESSAGES: [[@LINE-3]]:28: note: move occurred here
}

void viaMyMakeUnique2(std::unique_ptr<A> x) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this test called viaMyMakeUnique2? I don't see my_make_unique being used?

@martinboehme
Copy link
Contributor

Nope, there is no reliable way to "survey users". Default behavior of checks changes every release. Some checks are getting limited, by excluding some use cases, some checks are getting extended. In this case situation is easy. Users will update Clang-tidy, and if check will report "new issues", they have an option, to fix them or look into release notes to find out what changed in check and then change configuration to revert back to previous behavior. This is how things were handled in past for other checks.

OK, in that case this sounds fine.

Copy link
Contributor

@5chmidti 5chmidti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM minus the other comments

Comment on lines +109 to +110
like any other objects allowing to detect more cases, previous behavior can
be restored by setting `AllowMovedSmartPtrUse` option to `true`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit:

objects allowing to detect more cases, previous behavior

->

objects to detect more cases, and added AllowMovedSmartPtrUse to restore the previous behavior

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[clang-tidy] bugprone-use-after-move - false-negative
4 participants