https://github.com/PiotrZSL updated https://github.com/llvm/llvm-project/pull/94869
>From ceeb4006d54b40a226a7a1f4f78f7f5f9d9dd7a1 Mon Sep 17 00:00:00 2001 From: Piotr Zegar <m...@piotrzegar.pl> Date: Tue, 16 Jul 2024 18:34:25 +0000 Subject: [PATCH] [clang-tidy] Fix smart pointers handling in bugprone-use-after-move Removed custom handling of smart pointers and added option IgnoreNonDerefSmartPtrs to restore previous behavior if needed. --- .../clang-tidy/bugprone/UseAfterMoveCheck.cpp | 23 +++++-- .../clang-tidy/bugprone/UseAfterMoveCheck.h | 7 +- clang-tools-extra/docs/ReleaseNotes.rst | 4 +- .../checks/bugprone/use-after-move.rst | 14 +++- .../bugprone/use-after-move-smart-ptr.cpp | 59 +++++++++++++++++ .../checkers/bugprone/use-after-move.cpp | 65 ++----------------- .../modernize/Inputs/smart-ptr/shared_ptr.h | 2 + .../modernize/Inputs/smart-ptr/unique_ptr.h | 42 ++++++++++++ 8 files changed, 148 insertions(+), 68 deletions(-) create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move-smart-ptr.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp index 8f4b5e8092dda..954b6324e9674 100644 --- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.cpp @@ -48,7 +48,7 @@ struct UseAfterMove { /// various internal helper functions). class UseAfterMoveFinder { public: - UseAfterMoveFinder(ASTContext *TheContext); + UseAfterMoveFinder(ASTContext *TheContext, bool IgnoreNonDerefSmartPtrs); // Within the given code block, finds the first use of 'MovedVariable' that // occurs after 'MovingCall' (the expression that performs the move). If a @@ -71,6 +71,7 @@ class UseAfterMoveFinder { llvm::SmallPtrSetImpl<const DeclRefExpr *> *DeclRefs); ASTContext *Context; + const bool IgnoreNonDerefSmartPtrs; std::unique_ptr<ExprSequence> Sequence; std::unique_ptr<StmtToBlockMap> BlockMap; llvm::SmallPtrSet<const CFGBlock *, 8> Visited; @@ -92,8 +93,9 @@ static StatementMatcher inDecltypeOrTemplateArg() { hasAncestor(expr(hasUnevaluatedContext()))); } -UseAfterMoveFinder::UseAfterMoveFinder(ASTContext *TheContext) - : Context(TheContext) {} +UseAfterMoveFinder::UseAfterMoveFinder(ASTContext *TheContext, + bool IgnoreNonDerefSmartPtrs) + : Context(TheContext), IgnoreNonDerefSmartPtrs(IgnoreNonDerefSmartPtrs) {} std::optional<UseAfterMove> UseAfterMoveFinder::find(Stmt *CodeBlock, const Expr *MovingCall, @@ -275,11 +277,13 @@ 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())) { + const auto *Operator = + Match.getNodeAs<CXXOperatorCallExpr>("operator"); + if (Operator || !IgnoreNonDerefSmartPtrs || + !isStandardSmartPointer(DeclRef->getDecl())) { DeclRefs->insert(DeclRef); } } @@ -429,6 +433,13 @@ static void emitDiagnostic(const Expr *MovingCall, const DeclRefExpr *MoveArg, << IsMove; } } +UseAfterMoveCheck::UseAfterMoveCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IgnoreNonDerefSmartPtrs(Options.get("IgnoreNonDerefSmartPtrs", false)) {} + +void UseAfterMoveCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IgnoreNonDerefSmartPtrs", IgnoreNonDerefSmartPtrs); +} void UseAfterMoveCheck::registerMatchers(MatchFinder *Finder) { // try_emplace is a common maybe-moving function that returns a @@ -520,7 +531,7 @@ void UseAfterMoveCheck::check(const MatchFinder::MatchResult &Result) { } for (Stmt *CodeBlock : CodeBlocks) { - UseAfterMoveFinder Finder(Result.Context); + UseAfterMoveFinder Finder(Result.Context, IgnoreNonDerefSmartPtrs); if (auto Use = Finder.find(CodeBlock, MovingCall, Arg)) emitDiagnostic(MovingCall, Arg, *Use, this, Result.Context, determineMoveType(MoveDecl)); diff --git a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.h b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.h index c14e802847415..ace97c58c1966 100644 --- a/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.h +++ b/clang-tools-extra/clang-tidy/bugprone/UseAfterMoveCheck.h @@ -20,13 +20,16 @@ namespace clang::tidy::bugprone { /// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/use-after-move.html class UseAfterMoveCheck : public ClangTidyCheck { public: - UseAfterMoveCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + UseAfterMoveCheck(StringRef Name, ClangTidyContext *Context); bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { return LangOpts.CPlusPlus11; } void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + +private: + const bool IgnoreNonDerefSmartPtrs; }; } // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 004811d2eca4f..00ed708b3db6e 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -301,7 +301,9 @@ Changes in existing checks calls to ``std::forward``. Fixed sequencing of designated initializers. Fixed sequencing of callees: In C++17 and later, the callee of a function is guaranteed to be sequenced before the arguments, so don't warn if the use happens in the - callee and the move happens in one of the arguments. + callee and the move happens in one of the arguments. Smart pointers are now + handled like any other objects allowing to detect more cases, previous behavior + can be restored by setting `IgnoreNonDerefSmartPtrs` option to `true`. - Improved :doc:`cppcoreguidelines-avoid-non-const-global-variables <clang-tidy/checks/cppcoreguidelines/avoid-non-const-global-variables>` check 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..8fd6d634f7891 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 @@ -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 +overridden with the option :option:`IgnoreNonDerefSmartPtrs`. If multiple uses occur after a move, only the first of these is flagged. @@ -250,3 +251,14 @@ For example, if an additional member variable is added to ``S``, it is easy to forget to add the reinitialization for this additional member. Instead, it is safer to assign to the entire struct in one go, and this will also avoid the use-after-move warning. + +Options +------- + +.. option:: IgnoreNonDerefSmartPtrs + + 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`. + diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move-smart-ptr.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move-smart-ptr.cpp new file mode 100644 index 0000000000000..db06d3f121d79 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/use-after-move-smart-ptr.cpp @@ -0,0 +1,59 @@ +// RUN: %check_clang_tidy -std=c++17-or-later %s bugprone-use-after-move %t -- \ +// RUN: -config="{CheckOptions: {bugprone-use-after-move.IgnoreNonDerefSmartPtrs: false}}" -- -fno-delayed-template-parsing -I %S/../modernize/Inputs/smart-ptr/ + +#include "unique_ptr.h" + +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-MESSAGES: [[@LINE-1]]:43: warning: 'aaa' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:36: 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-MESSAGES: [[@LINE-1]]:49: warning: 'x' used after it was moved + // CHECK-MESSAGES: [[@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-MESSAGES: [[@LINE-1]]:41: warning: 'x' used after it was moved + // CHECK-MESSAGES: [[@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-MESSAGES: [[@LINE-1]]:63: warning: 'x' used after it was moved + // CHECK-MESSAGES: [[@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-MESSAGES: [[@LINE-1]]:5: warning: 'x' used after it was moved + // CHECK-MESSAGES: [[@LINE-3]]:28: note: move occurred here +} + +} 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 6a4e3990e36dc..1363a959b7991 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 @@ -1,32 +1,16 @@ -// RUN: %check_clang_tidy -std=c++11 -check-suffixes=,CXX11 %s bugprone-use-after-move %t -- -- -fno-delayed-template-parsing -// RUN: %check_clang_tidy -std=c++17-or-later %s bugprone-use-after-move %t -- -- -fno-delayed-template-parsing +// RUN: %check_clang_tidy -std=c++11 -check-suffixes=,CXX11 %s bugprone-use-after-move %t -- \ +// RUN: -config="{CheckOptions: {bugprone-use-after-move.IgnoreNonDerefSmartPtrs: true}}" -- -fno-delayed-template-parsing -I %S/../modernize/Inputs/smart-ptr/ +// RUN: %check_clang_tidy -std=c++17-or-later %s bugprone-use-after-move %t -- \ +// RUN: -config="{CheckOptions: {bugprone-use-after-move.IgnoreNonDerefSmartPtrs: true}}" -- -fno-delayed-template-parsing -I %S/../modernize/Inputs/smart-ptr/ + +#include "unique_ptr.h" +#include "shared_ptr.h" typedef decltype(nullptr) nullptr_t; namespace std { typedef unsigned size_t; -template <typename T> -struct unique_ptr { - unique_ptr(); - T *get() const; - explicit operator bool() const; - void reset(T *ptr); - T &operator*() const; - T *operator->() const; - T& operator[](size_t i) const; -}; - -template <typename T> -struct shared_ptr { - shared_ptr(); - T *get() const; - explicit operator bool() const; - void reset(T *ptr); - T &operator*() const; - T *operator->() const; -}; - template <typename T> struct weak_ptr { weak_ptr(); @@ -89,41 +73,6 @@ DECLARE_STANDARD_CONTAINER(unordered_multimap); typedef basic_string<char> string; -template <typename> -struct remove_reference; - -template <typename _Tp> -struct remove_reference { - typedef _Tp type; -}; - -template <typename _Tp> -struct remove_reference<_Tp &> { - typedef _Tp type; -}; - -template <typename _Tp> -struct remove_reference<_Tp &&> { - typedef _Tp type; -}; - -template <typename _Tp> -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 { diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/smart-ptr/shared_ptr.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/smart-ptr/shared_ptr.h index 337cb28228b09..09613c54fee2f 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/smart-ptr/shared_ptr.h +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/smart-ptr/shared_ptr.h @@ -9,9 +9,11 @@ class __shared_ptr { public: type &operator*() { return *ptr; } type *operator->() { return ptr; } + type *get() const; type *release(); void reset(); void reset(type *pt); + explicit operator bool() const; private: type *ptr; diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/smart-ptr/unique_ptr.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/smart-ptr/unique_ptr.h index 5dc9e02b637a2..2fca61141c53e 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/smart-ptr/unique_ptr.h +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/smart-ptr/unique_ptr.h @@ -14,9 +14,12 @@ class unique_ptr { type &operator*() { return *ptr; } type *operator->() { return ptr; } type *release() { return ptr; } + type *get() const; + type& operator[](unsigned i) const; void reset() {} void reset(type *pt) {} void reset(type pt) {} + explicit operator bool() const; unique_ptr &operator=(unique_ptr &&) { return *this; } template <typename T> unique_ptr &operator=(unique_ptr<T> &&) { return *this; } @@ -25,4 +28,43 @@ class unique_ptr { type *ptr; }; +template <typename> +struct remove_reference; + +template <typename _Tp> +struct remove_reference { + typedef _Tp type; +}; + +template <typename _Tp> +struct remove_reference<_Tp &> { + typedef _Tp type; +}; + +template <typename _Tp> +struct remove_reference<_Tp &&> { + typedef _Tp type; +}; + +template <typename _Tp> +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); +} + +template <typename T, typename... Args> std::unique_ptr<T> make_unique(Args &&...args) { + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); +} + } // namespace std _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits