[clang-tidy] Migrate UseAuto from clang-modernize to clang-tidy.

http://reviews.llvm.org/D12231

llvm-svn: 245703
diff --git a/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp
new file mode 100644
index 0000000..b688f10
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp
@@ -0,0 +1,368 @@
+//===--- UseAutoCheck.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 "UseAutoCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using namespace clang::ast_matchers::internal;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+namespace {
+
+const char IteratorDeclStmtId[] = "iterator_decl";
+const char DeclWithNewId[] = "decl_new";
+
+/// \brief Matches variable declarations that have explicit initializers that
+/// are not initializer lists.
+///
+/// Given
+/// \code
+///   iterator I = Container.begin();
+///   MyType A(42);
+///   MyType B{2};
+///   MyType C;
+/// \endcode
+///
+/// varDecl(hasWrittenNonListInitializer()) maches \c I and \c A but not \c B
+/// or \c C.
+AST_MATCHER(VarDecl, hasWrittenNonListInitializer) {
+  const Expr *Init = Node.getAnyInitializer();
+  if (!Init)
+    return false;
+
+  // The following test is based on DeclPrinter::VisitVarDecl() to find if an
+  // initializer is implicit or not.
+  if (const auto *Construct = dyn_cast<CXXConstructExpr>(Init)) {
+    return !Construct->isListInitialization() && Construct->getNumArgs() > 0 &&
+           !Construct->getArg(0)->isDefaultArgument();
+  }
+  return Node.getInitStyle() != VarDecl::ListInit;
+}
+
+/// \brief Matches QualTypes that are type sugar for QualTypes that match \c
+/// SugarMatcher.
+///
+/// Given
+/// \code
+///   class C {};
+///   typedef C my_type;
+///   typedef my_type my_other_type;
+/// \endcode
+///
+/// qualType(isSugarFor(recordType(hasDeclaration(namedDecl(hasName("C"))))))
+/// matches \c my_type and \c my_other_type.
+AST_MATCHER_P(QualType, isSugarFor, Matcher<QualType>, SugarMatcher) {
+  QualType QT = Node;
+  while (true) {
+    if (SugarMatcher.matches(QT, Finder, Builder))
+      return true;
+
+    QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
+    if (NewQT == QT)
+      return false;
+    QT = NewQT;
+  }
+}
+
+/// \brief Matches named declarations that have one of the standard iterator
+/// names: iterator, reverse_iterator, const_iterator, const_reverse_iterator.
+///
+/// Given
+/// \code
+///   iterator I;
+///   const_iterator CI;
+/// \endcode
+///
+/// namedDecl(hasStdIteratorName()) matches \c I and \c CI.
+AST_MATCHER(NamedDecl, hasStdIteratorName) {
+  static const char *IteratorNames[] = {"iterator", "reverse_iterator",
+                                        "const_iterator",
+                                        "const_reverse_iterator"};
+
+  for (const char *Name : IteratorNames) {
+    if (hasName(Name).matches(Node, Finder, Builder))
+      return true;
+  }
+  return false;
+}
+
+/// \brief Matches named declarations that have one of the standard container
+/// names.
+///
+/// Given
+/// \code
+///   class vector {};
+///   class forward_list {};
+///   class my_ver{};
+/// \endcode
+///
+/// recordDecl(hasStdContainerName()) matches \c vector and \c forward_list
+/// but not \c my_vec.
+AST_MATCHER(NamedDecl, hasStdContainerName) {
+  static const char *ContainerNames[] = {"array",         "deque",
+                                         "forward_list",  "list",
+                                         "vector",
+
+                                         "map",           "multimap",
+                                         "set",           "multiset",
+
+                                         "unordered_map", "unordered_multimap",
+                                         "unordered_set", "unordered_multiset",
+
+                                         "queue",         "priority_queue",
+                                         "stack"};
+
+  for (const char *Name : ContainerNames) {
+    if (hasName(Name).matches(Node, Finder, Builder))
+      return true;
+  }
+  return false;
+}
+
+/// Matches declarations whose declaration context is the C++ standard library
+/// namespace std.
+///
+/// Note that inline namespaces are silently ignored during the lookup since
+/// both libstdc++ and libc++ are known to use them for versioning purposes.
+///
+/// Given:
+/// \code
+///   namespace ns {
+///     struct my_type {};
+///     using namespace std;
+///   }
+///
+///   using std::vector;
+///   using ns:my_type;
+///   using ns::list;
+/// \code
+///
+/// usingDecl(hasAnyUsingShadowDecl(hasTargetDecl(isFromStdNamespace())))
+/// matches "using std::vector" and "using ns::list".
+AST_MATCHER(Decl, isFromStdNamespace) {
+  const DeclContext *D = Node.getDeclContext();
+
+  while (D->isInlineNamespace())
+    D = D->getParent();
+
+  if (!D->isNamespace() || !D->getParent()->isTranslationUnit())
+    return false;
+
+  const IdentifierInfo *Info = cast<NamespaceDecl>(D)->getIdentifier();
+
+  return (Info && Info->isStr("std"));
+}
+
+/// \brief Returns a DeclarationMatcher that matches standard iterators nested
+/// inside records with a standard container name.
+DeclarationMatcher standardIterator() {
+  return allOf(
+      namedDecl(hasStdIteratorName()),
+      hasDeclContext(recordDecl(hasStdContainerName(), isFromStdNamespace())));
+}
+
+/// \brief Returns a TypeMatcher that matches typedefs for standard iterators
+/// inside records with a standard container name.
+TypeMatcher typedefIterator() {
+  return typedefType(hasDeclaration(standardIterator()));
+}
+
+/// \brief Returns a TypeMatcher that matches records named for standard
+/// iterators nested inside records named for standard containers.
+TypeMatcher nestedIterator() {
+  return recordType(hasDeclaration(standardIterator()));
+}
+
+/// \brief Returns a TypeMatcher that matches types declared with using
+/// declarations and which name standard iterators for standard containers.
+TypeMatcher iteratorFromUsingDeclaration() {
+  auto HasIteratorDecl = hasDeclaration(namedDecl(hasStdIteratorName()));
+  // Types resulting from using declarations are represented by elaboratedType.
+  return elaboratedType(allOf(
+      // Unwrap the nested name specifier to test for one of the standard
+      // containers.
+      hasQualifier(specifiesType(templateSpecializationType(hasDeclaration(
+          namedDecl(hasStdContainerName(), isFromStdNamespace()))))),
+      // the named type is what comes after the final '::' in the type. It
+      // should name one of the standard iterator names.
+      namesType(
+          anyOf(typedefType(HasIteratorDecl), recordType(HasIteratorDecl)))));
+}
+
+/// \brief This matcher returns declaration statements that contain variable
+/// declarations with written non-list initializer for standard iterators.
+StatementMatcher makeIteratorDeclMatcher() {
+  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()),
+             unless(has(varDecl(anyOf(
+                 unless(hasWrittenNonListInitializer()), hasType(autoType()),
+                 unless(hasType(
+                     isSugarFor(anyOf(typedefIterator(), nestedIterator(),
+                                      iteratorFromUsingDeclaration())))))))))
+      .bind(IteratorDeclStmtId);
+}
+
+StatementMatcher makeDeclWithNewMatcher() {
+  return declStmt(has(varDecl()),
+                  unless(has(varDecl(anyOf(
+                      unless(hasInitializer(ignoringParenImpCasts(newExpr()))),
+                      // FIXME: TypeLoc information is not reliable where CV
+                      // qualifiers are concerned so these types can't be
+                      // handled for now.
+                      hasType(pointerType(
+                          pointee(hasCanonicalType(hasLocalQualifiers())))),
+
+                      // FIXME: Handle function pointers. For now we ignore them
+                      // because the replacement replaces the entire type
+                      // specifier source range which includes the identifier.
+                      hasType(pointsTo(
+                          pointsTo(parenType(innerType(functionType()))))))))))
+      .bind(DeclWithNewId);
+}
+
+} // namespace
+
+void UseAutoCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(makeIteratorDeclMatcher(), this);
+  Finder->addMatcher(makeDeclWithNewMatcher(), this);
+}
+
+void UseAutoCheck::replaceIterators(const DeclStmt *D, ASTContext *Context) {
+  for (const auto *Dec : D->decls()) {
+    const auto *V = cast<VarDecl>(Dec);
+    const Expr *ExprInit = V->getInit();
+
+    // Skip expressions with cleanups from the intializer expression.
+    if (const auto *E = dyn_cast<ExprWithCleanups>(ExprInit))
+      ExprInit = E->getSubExpr();
+
+    const auto *Construct = dyn_cast<CXXConstructExpr>(ExprInit);
+    if (!Construct)
+      continue;
+
+    // Ensure that the constructor receives a single argument.
+    if (Construct->getNumArgs() != 1)
+      return;
+
+    // Drill down to the as-written initializer.
+    const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts();
+    if (E != E->IgnoreConversionOperator()) {
+      // We hit a conversion operator. Early-out now as they imply an implicit
+      // conversion from a different type. Could also mean an explicit
+      // conversion from the same type but that's pretty rare.
+      return;
+    }
+
+    if (const auto *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) {
+      // If we ran into an implicit conversion contructor, can't convert.
+      //
+      // FIXME: The following only checks if the constructor can be used
+      // implicitly, not if it actually was. Cases where the converting
+      // constructor was used explicitly won't get converted.
+      if (NestedConstruct->getConstructor()->isConvertingConstructor(false))
+        return;
+    }
+    if (!Context->hasSameType(V->getType(), E->getType()))
+      return;
+  }
+
+  // Get the type location using the first declaration.
+  const auto *V = cast<VarDecl>(*D->decl_begin());
+
+  // WARNING: TypeLoc::getSourceRange() will include the identifier for things
+  // like function pointers. Not a concern since this action only works with
+  // iterators but something to keep in mind in the future.
+
+  SourceRange Range(V->getTypeSourceInfo()->getTypeLoc().getSourceRange());
+  diag(Range.getBegin(), "use auto when declaring iterators")
+      << FixItHint::CreateReplacement(Range, "auto");
+}
+
+void UseAutoCheck::replaceNew(const DeclStmt *D, ASTContext *Context) {
+  const auto *FirstDecl = cast<VarDecl>(*D->decl_begin());
+  // Ensure that there is at least one VarDecl within the DeclStmt.
+  if (!FirstDecl)
+    return;
+
+  const QualType FirstDeclType = FirstDecl->getType().getCanonicalType();
+
+  std::vector<SourceLocation> StarLocations;
+  for (const auto *Dec : D->decls()) {
+    const auto *V = cast<VarDecl>(Dec);
+    // Ensure that every DeclStmt child is a VarDecl.
+    if (!V)
+      return;
+
+    const auto *NewExpr = cast<CXXNewExpr>(V->getInit()->IgnoreParenImpCasts());
+    // Ensure that every VarDecl has a CXXNewExpr initializer.
+    if (!NewExpr)
+      return;
+
+    // If VarDecl and Initializer have mismatching unqualified types.
+    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>();
+    }
+  }
+
+  // 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());
+  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, "");
+  }
+}
+
+void UseAutoCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Decl = Result.Nodes.getNodeAs<DeclStmt>(IteratorDeclStmtId)) {
+    replaceIterators(Decl, Result.Context);
+  } else if (const auto *Decl =
+                 Result.Nodes.getNodeAs<DeclStmt>(DeclWithNewId)) {
+    replaceNew(Decl, Result.Context);
+  } else {
+    llvm_unreachable("Bad Callback. No node provided.");
+  }
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang