diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:44:14 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:44:14 +0000 |
| commit | 2b6b257f4e5503a7a2675bdb8735693db769f75c (patch) | |
| tree | e85e046ae7003fe3bcc8b5454cd0fa3f7407b470 /lib/Tooling | |
| parent | b4348ed0b7e90c0831b925fbee00b5f179a99796 (diff) | |
Notes
Diffstat (limited to 'lib/Tooling')
| -rw-r--r-- | lib/Tooling/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | lib/Tooling/CommonOptionsParser.cpp | 4 | ||||
| -rw-r--r-- | lib/Tooling/CompilationDatabase.cpp | 5 | ||||
| -rw-r--r-- | lib/Tooling/Core/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | lib/Tooling/Core/Makefile | 13 | ||||
| -rw-r--r-- | lib/Tooling/Core/QualTypeNames.cpp | 479 | ||||
| -rw-r--r-- | lib/Tooling/Core/Replacement.cpp | 84 | ||||
| -rw-r--r-- | lib/Tooling/FixIt.cpp | 31 | ||||
| -rw-r--r-- | lib/Tooling/Makefile | 14 | ||||
| -rw-r--r-- | lib/Tooling/Refactoring.cpp | 29 | ||||
| -rw-r--r-- | lib/Tooling/Tooling.cpp | 81 |
11 files changed, 670 insertions, 73 deletions
diff --git a/lib/Tooling/CMakeLists.txt b/lib/Tooling/CMakeLists.txt index b5c3d54e5fc1..56134c1164d4 100644 --- a/lib/Tooling/CMakeLists.txt +++ b/lib/Tooling/CMakeLists.txt @@ -7,6 +7,7 @@ add_clang_library(clangTooling CommonOptionsParser.cpp CompilationDatabase.cpp FileMatchTrie.cpp + FixIt.cpp JSONCompilationDatabase.cpp Refactoring.cpp RefactoringCallbacks.cpp @@ -17,6 +18,7 @@ add_clang_library(clangTooling clangASTMatchers clangBasic clangDriver + clangFormat clangFrontend clangLex clangRewrite diff --git a/lib/Tooling/CommonOptionsParser.cpp b/lib/Tooling/CommonOptionsParser.cpp index 82f560140085..5a44061cbd4c 100644 --- a/lib/Tooling/CommonOptionsParser.cpp +++ b/lib/Tooling/CommonOptionsParser.cpp @@ -62,7 +62,7 @@ public: : Compilations(std::move(Compilations)) {} void appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) { - Adjusters.push_back(Adjuster); + Adjusters.push_back(std::move(Adjuster)); } std::vector<CompileCommand> @@ -118,6 +118,8 @@ CommonOptionsParser::CommonOptionsParser( Compilations.reset(FixedCompilationDatabase::loadFromCommandLine(argc, argv)); cl::ParseCommandLineOptions(argc, argv, Overview); + cl::PrintOptionValues(); + SourcePathList = SourcePaths; if ((OccurrencesFlag == cl::ZeroOrMore || OccurrencesFlag == cl::Optional) && SourcePathList.empty()) diff --git a/lib/Tooling/CompilationDatabase.cpp b/lib/Tooling/CompilationDatabase.cpp index 957e40137eac..8fc4a1fe5beb 100644 --- a/lib/Tooling/CompilationDatabase.cpp +++ b/lib/Tooling/CompilationDatabase.cpp @@ -139,9 +139,8 @@ private: ; } - for (driver::ActionList::const_iterator I = A->begin(), E = A->end(); - I != E; ++I) - runImpl(*I, CollectChildren); + for (const driver::Action *AI : A->inputs()) + runImpl(AI, CollectChildren); } }; diff --git a/lib/Tooling/Core/CMakeLists.txt b/lib/Tooling/Core/CMakeLists.txt index b88e1f8333a2..f6348cbf80ed 100644 --- a/lib/Tooling/Core/CMakeLists.txt +++ b/lib/Tooling/Core/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangToolingCore Lookup.cpp Replacement.cpp + QualTypeNames.cpp LINK_LIBS clangAST diff --git a/lib/Tooling/Core/Makefile b/lib/Tooling/Core/Makefile deleted file mode 100644 index 366466c192e4..000000000000 --- a/lib/Tooling/Core/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -##===- clang/lib/Tooling/Core/Makefile ---------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -CLANG_LEVEL := ../../.. -LIBRARYNAME := clangToolingCore - -include $(CLANG_LEVEL)/Makefile diff --git a/lib/Tooling/Core/QualTypeNames.cpp b/lib/Tooling/Core/QualTypeNames.cpp new file mode 100644 index 000000000000..619dae1ee106 --- /dev/null +++ b/lib/Tooling/Core/QualTypeNames.cpp @@ -0,0 +1,479 @@ +//===------- QualTypeNames.cpp - Generate Complete QualType Names ---------===// +// +// The LLVM Compiler Infrastructure +// +//===----------------------------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/Tooling/Core/QualTypeNames.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/GlobalDecl.h" +#include "clang/AST/Mangle.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" + +#include <stdio.h> +#include <memory> + +namespace clang { + +namespace TypeName { +/// \brief Generates a QualType that can be used to name the same type +/// if used at the end of the current translation unit. This ignores +/// issues such as type shadowing. +/// +/// \param[in] QT - the type for which the fully qualified type will be +/// returned. +/// \param[in] Ctx - the ASTContext to be used. +/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace +/// specifier "::" should be prepended or not. +static QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx, + bool WithGlobalNsPrefix); + +/// \brief Create a NestedNameSpecifier for Namesp and its enclosing +/// scopes. +/// +/// \param[in] Ctx - the AST Context to be used. +/// \param[in] Namesp - the NamespaceDecl for which a NestedNameSpecifier +/// is requested. +/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace +/// specifier "::" should be prepended or not. +static NestedNameSpecifier *createNestedNameSpecifier( + const ASTContext &Ctx, + const NamespaceDecl *Namesp, + bool WithGlobalNsPrefix); + +/// \brief Create a NestedNameSpecifier for TagDecl and its enclosing +/// scopes. +/// +/// \param[in] Ctx - the AST Context to be used. +/// \param[in] TD - the TagDecl for which a NestedNameSpecifier is +/// requested. +/// \param[in] FullyQualify - Convert all template arguments into fully +/// qualified names. +/// \param[in] WithGlobalNsPrefix - Indicate whether the global namespace +/// specifier "::" should be prepended or not. +static NestedNameSpecifier *createNestedNameSpecifier( + const ASTContext &Ctx, const TypeDecl *TD, + bool FullyQualify, bool WithGlobalNsPrefix); + +static NestedNameSpecifier *createNestedNameSpecifierForScopeOf( + const ASTContext &Ctx, const Decl *decl, + bool FullyQualified, bool WithGlobalNsPrefix); + +static NestedNameSpecifier *getFullyQualifiedNestedNameSpecifier( + const ASTContext &Ctx, NestedNameSpecifier *scope, bool WithGlobalNsPrefix); + +static bool getFullyQualifiedTemplateName(const ASTContext &Ctx, + TemplateName &TName, + bool WithGlobalNsPrefix) { + bool Changed = false; + NestedNameSpecifier *NNS = nullptr; + + TemplateDecl *ArgTDecl = TName.getAsTemplateDecl(); + // ArgTDecl won't be NULL because we asserted that this isn't a + // dependent context very early in the call chain. + assert(ArgTDecl != nullptr); + QualifiedTemplateName *QTName = TName.getAsQualifiedTemplateName(); + + if (QTName && !QTName->hasTemplateKeyword()) { + NNS = QTName->getQualifier(); + NestedNameSpecifier *QNNS = getFullyQualifiedNestedNameSpecifier( + Ctx, NNS, WithGlobalNsPrefix); + if (QNNS != NNS) { + Changed = true; + NNS = QNNS; + } else { + NNS = nullptr; + } + } else { + NNS = createNestedNameSpecifierForScopeOf( + Ctx, ArgTDecl, true, WithGlobalNsPrefix); + } + if (NNS) { + TName = Ctx.getQualifiedTemplateName(NNS, + /*TemplateKeyword=*/false, ArgTDecl); + Changed = true; + } + return Changed; +} + +static bool getFullyQualifiedTemplateArgument(const ASTContext &Ctx, + TemplateArgument &Arg, + bool WithGlobalNsPrefix) { + bool Changed = false; + + // Note: we do not handle TemplateArgument::Expression, to replace it + // we need the information for the template instance decl. + + if (Arg.getKind() == TemplateArgument::Template) { + TemplateName TName = Arg.getAsTemplate(); + Changed = getFullyQualifiedTemplateName(Ctx, TName, WithGlobalNsPrefix); + if (Changed) { + Arg = TemplateArgument(TName); + } + } else if (Arg.getKind() == TemplateArgument::Type) { + QualType SubTy = Arg.getAsType(); + // Check if the type needs more desugaring and recurse. + QualType QTFQ = getFullyQualifiedType(SubTy, Ctx, WithGlobalNsPrefix); + if (QTFQ != SubTy) { + Arg = TemplateArgument(QTFQ); + Changed = true; + } + } + return Changed; +} + +static const Type *getFullyQualifiedTemplateType(const ASTContext &Ctx, + const Type *TypePtr, + bool WithGlobalNsPrefix) { + // DependentTemplateTypes exist within template declarations and + // definitions. Therefore we shouldn't encounter them at the end of + // a translation unit. If we do, the caller has made an error. + assert(!isa<DependentTemplateSpecializationType>(TypePtr)); + // In case of template specializations, iterate over the arguments + // and fully qualify them as well. + if (const auto *TST = dyn_cast<const TemplateSpecializationType>(TypePtr)) { + bool MightHaveChanged = false; + SmallVector<TemplateArgument, 4> FQArgs; + for (TemplateSpecializationType::iterator I = TST->begin(), E = TST->end(); + I != E; ++I) { + // Cheap to copy and potentially modified by + // getFullyQualifedTemplateArgument. + TemplateArgument Arg(*I); + MightHaveChanged |= getFullyQualifiedTemplateArgument( + Ctx, Arg, WithGlobalNsPrefix); + FQArgs.push_back(Arg); + } + + // If a fully qualified arg is different from the unqualified arg, + // allocate new type in the AST. + if (MightHaveChanged) { + QualType QT = Ctx.getTemplateSpecializationType( + TST->getTemplateName(), FQArgs, + TST->getCanonicalTypeInternal()); + // getTemplateSpecializationType returns a fully qualified + // version of the specialization itself, so no need to qualify + // it. + return QT.getTypePtr(); + } + } else if (const auto *TSTRecord = dyn_cast<const RecordType>(TypePtr)) { + // We are asked to fully qualify and we have a Record Type, + // which can point to a template instantiation with no sugar in any of + // its template argument, however we still need to fully qualify them. + + if (const auto *TSTDecl = + dyn_cast<ClassTemplateSpecializationDecl>(TSTRecord->getDecl())) { + const TemplateArgumentList &TemplateArgs = TSTDecl->getTemplateArgs(); + + bool MightHaveChanged = false; + SmallVector<TemplateArgument, 4> FQArgs; + for (unsigned int I = 0, E = TemplateArgs.size(); I != E; ++I) { + // cheap to copy and potentially modified by + // getFullyQualifedTemplateArgument + TemplateArgument Arg(TemplateArgs[I]); + MightHaveChanged |= getFullyQualifiedTemplateArgument( + Ctx, Arg, WithGlobalNsPrefix); + FQArgs.push_back(Arg); + } + + // If a fully qualified arg is different from the unqualified arg, + // allocate new type in the AST. + if (MightHaveChanged) { + TemplateName TN(TSTDecl->getSpecializedTemplate()); + QualType QT = Ctx.getTemplateSpecializationType( + TN, FQArgs, + TSTRecord->getCanonicalTypeInternal()); + // getTemplateSpecializationType returns a fully qualified + // version of the specialization itself, so no need to qualify + // it. + return QT.getTypePtr(); + } + } + } + return TypePtr; +} + +static NestedNameSpecifier *createOuterNNS(const ASTContext &Ctx, const Decl *D, + bool FullyQualify, + bool WithGlobalNsPrefix) { + const DeclContext *DC = D->getDeclContext(); + if (const auto *NS = dyn_cast<NamespaceDecl>(DC)) { + while (NS && NS->isInline()) { + // Ignore inline namespace; + NS = dyn_cast<NamespaceDecl>(NS->getDeclContext()); + } + if (NS->getDeclName()) { + return createNestedNameSpecifier(Ctx, NS, WithGlobalNsPrefix); + } + return nullptr; // no starting '::', no anonymous + } else if (const auto *TD = dyn_cast<TagDecl>(DC)) { + return createNestedNameSpecifier(Ctx, TD, FullyQualify, WithGlobalNsPrefix); + } else if (const auto *TDD = dyn_cast<TypedefNameDecl>(DC)) { + return createNestedNameSpecifier( + Ctx, TDD, FullyQualify, WithGlobalNsPrefix); + } else if (WithGlobalNsPrefix && DC->isTranslationUnit()) { + return NestedNameSpecifier::GlobalSpecifier(Ctx); + } + return nullptr; // no starting '::' if |WithGlobalNsPrefix| is false +} + +/// \brief Return a fully qualified version of this name specifier. +static NestedNameSpecifier *getFullyQualifiedNestedNameSpecifier( + const ASTContext &Ctx, NestedNameSpecifier *Scope, + bool WithGlobalNsPrefix) { + switch (Scope->getKind()) { + case NestedNameSpecifier::Global: + // Already fully qualified + return Scope; + case NestedNameSpecifier::Namespace: + return TypeName::createNestedNameSpecifier( + Ctx, Scope->getAsNamespace(), WithGlobalNsPrefix); + case NestedNameSpecifier::NamespaceAlias: + // Namespace aliases are only valid for the duration of the + // scope where they were introduced, and therefore are often + // invalid at the end of the TU. So use the namespace name more + // likely to be valid at the end of the TU. + return TypeName::createNestedNameSpecifier( + Ctx, + Scope->getAsNamespaceAlias()->getNamespace()->getCanonicalDecl(), + WithGlobalNsPrefix); + case NestedNameSpecifier::Identifier: + // A function or some other construct that makes it un-namable + // at the end of the TU. Skip the current component of the name, + // but use the name of it's prefix. + return getFullyQualifiedNestedNameSpecifier( + Ctx, Scope->getPrefix(), WithGlobalNsPrefix); + case NestedNameSpecifier::Super: + case NestedNameSpecifier::TypeSpec: + case NestedNameSpecifier::TypeSpecWithTemplate: { + const Type *Type = Scope->getAsType(); + // Find decl context. + const TagDecl *TD = nullptr; + if (const TagType *TagDeclType = Type->getAs<TagType>()) { + TD = TagDeclType->getDecl(); + } else { + TD = Type->getAsCXXRecordDecl(); + } + if (TD) { + return TypeName::createNestedNameSpecifier(Ctx, TD, + true /*FullyQualified*/, + WithGlobalNsPrefix); + } else if (const auto *TDD = dyn_cast<TypedefType>(Type)) { + return TypeName::createNestedNameSpecifier(Ctx, TDD->getDecl(), + true /*FullyQualified*/, + WithGlobalNsPrefix); + } + return Scope; + } + } + llvm_unreachable("bad NNS kind"); +} + +/// \brief Create a nested name specifier for the declaring context of +/// the type. +static NestedNameSpecifier *createNestedNameSpecifierForScopeOf( + const ASTContext &Ctx, const Decl *Decl, + bool FullyQualified, bool WithGlobalNsPrefix) { + assert(Decl); + + const DeclContext *DC = Decl->getDeclContext()->getRedeclContext(); + const auto *Outer = dyn_cast_or_null<NamedDecl>(DC); + const auto *OuterNS = dyn_cast_or_null<NamespaceDecl>(DC); + if (Outer && !(OuterNS && OuterNS->isAnonymousNamespace())) { + if (const auto *CxxDecl = dyn_cast<CXXRecordDecl>(DC)) { + if (ClassTemplateDecl *ClassTempl = + CxxDecl->getDescribedClassTemplate()) { + // We are in the case of a type(def) that was declared in a + // class template but is *not* type dependent. In clang, it + // gets attached to the class template declaration rather than + // any specific class template instantiation. This result in + // 'odd' fully qualified typename: + // + // vector<_Tp,_Alloc>::size_type + // + // Make the situation is 'useable' but looking a bit odd by + // picking a random instance as the declaring context. + if (ClassTempl->spec_begin() != ClassTempl->spec_end()) { + Decl = *(ClassTempl->spec_begin()); + Outer = dyn_cast<NamedDecl>(Decl); + OuterNS = dyn_cast<NamespaceDecl>(Decl); + } + } + } + + if (OuterNS) { + return createNestedNameSpecifier(Ctx, OuterNS, WithGlobalNsPrefix); + } else if (const auto *TD = dyn_cast<TagDecl>(Outer)) { + return createNestedNameSpecifier( + Ctx, TD, FullyQualified, WithGlobalNsPrefix); + } else if (dyn_cast<TranslationUnitDecl>(Outer)) { + // Context is the TU. Nothing needs to be done. + return nullptr; + } else { + // Decl's context was neither the TU, a namespace, nor a + // TagDecl, which means it is a type local to a scope, and not + // accessible at the end of the TU. + return nullptr; + } + } else if (WithGlobalNsPrefix && DC->isTranslationUnit()) { + return NestedNameSpecifier::GlobalSpecifier(Ctx); + } + return nullptr; +} + +/// \brief Create a nested name specifier for the declaring context of +/// the type. +static NestedNameSpecifier *createNestedNameSpecifierForScopeOf( + const ASTContext &Ctx, const Type *TypePtr, + bool FullyQualified, bool WithGlobalNsPrefix) { + if (!TypePtr) return nullptr; + + Decl *Decl = nullptr; + // There are probably other cases ... + if (const auto *TDT = dyn_cast<TypedefType>(TypePtr)) { + Decl = TDT->getDecl(); + } else if (const auto *TagDeclType = dyn_cast<TagType>(TypePtr)) { + Decl = TagDeclType->getDecl(); + } else if (const auto *TST = dyn_cast<TemplateSpecializationType>(TypePtr)) { + Decl = TST->getTemplateName().getAsTemplateDecl(); + } else { + Decl = TypePtr->getAsCXXRecordDecl(); + } + + if (!Decl) return nullptr; + + return createNestedNameSpecifierForScopeOf( + Ctx, Decl, FullyQualified, WithGlobalNsPrefix); +} + +NestedNameSpecifier *createNestedNameSpecifier(const ASTContext &Ctx, + const NamespaceDecl *Namespace, + bool WithGlobalNsPrefix) { + while (Namespace && Namespace->isInline()) { + // Ignore inline namespace; + Namespace = dyn_cast<NamespaceDecl>(Namespace->getDeclContext()); + } + if (!Namespace) return nullptr; + + bool FullyQualified = true; // doesn't matter, DeclContexts are namespaces + return NestedNameSpecifier::Create( + Ctx, + createOuterNNS(Ctx, Namespace, FullyQualified, WithGlobalNsPrefix), + Namespace); +} + +NestedNameSpecifier *createNestedNameSpecifier(const ASTContext &Ctx, + const TypeDecl *TD, + bool FullyQualify, + bool WithGlobalNsPrefix) { + return NestedNameSpecifier::Create( + Ctx, + createOuterNNS(Ctx, TD, FullyQualify, WithGlobalNsPrefix), + false /*No TemplateKeyword*/, + TD->getTypeForDecl()); +} + +/// \brief Return the fully qualified type, including fully-qualified +/// versions of any template parameters. +QualType getFullyQualifiedType(QualType QT, const ASTContext &Ctx, + bool WithGlobalNsPrefix) { + // In case of myType* we need to strip the pointer first, fully + // qualify and attach the pointer once again. + if (isa<PointerType>(QT.getTypePtr())) { + // Get the qualifiers. + Qualifiers Quals = QT.getQualifiers(); + QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix); + QT = Ctx.getPointerType(QT); + // Add back the qualifiers. + QT = Ctx.getQualifiedType(QT, Quals); + return QT; + } + + // In case of myType& we need to strip the reference first, fully + // qualify and attach the reference once again. + if (isa<ReferenceType>(QT.getTypePtr())) { + // Get the qualifiers. + bool IsLValueRefTy = isa<LValueReferenceType>(QT.getTypePtr()); + Qualifiers Quals = QT.getQualifiers(); + QT = getFullyQualifiedType(QT->getPointeeType(), Ctx, WithGlobalNsPrefix); + // Add the r- or l-value reference type back to the fully + // qualified one. + if (IsLValueRefTy) + QT = Ctx.getLValueReferenceType(QT); + else + QT = Ctx.getRValueReferenceType(QT); + // Add back the qualifiers. + QT = Ctx.getQualifiedType(QT, Quals); + return QT; + } + + // Remove the part of the type related to the type being a template + // parameter (we won't report it as part of the 'type name' and it + // is actually make the code below to be more complex (to handle + // those) + while (isa<SubstTemplateTypeParmType>(QT.getTypePtr())) { + // Get the qualifiers. + Qualifiers Quals = QT.getQualifiers(); + + QT = dyn_cast<SubstTemplateTypeParmType>(QT.getTypePtr())->desugar(); + + // Add back the qualifiers. + QT = Ctx.getQualifiedType(QT, Quals); + } + + NestedNameSpecifier *Prefix = nullptr; + // Local qualifiers are attached to the QualType outside of the + // elaborated type. Retrieve them before descending into the + // elaborated type. + Qualifiers PrefixQualifiers = QT.getLocalQualifiers(); + QT = QualType(QT.getTypePtr(), 0); + ElaboratedTypeKeyword Keyword = ETK_None; + if (const auto *ETypeInput = dyn_cast<ElaboratedType>(QT.getTypePtr())) { + QT = ETypeInput->getNamedType(); + assert(!QT.hasLocalQualifiers()); + Keyword = ETypeInput->getKeyword(); + } + // Create a nested name specifier if needed. + Prefix = createNestedNameSpecifierForScopeOf(Ctx, QT.getTypePtr(), + true /*FullyQualified*/, + WithGlobalNsPrefix); + + // In case of template specializations iterate over the arguments and + // fully qualify them as well. + if (isa<const TemplateSpecializationType>(QT.getTypePtr()) || + isa<const RecordType>(QT.getTypePtr())) { + // We are asked to fully qualify and we have a Record Type (which + // may point to a template specialization) or Template + // Specialization Type. We need to fully qualify their arguments. + + const Type *TypePtr = getFullyQualifiedTemplateType( + Ctx, QT.getTypePtr(), WithGlobalNsPrefix); + QT = QualType(TypePtr, 0); + } + if (Prefix || Keyword != ETK_None) { + QT = Ctx.getElaboratedType(Keyword, Prefix, QT); + } + QT = Ctx.getQualifiedType(QT, PrefixQualifiers); + return QT; +} + +std::string getFullyQualifiedName(QualType QT, + const ASTContext &Ctx, + bool WithGlobalNsPrefix) { + PrintingPolicy Policy(Ctx.getPrintingPolicy()); + Policy.SuppressScope = false; + Policy.AnonymousTagLocations = false; + Policy.PolishForDeclaration = true; + Policy.SuppressUnwrittenScope = true; + QualType FQQT = getFullyQualifiedType(QT, Ctx, WithGlobalNsPrefix); + return FQQT.getAsString(Policy); +} + +} // end namespace TypeName +} // end namespace clang diff --git a/lib/Tooling/Core/Replacement.cpp b/lib/Tooling/Core/Replacement.cpp index 47bbdeb470ee..4f130709ac16 100644 --- a/lib/Tooling/Core/Replacement.cpp +++ b/lib/Tooling/Core/Replacement.cpp @@ -11,6 +11,8 @@ // //===----------------------------------------------------------------------===// +#include "clang/Tooling/Core/Replacement.h" + #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticIDs.h" #include "clang/Basic/DiagnosticOptions.h" @@ -18,7 +20,6 @@ #include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" #include "clang/Rewrite/Core/Rewriter.h" -#include "clang/Tooling/Core/Replacement.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_os_ostream.h" @@ -57,14 +58,8 @@ bool Replacement::apply(Rewriter &Rewrite) const { const FileEntry *Entry = SM.getFileManager().getFile(FilePath); if (!Entry) return false; - FileID ID; - // FIXME: Use SM.translateFile directly. - SourceLocation Location = SM.translateFileLineCol(Entry, 1, 1); - ID = Location.isValid() ? - SM.getFileID(Location) : - SM.createFileID(Entry, SourceLocation(), SrcMgr::C_User); - // FIXME: We cannot check whether Offset + Length is in the file, as - // the remapping API is not public in the RewriteBuffer. + + FileID ID = SM.getOrCreateFileID(Entry, SrcMgr::C_User); const SourceLocation Start = SM.getLocForStartOfFile(ID). getLocWithOffset(ReplacementRange.getOffset()); @@ -254,7 +249,11 @@ bool applyAllReplacements(const std::vector<Replacement> &Replaces, return Result; } -std::string applyAllReplacements(StringRef Code, const Replacements &Replaces) { +llvm::Expected<std::string> applyAllReplacements(StringRef Code, + const Replacements &Replaces) { + if (Replaces.empty()) + return Code.str(); + IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem( new vfs::InMemoryFileSystem); FileManager Files(FileSystemOptions(), InMemoryFileSystem); @@ -272,7 +271,9 @@ std::string applyAllReplacements(StringRef Code, const Replacements &Replaces) { Replacement Replace("<stdin>", I->getOffset(), I->getLength(), I->getReplacementText()); if (!Replace.apply(Rewrite)) - return ""; + return llvm::make_error<llvm::StringError>( + "Failed to apply replacement: " + Replace.toString(), + llvm::inconvertibleErrorCode()); } std::string Result; llvm::raw_string_ostream OS(Result); @@ -281,6 +282,55 @@ std::string applyAllReplacements(StringRef Code, const Replacements &Replaces) { return Result; } +// Merge and sort overlapping ranges in \p Ranges. +static std::vector<Range> mergeAndSortRanges(std::vector<Range> Ranges) { + std::sort(Ranges.begin(), Ranges.end(), + [](const Range &LHS, const Range &RHS) { + if (LHS.getOffset() != RHS.getOffset()) + return LHS.getOffset() < RHS.getOffset(); + return LHS.getLength() < RHS.getLength(); + }); + std::vector<Range> Result; + for (const auto &R : Ranges) { + if (Result.empty() || + Result.back().getOffset() + Result.back().getLength() < R.getOffset()) { + Result.push_back(R); + } else { + unsigned NewEnd = + std::max(Result.back().getOffset() + Result.back().getLength(), + R.getOffset() + R.getLength()); + Result[Result.size() - 1] = + Range(Result.back().getOffset(), NewEnd - Result.back().getOffset()); + } + } + return Result; +} + +std::vector<Range> calculateChangedRanges(const Replacements &Replaces) { + std::vector<Range> ChangedRanges; + int Shift = 0; + for (const Replacement &R : Replaces) { + unsigned Offset = R.getOffset() + Shift; + unsigned Length = R.getReplacementText().size(); + Shift += Length - R.getLength(); + ChangedRanges.push_back(Range(Offset, Length)); + } + return mergeAndSortRanges(ChangedRanges); +} + +std::vector<Range> +calculateRangesAfterReplacements(const Replacements &Replaces, + const std::vector<Range> &Ranges) { + auto MergedRanges = mergeAndSortRanges(Ranges); + tooling::Replacements FakeReplaces; + for (const auto &R : MergedRanges) + FakeReplaces.insert(Replacement(Replaces.begin()->getFilePath(), + R.getOffset(), R.getLength(), + std::string(R.getLength(), ' '))); + tooling::Replacements NewReplaces = mergeReplacements(FakeReplaces, Replaces); + return calculateChangedRanges(NewReplaces); +} + namespace { // Represents a merged replacement, i.e. a replacement consisting of multiple // overlapping replacements from 'First' and 'Second' in mergeReplacements. @@ -314,7 +364,7 @@ public: // Merges the next element 'R' into this merged element. As we always merge // from 'First' into 'Second' or vice versa, the MergedReplacement knows what - // set the next element is coming from. + // set the next element is coming from. void merge(const Replacement &R) { if (MergeSecond) { unsigned REnd = R.getOffset() + Delta + R.getLength(); @@ -377,6 +427,15 @@ private: }; } // namespace +std::map<std::string, Replacements> +groupReplacementsByFile(const Replacements &Replaces) { + std::map<std::string, Replacements> FileToReplaces; + for (const auto &Replace : Replaces) { + FileToReplaces[Replace.getFilePath()].insert(Replace); + } + return FileToReplaces; +} + Replacements mergeReplacements(const Replacements &First, const Replacements &Second) { if (First.empty() || Second.empty()) @@ -416,4 +475,3 @@ Replacements mergeReplacements(const Replacements &First, } // end namespace tooling } // end namespace clang - diff --git a/lib/Tooling/FixIt.cpp b/lib/Tooling/FixIt.cpp new file mode 100644 index 000000000000..70942c5ac845 --- /dev/null +++ b/lib/Tooling/FixIt.cpp @@ -0,0 +1,31 @@ +//===--- FixIt.cpp - FixIt Hint utilities -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains implementations of utitilies to ease source code rewriting +// by providing helper functions related to FixItHint. +// +//===----------------------------------------------------------------------===// +#include "clang/Tooling/FixIt.h" +#include "clang/Lex/Lexer.h" + +namespace clang { +namespace tooling { +namespace fixit { + +namespace internal { +StringRef getText(SourceRange Range, const ASTContext &Context) { + return Lexer::getSourceText(CharSourceRange::getTokenRange(Range), + Context.getSourceManager(), + Context.getLangOpts()); +} +} // end namespace internal + +} // end namespace fixit +} // end namespace tooling +} // end namespace clang diff --git a/lib/Tooling/Makefile b/lib/Tooling/Makefile deleted file mode 100644 index 7ea85a8908d6..000000000000 --- a/lib/Tooling/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -##===- clang/lib/Tooling/Makefile ---------------------------*- Makefile -*-===## -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## - -CLANG_LEVEL := ../.. -LIBRARYNAME := clangTooling -PARALLEL_DIRS := Core - -include $(CLANG_LEVEL)/Makefile diff --git a/lib/Tooling/Refactoring.cpp b/lib/Tooling/Refactoring.cpp index d32452f6f293..28d535aeb45f 100644 --- a/lib/Tooling/Refactoring.cpp +++ b/lib/Tooling/Refactoring.cpp @@ -14,6 +14,7 @@ #include "clang/Basic/DiagnosticOptions.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" +#include "clang/Format/Format.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Lex/Lexer.h" #include "clang/Rewrite/Core/Rewriter.h" @@ -61,5 +62,33 @@ int RefactoringTool::saveRewrittenFiles(Rewriter &Rewrite) { return Rewrite.overwriteChangedFiles() ? 1 : 0; } +bool formatAndApplyAllReplacements(const Replacements &Replaces, + Rewriter &Rewrite, StringRef Style) { + SourceManager &SM = Rewrite.getSourceMgr(); + FileManager &Files = SM.getFileManager(); + + auto FileToReplaces = groupReplacementsByFile(Replaces); + + bool Result = true; + for (const auto &FileAndReplaces : FileToReplaces) { + const std::string &FilePath = FileAndReplaces.first; + auto &CurReplaces = FileAndReplaces.second; + + const FileEntry *Entry = Files.getFile(FilePath); + FileID ID = SM.getOrCreateFileID(Entry, SrcMgr::C_User); + StringRef Code = SM.getBufferData(ID); + + format::FormatStyle CurStyle = format::getStyle(Style, FilePath, "LLVM"); + auto NewReplacements = + format::formatReplacements(Code, CurReplaces, CurStyle); + if (!NewReplacements) { + llvm::errs() << llvm::toString(NewReplacements.takeError()) << "\n"; + return false; + } + Result = applyAllReplacements(*NewReplacements, Rewrite) && Result; + } + return Result; +} + } // end namespace tooling } // end namespace clang diff --git a/lib/Tooling/Tooling.cpp b/lib/Tooling/Tooling.cpp index fd5596ec2ded..4c7fed1e617c 100644 --- a/lib/Tooling/Tooling.cpp +++ b/lib/Tooling/Tooling.cpp @@ -31,6 +31,7 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" #include "llvm/Support/raw_ostream.h" +#include <utility> #define DEBUG_TYPE "clang-tooling" @@ -49,8 +50,9 @@ FrontendActionFactory::~FrontendActionFactory() {} static clang::driver::Driver *newDriver( clang::DiagnosticsEngine *Diagnostics, const char *BinaryName, IntrusiveRefCntPtr<vfs::FileSystem> VFS) { - clang::driver::Driver *CompilerDriver = new clang::driver::Driver( - BinaryName, llvm::sys::getDefaultTargetTriple(), *Diagnostics, VFS); + clang::driver::Driver *CompilerDriver = + new clang::driver::Driver(BinaryName, llvm::sys::getDefaultTargetTriple(), + *Diagnostics, std::move(VFS)); CompilerDriver->setTitle("clang_based_tool"); return CompilerDriver; } @@ -103,14 +105,16 @@ bool runToolOnCode(clang::FrontendAction *ToolAction, const Twine &Code, const Twine &FileName, std::shared_ptr<PCHContainerOperations> PCHContainerOps) { return runToolOnCodeWithArgs(ToolAction, Code, std::vector<std::string>(), - FileName, PCHContainerOps); + FileName, "clang-tool", + std::move(PCHContainerOps)); } static std::vector<std::string> -getSyntaxOnlyToolArgs(const std::vector<std::string> &ExtraArgs, +getSyntaxOnlyToolArgs(const Twine &ToolName, + const std::vector<std::string> &ExtraArgs, StringRef FileName) { std::vector<std::string> Args; - Args.push_back("clang-tool"); + Args.push_back(ToolName.str()); Args.push_back("-fsyntax-only"); Args.insert(Args.end(), ExtraArgs.begin(), ExtraArgs.end()); Args.push_back(FileName.str()); @@ -120,6 +124,7 @@ getSyntaxOnlyToolArgs(const std::vector<std::string> &ExtraArgs, bool runToolOnCodeWithArgs( clang::FrontendAction *ToolAction, const Twine &Code, const std::vector<std::string> &Args, const Twine &FileName, + const Twine &ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps, const FileContentMappings &VirtualMappedFiles) { @@ -132,8 +137,9 @@ bool runToolOnCodeWithArgs( OverlayFileSystem->pushOverlay(InMemoryFileSystem); llvm::IntrusiveRefCntPtr<FileManager> Files( new FileManager(FileSystemOptions(), OverlayFileSystem)); - ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef), - ToolAction, Files.get(), PCHContainerOps); + ToolInvocation Invocation(getSyntaxOnlyToolArgs(ToolName, Args, FileNameRef), + ToolAction, Files.get(), + std::move(PCHContainerOps)); SmallString<1024> CodeStorage; InMemoryFileSystem->addFile(FileNameRef, 0, @@ -206,14 +212,16 @@ ToolInvocation::ToolInvocation( std::vector<std::string> CommandLine, ToolAction *Action, FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps) : CommandLine(std::move(CommandLine)), Action(Action), OwnsAction(false), - Files(Files), PCHContainerOps(PCHContainerOps), DiagConsumer(nullptr) {} + Files(Files), PCHContainerOps(std::move(PCHContainerOps)), + DiagConsumer(nullptr) {} ToolInvocation::ToolInvocation( std::vector<std::string> CommandLine, FrontendAction *FAction, FileManager *Files, std::shared_ptr<PCHContainerOperations> PCHContainerOps) : CommandLine(std::move(CommandLine)), Action(new SingleFrontendActionFactory(FAction)), OwnsAction(true), - Files(Files), PCHContainerOps(PCHContainerOps), DiagConsumer(nullptr) {} + Files(Files), PCHContainerOps(std::move(PCHContainerOps)), + DiagConsumer(nullptr) {} ToolInvocation::~ToolInvocation() { if (OwnsAction) @@ -260,7 +268,7 @@ bool ToolInvocation::run() { Input.release()); } return runInvocation(BinaryName, Compilation.get(), Invocation.release(), - PCHContainerOps); + std::move(PCHContainerOps)); } bool ToolInvocation::runInvocation( @@ -274,7 +282,7 @@ bool ToolInvocation::runInvocation( llvm::errs() << "\n"; } - return Action->runInvocation(Invocation, Files, PCHContainerOps, + return Action->runInvocation(Invocation, Files, std::move(PCHContainerOps), DiagConsumer); } @@ -283,7 +291,7 @@ bool FrontendActionFactory::runInvocation( std::shared_ptr<PCHContainerOperations> PCHContainerOps, DiagnosticConsumer *DiagConsumer) { // Create a compiler instance to handle the actual work. - clang::CompilerInstance Compiler(PCHContainerOps); + clang::CompilerInstance Compiler(std::move(PCHContainerOps)); Compiler.setInvocation(Invocation); Compiler.setFileManager(Files); @@ -309,7 +317,7 @@ ClangTool::ClangTool(const CompilationDatabase &Compilations, ArrayRef<std::string> SourcePaths, std::shared_ptr<PCHContainerOperations> PCHContainerOps) : Compilations(Compilations), SourcePaths(SourcePaths), - PCHContainerOps(PCHContainerOps), + PCHContainerOps(std::move(PCHContainerOps)), OverlayFileSystem(new vfs::OverlayFileSystem(vfs::getRealFileSystem())), InMemoryFileSystem(new vfs::InMemoryFileSystem), Files(new FileManager(FileSystemOptions(), OverlayFileSystem)), @@ -327,26 +335,32 @@ void ClangTool::mapVirtualFile(StringRef FilePath, StringRef Content) { void ClangTool::appendArgumentsAdjuster(ArgumentsAdjuster Adjuster) { if (ArgsAdjuster) - ArgsAdjuster = combineAdjusters(ArgsAdjuster, Adjuster); + ArgsAdjuster = + combineAdjusters(std::move(ArgsAdjuster), std::move(Adjuster)); else - ArgsAdjuster = Adjuster; + ArgsAdjuster = std::move(Adjuster); } void ClangTool::clearArgumentsAdjusters() { ArgsAdjuster = nullptr; } +static void injectResourceDir(CommandLineArguments &Args, const char *Argv0, + void *MainAddr) { + // Allow users to override the resource dir. + for (StringRef Arg : Args) + if (Arg.startswith("-resource-dir")) + return; + + // If there's no override in place add our resource dir. + Args.push_back("-resource-dir=" + + CompilerInvocation::GetResourcesPath(Argv0, MainAddr)); +} + int ClangTool::run(ToolAction *Action) { // Exists solely for the purpose of lookup of the resource path. // This just needs to be some symbol in the binary. static int StaticSymbol; - // The driver detects the builtin header path based on the path of the - // executable. - // FIXME: On linux, GetMainExecutable is independent of the value of the - // first argument, thus allowing ClangTool and runToolOnCode to just - // pass in made-up names here. Make sure this works on other platforms. - std::string MainExecutable = - llvm::sys::fs::getMainExecutable("clang_tool", &StaticSymbol); llvm::SmallString<128> InitialDirectory; if (std::error_code EC = llvm::sys::fs::current_path(InitialDirectory)) @@ -411,7 +425,17 @@ int ClangTool::run(ToolAction *Action) { if (ArgsAdjuster) CommandLine = ArgsAdjuster(CommandLine, CompileCommand.Filename); assert(!CommandLine.empty()); - CommandLine[0] = MainExecutable; + + // Add the resource dir based on the binary of this tool. argv[0] in the + // compilation database may refer to a different compiler and we want to + // pick up the very same standard library that compiler is using. The + // builtin headers in the resource dir need to match the exact clang + // version the tool is using. + // FIXME: On linux, GetMainExecutable is independent of the value of the + // first argument, thus allowing ClangTool and runToolOnCode to just + // pass in made-up names here. Make sure this works on other platforms. + injectResourceDir(CommandLine, "clang_tool", &StaticSymbol); + // FIXME: We need a callback mechanism for the tool writer to output a // customized message for each file. DEBUG({ llvm::dbgs() << "Processing: " << File << ".\n"; }); @@ -446,7 +470,7 @@ public: std::shared_ptr<PCHContainerOperations> PCHContainerOps, DiagnosticConsumer *DiagConsumer) override { std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromCompilerInvocation( - Invocation, PCHContainerOps, + Invocation, std::move(PCHContainerOps), CompilerInstance::createDiagnostics(&Invocation->getDiagnosticOpts(), DiagConsumer, /*ShouldOwnClient=*/false), @@ -458,7 +482,6 @@ public: return true; } }; - } int ClangTool::buildASTs(std::vector<std::unique_ptr<ASTUnit>> &ASTs) { @@ -470,12 +493,12 @@ std::unique_ptr<ASTUnit> buildASTFromCode(const Twine &Code, const Twine &FileName, std::shared_ptr<PCHContainerOperations> PCHContainerOps) { return buildASTFromCodeWithArgs(Code, std::vector<std::string>(), FileName, - PCHContainerOps); + "clang-tool", std::move(PCHContainerOps)); } std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs( const Twine &Code, const std::vector<std::string> &Args, - const Twine &FileName, + const Twine &FileName, const Twine &ToolName, std::shared_ptr<PCHContainerOperations> PCHContainerOps) { SmallString<16> FileNameStorage; StringRef FileNameRef = FileName.toNullTerminatedStringRef(FileNameStorage); @@ -489,8 +512,8 @@ std::unique_ptr<ASTUnit> buildASTFromCodeWithArgs( OverlayFileSystem->pushOverlay(InMemoryFileSystem); llvm::IntrusiveRefCntPtr<FileManager> Files( new FileManager(FileSystemOptions(), OverlayFileSystem)); - ToolInvocation Invocation(getSyntaxOnlyToolArgs(Args, FileNameRef), &Action, - Files.get(), PCHContainerOps); + ToolInvocation Invocation(getSyntaxOnlyToolArgs(ToolName, Args, FileNameRef), + &Action, Files.get(), std::move(PCHContainerOps)); SmallString<1024> CodeStorage; InMemoryFileSystem->addFile(FileNameRef, 0, |
