Introducing Use-Auto transform for cpp11-migrate

The new Use-Auto transform replaces the type specifier for variable
declarations with the special C++11 'auto' type specifier. For now, the
replacement is done only for variables that are iterators of any of the
std containers and only if the type used is one of those explicitly
allowed by the standard (i.e. not an implementation-specific type).

Reviewers: gribozavr, silvas, klimek
llvm-svn: 176266
This commit is contained in:
Edwin Vane 2013-02-28 16:29:24 +00:00
parent 3305b177e6
commit 266b625ca9
12 changed files with 771 additions and 44 deletions

View File

@ -15,6 +15,9 @@ list(APPEND Cpp11MigrateSources ${LoopConvertSources})
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}
)

View File

@ -26,6 +26,8 @@ SOURCES += $(addprefix LoopConvert/,$(notdir $(wildcard $(PROJ_SRC_DIR)/LoopConv
BUILT_SOURCES = $(ObjDir)/LoopConvert/.objdir
SOURCES += $(addprefix UseNullptr/,$(notdir $(wildcard $(PROJ_SRC_DIR)/UseNullptr/*.cpp)))
BUILT_SOURCES += $(ObjDir)/UseNullptr/.objdir
SOURCES += $(addprefix UseAuto/,$(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 \

View File

@ -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 @@ void Transforms::createTransformOpts() {
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.
}

View File

@ -0,0 +1,58 @@
//===-- UseAuto/UseAuto.cpp - Use auto type specifier ---------------------===//
//
// 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(), InputStates, ResultStates);
if (AcceptedChanges > 0)
setChangesMade();
return 0;
}

View File

@ -0,0 +1,40 @@
//===-- 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 for variable
/// declarations into the special C++11 'auto' type specifier for certain cases:
/// * Iterators of std containers.
/// * More to come...
///
/// Other uses of the auto type specifier as outlined in C++11 [dcl.spec.auto]
/// p2 are not handled by this transform.
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

View File

@ -0,0 +1,63 @@
//===-- 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"
#include "clang/AST/ASTContext.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 CXXConstructExpr *Construct = cast<CXXConstructExpr>(D->getInit());
assert(Construct->getNumArgs() == 1u &&
"Expected constructor with single argument");
// 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 CXXConstructExpr *NestedConstruct = dyn_cast<CXXConstructExpr>(E))
// If we ran into an implicit conversion constructor, 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 (Result.Context->hasSameType(D->getType(), E->getType())) {
TypeLoc TL = D->getTypeSourceInfo()->getTypeLoc();
// 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.
CharSourceRange Range(TL.getSourceRange(), true);
Replace.insert(tooling::Replacement(SM, Range, "auto"));
++AcceptedChanges;
}
}

View File

@ -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

View File

@ -0,0 +1,247 @@
//===-- 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"
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 = Container.begin();
/// MyType A(42);
/// MyType B{2};
/// MyType C;
/// \endcode
/// varDecl(hasWrittenNonListInitializer()) matches \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.
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 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;
/// \code
///
/// \c qualType(isSugarFor(recordType(hasDeclaration(namedDecl(hasName("C"))))))
/// matches \c my_type and \c my_other_type.
AST_MATCHER_P(QualType, isSugarFor, internal::Matcher<QualType>, SugarMatcher) {
QualType QT = Node;
for (;;) {
if (SugarMatcher.matches(QT, Finder, Builder))
return true;
QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext());
if (NewQT == QT)
break;
QT = NewQT;
}
return false;
}
/// \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;
/// \code
///
/// \c 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 (unsigned int i = 0;
i < llvm::array_lengthof(IteratorNames);
++i) {
if (hasName(IteratorNames[i]).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_vec {};
/// \code
///
/// \c recordDecl(hasStdContainerName()) matches \c vector and \c forward_list
/// but not \c my_vec.
AST_MATCHER_P(NamedDecl, hasStdContainerName, bool, WithStd) {
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 (unsigned int i = 0;
i < llvm::array_lengthof(ContainerNames);
++i) {
std::string Name(ContainerNames[i]);
if (WithStd)
Name = "std::" + Name;
if (hasName(Name).matches(Node, Finder, Builder))
return true;
}
return false;
}
} // namespace ast_matchers
} // namespace clang
namespace {
// \brief Returns a TypeMatcher that matches typedefs for standard iterators
// inside records with a standard container name.
TypeMatcher typedefIterator() {
return typedefType(
hasDeclaration(
allOf(
namedDecl(hasStdIteratorName()),
hasDeclContext(
recordDecl(hasStdContainerName(true))
)
)
)
);
}
// \brief Returns a TypeMatcher that matches records named for standard
// iterators nested inside records named for standard containers.
TypeMatcher nestedIterator() {
return recordType(
hasDeclaration(
allOf(
namedDecl(hasStdIteratorName()),
hasDeclContext(
recordDecl(hasStdContainerName(true))
)
)
)
);
}
// \brief Returns a TypeMatcher that matches types declared with using
// declarations and which name standard iterators for standard containers.
TypeMatcher iteratorFromUsingDeclaration() {
// 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(allOf(
specifiesType(
templateSpecializationType(
hasDeclaration(
namedDecl(hasStdContainerName(false))
)
)
),
hasPrefix(
specifiesNamespace(hasName("std"))
)
)),
// The named type is what comes after the final
// '::' in the type. It should name one of the
// standard iterator names.
namesType(anyOf(
typedefType(
hasDeclaration(
namedDecl(hasStdIteratorName())
)
),
recordType(
hasDeclaration(
namedDecl(hasStdIteratorName())
)
)
))
)
);
}
} // namespace
DeclarationMatcher makeIteratorMatcher() {
return varDecl(allOf(
hasWrittenNonListInitializer(),
unless(hasType(autoType())),
hasType(
isSugarFor(
anyOf(
typedefIterator(),
nestedIterator(),
iteratorFromUsingDeclaration()
)
)
)
)).bind(DeclNodeId);
}

View File

@ -0,0 +1,27 @@
//===-- 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 variable declarations where the type
/// is an iterator for an std container and has an explicit initializer of the
/// same type.
clang::ast_matchers::DeclarationMatcher makeIteratorMatcher();
#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_MATCHERS_H

View File

@ -1,47 +1,57 @@
#!/usr/bin/python
# Each std container is represented below. To test the various ways in which
# a type may be defined, the containers are split into categories:
# * Define iterator types with typedefs
# * Define iterator types as nested classes
# * Define iterator types with using declarations
#
# Further, one class in each category is chosen to be defined in a way mimicing
# libc++: The container is actually defined in a different namespace (std::_1
# is used here) and then imported into the std namespace with a using
# declaration. This is controlled with the 'using' key in the dictionary
# describing each container.
typedef_containers = [
"array",
"deque",
"forward_list",
"list",
"vector"
{"name" : "array",
"using" : True},
{"name" : "deque",
"using" : False},
{"name" : "forward_list",
"using" : False},
{"name" : "list",
"using" : False},
{"name" : "vector",
"using" : False}
]
subclass_containers = [
"map",
"multimap",
"set",
"multiset",
{"name" : "map",
"using" : True},
{"name" : "multimap",
"using" : False},
{"name" : "set",
"using" : False},
{"name" : "multiset",
"using" : False},
]
using_containers = [
"unordered_map",
"unordered_multimap",
"unordered_set",
"unordered_multiset",
"queue",
"priority_queue",
"stack"
{"name" : "unordered_map",
"using" : True},
{"name" : "unordered_multimap",
"using" : False},
{"name" : "unordered_set",
"using" : False},
{"name" : "unordered_multiset",
"using" : False},
{"name" : "queue",
"using" : False},
{"name" : "priority_queue",
"using" : False},
{"name" : "stack",
"using" : False}
]
print """namespace internal {
template <typename T, int i>
struct iterator_wrapper {
};
template <typename T>
class iterator_provider {
public:
class iterator {};
class const_iterator {};
class reverse_iterator {};
class const_reverse_iterator {};
};
} // namespace internal
namespace std {"""
# Every class requires these functions.
iterator_generators = """
iterator begin() { return iterator(); }
iterator end() { return iterator(); }
@ -56,21 +66,97 @@ iterator_generators = """
const_reverse_iterator rend() const { return const_reverse_iterator(); }
"""
# Convenience function for nested class definition within a special namespace
# to mimic libc++ style std container definitions.
def outputClassDef(Definition, ClassName, Import):
if Import:
print "namespace _1 {"
print Definition
if Import:
print """
}} // namespace _1
using _1::{0};""".format(ClassName)
# Output preamble and common functionality
print """
//===-----------------------------------------------------------*- C++ -*--===//
//
// This file was automatically generated from gen_my_std.h.py by the build
// system as a dependency for cpp11-migrate's test suite.
//
// This file contains a shell implementation of std containers and iterators for
// testing the use-auto transform of cpp11-migrate. All std containers and
// iterators are present. Container and iterator implementations vary to cover
// various ways the std container and iterator types are made available:
//
// Variations for how iterator types are presented:
// * Typedef (array, deque, forward_list, list, vector)
// * Nested class (map, multimap, set, multiset)
// * Using declaration {unordered_} X {map, multimap, set, multiset}
//
// Variations for how container types are presented:
// * Defined directly in namespace std
// * Imported into namespace std with using declarations (a la libc++).
//
//===----------------------------------------------------------------------===//
namespace internal {
template <typename T, int i>
struct iterator_wrapper {
iterator_wrapper() {}
// These are required for tests using iteration statements.
bool operator!=(const iterator_wrapper<T, i>&) { return false; }
iterator_wrapper& operator++() { return *this; }
typename T::value_type operator*() { return typename T::value_type(); }
};
template <typename T>
class iterator_provider {
public:
class iterator {
public:
iterator() {}
iterator(const iterator&) {}
};
class const_iterator {
public:
const_iterator(int i=0) {}
const_iterator(const iterator &) {}
const_iterator(const const_iterator &) {}
operator iterator() { return iterator(); }
};
class reverse_iterator {};
class const_reverse_iterator {};
};
} // namespace internal
namespace std {""".lstrip() # Take off leading newline
for c in typedef_containers:
print """
Definition = """
template <typename T>
class {0} {{
public:
typedef T value_type;
typedef typename internal::iterator_wrapper<{0}<T>, 0> iterator;
typedef typename internal::iterator_wrapper<{0}<T>, 1> const_iterator;
typedef typename internal::iterator_wrapper<{0}<T>, 3> reverse_iterator;
typedef typename internal::iterator_wrapper<{0}<T>, 2> const_reverse_iterator;
{0}() {{}}
{1}}};""".format(c, iterator_generators)
{1}}};""".format(c['name'], iterator_generators)
outputClassDef(Definition, c['name'], c['using'])
for c in subclass_containers:
print """
Definition = """
template <typename T>
class {0} {{
public:
@ -80,10 +166,12 @@ public:
class const_reverse_iterator {{}};
{0}() {{}}
{1}}};""".format(c, iterator_generators)
{1}}};""".format(c['name'], iterator_generators)
outputClassDef(Definition, c['name'], c['using'])
for c in using_containers:
print """
Definition = """
template <typename T>
class {0} : internal::iterator_provider<{0}<T> > {{
public:
@ -93,6 +181,8 @@ public:
using typename internal::iterator_provider<{0}<T> >::const_reverse_iterator;
{0}() {{}}
{1}}};""".format(c, iterator_generators)
{1}}};""".format(c['name'], iterator_generators)
outputClassDef(Definition, c['name'], c['using'])
print "} // namespace std"

View File

@ -19,13 +19,26 @@ containers = [
"stack"
]
print """// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: cpp11-migrate -use-auto %t.cpp -- --std=c++11 -I %S/Inputs
print """
//===----------------------------------------------------------------------===//
//
// This file was automatically generated from
// gen_basic_std_iterator_tests.cpp.py by the build system as a dependency for
// cpp11-migrate's test suite.
//
// This file contains basic positive tests for the use-auto transform's ability
// to replace standard iterators. Variables considered:
// * All std container names
// * All std iterator names
//
//===----------------------------------------------------------------------===//
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: cpp11-migrate -use-auto %t.cpp -- -I %S/Inputs
// RUN: FileCheck -input-file=%t.cpp %s
// XFAIL: *
#include "my_std.h"
int main(int argc, char **argv) {"""
int main(int argc, char **argv) {""".lstrip() # Strip leading newline
for c in containers:
print """

View File

@ -0,0 +1,139 @@
// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp
// RUN: cpp11-migrate -use-auto %t.cpp -- --std=c++11 -I %gen_root/UseAuto/Inputs
// RUN: FileCheck -input-file=%t.cpp %s
#include "my_std.h"
typedef std::vector<int>::iterator int_iterator;
namespace foo {
template <typename T>
class vector {
public:
class iterator {};
iterator begin() { return iterator(); }
};
} // namespace foo
int main(int argc, char **argv) {
std::vector<int> Vec;
// CHECK: std::vector<int> Vec;
std::unordered_map<int> Map;
// CHECK: std::unordered_map<int> Map;
// Types with more sugar should work. Types with less should not.
{
int_iterator more_sugar = Vec.begin();
// CHECK: auto more_sugar = Vec.begin();
internal::iterator_wrapper<std::vector<int>, 0> less_sugar = Vec.begin();
// CHECK: internal::iterator_wrapper<std::vector<int>, 0> less_sugar = Vec.begin();
}
// Initialization from initializer lists isn't allowed. Using 'auto'
// would result in std::initializer_list being deduced for the type.
{
std::unordered_map<int>::iterator I{Map.begin()};
// CHECK: std::unordered_map<int>::iterator I{Map.begin()};
std::unordered_map<int>::iterator I2 = {Map.begin()};
// CHECK: std::unordered_map<int>::iterator I2 = {Map.begin()};
}
// Various forms of construction. Default constructors and constructors with
// all-default parameters shouldn't get transformed. Construction from other
// types is also not allowed.
{
std::unordered_map<int>::iterator copy(Map.begin());
// CHECK: auto copy(Map.begin());
std::unordered_map<int>::iterator def;
// CHECK: std::unordered_map<int>::iterator def;
// const_iterator has no default constructor, just one that has >0 params
// with defaults.
std::unordered_map<int>::const_iterator constI;
// CHECK: std::unordered_map<int>::const_iterator constI;
// Uses iterator_provider::const_iterator's conversion constructor.
std::unordered_map<int>::const_iterator constI2 = def;
// CHECK: std::unordered_map<int>::const_iterator constI2 = def;
std::unordered_map<int>::const_iterator constI3(def);
// CHECK: std::unordered_map<int>::const_iterator constI3(def);
// Explicit use of conversion constructor
std::unordered_map<int>::const_iterator constI4 = std::unordered_map<int>::const_iterator(def);
// CHECK: auto constI4 = std::unordered_map<int>::const_iterator(def);
// Uses iterator_provider::iterator's const_iterator conversion operator.
std::unordered_map<int>::iterator I = constI;
// CHECK: std::unordered_map<int>::iterator I = constI;
std::unordered_map<int>::iterator I2(constI);
// CHECK: std::unordered_map<int>::iterator I2(constI);
}
// Weird cases of pointers and references to iterators are not transformed.
{
int_iterator I = Vec.begin();
int_iterator *IPtr = &I;
// CHECK: int_iterator *IPtr = &I;
int_iterator &IRef = I;
// CHECK: int_iterator &IRef = I;
}
{
// Variable declarations in iteration statements.
for (std::vector<int>::iterator I = Vec.begin(); I != Vec.end(); ++I) {
// CHECK: for (auto I = Vec.begin(); I != Vec.end(); ++I) {
}
// Range-based for loops.
std::array<std::vector<int>::iterator> iter_arr;
for (std::vector<int>::iterator I: iter_arr) {
// CHECK: for (auto I: iter_arr) {
}
// Test with init-declarator-list.
for (int_iterator I = Vec.begin(),
E = Vec.end(); I != E; ++I) {
// CHECK: for (auto I = Vec.begin(),
// CHECK-NEXT: E = Vec.end(); I != E; ++I) {
}
}
// Only std containers should be changed.
{
using namespace foo;
vector<int> foo_vec;
vector<int>::iterator I = foo_vec.begin();
// CHECK: vector<int>::iterator I = foo_vec.begin();
}
// Ensure using directives don't interfere with replacement.
{
using namespace std;
vector<int> std_vec;
vector<int>::iterator I = std_vec.begin();
// CHECK: auto I = std_vec.begin();
}
// Make sure references and cv qualifiers don't get removed (i.e. replaced
// with just 'auto').
{
const auto & I = Vec.begin();
// CHECK: const auto & I = Vec.begin();
auto && I2 = Vec.begin();
// CHECK: auto && I2 = Vec.begin();
}
return 0;
}