diff options
Diffstat (limited to 'clang-tools-extra/cpp11-migrate')
9 files changed, 485 insertions, 0 deletions
diff --git a/clang-tools-extra/cpp11-migrate/CMakeLists.txt b/clang-tools-extra/cpp11-migrate/CMakeLists.txt index 30e33b457d2..591c0a3f204 100644 --- a/clang-tools-extra/cpp11-migrate/CMakeLists.txt +++ b/clang-tools-extra/cpp11-migrate/CMakeLists.txt @@ -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} ) diff --git a/clang-tools-extra/cpp11-migrate/Makefile b/clang-tools-extra/cpp11-migrate/Makefile index 5b806d49203..4eca366538a 100644 --- a/clang-tools-extra/cpp11-migrate/Makefile +++ b/clang-tools-extra/cpp11-migrate/Makefile @@ -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 \ diff --git a/clang-tools-extra/cpp11-migrate/Transforms.cpp b/clang-tools-extra/cpp11-migrate/Transforms.cpp index 6b2ab5c9fa2..2b6dfd79d6b 100644 --- a/clang-tools-extra/cpp11-migrate/Transforms.cpp +++ b/clang-tools-extra/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 @@ 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. } diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.cpp b/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.cpp new file mode 100644 index 00000000000..00723cab218 --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.cpp @@ -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; +} diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.h b/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.h new file mode 100644 index 00000000000..799dad9582d --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.h @@ -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 diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.cpp b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.cpp new file mode 100644 index 00000000000..ceb22390a1f --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.cpp @@ -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; + } +} diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.h b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.h new file mode 100644 index 00000000000..d6ea557b369 --- /dev/null +++ b/clang-tools-extra/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 diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp new file mode 100644 index 00000000000..6522f4f10bd --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp @@ -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); +} diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.h b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.h new file mode 100644 index 00000000000..db160f8766e --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.h @@ -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 |