summaryrefslogtreecommitdiffstats
path: root/lib/Tooling
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Tooling')
-rw-r--r--lib/Tooling/Core/CMakeLists.txt1
-rw-r--r--lib/Tooling/Core/QualTypeNames.cpp477
-rw-r--r--lib/Tooling/Execution.cpp24
-rw-r--r--lib/Tooling/Refactoring/ASTSelection.cpp23
-rw-r--r--lib/Tooling/Refactoring/CMakeLists.txt3
-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.cpp112
-rw-r--r--lib/Tooling/Refactoring/Extract/SourceExtraction.h52
-rw-r--r--lib/Tooling/Refactoring/RefactoringActions.cpp20
-rw-r--r--lib/Tooling/Refactoring/Rename/RenamingAction.cpp83
-rw-r--r--lib/Tooling/StandaloneExecution.cpp8
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