Hi klimek,

Initial draft of UseAuto replaces the type specifier for declarations of
variables that inherit from std::iterator or have iterator type traits
with the C++11 'auto' keyword.

http://llvm-reviews.chandlerc.com/D392

Files:
  cpp11-migrate/CMakeLists.txt
  cpp11-migrate/Makefile
  cpp11-migrate/Transforms.cpp
  cpp11-migrate/UseAuto/UseAuto.cpp
  cpp11-migrate/UseAuto/UseAuto.h
  cpp11-migrate/UseAuto/UseAutoActions.cpp
  cpp11-migrate/UseAuto/UseAutoActions.h
  cpp11-migrate/UseAuto/UseAutoMatchers.cpp
  cpp11-migrate/UseAuto/UseAutoMatchers.h
  test/cpp11-migrate/UseAuto/iterator.cpp
Index: cpp11-migrate/CMakeLists.txt
===================================================================
--- cpp11-migrate/CMakeLists.txt
+++ cpp11-migrate/CMakeLists.txt
@@ -15,6 +15,9 @@
 file(GLOB_RECURSE UseNullptrSources "UseNullptr/*.cpp")
 list(APPEND Cpp11MigrateSources ${UseNullptrSources})
 
+file(GLOB_RECURSE UseAutoSources "UseAuto/*.cpp")
+list(APPEND Cpp11MigrateSources ${UseAutoSources})
+
 add_clang_executable(cpp11-migrate
   ${Cpp11MigrateSources}
   )
Index: cpp11-migrate/Makefile
===================================================================
--- cpp11-migrate/Makefile
+++ cpp11-migrate/Makefile
@@ -26,6 +26,8 @@
 BUILT_SOURCES = $(ObjDir)/LoopConvert/.objdir
 SOURCES += $(addprefix UseNullptr/,$(notdir $(wildcard $(PROJ_SRC_DIR)/UseNullptr/*.cpp)))
 BUILT_SOURCES += $(ObjDir)/UseNullptr/.objdir
+SOURCES += $(addprefix UseNullptr/,$(notdir $(wildcard $(PROJ_SRC_DIR)/UseAuto/*.cpp)))
+BUILT_SOURCES += $(ObjDir)/UseAuto/.objdir
 
 LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc
 USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
Index: cpp11-migrate/Transforms.cpp
===================================================================
--- cpp11-migrate/Transforms.cpp
+++ cpp11-migrate/Transforms.cpp
@@ -15,6 +15,7 @@
 #include "Transforms.h"
 #include "LoopConvert/LoopConvert.h"
 #include "UseNullptr/UseNullptr.h"
+#include "UseAuto/UseAuto.h"
 
 namespace cl = llvm::cl;
 
@@ -47,6 +48,12 @@
         cl::desc("Make use of nullptr keyword where possible")),
       &ConstructTransform<UseNullptrTransform>));
 
+  Options.push_back(
+    OptionVec::value_type(
+      new cl::opt<bool>("use-auto",
+        cl::desc("Use of 'auto' type specifier")),
+      &ConstructTransform<UseAutoTransform>));
+
   // Add more transform options here.
 }
 
Index: cpp11-migrate/UseAuto/UseAuto.cpp
===================================================================
--- /dev/null
+++ cpp11-migrate/UseAuto/UseAuto.cpp
@@ -0,0 +1,60 @@
+//===-- UseAuto/UseAuto.cpp - Use auto type specifier -----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides the implementation of the UseAutoTransform class.
+///
+//===----------------------------------------------------------------------===//
+
+#include "UseAuto.h"
+#include "UseAutoActions.h"
+#include "UseAutoMatchers.h"
+
+using clang::ast_matchers::MatchFinder;
+using namespace clang;
+using namespace clang::tooling;
+
+int UseAutoTransform::apply(const FileContentsByPath &InputStates,
+                            RiskLevel MaxRisk,
+                            const clang::tooling::CompilationDatabase &Database,
+                            const std::vector<std::string> &SourcePaths,
+                            FileContentsByPath &ResultStates) {
+  RefactoringTool UseAutoTool(Database, SourcePaths);
+
+  for (FileContentsByPath::const_iterator I = InputStates.begin(),
+                                          E = InputStates.end();
+       I != E; ++I) {
+    UseAutoTool.mapVirtualFile(I->first, I->second);
+  }
+
+  unsigned AcceptedChanges = 0;
+
+  MatchFinder Finder;
+  UseAutoFixer Fixer(UseAutoTool.getReplacements(), AcceptedChanges, MaxRisk);
+
+  Finder.addMatcher(makeIteratorMatcher(), &Fixer);
+
+  if (int result = UseAutoTool.run(newFrontendActionFactory(&Finder))) {
+    llvm::errs() << "Error encountered during translation.\n";
+    return result;
+  }
+
+  RewriterContainer Rewrite(UseAutoTool.getFiles(), InputStates);
+
+  // FIXME: Do something if some replacements didn't get applied?
+  UseAutoTool.applyAllReplacements(Rewrite.getRewriter());
+
+  collectResults(Rewrite.getRewriter(), ResultStates);
+
+  if (AcceptedChanges > 0) {
+    setChangesMade();
+  }
+
+  return 0;
+}
Index: cpp11-migrate/UseAuto/UseAuto.h
===================================================================
--- /dev/null
+++ cpp11-migrate/UseAuto/UseAuto.h
@@ -0,0 +1,49 @@
+//===-- UseAuto/UseAuto.h - Use auto type specifier -------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief This file provides the definition of the UseAutoTransform class
+/// which is the main interface to the use-auto transform that replaces
+/// type specifiers with the special C++11 'auto' type specifier in certain
+/// situations.
+///
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_H
+#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_H
+
+#include "Transform.h"
+#include "llvm/Support/Compiler.h"
+
+/// \brief Subclass of Transform that transforms type specifiers into the
+/// special C++11 'auto' type specifier in certain situations.
+///
+/// In addition to being used as the type specifier for variable declarations
+/// auto can be used in these ways (c++11 §7.1.6.4.2):
+/// * With functions with trailing return type
+///   - Not covered by this transform
+/// * In the condition of a selection statement but only if the type is
+/// convertible to bool.
+/// * Declaring a static data member with a brace-or-equal initializer.
+/// * In an iteration statement.
+/// * With operator new
+///   * This transform applies auto only to the variable declaration not to the
+///   new expression as described in (c++11 §5.3.4.2)
+/// * A for-range-declaration
+class UseAutoTransform : public Transform {
+public:
+  /// \see Transform::run().
+  virtual int apply(const FileContentsByPath &InputStates,
+                    RiskLevel MaxRiskLEvel,
+                    const clang::tooling::CompilationDatabase &Database,
+                    const std::vector<std::string> &SourcePaths,
+                    FileContentsByPath &ResultStates) LLVM_OVERRIDE;
+};
+
+#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_H
+
Index: cpp11-migrate/UseAuto/UseAutoActions.cpp
===================================================================
--- /dev/null
+++ cpp11-migrate/UseAuto/UseAutoActions.cpp
@@ -0,0 +1,57 @@
+//===-- UseAuto/UseAutoActions.cpp - Matcher callback impl ----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+///  \file
+///  \brief This file contains the implementation of the UseAutoFixer class.
+///
+//===----------------------------------------------------------------------===//
+#include "UseAutoActions.h"
+#include "UseAutoMatchers.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tooling;
+using namespace clang;
+
+void UseAutoFixer::run(const MatchFinder::MatchResult &Result) {
+  const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>(DeclNodeId);
+
+  assert(D && "Bad Callback. No node provided");
+
+  SourceManager &SM = *Result.SourceManager;
+  if (!SM.isFromMainFile(D->getLocStart())) {
+    return;
+  }
+
+  const Expr *Initializer = D->getInit();
+  const CXXConstructExpr *Construct =
+      dyn_cast<const CXXConstructExpr>(Initializer);
+
+  assert(Construct && "Expecteded a CXXConstructExpr for initializer");
+
+  // Drill down to the as-written initializer.
+  const Expr *E = *Construct->arg_begin();
+
+  if (const MaterializeTemporaryExpr *MTE =
+          dyn_cast<MaterializeTemporaryExpr>(E)) {
+    E = MTE->GetTemporaryExpr();
+  }
+  if (const CastExpr *CE = dyn_cast<CastExpr>(E)) {
+    if (!isa<ExplicitCastExpr>(E)) {
+      E = CE->getSubExprAsWritten();
+    }
+  }
+
+  // Compare canonical types so typedefs don't mess the comparison up.
+  if (D->getType().getCanonicalType() == E->getType().getCanonicalType()) {
+    TypeLoc TL = D->getTypeSourceInfo()->getTypeLoc();
+    CharSourceRange Range(TL.getSourceRange(), true);
+    Replace.insert(tooling::Replacement(SM, Range, "auto"));
+    ++AcceptedChanges;
+  }
+}
Index: cpp11-migrate/UseAuto/UseAutoActions.h
===================================================================
--- /dev/null
+++ cpp11-migrate/UseAuto/UseAutoActions.h
@@ -0,0 +1,38 @@
+//===-- UseAuto/Actions.h - Matcher callback ---------------------*- C++ -*-==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+///  \file
+///  \brief This file contains the declaration of the UseAutoFixer class which
+///  is used as an ASTMatcher callback.
+///
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_ACTIONS_H
+#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_ACTIONS_H
+
+#include "Transform.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Tooling/Refactoring.h"
+
+/// \brief The callback to be used for use-auto AST matchers.
+class UseAutoFixer : public clang::ast_matchers::MatchFinder::MatchCallback {
+public:
+  UseAutoFixer(clang::tooling::Replacements &Replace, unsigned &AcceptedChanges,
+               RiskLevel)
+      : Replace(Replace), AcceptedChanges(AcceptedChanges) {
+  }
+
+  /// \brief Entry point to the callback called when matches are made.
+  virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result);
+
+private:
+  clang::tooling::Replacements &Replace;
+  unsigned &AcceptedChanges;
+};
+
+#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_ACTIONS_H
Index: cpp11-migrate/UseAuto/UseAutoMatchers.cpp
===================================================================
--- /dev/null
+++ cpp11-migrate/UseAuto/UseAutoMatchers.cpp
@@ -0,0 +1,88 @@
+//===-- UseAutoMatchers.cpp - Matchers for use-auto transform -------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+///  \file
+///  \brief This file contains the implementation for matcher-generating
+///  functions and custom AST_MATCHERs.
+///
+//===----------------------------------------------------------------------===//
+#include "UseAutoMatchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang::ast_matchers;
+using namespace clang;
+
+const char *DeclNodeId = "decl";
+
+namespace clang {
+namespace ast_matchers {
+
+/// \brief Matches variable declarations that have explicit initializers that
+/// are not initializer lists.
+///
+/// Given
+/// \code
+///   iterator I = C.begin(); // A
+///   MyType A{2};            // B
+///   MyType B;               // C
+/// \endcode
+/// varDecl(hasWrittenNonListInitializer()) matches \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 taken from DeclPrinter::VisitVarDecl() to find if an
+  // initializer is implicit or not.
+  bool ImplicitInit = false;
+  if (const CXXConstructExpr *Construct = dyn_cast<CXXConstructExpr>(Init)) {
+    if (Construct->isListInitialization()) {
+      return false;
+    }
+    ImplicitInit = Construct->getNumArgs() == 0 ||
+                   Construct->getArg(0)->isDefaultArgument();
+  } else {
+    if (Node.getInitStyle() == VarDecl::ListInit) {
+      return false;
+    }
+  }
+
+  return !ImplicitInit;
+}
+
+/// \brief Overload of allOf accepting 5 args.
+template <typename M1, typename M2, typename M3, typename M4, typename M5>
+internal::PolymorphicMatcherWithParam2<
+    internal::AllOfMatcher,
+    internal::PolymorphicMatcherWithParam2<internal::AllOfMatcher, M1, M2>,
+    internal::PolymorphicMatcherWithParam2<
+        internal::AllOfMatcher, internal::PolymorphicMatcherWithParam2<
+                                    internal::AllOfMatcher, M3, M4>, M5> >
+allOf(const M1 &P1, const M2 &P2, const M3 &P3, const M4 &P4, const M5 &P5) {
+  return allOf(allOf(P1, P2), allOf(allOf(P3, P4), P5));
+}
+
+} // namespace ast_matchers
+} // namespace clang
+
+DeclarationMatcher makeIteratorMatcher() {
+  return varDecl(allOf(hasWrittenNonListInitializer(),
+                       hasType(recordDecl(anyOf(
+                           isDerivedFrom("std::iterator"),
+                           allOf(has(namedDecl(hasName("value_type"))),
+                                 has(namedDecl(hasName("difference_type"))),
+                                 has(namedDecl(hasName("iterator_category"))),
+                                 has(namedDecl(hasName("reference"))),
+                                 has(namedDecl(hasName("pointer"))))))),
+                       // Skip type-specifiers that are already 'auto'.
+                       unless(hasType(autoType())))).bind(DeclNodeId);
+}
Index: cpp11-migrate/UseAuto/UseAutoMatchers.h
===================================================================
--- /dev/null
+++ cpp11-migrate/UseAuto/UseAutoMatchers.h
@@ -0,0 +1,26 @@
+//===-- UseAutoMatchers.h - Matchers for use-auto transform ----*- C++ -*--===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+///  \file
+///  \brief This file contains the declarations for matcher-generating functions
+///  and names for bound nodes found by AST matchers.
+///
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_MATCHERS_H
+#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_MATCHERS_H
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+
+extern const char *DeclNodeId;
+
+/// \brief Create a matcher that matches declarations where the type is a
+/// subclass of std::iterator or has standard iterator traits.
+clang::ast_matchers::DeclarationMatcher makeIteratorMatcher();
+
+#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_MATCHERS_H
Index: test/cpp11-migrate/UseAuto/iterator.cpp
===================================================================
--- /dev/null
+++ test/cpp11-migrate/UseAuto/iterator.cpp
@@ -0,0 +1,116 @@
+// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
+// RUN: cpp11-migrate -use-auto %t.cpp -- --std=c++11
+// RUN: FileCheck -input-file=%t.cpp %s
+#include <vector>
+#include <array>
+
+class MyIterator : public std::iterator<std::forward_iterator_tag, int> {
+public:
+  MyIterator() = default;
+  MyIterator(std::vector<int>::iterator I) : I(I) {}
+
+  operator std::vector<int>::iterator()
+  {
+    return I;
+  }
+
+private:
+  std::vector<int>::iterator I;
+};
+
+MyIterator begin()
+{
+  return MyIterator();
+}
+
+class DummyIterator {
+public:
+  typedef void value_type;
+  typedef void difference_type;
+  typedef void iterator_category;
+  typedef void reference;
+  typedef void pointer;
+};
+DummyIterator makeDummy() {
+  return DummyIterator();
+}
+
+typedef std::vector<int>::iterator int_iterator;
+
+// TODO: Custom iterator class
+int main(int argc, char **argv)
+{
+  std::vector<int> blah;
+  // CHECK: std::vector<int> blah;
+
+  std::vector<int> no_transform{1,2,3};
+  // CHECK: std::vector<int> no_transform{1,2,3};
+
+  // MyIterator inherits from std::iterator so it should be transformed.
+  MyIterator myI = begin();
+  // CHECK: auto myI = begin();
+
+  // DummyIterator provides the iterator type traits so it should be
+  // transformed.
+  DummyIterator dummyI = makeDummy();
+  // CHECK: auto dummyI = makeDummy();
+
+  // declarator-id is not the same type as initializer expression. No transform
+  // should happen.
+  std::vector<int>::iterator I = myI;
+  // CHECK: std::vector<int>::iterator I = myI
+
+  // Not an iterator. No transform should happen.
+  int i = 9;
+  // CHECK: int i = 9;
+
+  // Simple variable declaration.
+  std::vector<int>::iterator begin = blah.begin();
+  // CHECK: auto begin = blah.begin();
+
+  // Make sure we test on canonical types.
+  int_iterator I2 = begin;
+  // CHECK: auto I2 = begin;
+
+  // Weird cases with pointers and references to iterators not covered.
+  int_iterator *pI = &begin;
+  // CHECK: int_iterator *pI = &begin;
+
+  int_iterator &rI = begin;
+  // CHECK: int_iterator &rI = begin;
+
+  // 'auto' will deduce to std::initializer_list in this case so no
+  // replacement.
+
+  std::vector<int>::iterator begin2{blah.begin()};
+  // CHECK: std::vector<int>::iterator begin2{blah.begin()};
+
+  std::vector<int>::iterator begin3 = {blah.begin()};
+  // CHECK: std::vector<int>::iterator begin3 = {blah.begin()};
+
+  std::vector<int>::reverse_iterator rbegin = blah.rbegin();
+  // CHECK: auto rbegin = blah.rbegin();
+
+  // Iteration statement.
+  for (std::vector<int>::iterator I = blah.begin(); I != blah.end(); ++I) {
+    // CHECK: for (auto I = blah.begin(); I != blah.end(); ++I) {
+    ++(*I);
+  }
+
+  // Range-based for.
+  std::array<std::vector<int>::iterator, 5> iter_arr;
+  for (std::vector<int>::iterator I: iter_arr) {
+    // CHECK: for (auto I: iter_arr) {
+    I = blah.begin();
+  }
+
+  // Test with init-declarator-list.
+  for (int_iterator I = blah.begin(),
+       E = blah.end(); I != E; ++I) {
+  // CHECK:      for (auto I = blah.begin(),
+  // CHECK-NEXT:      E = blah.end(); I != E; ++I) {
+    ++(*I);
+  }
+
+  return 0;
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to