hugoeg created this revision.
hugoeg added reviewers: alexfh, hokein.
hugoeg added a project: clang-tools-extra.
Herald added a subscriber: mgorny.

warns to use 'auto' to avoid repeating the type name and fixes the issue

Replace:
 std::unique_ptr<Foo> x = make_unique<Foo>(...);
with:

  auto x = make_unique<Foo>(...);


https://reviews.llvm.org/D50852

Files:
  clang-tidy/abseil/AbseilTidyModule.cpp
  clang-tidy/abseil/AutoMakeUniqueCheck.cpp
  clang-tidy/abseil/AutoMakeUniqueCheck.h
  clang-tidy/abseil/CMakeLists.txt
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/abseil-auto-make-unique.rst
  docs/clang-tidy/checks/list.rst
  test/clang-tidy/abseil-auto-make-unique.cpp

Index: test/clang-tidy/abseil-auto-make-unique.cpp
===================================================================
--- test/clang-tidy/abseil-auto-make-unique.cpp
+++ test/clang-tidy/abseil-auto-make-unique.cpp
@@ -0,0 +1,115 @@
+// RUN: %check_clang_tidy %s abseil-auto-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();
+  ~unique_ptr();
+  explicit unique_ptr(T*);
+  template <typename U, typename E>
+  unique_ptr(unique_ptr<U, E>&&);
+};
+template <typename T, typename... Args>
+unique_ptr<T> make_unique(Args&&...);
+}  // namespace std
+
+namespace absl {
+template <typename T, typename... Args>
+std::unique_ptr<T> MakeUnique(Args&&...);
+template <typename T, typename... Args>
+std::unique_ptr<T> make_unique(Args&&...);
+}  // namespace absl
+using absl::make_unique;
+
+typedef int integer;
+
+struct Base {};
+struct Derived : public Base {};
+
+void Primitive() {
+  std::unique_ptr<int> x = absl::make_unique<int>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'auto' to avoid repeating the type name
+  // CHECK-FIXES: auto x = absl::make_unique<int>();
+
+  std::unique_ptr< int >y = absl::make_unique<int>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'auto' to avoid repeating the type name
+  // CHECK-FIXES: auto y = absl::make_unique<int>();
+
+  const std::unique_ptr<int> z = absl::make_unique<int>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: use 'auto' to avoid repeating the type name
+  // CHECK-FIXES: const auto z = absl::make_unique<int>();
+
+  std::unique_ptr<const int> t = absl::make_unique<int>();
+}
+
+void Typedefs() {
+  std::unique_ptr<int> x = absl::make_unique<integer>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'auto' to avoid repeating the type name
+  // CHECK-FIXES: auto x = absl::make_unique<integer>();
+
+  std::unique_ptr<integer> y = absl::make_unique<int>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'auto' to avoid repeating the type name
+  // CHECK-FIXES: auto y = absl::make_unique<int>();
+
+  std::unique_ptr<integer> z = absl::make_unique<integer>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'auto' to avoid repeating the type name
+  // CHECK-FIXES: auto z = absl::make_unique<integer>();
+}
+
+void Class() {
+  std::unique_ptr<Base> base = make_unique<Base>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'auto' to avoid repeating the type name
+  // CHECK-FIXES: auto base = make_unique<Base>();
+
+  std::unique_ptr<Base> base2(make_unique<Base>());
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'auto' to avoid repeating the type name
+  // CHECK-FIXES: auto base2(make_unique<Base>());
+
+  // Different type. No change.
+  std::unique_ptr<Base> z = make_unique<Derived>();
+  std::unique_ptr<Base> z2(make_unique<Derived>());
+}
+
+template <typename A, typename B>
+void f() {
+  std::unique_ptr<A> x = make_unique<B>();
+}
+
+void Negatives() {
+  // Different deleter. No change.
+  struct MyDeleter {};
+  std::unique_ptr<int, MyDeleter> z3 = make_unique<int>();
+  std::unique_ptr<int, MyDeleter> z4(make_unique<int>());
+
+  f<int, int>();
+}
+
+std::unique_ptr<int> global_var = make_unique<int>();
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: use 'auto' to avoid repeating the type name
+// CHECK-FIXES: auto global_var = make_unique<int>();
+
+struct Struct {
+  static std::unique_ptr<int> static_field;
+};
+// This code with "auto" replaced doesn't compile in GCC.
+std::unique_ptr<int> Struct::static_field = make_unique<int>();
+
+void FunctionWithStatic() {
+  static std::unique_ptr<int> static_var = make_unique<int>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use 'auto' to avoid repeating the type name
+  // CHECK-FIXES: auto static_var = make_unique<int>();
+}
+
+void OtherNames() {
+  std::unique_ptr<int> x = absl::MakeUnique<int>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'auto' to avoid repeating the type name
+  // CHECK-FIXES: auto x = absl::MakeUnique<int>();
+
+  std::unique_ptr<int> y = std::make_unique<int>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use 'auto' to avoid repeating the type name
+  // CHECK-FIXES: auto y = std::make_unique<int>();
+}
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -4,6 +4,7 @@
 =================
 
 .. toctree::
+   abseil-auto-make-unique
    abseil-string-find-startswith
    android-cloexec-accept
    android-cloexec-accept4
Index: docs/clang-tidy/checks/abseil-auto-make-unique.rst
===================================================================
--- docs/clang-tidy/checks/abseil-auto-make-unique.rst
+++ docs/clang-tidy/checks/abseil-auto-make-unique.rst
@@ -0,0 +1,19 @@
+.. title:: clang-tidy - abseil-auto-make-unique
+
+abseil-auto-make-unique
+=======================
+
+The check suggests replacing:
+
+.. code-block:: c++
+
+  std::unique_ptr<Foo> x = MakeUnique<Foo>(...);
+
+with
+
+.. code-block:: c++
+
+  auto x = MakeUnique<Foo>(...);
+
+The initializer already spells the type, so it can be safely omitted from the
+declaration while making the code more readable.
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -57,6 +57,11 @@
 Improvements to clang-tidy
 --------------------------
 
+- New :doc:`abseil-auto-make-unique
+  <clang-tidy/checks/abseil-auto-make-unique>` check.
+
+  FIXME: add release notes.
+
 - New :doc:`readability-magic-numbers
   <clang-tidy/checks/readability-magic-numbers>` check.
 
Index: clang-tidy/abseil/CMakeLists.txt
===================================================================
--- clang-tidy/abseil/CMakeLists.txt
+++ clang-tidy/abseil/CMakeLists.txt
@@ -2,6 +2,7 @@
 
 add_clang_library(clangTidyAbseilModule
   AbseilTidyModule.cpp
+  AutoMakeUniqueCheck.cpp
   StringFindStartswithCheck.cpp
 
   LINK_LIBS
Index: clang-tidy/abseil/AutoMakeUniqueCheck.h
===================================================================
--- clang-tidy/abseil/AutoMakeUniqueCheck.h
+++ clang-tidy/abseil/AutoMakeUniqueCheck.h
@@ -0,0 +1,38 @@
+//===--- AutoMakeUniqueCheck.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_AUTOMAKEUNIQUECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ABSEIL_AUTOMAKEUNIQUECHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace abseil {
+
+/// Replace:
+///   std::unique_ptr<Foo> x = make_unique<Foo>(...);
+/// with:
+///   auto x = make_unique<Foo>(...);
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/abseil-auto-make-unique.html
+class AutoMakeUniqueCheck : public ClangTidyCheck {
+public:
+  AutoMakeUniqueCheck(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_AUTOMAKEUNIQUECHECK_H
Index: clang-tidy/abseil/AutoMakeUniqueCheck.cpp
===================================================================
--- clang-tidy/abseil/AutoMakeUniqueCheck.cpp
+++ clang-tidy/abseil/AutoMakeUniqueCheck.cpp
@@ -0,0 +1,88 @@
+//===--- AutoMakeUniqueCheck.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 "AutoMakeUniqueCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace abseil {
+
+void AutoMakeUniqueCheck::registerMatchers(MatchFinder* finder) {
+  if (!getLangOpts().CPlusPlus) return;
+
+  using clang::ast_matchers::isTemplateInstantiation;
+  auto is_instantiation = decl(anyOf(cxxRecordDecl(isTemplateInstantiation()),
+                                     varDecl(isTemplateInstantiation()),
+                                     functionDecl(isTemplateInstantiation())));
+  // There should be no additional expressions inbetween.
+  // E.g. this statement contains implicitCastExpr which makes it not eligible:
+  // std::unique_ptr<Base> p = make_unique<Derived>();
+  finder->addMatcher(
+      varDecl(has(exprWithCleanups(has(cxxConstructExpr(
+                  has(ignoringParenImpCasts(cxxBindTemporaryExpr(
+                      has(ignoringParenImpCasts(callExpr(callee(functionDecl(
+                          hasAnyName("absl::MakeUnique", "absl::make_unique",
+                                     "gtl::MakeUnique", "std::make_unique"),
+                          decl().bind("make_unique_decl"))))))))))))),
+              hasType(classTemplateSpecializationDecl(
+                  hasName("std::unique_ptr"),
+                  hasTemplateArgument(
+                      1, refersToType(qualType(hasDeclaration(cxxRecordDecl(
+                             hasName("std::default_delete")))))))),
+              unless(hasType(autoType())),
+              unless(anyOf(is_instantiation, hasAncestor(is_instantiation))))
+          .bind("var_decl"),
+      this);
+}
+
+const Type* GetUniquePtrType(const VarDecl* var) {
+  const auto* unique = dyn_cast_or_null<ClassTemplateSpecializationDecl>(
+      var->getType()->getAsCXXRecordDecl());
+  if (!unique) return nullptr;
+  QualType type = unique->getTemplateArgs().get(0).getAsType();
+  return type->getUnqualifiedDesugaredType();
+}
+
+const Type* GetMakeUniqueType(const FunctionDecl* make_unique_decl) {
+  const auto& template_arg =
+      make_unique_decl->getTemplateSpecializationInfo()->TemplateArguments->get(
+          0);
+  return template_arg.getAsType()->getUnqualifiedDesugaredType();
+}
+
+void AutoMakeUniqueCheck::check(
+    const ast_matchers::MatchFinder::MatchResult& result) {
+  const auto* var_decl = result.Nodes.getNodeAs<VarDecl>("var_decl");
+  const auto* make_unique_decl =
+      result.Nodes.getNodeAs<FunctionDecl>("make_unique_decl");
+  if (var_decl->isOutOfLine()) {
+    // "auto Struct::field = make_unique<...>();" doesn't work in GCC.
+    return;
+  }
+  if (GetUniquePtrType(var_decl) != GetMakeUniqueType(make_unique_decl)) {
+    // The variable and the make_unique expression do not share the same type.
+    // Ignore this.
+    return;
+  }
+
+  diag(var_decl->getTypeSpecStartLoc(),
+       "use 'auto' to avoid repeating the type name")
+      << FixItHint::CreateReplacement(
+             clang::CharSourceRange::getCharRange(
+                 var_decl->getTypeSpecStartLoc(), var_decl->getLocation()),
+             "auto ");
+}
+
+} // namespace abseil
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/abseil/AbseilTidyModule.cpp
===================================================================
--- clang-tidy/abseil/AbseilTidyModule.cpp
+++ clang-tidy/abseil/AbseilTidyModule.cpp
@@ -10,6 +10,7 @@
 #include "../ClangTidy.h"
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
+#include "AutoMakeUniqueCheck.h"
 #include "StringFindStartswithCheck.h"
 
 namespace clang {
@@ -19,6 +20,8 @@
 class AbseilModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<AutoMakeUniqueCheck>(
+        "abseil-auto-make-unique");
     CheckFactories.registerCheck<StringFindStartswithCheck>(
         "abseil-string-find-startswith");
   }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to