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

Reply via email to