axzhang created this revision. axzhang added reviewers: klimek, EricWF. axzhang added a project: clang-tools-extra. Herald added a subscriber: mgorny.
Add a new check for https://abseil.io/tips/126, to detect uses of constructing a std::unique_ptr with a call to new, and recommend substituting with absl::make_unique or absl::WrapUnique. Note that some functionality may overlap with existing checks to substitute with std::make_unique, but this check differs in that it is specific to Abseil, and in how it utilizes both absl::make_unique and absl::WrapUnique. Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D55044 Files: clang-tidy/abseil/AbseilTidyModule.cpp clang-tidy/abseil/CMakeLists.txt clang-tidy/abseil/MakeUniqueCheck.cpp clang-tidy/abseil/MakeUniqueCheck.h docs/ReleaseNotes.rst docs/clang-tidy/checks/abseil-make-unique.rst docs/clang-tidy/checks/list.rst test/clang-tidy/abseil-make-unique.cpp
Index: test/clang-tidy/abseil-make-unique.cpp =================================================================== --- /dev/null +++ test/clang-tidy/abseil-make-unique.cpp @@ -0,0 +1,116 @@ +// RUN: %check_clang_tidy %s abseil-make-unique %t +namespace std { +template <typename T> +struct default_delete {}; + +template <typename T, typename D = default_delete<T>> +class unique_ptr { + public: + unique_ptr(); + explicit unique_ptr(T*); + void reset(T*); +}; +} // namespace std + +class A { + int x; + int y; + + public: + A(int _x, int _y): x(_x), y(_y) {} +}; + +struct B { + int x; + int y; +}; + +int* ReturnPointer(); +void ExpectPointer(std::unique_ptr<int> p); + +std::unique_ptr<int> MakeAndReturnPointer() { + // Make smart pointer in return statement + return std::unique_ptr<int>(new int(0)); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: prefer absl::make_unique to constructing unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: return absl::make_unique<int>(0); +} + +void Positives() { + std::unique_ptr<int> a(new int(1)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer absl::make_unique to constructing unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: auto a = absl::make_unique<int>(1); + + std::unique_ptr<int> b; + b.reset(new int(2)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer absl::make_unique to resetting unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: b = absl::make_unique<int>(2); + + // Non-primitive paramter + std::unique_ptr<A> c(new A(1, 2)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer absl::make_unique to constructing unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: auto c = absl::make_unique<A>(1, 2); + + std::unique_ptr<A> d; + d.reset(new A(3, 4)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer absl::make_unique to resetting unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: d = absl::make_unique<A>(3, 4); + + // No arguments to new expression + std::unique_ptr<int> e(new int); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer absl::make_unique to constructing unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: auto e = absl::make_unique<int>(); + + std::unique_ptr<int> f; + f.reset(new int); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer absl::make_unique to resetting unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: f = absl::make_unique<int>(); + + // Nested parentheses + std::unique_ptr<int> g((new int(3))); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer absl::make_unique to constructing unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: auto g = absl::make_unique<int>(3); + + std::unique_ptr<int> h(((new int(4)))); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer absl::make_unique to constructing unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: auto h = absl::make_unique<int>(4); + + std::unique_ptr<int> i; + i.reset((new int(5))); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer absl::make_unique to resetting unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: i = absl::make_unique<int>(5); + + std::unique_ptr<int> j; + j.reset(((new int(6)))); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer absl::make_unique to resetting unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: j = absl::make_unique<int>(6); + + // Construct unique_ptr within a function + ExpectPointer(std::unique_ptr<int>(new int(5))); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: prefer absl::make_unique to constructing unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: ExpectPointer(absl::make_unique<int>(5)); + + // Brace initialization + std::unique_ptr<B> k(new B{1, 2}); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer absl::WrapUnique to constructing unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: auto k = absl::WrapUnique(1, 2); + + std::unique_ptr<B> l; + l.reset(new B{3, 4}); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer absl::WrapUnique to resetting unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: l = absl::WrapUnique(3, 4); +} + +// Checks within namespaces +namespace std { + unique_ptr<int> k(new int(7)); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: prefer absl::make_unique to constructing unique_ptr with new [abseil-make-unique] + // CHECK-FIXES: auto k = absl::make_unique<int>(7); +} + +void Negatives() { + // Do not warn for functions that return a pointer + std::unique_ptr<int> a(ReturnPointer()); + + std::unique_ptr<int> b; + b.reset(ReturnPointer()); +} Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -7,11 +7,12 @@ abseil-duration-division abseil-duration-factory-float abseil-faster-strsplit-delimiter + abseil-make-unique abseil-no-internal-dependencies abseil-no-namespace abseil-redundant-strcat-calls - abseil-string-find-startswith abseil-str-cat-append + abseil-string-find-startswith android-cloexec-accept android-cloexec-accept4 android-cloexec-creat @@ -151,6 +152,7 @@ hicpp-special-member-functions (redirects to cppcoreguidelines-special-member-functions) <hicpp-special-member-functions> hicpp-static-assert (redirects to misc-static-assert) <hicpp-static-assert> hicpp-undelegated-constructor (redirects to bugprone-undelegated-constructor) <hicpp-undelegated-constructor> + hicpp-uppercase-literal-suffix (redirects to readability-uppercase-literal-suffix) <hicpp-uppercase-literal-suffix> hicpp-use-auto (redirects to modernize-use-auto) <hicpp-use-auto> hicpp-use-emplace (redirects to modernize-use-emplace) <hicpp-use-emplace> hicpp-use-equals-default (redirects to modernize-use-equals-default) <hicpp-use-equals-default> @@ -159,7 +161,6 @@ hicpp-use-nullptr (redirects to modernize-use-nullptr) <hicpp-use-nullptr> hicpp-use-override (redirects to modernize-use-override) <hicpp-use-override> hicpp-vararg (redirects to cppcoreguidelines-pro-type-vararg) <hicpp-vararg> - hicpp-uppercase-literal-suffix (redirects to readability-uppercase-literal-suffix) <hicpp-uppercase-literal-suffix> llvm-header-guard llvm-include-order llvm-namespace-comment Index: docs/clang-tidy/checks/abseil-make-unique.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/abseil-make-unique.rst @@ -0,0 +1,16 @@ +.. title:: clang-tidy - abseil-make-unique + +abseil-make-unique +================== + +Replaces unique pointers that are constructed with raw pointers with ``absl::make_unique``, for leak-free dynamic allocation. + +.. code-block:: c++ + std::unique_ptr<int> upi(new int); + +will be replaced with + +.. code-block:: c++ + auto upi = absl::make_unique<int>(); + +See https://abseil.io/tips/126 for full explanation and justification. Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -67,6 +67,13 @@ Improvements to clang-tidy -------------------------- +- New :doc:`abseil-make-unique + <clang-tidy/checks/abseil-make-unique>` check. + + Checks for cases where ``std::unique_ptr`` is constructed with a call + to new, and recommends that ``absl::make_unique`` or ``absl::WrapUnique`` + are used instead. + - New :doc:`abseil-duration-division <clang-tidy/checks/abseil-duration-division>` check. Index: clang-tidy/abseil/MakeUniqueCheck.h =================================================================== --- /dev/null +++ clang-tidy/abseil/MakeUniqueCheck.h @@ -0,0 +1,40 @@ +//===--- MakeUniqueCheck.h - clang-tidy -------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_MAKEUNIQUECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_MAKEUNIQUECHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace abseil { + +/// Checks for unclear pointer ownership through constructing std::unique_ptr with +/// a call to new, and recommends using absl::make_unique or absl::WrapUnique +/// instead. Note that these are similar to the std::make_unique functions, but +/// differ in how they handle factory constructors and brace initialization, +/// choosing to defer to absl::WrapUnique. +class MakeUniqueCheck : public ClangTidyCheck { +private: + std::string getArgs(const SourceManager *SM, const CXXNewExpr *NewExpr); + std::string getType(const SourceManager *SM, const CXXNewExpr *NewExpr, const Expr *Outer); + +public: + MakeUniqueCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace abseil +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_MAKEUNIQUECHECK_H Index: clang-tidy/abseil/MakeUniqueCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/abseil/MakeUniqueCheck.cpp @@ -0,0 +1,141 @@ +//===--- MakeUniqueCheck.cpp - clang-tidy ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <string> +#include "MakeUniqueCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace abseil { + +std::string MakeUniqueCheck::getArgs(const SourceManager *SM, + const CXXNewExpr *NewExpr) { + if (NewExpr->getInitializationStyle() == + CXXNewExpr::InitializationStyle::ListInit) { + SourceRange InitRange = NewExpr->getInitializer()->getSourceRange(); + llvm::StringRef ArgRef = Lexer::getSourceText(CharSourceRange::getCharRange( + InitRange.getBegin().getLocWithOffset(1), InitRange.getEnd()), + *SM, LangOptions()); + return "(" + ArgRef.str() + ")"; + } + llvm::StringRef ArgRef = Lexer::getSourceText(CharSourceRange::getCharRange( + NewExpr->getDirectInitRange()), + *SM, LangOptions()); + return (ArgRef.str().length() > 0) ? ArgRef.str() + ")" : "()"; +} + +std::string MakeUniqueCheck::getType(const SourceManager *SM, + const CXXNewExpr *NewExpr, + const Expr *Outer) { + SourceRange TypeRange( + NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(), + NewExpr->getDirectInitRange().getBegin() + ); + if (!TypeRange.isValid()) { + TypeRange.setEnd(Outer->getEndLoc()); + } + llvm::StringRef TypeRef = Lexer::getSourceText( + CharSourceRange::getCharRange(TypeRange), *SM, LangOptions() + ); + return TypeRef.str(); +} + +void MakeUniqueCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(cxxConstructExpr( + hasType(cxxRecordDecl(hasName("std::unique_ptr"))), + argumentCountIs(1), + hasArgument(0, cxxNewExpr().bind("cons_new")), + anyOf(hasParent(decl().bind("cons_decl")), anything())).bind("cons"), + this); + + Finder->addMatcher(cxxMemberCallExpr( + callee(cxxMethodDecl( + hasName("reset"), + ofClass(cxxRecordDecl(hasName("std::unique_ptr"))))), + argumentCountIs(1), + hasArgument(0, cxxNewExpr().bind("reset_new"))).bind("reset_call"), + this); +} + +void MakeUniqueCheck::check(const MatchFinder::MatchResult &Result) { + const SourceManager *SM = Result.SourceManager; + const auto *Cons = Result.Nodes.getNodeAs<CXXConstructExpr>("cons"); + const auto *ConsNew = Result.Nodes.getNodeAs<CXXNewExpr>("cons_new"); + const auto *ConsDecl = Result.Nodes.getNodeAs<Decl>("cons_decl"); + + if (Cons) { + // Get name of declared variable, if exists + llvm::StringRef NameRef = Lexer::getSourceText( + CharSourceRange::getCharRange(Cons->getBeginLoc(), + Cons->getParenOrBraceRange().getBegin()), + *SM, LangOptions()); + std::string Left = (ConsDecl) ? "auto " + NameRef.str() + " = " : ""; + + std::string NewText; + std::string DiagText; + + // Use WrapUnique for list initialization + if (ConsNew->getInitializationStyle() == + CXXNewExpr::InitializationStyle::ListInit) { + NewText = Left + "absl::WrapUnique" + getArgs(SM, ConsNew); + DiagText = "prefer absl::WrapUnique to constructing unique_ptr with new"; + } else { + NewText = Left + "absl::make_unique<" + getType(SM, ConsNew, Cons) + ">" + + getArgs(SM, ConsNew); + DiagText = "prefer absl::make_unique to constructing unique_ptr with new"; + } + + // If there is an associated Decl, start diagnostic there, otherwise use the + // beginning of the Expr + SourceLocation Target = (ConsDecl) ? ConsDecl->getBeginLoc() + : Cons->getExprLoc(); + diag(Target, DiagText) << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(Target, Cons->getEndLoc()), NewText); + } + + const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>("reset_call"); + const auto *ResetNew = Result.Nodes.getNodeAs<CXXNewExpr>("reset_new"); + if (Reset) { + // Get name of caller object + const Expr *ObjectArg = Reset->getImplicitObjectArgument(); + llvm::StringRef ObjName = Lexer::getSourceText( + CharSourceRange::getCharRange(ObjectArg->getBeginLoc(), + Reset->getExprLoc().getLocWithOffset(-1)), + *SM, LangOptions() + ); + + std::string NewText = ObjName.str() + " = absl::make_unique<" + + getType(SM, ResetNew, Reset) + ">" + getArgs(SM, ResetNew); + std::string DiagText = "prefer absl::make_unique to resetting unique_ptr \ + with new"; + + // Use WrapUnique for list initialization + if (ResetNew->getInitializationStyle() == + CXXNewExpr::InitializationStyle::ListInit) { + NewText = ObjName.str() + " = absl::WrapUnique" + getArgs(SM, ResetNew); + DiagText = "prefer absl::WrapUnique to resetting unique_ptr with new"; + } else { + NewText = ObjName.str() + " = absl::make_unique<" + + getType(SM, ResetNew, Reset) + ">" + getArgs(SM, ResetNew); + DiagText = "prefer absl::make_unique to resetting unique_ptr with new"; + } + + diag(ObjectArg->getExprLoc(), DiagText) << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(ObjectArg->getExprLoc(), + Reset->getEndLoc()), NewText); + } +} + +} // namespace abseil +} // namespace tidy +} // namespace clang Index: clang-tidy/abseil/CMakeLists.txt =================================================================== --- clang-tidy/abseil/CMakeLists.txt +++ clang-tidy/abseil/CMakeLists.txt @@ -5,6 +5,7 @@ DurationDivisionCheck.cpp DurationFactoryFloatCheck.cpp FasterStrsplitDelimiterCheck.cpp + MakeUniqueCheck.cpp NoInternalDependenciesCheck.cpp NoNamespaceCheck.cpp RedundantStrcatCallsCheck.cpp Index: clang-tidy/abseil/AbseilTidyModule.cpp =================================================================== --- clang-tidy/abseil/AbseilTidyModule.cpp +++ clang-tidy/abseil/AbseilTidyModule.cpp @@ -13,6 +13,7 @@ #include "DurationDivisionCheck.h" #include "DurationFactoryFloatCheck.h" #include "FasterStrsplitDelimiterCheck.h" +#include "MakeUniqueCheck.h" #include "NoInternalDependenciesCheck.h" #include "NoNamespaceCheck.h" #include "RedundantStrcatCallsCheck.h" @@ -32,6 +33,8 @@ "abseil-duration-factory-float"); CheckFactories.registerCheck<FasterStrsplitDelimiterCheck>( "abseil-faster-strsplit-delimiter"); + CheckFactories.registerCheck<MakeUniqueCheck>( + "abseil-make-unique"); CheckFactories.registerCheck<NoInternalDependenciesCheck>( "abseil-no-internal-dependencies"); CheckFactories.registerCheck<NoNamespaceCheck>("abseil-no-namespace");
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits