alexfh created this revision.
alexfh added reviewers: sbenza, aaron.ballman.
alexfh added a subscriber: cfe-commits.

Add RemoveStars option to the modernize-use-auto check. With the new option
turned on (by default) the check will change `MyType *t = new MyType;` to
`auto *t = new MyType;` instead of `auto t = new MyType;`, thus achieving more
consistency with the recommendations to use `auto *` for iterating over pointers
in range-based for loops:
http://llvm.org/docs/CodingStandards.html#beware-unnecessary-copies-with-auto

http://reviews.llvm.org/D20917

Files:
  clang-tidy/modernize/UseAutoCheck.cpp
  clang-tidy/modernize/UseAutoCheck.h
  docs/clang-tidy/checks/modernize-use-auto.rst
  test/clang-tidy/modernize-use-auto-new-remove-stars.cpp
  test/clang-tidy/modernize-use-auto-new.cpp

Index: test/clang-tidy/modernize-use-auto-new.cpp
===================================================================
--- test/clang-tidy/modernize-use-auto-new.cpp
+++ test/clang-tidy/modernize-use-auto-new.cpp
@@ -9,11 +9,11 @@
 void auto_new() {
   MyType *a_new = new MyType();
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
-  // CHECK-FIXES: auto a_new = new MyType();
+  // CHECK-FIXES: auto *a_new = new MyType();
 
   static MyType *a_static = new MyType();
   // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use auto when initializing with new
-  // CHECK-FIXES: static auto a_static = new MyType();
+  // CHECK-FIXES: static auto *a_static = new MyType();
 
   MyType *derived = new MyDerivedType();
 
@@ -27,42 +27,42 @@
   // not "MyType * const".
   static MyType * const d_static = new MyType();
   // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use auto when initializing with new
-  // CHECK-FIXES: static auto  const d_static = new MyType();
+  // CHECK-FIXES: static auto * const d_static = new MyType();
 
   MyType * const a_const = new MyType();
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
-  // CHECK-FIXES: auto  const a_const = new MyType();
+  // CHECK-FIXES: auto * const a_const = new MyType();
 
   MyType * volatile vol = new MyType();
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
-  // CHECK-FIXES: auto  volatile vol = new MyType();
+  // CHECK-FIXES: auto * volatile vol = new MyType();
 
   struct SType {} *stype = new SType;
 
   int (**func)(int, int) = new (int(*[5])(int,int));
 
   int *array = new int[5];
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
-  // CHECK-FIXES: auto array = new int[5];
+  // CHECK-FIXES: auto *array = new int[5];
 
   MyType *ptr(new MyType);
   // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use auto when initializing with new
-  // CHECK-FIXES: auto ptr(new MyType);
+  // CHECK-FIXES: auto *ptr(new MyType);
 
   MyType *ptr2{new MyType};
 
   {
     // Test for declaration lists.
     MyType *a = new MyType(), *b = new MyType(), *c = new MyType();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
-    // CHECK-FIXES: auto a = new MyType(), b = new MyType(), c = new MyType();
+    // CHECK-FIXES: auto *a = new MyType(), *b = new MyType(), *c = new MyType();
 
     // Non-initialized declaration should not be transformed.
     MyType *d = new MyType(), *e;
 
     MyType **f = new MyType*(), **g = new MyType*();
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
-    // CHECK-FIXES: auto f = new MyType*(), g = new MyType*();
+    // CHECK-FIXES: auto **f = new MyType*(), **g = new MyType*();
 
     // Mismatching types in declaration lists should not be transformed.
     MyType *h = new MyType(), **i = new MyType*();
@@ -75,25 +75,26 @@
   {
     // Test for typedefs.
     typedef int * int_p;
+    // CHECK-FIXES: typedef int * int_p;
 
     int_p a = new int;
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
-    // CHECK-FIXES: auto  a = new int;
+    // CHECK-FIXES: auto a = new int;
     int_p *b = new int*;
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
-    // CHECK-FIXES: auto b = new int*;
+    // CHECK-FIXES: auto *b = new int*;
 
     // Test for typedefs in declarations lists.
     int_p c = new int, d = new int;
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
-    // CHECK-FIXES: auto  c = new int, d = new int;
+    // CHECK-FIXES: auto c = new int, d = new int;
 
     // Different types should not be transformed.
     int_p e = new int, *f = new int_p;
 
     int_p *g = new int*, *h = new int_p;
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
-    // CHECK-FIXES: auto g = new int*, h = new int_p;
+    // CHECK-FIXES: auto *g = new int*, *h = new int_p;
   }
 
   // Don't warn when 'auto' is already being used.
Index: test/clang-tidy/modernize-use-auto-new-remove-stars.cpp
===================================================================
--- test/clang-tidy/modernize-use-auto-new-remove-stars.cpp
+++ test/clang-tidy/modernize-use-auto-new-remove-stars.cpp
@@ -1,4 +1,6 @@
-// RUN: %check_clang_tidy %s modernize-use-auto %t
+// RUN: %check_clang_tidy %s modernize-use-auto %t -- \
+// RUN:   -config="{CheckOptions: [{key: modernize-use-auto.RemoveStars, value: '1'}]}" \
+// RUN:   -- -std=c++11
 
 class MyType {};
 
@@ -75,6 +77,7 @@
   {
     // Test for typedefs.
     typedef int * int_p;
+    // CHECK-FIXES: typedef int * int_p;
 
     int_p a = new int;
     // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: use auto when initializing with new
Index: docs/clang-tidy/checks/modernize-use-auto.rst
===================================================================
--- docs/clang-tidy/checks/modernize-use-auto.rst
+++ docs/clang-tidy/checks/modernize-use-auto.rst
@@ -124,7 +124,7 @@
 
   // becomes
 
-  auto my_pointer = new TypeName(my_param);
+  auto *my_pointer = new TypeName(my_param);
 
 The check will also replace the declaration type in multiple declarations, if
 the following conditions are satisfied:
@@ -141,12 +141,29 @@
 
   // becomes
 
-  auto my_first_pointer = new TypeName, my_second_pointer = new TypeName;
+  auto *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
 
 Known Limitations
 -----------------
 * If the initializer is an explicit conversion constructor, the check will not
   replace the type specifier even though it would be safe to do so.
 
 * User-defined iterators are not handled at this time.
 
+RemoveStars option
+------------------
+If the option is set to non-zero (default is ``0``), the check will remove stars
+from the non-typedef pointer types when replacing type names with ``auto``.
+Otherwise, the check will leave stars. For example:
+
+.. code-block:: c++
+
+  TypeName *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
+
+  // RemoveStars = 0
+
+  auto *my_first_pointer = new TypeName, *my_second_pointer = new TypeName;
+
+  // RemoveStars = 1
+
+  auto my_first_pointer = new TypeName, my_second_pointer = new TypeName;
Index: clang-tidy/modernize/UseAutoCheck.h
===================================================================
--- clang-tidy/modernize/UseAutoCheck.h
+++ clang-tidy/modernize/UseAutoCheck.h
@@ -18,15 +18,16 @@
 
 class UseAutoCheck : public ClangTidyCheck {
 public:
-  UseAutoCheck(StringRef Name, ClangTidyContext *Context)
-      : ClangTidyCheck(Name, Context) {}
-
+  UseAutoCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
   void registerMatchers(ast_matchers::MatchFinder *Finder) override;
   void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
 
 private:
   void replaceIterators(const DeclStmt *D, ASTContext *Context);
   void replaceNew(const DeclStmt *D, ASTContext *Context);
+
+  const bool RemoveStars;
 };
 
 } // namespace modernize
Index: clang-tidy/modernize/UseAutoCheck.cpp
===================================================================
--- clang-tidy/modernize/UseAutoCheck.cpp
+++ clang-tidy/modernize/UseAutoCheck.cpp
@@ -243,6 +243,14 @@
 
 } // namespace
 
+UseAutoCheck::UseAutoCheck(StringRef Name, ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      RemoveStars(Options.get("RemoveStars", 0)) {}
+
+void UseAutoCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "RemoveStars", RemoveStars ? 1 : 0);
+}
+
 void UseAutoCheck::registerMatchers(MatchFinder *Finder) {
   // Only register the matchers for C++; the functionality currently does not
   // provide any benefit to other languages, despite being benign.
@@ -311,7 +319,7 @@
 
   const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
 
-  std::vector<SourceLocation> StarLocations;
+  std::vector<FixItHint> StarRemovals;
   for (const auto *Dec : D->decls()) {
     const auto *V = cast<VarDecl>(Dec);
     // Ensure that every DeclStmt child is a VarDecl.
@@ -327,39 +335,42 @@
     if (!Context->hasSameUnqualifiedType(V->getType(), NewExpr->getType()))
       return;
 
-    // Remove explicitly written '*' from declarations where there's more than
-    // one declaration in the declaration list.
-    if (Dec == *D->decl_begin())
-      continue;
-
     // All subsequent declarations should match the same non-decorated type.
     if (FirstDeclType != V->getType().getCanonicalType())
       return;
 
-    auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
-    while (!Q.isNull()) {
-      StarLocations.push_back(Q.getStarLoc());
-      Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
+    if (RemoveStars) {
+      // Remove explicitly written '*' from declarations where there's more than
+      // one declaration in the declaration list.
+      if (Dec == *D->decl_begin())
+        continue;
+
+      auto Q = V->getTypeSourceInfo()->getTypeLoc().getAs<PointerTypeLoc>();
+      while (!Q.isNull()) {
+        StarRemovals.push_back(FixItHint::CreateRemoval(Q.getStarLoc()));
+        Q = Q.getNextTypeLoc().getAs<PointerTypeLoc>();
+      }
     }
   }
 
   // FIXME: There is, however, one case we can address: when the VarDecl pointee
   // is the same as the initializer, just more CV-qualified. However, TypeLoc
   // information is not reliable where CV qualifiers are concerned so we can't
   // do anything about this case for now.
-  SourceRange Range(
-      FirstDecl->getTypeSourceInfo()->getTypeLoc().getSourceRange());
+  TypeLoc Loc = FirstDecl->getTypeSourceInfo()->getTypeLoc();
+  if (!RemoveStars) {
+    while (Loc.getTypeLocClass() == TypeLoc::Pointer ||
+           Loc.getTypeLocClass() == TypeLoc::Qualified)
+      Loc = Loc.getNextTypeLoc();
+  }
+  SourceRange Range(Loc.getSourceRange());
   auto Diag = diag(Range.getBegin(), "use auto when initializing with new"
                                      " to avoid duplicating the type name");
 
   // Space after 'auto' to handle cases where the '*' in the pointer type is
   // next to the identifier. This avoids changing 'int *p' into 'autop'.
-  Diag << FixItHint::CreateReplacement(Range, "auto ");
-
-  // Remove '*' from declarations using the saved star locations.
-  for (const auto &Loc : StarLocations) {
-    Diag << FixItHint::CreateReplacement(Loc, "");
-  }
+  Diag << FixItHint::CreateReplacement(Range, RemoveStars ? "auto " : "auto")
+       << StarRemovals;
 }
 
 void UseAutoCheck::check(const MatchFinder::MatchResult &Result) {
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to