[clang-tidy] Enhance modernize-use-auto to templated function casts

Summary:
Use auto when declaring variables that are initialized by calling a templated
function that returns its explicit first argument.

Fixes PR26763.

Reviewers: aaron.ballman, alexfh, staronj, Prazek

Subscribers: Eugene.Zelenko, JDevlieghere, cfe-commits

Differential Revision: https://reviews.llvm.org/D27166

llvm-svn: 289797
diff --git a/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp
index e2ddceb..12c19f6 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp
@@ -24,6 +24,7 @@
 const char IteratorDeclStmtId[] = "iterator_decl";
 const char DeclWithNewId[] = "decl_new";
 const char DeclWithCastId[] = "decl_cast";
+const char DeclWithTemplateCastId[] = "decl_template";
 
 /// \brief Matches variable declarations that have explicit initializers that
 /// are not initializer lists.
@@ -169,6 +170,14 @@
   return (Info && Info->isStr("std"));
 }
 
+/// Matches declaration reference or member expressions with explicit template
+/// arguments.
+AST_POLYMORPHIC_MATCHER(hasExplicitTemplateArgs,
+                        AST_POLYMORPHIC_SUPPORTED_TYPES(DeclRefExpr,
+                                                        MemberExpr)) {
+  return Node.hasExplicitTemplateArgs();
+}
+
 /// \brief Returns a DeclarationMatcher that matches standard iterators nested
 /// inside records with a standard container name.
 DeclarationMatcher standardIterator() {
@@ -240,18 +249,38 @@
       .bind(DeclWithCastId);
 }
 
+StatementMatcher makeDeclWithTemplateCastMatcher() {
+  auto ST =
+      substTemplateTypeParmType(hasReplacementType(equalsBoundNode("arg")));
+
+  auto ExplicitCall =
+      anyOf(has(memberExpr(hasExplicitTemplateArgs())),
+            has(ignoringImpCasts(declRefExpr(hasExplicitTemplateArgs()))));
+
+  auto TemplateArg =
+      hasTemplateArgument(0, refersToType(qualType().bind("arg")));
+
+  auto TemplateCall = callExpr(
+      ExplicitCall,
+      callee(functionDecl(TemplateArg,
+                          returns(anyOf(ST, pointsTo(ST), references(ST))))));
+
+  return declStmt(unless(has(varDecl(
+                      unless(hasInitializer(ignoringImplicit(TemplateCall)))))))
+      .bind(DeclWithTemplateCastId);
+}
+
 StatementMatcher makeCombinedMatcher() {
   return declStmt(
       // At least one varDecl should be a child of the declStmt to ensure
       // it's a declaration list and avoid matching other declarations,
       // e.g. using directives.
-      has(varDecl()),
+      has(varDecl(unless(isImplicit()))),
       // Skip declarations that are already using auto.
       unless(has(varDecl(anyOf(hasType(autoType()),
-                               hasType(pointerType(pointee(autoType()))),
-                               hasType(referenceType(pointee(autoType()))))))),
+                               hasType(qualType(hasDescendant(autoType()))))))),
       anyOf(makeIteratorDeclMatcher(), makeDeclWithNewMatcher(),
-            makeDeclWithCastMatcher()));
+            makeDeclWithCastMatcher(), makeDeclWithTemplateCastMatcher()));
 }
 
 } // namespace
@@ -389,6 +418,8 @@
 
   // Space after 'auto' to handle cases where the '*' in the pointer type is
   // next to the identifier. This avoids changing 'int *p' into 'autop'.
+  // FIXME: This doesn't work for function pointers because the variable name
+  // is inside the type.
   Diag << FixItHint::CreateReplacement(Range, RemoveStars ? "auto " : "auto")
        << StarRemovals;
 }
@@ -411,6 +442,17 @@
         },
         "use auto when initializing with a cast to avoid duplicating the type "
         "name");
+  } else if (const auto *Decl =
+                 Result.Nodes.getNodeAs<DeclStmt>(DeclWithTemplateCastId)) {
+    replaceExpr(
+        Decl, Result.Context,
+        [](const Expr *Expr) {
+          return cast<CallExpr>(Expr->IgnoreImplicit())
+              ->getDirectCallee()
+              ->getReturnType();
+        },
+        "use auto when initializing with a template cast to avoid duplicating "
+        "the type name");
   } else {
     llvm_unreachable("Bad Callback. No node provided.");
   }