diff options
Diffstat (limited to 'lib/Tooling')
-rw-r--r-- | lib/Tooling/Core/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/Tooling/Core/QualTypeNames.cpp | 477 | ||||
-rw-r--r-- | lib/Tooling/Execution.cpp | 24 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/ASTSelection.cpp | 23 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/CMakeLists.txt | 3 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/Extract/Extract.cpp (renamed from lib/Tooling/Refactoring/Extract.cpp) | 10 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/Extract/SourceExtraction.cpp | 112 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/Extract/SourceExtraction.h | 52 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/RefactoringActions.cpp | 20 | ||||
-rw-r--r-- | lib/Tooling/Refactoring/Rename/RenamingAction.cpp | 83 | ||||
-rw-r--r-- | lib/Tooling/StandaloneExecution.cpp | 8 |
11 files changed, 304 insertions, 509 deletions
diff --git a/lib/Tooling/Core/CMakeLists.txt b/lib/Tooling/Core/CMakeLists.txt index e2b0dd424d..b302479358 100644 --- a/lib/Tooling/Core/CMakeLists.txt +++ b/lib/Tooling/Core/CMakeLists.txt @@ -3,7 +3,6 @@ set(LLVM_LINK_COMPONENTS support) add_clang_library(clangToolingCore Lookup.cpp Replacement.cpp - QualTypeNames.cpp Diagnostic.cpp LINK_LIBS diff --git a/lib/Tooling/Core/QualTypeNames.cpp b/lib/Tooling/Core/QualTypeNames.cpp deleted file mode 100644 index 721c2c92fc..0000000000 --- a/lib/Tooling/Core/QualTypeNames.cpp +++ /dev/null @@ -1,477 +0,0 @@ -//===------- 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 <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/Execution.cpp b/lib/Tooling/Execution.cpp index 1a078ef7e1..498d683f89 100644 --- a/lib/Tooling/Execution.cpp +++ b/lib/Tooling/Execution.cpp @@ -54,13 +54,14 @@ llvm::Error ToolExecutor::execute(std::unique_ptr<FrontendActionFactory> Action, return execute(Actions); } +namespace internal { llvm::Expected<std::unique_ptr<ToolExecutor>> -createExecutorFromCommandLineArgs(int &argc, const char **argv, - llvm::cl::OptionCategory &Category, - const char *Overview) { +createExecutorFromCommandLineArgsImpl(int &argc, const char **argv, + llvm::cl::OptionCategory &Category, + const char *Overview) { auto OptionsParser = CommonOptionsParser::create(argc, argv, Category, llvm::cl::ZeroOrMore, - /*Overview=*/nullptr); + /*Overview=*/Overview); if (!OptionsParser) return OptionsParser.takeError(); for (auto I = ToolExecutorPluginRegistry::begin(), @@ -84,6 +85,21 @@ createExecutorFromCommandLineArgs(int &argc, const char **argv, llvm::Twine("Executor \"") + ExecutorName + "\" is not registered.", llvm::inconvertibleErrorCode()); } +} // end namespace internal + +llvm::Expected<std::unique_ptr<ToolExecutor>> +createExecutorFromCommandLineArgs(int &argc, const char **argv, + llvm::cl::OptionCategory &Category, + const char *Overview) { + return internal::createExecutorFromCommandLineArgsImpl(argc, argv, Category, + Overview); +} + +// This anchor is used to force the linker to link in the generated object file +// and thus register the StandaloneToolExecutorPlugin. +extern volatile int StandaloneToolExecutorAnchorSource; +static int LLVM_ATTRIBUTE_UNUSED StandaloneToolExecutorAnchorDest = + StandaloneToolExecutorAnchorSource; } // end namespace tooling } // end namespace clang diff --git a/lib/Tooling/Refactoring/ASTSelection.cpp b/lib/Tooling/Refactoring/ASTSelection.cpp index 71a0d44be1..ab2be15512 100644 --- a/lib/Tooling/Refactoring/ASTSelection.cpp +++ b/lib/Tooling/Refactoring/ASTSelection.cpp @@ -249,9 +249,30 @@ struct SelectedNodeWithParents { SelectedNodeWithParents &operator=(SelectedNodeWithParents &&) = default; SelectedASTNode::ReferenceType Node; llvm::SmallVector<SelectedASTNode::ReferenceType, 8> Parents; + + /// Canonicalizes the given selection by selecting different related AST nodes + /// when it makes sense to do so. + void canonicalize(); }; } // end anonymous namespace +void SelectedNodeWithParents::canonicalize() { + const Stmt *S = Node.get().Node.get<Stmt>(); + assert(S && "non statement selection!"); + const Stmt *Parent = Parents[Parents.size() - 1].get().Node.get<Stmt>(); + if (!Parent) + return; + // Select the parent expression when: + // - The string literal in ObjC string literal is selected, e.g.: + // @"test" becomes @"test" + // ~~~~~~ ~~~~~~~ + if (isa<StringLiteral>(S) && isa<ObjCStringLiteral>(Parent)) + Node = Parents.pop_back_val(); + // FIXME: Syntactic form -> Entire pseudo-object expr. + // FIXME: Callee -> Call. + // FIXME: Callee member expr -> Call. +} + /// Finds the set of bottom-most selected AST nodes that are in the selection /// tree with the specified selection kind. /// @@ -330,7 +351,7 @@ CodeRangeASTSelection::create(SourceRange SelectionRange, return None; const Stmt *CodeRangeStmt = Selected.Node.get().Node.get<Stmt>(); if (!isa<CompoundStmt>(CodeRangeStmt)) { - // FIXME (Alex L): Canonicalize. + Selected.canonicalize(); return CodeRangeASTSelection(Selected.Node, Selected.Parents, /*AreChildrenSelected=*/false); } diff --git a/lib/Tooling/Refactoring/CMakeLists.txt b/lib/Tooling/Refactoring/CMakeLists.txt index 5d3ddd45b6..402b5d3c6a 100644 --- a/lib/Tooling/Refactoring/CMakeLists.txt +++ b/lib/Tooling/Refactoring/CMakeLists.txt @@ -4,7 +4,8 @@ add_clang_library(clangToolingRefactor ASTSelection.cpp ASTSelectionRequirements.cpp AtomicChange.cpp - Extract.cpp + Extract/Extract.cpp + Extract/SourceExtraction.cpp RefactoringActions.cpp Rename/RenamingAction.cpp Rename/SymbolOccurrences.cpp diff --git a/lib/Tooling/Refactoring/Extract.cpp b/lib/Tooling/Refactoring/Extract/Extract.cpp index 3f5a839318..b0847a7400 100644 --- a/lib/Tooling/Refactoring/Extract.cpp +++ b/lib/Tooling/Refactoring/Extract/Extract.cpp @@ -14,6 +14,7 @@ //===----------------------------------------------------------------------===// #include "clang/Tooling/Refactoring/Extract/Extract.h" +#include "SourceExtraction.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" @@ -145,6 +146,8 @@ ExtractFunction::createSourceReplacements(RefactoringRuleContext &Context) { PP.SuppressLifetimeQualifiers = true; PP.SuppressUnwrittenScope = true; + ExtractionSemicolonPolicy Semicolons = ExtractionSemicolonPolicy::compute( + Code[Code.size() - 1], ExtractedRange, SM, LangOpts); AtomicChange Change(SM, ExtractedDeclLocation); // Create the replacement for the extracted declaration. { @@ -162,8 +165,8 @@ ExtractFunction::createSourceReplacements(RefactoringRuleContext &Context) { if (IsExpr && !ReturnType->isVoidType()) OS << "return "; OS << ExtractedCodeRewriter.getRewrittenText(ExtractedRange); - // FIXME: Compute the correct semicolon policy. - OS << ';'; + if (Semicolons.isNeededInExtractedFunction()) + OS << ';'; OS << "\n}\n\n"; auto Err = Change.insert(SM, ExtractedDeclLocation, OS.str()); if (Err) @@ -178,7 +181,8 @@ ExtractFunction::createSourceReplacements(RefactoringRuleContext &Context) { OS << DeclName << '('; // FIXME: Forward arguments. OS << ')'; - // FIXME: Add semicolon if needed. + if (Semicolons.isNeededInOriginalFunction()) + OS << ';'; auto Err = Change.replace( SM, CharSourceRange::getTokenRange(ExtractedRange), OS.str()); diff --git a/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp b/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp new file mode 100644 index 0000000000..7fd8cc2d3c --- /dev/null +++ b/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp @@ -0,0 +1,112 @@ +//===--- SourceExtraction.cpp - Clang refactoring library -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SourceExtraction.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/StmtCXX.h" +#include "clang/AST/StmtObjC.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" + +using namespace clang; + +namespace { + +/// Returns true if the token at the given location is a semicolon. +bool isSemicolonAtLocation(SourceLocation TokenLoc, const SourceManager &SM, + const LangOptions &LangOpts) { + return Lexer::getSourceText( + CharSourceRange::getTokenRange(TokenLoc, TokenLoc), SM, + LangOpts) == ";"; +} + +/// Returns true if there should be a semicolon after the given statement. +bool isSemicolonRequiredAfter(const Stmt *S) { + if (isa<CompoundStmt>(S)) + return false; + if (const auto *If = dyn_cast<IfStmt>(S)) + return isSemicolonRequiredAfter(If->getElse() ? If->getElse() + : If->getThen()); + if (const auto *While = dyn_cast<WhileStmt>(S)) + return isSemicolonRequiredAfter(While->getBody()); + if (const auto *For = dyn_cast<ForStmt>(S)) + return isSemicolonRequiredAfter(For->getBody()); + if (const auto *CXXFor = dyn_cast<CXXForRangeStmt>(S)) + return isSemicolonRequiredAfter(CXXFor->getBody()); + if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(S)) + return isSemicolonRequiredAfter(ObjCFor->getBody()); + switch (S->getStmtClass()) { + case Stmt::SwitchStmtClass: + case Stmt::CXXTryStmtClass: + case Stmt::ObjCAtSynchronizedStmtClass: + case Stmt::ObjCAutoreleasePoolStmtClass: + case Stmt::ObjCAtTryStmtClass: + return false; + default: + return true; + } +} + +/// Returns true if the two source locations are on the same line. +bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2, + const SourceManager &SM) { + return !Loc1.isMacroID() && !Loc2.isMacroID() && + SM.getSpellingLineNumber(Loc1) == SM.getSpellingLineNumber(Loc2); +} + +} // end anonymous namespace + +namespace clang { +namespace tooling { + +ExtractionSemicolonPolicy +ExtractionSemicolonPolicy::compute(const Stmt *S, SourceRange &ExtractedRange, + const SourceManager &SM, + const LangOptions &LangOpts) { + auto neededInExtractedFunction = []() { + return ExtractionSemicolonPolicy(true, false); + }; + auto neededInOriginalFunction = []() { + return ExtractionSemicolonPolicy(false, true); + }; + + /// The extracted expression should be terminated with a ';'. The call to + /// the extracted function will replace this expression, so it won't need + /// a terminating ';'. + if (isa<Expr>(S)) + return neededInExtractedFunction(); + + /// Some statements don't need to be terminated with ';'. The call to the + /// extracted function will be a standalone statement, so it should be + /// terminated with a ';'. + bool NeedsSemi = isSemicolonRequiredAfter(S); + if (!NeedsSemi) + return neededInOriginalFunction(); + + /// Some statements might end at ';'. The extraction will move that ';', so + /// the call to the extracted function should be terminated with a ';'. + SourceLocation End = ExtractedRange.getEnd(); + if (isSemicolonAtLocation(End, SM, LangOpts)) + return neededInOriginalFunction(); + + /// Other statements should generally have a trailing ';'. We can try to find + /// it and move it together it with the extracted code. + Optional<Token> NextToken = Lexer::findNextToken(End, SM, LangOpts); + if (NextToken && NextToken->is(tok::semi) && + areOnSameLine(NextToken->getLocation(), End, SM)) { + ExtractedRange.setEnd(NextToken->getLocation()); + return neededInOriginalFunction(); + } + + /// Otherwise insert semicolons in both places. + return ExtractionSemicolonPolicy(true, true); +} + +} // end namespace tooling +} // end namespace clang diff --git a/lib/Tooling/Refactoring/Extract/SourceExtraction.h b/lib/Tooling/Refactoring/Extract/SourceExtraction.h new file mode 100644 index 0000000000..4b4bd8b477 --- /dev/null +++ b/lib/Tooling/Refactoring/Extract/SourceExtraction.h @@ -0,0 +1,52 @@ +//===--- SourceExtraction.cpp - Clang refactoring library -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H +#define LLVM_CLANG_LIB_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H + +#include "clang/Basic/LLVM.h" + +namespace clang { + +class LangOptions; +class SourceManager; +class SourceRange; +class Stmt; + +namespace tooling { + +/// Determines which semicolons should be inserted during extraction. +class ExtractionSemicolonPolicy { +public: + bool isNeededInExtractedFunction() const { + return IsNeededInExtractedFunction; + } + + bool isNeededInOriginalFunction() const { return IsNeededInOriginalFunction; } + + /// Returns the semicolon insertion policy that is needed for extraction of + /// the given statement from the given source range. + static ExtractionSemicolonPolicy compute(const Stmt *S, + SourceRange &ExtractedRange, + const SourceManager &SM, + const LangOptions &LangOpts); + +private: + ExtractionSemicolonPolicy(bool IsNeededInExtractedFunction, + bool IsNeededInOriginalFunction) + : IsNeededInExtractedFunction(IsNeededInExtractedFunction), + IsNeededInOriginalFunction(IsNeededInOriginalFunction) {} + bool IsNeededInExtractedFunction; + bool IsNeededInOriginalFunction; +}; + +} // end namespace tooling +} // end namespace clang + +#endif // LLVM_CLANG_LIB_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H diff --git a/lib/Tooling/Refactoring/RefactoringActions.cpp b/lib/Tooling/Refactoring/RefactoringActions.cpp index 73a3118396..37a1639cb4 100644 --- a/lib/Tooling/Refactoring/RefactoringActions.cpp +++ b/lib/Tooling/Refactoring/RefactoringActions.cpp @@ -46,6 +46,22 @@ public: } }; +class OldQualifiedNameOption : public RequiredRefactoringOption<std::string> { +public: + StringRef getName() const override { return "old-qualified-name"; } + StringRef getDescription() const override { + return "The old qualified name to be renamed"; + } +}; + +class NewQualifiedNameOption : public RequiredRefactoringOption<std::string> { +public: + StringRef getName() const override { return "new-qualified-name"; } + StringRef getDescription() const override { + return "The new qualified name to change the symbol to"; + } +}; + class NewNameOption : public RequiredRefactoringOption<std::string> { public: StringRef getName() const override { return "new-name"; } @@ -70,6 +86,10 @@ public: RefactoringActionRules Rules; Rules.push_back(createRefactoringActionRule<RenameOccurrences>( SourceRangeSelectionRequirement(), OptionRequirement<NewNameOption>())); + // FIXME: Use NewNameOption. + Rules.push_back(createRefactoringActionRule<QualifiedRenameRule>( + OptionRequirement<OldQualifiedNameOption>(), + OptionRequirement<NewQualifiedNameOption>())); return Rules; } }; diff --git a/lib/Tooling/Refactoring/Rename/RenamingAction.cpp b/lib/Tooling/Refactoring/Rename/RenamingAction.cpp index 695fa553b4..c8ed9dd19a 100644 --- a/lib/Tooling/Refactoring/Rename/RenamingAction.cpp +++ b/lib/Tooling/Refactoring/Rename/RenamingAction.cpp @@ -31,6 +31,8 @@ #include "clang/Tooling/Refactoring/Rename/USRLocFinder.h" #include "clang/Tooling/Tooling.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" #include <string> #include <vector> @@ -41,22 +43,14 @@ namespace tooling { namespace { -class OccurrenceFinder final : public FindSymbolOccurrencesRefactoringRule { -public: - OccurrenceFinder(const NamedDecl *ND) : ND(ND) {} - - Expected<SymbolOccurrences> - findSymbolOccurrences(RefactoringRuleContext &Context) override { - std::vector<std::string> USRs = - getUSRsForDeclaration(ND, Context.getASTContext()); - std::string PrevName = ND->getNameAsString(); - return getOccurrencesOfUSRs( - USRs, PrevName, Context.getASTContext().getTranslationUnitDecl()); - } - -private: - const NamedDecl *ND; -}; +Expected<SymbolOccurrences> +findSymbolOccurrences(const NamedDecl *ND, RefactoringRuleContext &Context) { + std::vector<std::string> USRs = + getUSRsForDeclaration(ND, Context.getASTContext()); + std::string PrevName = ND->getNameAsString(); + return getOccurrencesOfUSRs(USRs, PrevName, + Context.getASTContext().getTranslationUnitDecl()); +} } // end anonymous namespace @@ -83,8 +77,7 @@ RenameOccurrences::initiate(RefactoringRuleContext &Context, Expected<AtomicChanges> RenameOccurrences::createSourceReplacements(RefactoringRuleContext &Context) { - Expected<SymbolOccurrences> Occurrences = - OccurrenceFinder(ND).findSymbolOccurrences(Context); + Expected<SymbolOccurrences> Occurrences = findSymbolOccurrences(ND, Context); if (!Occurrences) return Occurrences.takeError(); // FIXME: Verify that the new name is valid. @@ -93,6 +86,60 @@ RenameOccurrences::createSourceReplacements(RefactoringRuleContext &Context) { *Occurrences, Context.getASTContext().getSourceManager(), Name); } +Expected<QualifiedRenameRule> +QualifiedRenameRule::initiate(RefactoringRuleContext &Context, + std::string OldQualifiedName, + std::string NewQualifiedName) { + const NamedDecl *ND = + getNamedDeclFor(Context.getASTContext(), OldQualifiedName); + if (!ND) + return llvm::make_error<llvm::StringError>("Could not find symbol " + + OldQualifiedName, + llvm::errc::invalid_argument); + return QualifiedRenameRule(ND, std::move(NewQualifiedName)); +} + +const RefactoringDescriptor &QualifiedRenameRule::describe() { + static const RefactoringDescriptor Descriptor = { + /*Name=*/"local-qualified-rename", + /*Title=*/"Qualified Rename", + /*Description=*/ + R"(Finds and renames qualified symbols in code within a translation unit. +It is used to move/rename a symbol to a new namespace/name: + * Supported symbols: classes, class members, functions, enums, and type alias. + * Renames all symbol occurrences from the old qualified name to the new + qualified name. All symbol references will be correctly qualified; For + symbol definitions, only name will be changed. +For example, rename "A::Foo" to "B::Bar": + Old code: + namespace foo { + class A {}; + } + + namespace bar { + void f(foo::A a) {} + } + + New code after rename: + namespace foo { + class B {}; + } + + namespace bar { + void f(B b) {} + })" + }; + return Descriptor; +} + +Expected<AtomicChanges> +QualifiedRenameRule::createSourceReplacements(RefactoringRuleContext &Context) { + auto USRs = getUSRsForDeclaration(ND, Context.getASTContext()); + assert(!USRs.empty()); + return tooling::createRenameAtomicChanges( + USRs, NewQualifiedName, Context.getASTContext().getTranslationUnitDecl()); +} + Expected<std::vector<AtomicChange>> createRenameReplacements(const SymbolOccurrences &Occurrences, const SourceManager &SM, const SymbolName &NewName) { diff --git a/lib/Tooling/StandaloneExecution.cpp b/lib/Tooling/StandaloneExecution.cpp index e52e4a611f..eea8e39d13 100644 --- a/lib/Tooling/StandaloneExecution.cpp +++ b/lib/Tooling/StandaloneExecution.cpp @@ -79,13 +79,13 @@ public: } }; -// This anchor is used to force the linker to link in the generated object file -// and thus register the plugin. -volatile int ToolExecutorPluginAnchorSource = 0; - static ToolExecutorPluginRegistry::Add<StandaloneToolExecutorPlugin> X("standalone", "Runs FrontendActions on a set of files provided " "via positional arguments."); +// This anchor is used to force the linker to link in the generated object file +// and thus register the plugin. +volatile int StandaloneToolExecutorAnchorSource = 0; + } // end namespace tooling } // end namespace clang |