mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
640 lines
23 KiB
C++
640 lines
23 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "LocalRename.h"
|
|
#include "RefactoringActions.h"
|
|
#include "Renamer.h"
|
|
#include "swift/AST/DiagnosticsRefactoring.h"
|
|
#include "swift/AST/ParameterList.h"
|
|
#include "swift/AST/USRGeneration.h"
|
|
#include "swift/Basic/StringExtras.h"
|
|
#include "swift/Index/Index.h"
|
|
|
|
using namespace swift::refactoring;
|
|
using namespace swift::index;
|
|
|
|
class TextReplacementsRenamer : public Renamer {
|
|
llvm::StringSet<> &ReplaceTextContext;
|
|
SmallVector<Replacement> Replacements;
|
|
|
|
public:
|
|
const DeclNameViewer New;
|
|
|
|
private:
|
|
StringRef registerText(StringRef Text) {
|
|
if (Text.empty())
|
|
return Text;
|
|
return ReplaceTextContext.insert(Text).first->getKey();
|
|
}
|
|
|
|
StringRef getCallArgLabelReplacement(StringRef OldLabelRange,
|
|
StringRef NewLabel) {
|
|
return NewLabel.empty() ? "" : NewLabel;
|
|
}
|
|
|
|
StringRef getCallArgColonReplacement(StringRef OldLabelRange,
|
|
StringRef NewLabel) {
|
|
// Expected OldLabelRange: foo( []3, a[: ]2, b[ : ]3 ...)
|
|
// FIXME: Preserve comments: foo([a/*:*/ : /*:*/ ]2, ...)
|
|
if (NewLabel.empty())
|
|
return "";
|
|
if (OldLabelRange.empty())
|
|
return ": ";
|
|
return registerText(OldLabelRange);
|
|
}
|
|
|
|
StringRef getCallArgCombinedReplacement(StringRef OldArgLabel,
|
|
StringRef NewArgLabel) {
|
|
// This case only happens when going from foo([]1) to foo([a: ]1).
|
|
assert(OldArgLabel.empty());
|
|
if (NewArgLabel.empty())
|
|
return "";
|
|
return registerText((Twine(NewArgLabel) + ": ").str());
|
|
}
|
|
|
|
StringRef getParamNameReplacement(StringRef OldParam, StringRef OldArgLabel,
|
|
StringRef NewArgLabel) {
|
|
// We don't want to get foo(a a: Int), so drop the parameter name if the
|
|
// argument label will match the original name.
|
|
// Note: the leading whitespace is part of the parameter range.
|
|
if (!NewArgLabel.empty() && OldParam.ltrim() == NewArgLabel)
|
|
return "";
|
|
|
|
// If we're renaming foo(x: Int) to foo(_:), then use the original argument
|
|
// label as the parameter name so as to not break references in the body.
|
|
if (NewArgLabel.empty() && !OldArgLabel.empty() && OldParam.empty())
|
|
return registerText((Twine(" ") + OldArgLabel).str());
|
|
|
|
return registerText(OldParam);
|
|
}
|
|
|
|
StringRef getDeclArgumentLabelReplacement(StringRef OldLabelRange,
|
|
StringRef NewArgLabel) {
|
|
// OldLabelRange is subscript([]a: Int), foo([a]: Int) or foo([a] b: Int)
|
|
if (NewArgLabel.empty())
|
|
return OldLabelRange.empty() ? "" : "_";
|
|
|
|
if (OldLabelRange.empty())
|
|
return registerText((Twine(NewArgLabel) + " ").str());
|
|
return registerText(NewArgLabel);
|
|
}
|
|
|
|
StringRef getReplacementText(StringRef LabelRange,
|
|
RefactoringRangeKind RangeKind,
|
|
StringRef OldLabel, StringRef NewLabel) {
|
|
switch (RangeKind) {
|
|
case RefactoringRangeKind::CallArgumentLabel:
|
|
return getCallArgLabelReplacement(LabelRange, NewLabel);
|
|
case RefactoringRangeKind::CallArgumentColon:
|
|
return getCallArgColonReplacement(LabelRange, NewLabel);
|
|
case RefactoringRangeKind::CallArgumentCombined:
|
|
return getCallArgCombinedReplacement(LabelRange, NewLabel);
|
|
case RefactoringRangeKind::ParameterName:
|
|
return getParamNameReplacement(LabelRange, OldLabel, NewLabel);
|
|
case RefactoringRangeKind::NoncollapsibleParameterName:
|
|
return LabelRange;
|
|
case RefactoringRangeKind::DeclArgumentLabel:
|
|
return getDeclArgumentLabelReplacement(LabelRange, NewLabel);
|
|
case RefactoringRangeKind::SelectorArgumentLabel:
|
|
return NewLabel.empty() ? "_" : registerText(NewLabel);
|
|
default:
|
|
llvm_unreachable("label range type is none but there are labels");
|
|
}
|
|
}
|
|
|
|
void addReplacement(CharSourceRange LabelRange,
|
|
RefactoringRangeKind RangeKind, StringRef OldLabel,
|
|
StringRef NewLabel) {
|
|
StringRef ExistingLabel = LabelRange.str();
|
|
StringRef Text =
|
|
getReplacementText(ExistingLabel, RangeKind, OldLabel, NewLabel);
|
|
if (Text != ExistingLabel)
|
|
Replacements.push_back({/*Path=*/{}, LabelRange, /*BufferName=*/{}, Text,
|
|
/*RegionsWorthNote=*/{}});
|
|
}
|
|
|
|
void doRenameLabel(CharSourceRange Label, RefactoringRangeKind RangeKind,
|
|
unsigned NameIndex) override {
|
|
addReplacement(Label, RangeKind, Old.args()[NameIndex],
|
|
New.args()[NameIndex]);
|
|
}
|
|
|
|
void doRenameBase(CharSourceRange Range, RefactoringRangeKind) override {
|
|
if (Old.base() != New.base())
|
|
Replacements.push_back({/*Path=*/{}, Range, /*BufferName=*/{},
|
|
registerText(New.base()),
|
|
/*RegionsWorthNote=*/{}});
|
|
}
|
|
|
|
public:
|
|
TextReplacementsRenamer(const SourceManager &SM, StringRef OldName,
|
|
StringRef NewName,
|
|
llvm::StringSet<> &ReplaceTextContext)
|
|
: Renamer(SM, OldName), ReplaceTextContext(ReplaceTextContext),
|
|
New(NewName) {
|
|
assert(Old.isValid() && New.isValid());
|
|
assert(Old.partsCount() == New.partsCount());
|
|
}
|
|
|
|
ArrayRef<Replacement> getReplacements() const { return Replacements; }
|
|
};
|
|
|
|
static const ValueDecl *getRelatedSystemDecl(const ValueDecl *VD) {
|
|
if (VD->getModuleContext()->isNonUserModule())
|
|
return VD;
|
|
for (auto *Req : VD->getSatisfiedProtocolRequirements()) {
|
|
if (Req->getModuleContext()->isNonUserModule())
|
|
return Req;
|
|
}
|
|
for (auto Over = VD->getOverriddenDecl(); Over;
|
|
Over = Over->getOverriddenDecl()) {
|
|
if (Over->getModuleContext()->isNonUserModule())
|
|
return Over;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
/// Stores information about the reference that rename availability is being
|
|
/// queried on.
|
|
struct RenameRefInfo {
|
|
SourceFile *SF; ///< The source file containing the reference.
|
|
SourceLoc Loc; ///< The reference's source location.
|
|
bool IsArgLabel; ///< Whether Loc is on an arg label, rather than base name.
|
|
};
|
|
|
|
static llvm::Optional<RefactorAvailabilityInfo>
|
|
renameAvailabilityInfo(const ValueDecl *VD,
|
|
llvm::Optional<RenameRefInfo> RefInfo) {
|
|
RefactorAvailableKind AvailKind = RefactorAvailableKind::Available;
|
|
if (getRelatedSystemDecl(VD)) {
|
|
AvailKind = RefactorAvailableKind::Unavailable_system_symbol;
|
|
} else if (VD->getClangDecl()) {
|
|
AvailKind = RefactorAvailableKind::Unavailable_decl_from_clang;
|
|
} else if (!VD->hasName()) {
|
|
AvailKind = RefactorAvailableKind::Unavailable_has_no_name;
|
|
}
|
|
|
|
auto isInMacroExpansionBuffer = [](const ValueDecl *VD) -> bool {
|
|
auto *module = VD->getModuleContext();
|
|
auto *file = module->getSourceFileContainingLocation(VD->getLoc());
|
|
if (!file)
|
|
return false;
|
|
|
|
return file->getFulfilledMacroRole() != llvm::None;
|
|
};
|
|
|
|
if (AvailKind == RefactorAvailableKind::Available) {
|
|
SourceLoc Loc = VD->getLoc();
|
|
if (!Loc.isValid()) {
|
|
AvailKind = RefactorAvailableKind::Unavailable_has_no_location;
|
|
} else if (isInMacroExpansionBuffer(VD)) {
|
|
AvailKind = RefactorAvailableKind::Unavailable_decl_in_macro;
|
|
}
|
|
}
|
|
|
|
if (isa<AbstractFunctionDecl>(VD)) {
|
|
// Disallow renaming accessors.
|
|
if (isa<AccessorDecl>(VD))
|
|
return llvm::None;
|
|
|
|
// Disallow renaming deinit.
|
|
if (isa<DestructorDecl>(VD))
|
|
return llvm::None;
|
|
|
|
// Disallow renaming init with no arguments.
|
|
if (auto CD = dyn_cast<ConstructorDecl>(VD)) {
|
|
if (!CD->getParameters()->size())
|
|
return llvm::None;
|
|
|
|
if (RefInfo && !RefInfo->IsArgLabel) {
|
|
NameMatcher Matcher(*(RefInfo->SF));
|
|
auto Resolved = Matcher.resolve({RefInfo->Loc, /*ResolveArgs*/ true});
|
|
if (Resolved.LabelRanges.empty())
|
|
return llvm::None;
|
|
}
|
|
}
|
|
|
|
// Disallow renaming 'callAsFunction' method with no arguments.
|
|
if (auto FD = dyn_cast<FuncDecl>(VD)) {
|
|
// FIXME: syntactic rename can only decide by checking the spelling, not
|
|
// whether it's an instance method, so we do the same here for now.
|
|
if (FD->getBaseIdentifier() == FD->getASTContext().Id_callAsFunction) {
|
|
if (!FD->getParameters()->size())
|
|
return llvm::None;
|
|
|
|
if (RefInfo && !RefInfo->IsArgLabel) {
|
|
NameMatcher Matcher(*(RefInfo->SF));
|
|
auto Resolved = Matcher.resolve({RefInfo->Loc, /*ResolveArgs*/ true});
|
|
if (Resolved.LabelRanges.empty())
|
|
return llvm::None;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Always return local rename for parameters.
|
|
// FIXME: if the cursor is on the argument, we should return global rename.
|
|
if (isa<ParamDecl>(VD))
|
|
return RefactorAvailabilityInfo{RefactoringKind::LocalRename, AvailKind};
|
|
|
|
// If the indexer considers VD a global symbol, then we apply global rename.
|
|
if (index::isLocalSymbol(VD))
|
|
return RefactorAvailabilityInfo{RefactoringKind::LocalRename, AvailKind};
|
|
return RefactorAvailabilityInfo{RefactoringKind::GlobalRename, AvailKind};
|
|
}
|
|
|
|
/// Given a cursor, return the decl and its rename availability. \c None if
|
|
/// the cursor did not resolve to a decl or it resolved to a decl that we do
|
|
/// not allow renaming on.
|
|
llvm::Optional<RenameInfo>
|
|
swift::refactoring::getRenameInfo(ResolvedCursorInfoPtr cursorInfo) {
|
|
auto valueCursor = dyn_cast<ResolvedValueRefCursorInfo>(cursorInfo);
|
|
if (!valueCursor)
|
|
return llvm::None;
|
|
|
|
ValueDecl *VD = valueCursor->typeOrValue();
|
|
if (!VD)
|
|
return llvm::None;
|
|
|
|
llvm::Optional<RenameRefInfo> refInfo;
|
|
if (!valueCursor->getShorthandShadowedDecls().empty()) {
|
|
// Find the outermost decl for a shorthand if let/closure capture
|
|
VD = valueCursor->getShorthandShadowedDecls().back();
|
|
} else if (valueCursor->isRef()) {
|
|
refInfo = {valueCursor->getSourceFile(), valueCursor->getLoc(),
|
|
valueCursor->isKeywordArgument()};
|
|
}
|
|
|
|
llvm::Optional<RefactorAvailabilityInfo> info =
|
|
renameAvailabilityInfo(VD, refInfo);
|
|
if (!info)
|
|
return llvm::None;
|
|
|
|
return RenameInfo{VD, *info};
|
|
}
|
|
|
|
class RenameRangeCollector : public IndexDataConsumer {
|
|
public:
|
|
RenameRangeCollector(StringRef USR, StringRef newName)
|
|
: USR(USR), newName(newName) {}
|
|
|
|
RenameRangeCollector(const ValueDecl *D, StringRef newName)
|
|
: newName(newName) {
|
|
SmallString<64> SS;
|
|
llvm::raw_svector_ostream OS(SS);
|
|
printValueDeclUSR(D, OS);
|
|
USR = stringStorage.copyString(SS.str());
|
|
}
|
|
|
|
RenameRangeCollector(RenameRangeCollector &&collector) = default;
|
|
|
|
ArrayRef<RenameLoc> results() const { return locations; }
|
|
|
|
private:
|
|
bool indexLocals() override { return true; }
|
|
void failed(StringRef error) override {}
|
|
bool startDependency(StringRef name, StringRef path, bool isClangModule,
|
|
bool isSystem) override {
|
|
return true;
|
|
}
|
|
bool finishDependency(bool isClangModule) override { return true; }
|
|
|
|
Action startSourceEntity(const IndexSymbol &symbol) override {
|
|
if (symbol.USR == USR) {
|
|
if (auto loc = indexSymbolToRenameLoc(symbol, newName)) {
|
|
// Inside capture lists like `{ [test] in }`, 'test' refers to both the
|
|
// newly declared, captured variable and the referenced variable it is
|
|
// initialized from. Make sure to only rename it once.
|
|
auto existingLoc = llvm::find_if(locations, [&](RenameLoc searchLoc) {
|
|
return searchLoc.Line == loc->Line && searchLoc.Column == loc->Column;
|
|
});
|
|
if (existingLoc == locations.end()) {
|
|
locations.push_back(std::move(*loc));
|
|
} else {
|
|
assert(existingLoc->OldName == loc->OldName &&
|
|
existingLoc->NewName == loc->NewName &&
|
|
existingLoc->IsFunctionLike == loc->IsFunctionLike &&
|
|
existingLoc->IsNonProtocolType == loc->IsNonProtocolType &&
|
|
"Asked to do a different rename for the same location?");
|
|
}
|
|
}
|
|
}
|
|
return IndexDataConsumer::Continue;
|
|
}
|
|
|
|
bool finishSourceEntity(SymbolInfo symInfo, SymbolRoleSet roles) override {
|
|
return true;
|
|
}
|
|
|
|
llvm::Optional<RenameLoc>
|
|
indexSymbolToRenameLoc(const index::IndexSymbol &symbol, StringRef NewName);
|
|
|
|
private:
|
|
StringRef USR;
|
|
StringRef newName;
|
|
StringScratchSpace stringStorage;
|
|
std::vector<RenameLoc> locations;
|
|
};
|
|
|
|
llvm::Optional<RenameLoc>
|
|
RenameRangeCollector::indexSymbolToRenameLoc(const index::IndexSymbol &symbol,
|
|
StringRef newName) {
|
|
if (symbol.roles & (unsigned)index::SymbolRole::Implicit) {
|
|
return llvm::None;
|
|
}
|
|
|
|
NameUsage usage = NameUsage::Unknown;
|
|
if (symbol.roles & (unsigned)index::SymbolRole::Call) {
|
|
usage = NameUsage::Call;
|
|
} else if (symbol.roles & (unsigned)index::SymbolRole::Definition) {
|
|
usage = NameUsage::Definition;
|
|
} else if (symbol.roles & (unsigned)index::SymbolRole::Reference) {
|
|
usage = NameUsage::Reference;
|
|
} else {
|
|
llvm_unreachable("unexpected role");
|
|
}
|
|
|
|
bool isFunctionLike = false;
|
|
bool isNonProtocolType = false;
|
|
|
|
switch (symbol.symInfo.Kind) {
|
|
case index::SymbolKind::EnumConstant:
|
|
case index::SymbolKind::Function:
|
|
case index::SymbolKind::Constructor:
|
|
case index::SymbolKind::ConversionFunction:
|
|
case index::SymbolKind::InstanceMethod:
|
|
case index::SymbolKind::ClassMethod:
|
|
case index::SymbolKind::StaticMethod:
|
|
isFunctionLike = true;
|
|
break;
|
|
case index::SymbolKind::Class:
|
|
case index::SymbolKind::Enum:
|
|
case index::SymbolKind::Struct:
|
|
isNonProtocolType = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
StringRef oldName = stringStorage.copyString(symbol.name);
|
|
return RenameLoc{symbol.line, symbol.column, usage, oldName, newName,
|
|
isFunctionLike, isNonProtocolType};
|
|
}
|
|
|
|
bool RefactoringActionLocalRename::isApplicable(
|
|
ResolvedCursorInfoPtr CursorInfo, DiagnosticEngine &Diag) {
|
|
llvm::Optional<RenameInfo> Info = getRenameInfo(CursorInfo);
|
|
return Info &&
|
|
Info->Availability.AvailableKind == RefactorAvailableKind::Available &&
|
|
Info->Availability.Kind == RefactoringKind::LocalRename;
|
|
}
|
|
|
|
static void analyzeRenameScope(ValueDecl *VD,
|
|
SmallVectorImpl<DeclContext *> &Scopes) {
|
|
auto *Scope = VD->getDeclContext();
|
|
// There may be sibling decls that the renamed symbol is visible from.
|
|
switch (Scope->getContextKind()) {
|
|
case DeclContextKind::GenericTypeDecl:
|
|
case DeclContextKind::ExtensionDecl:
|
|
case DeclContextKind::TopLevelCodeDecl:
|
|
case DeclContextKind::SubscriptDecl:
|
|
case DeclContextKind::EnumElementDecl:
|
|
case DeclContextKind::AbstractFunctionDecl:
|
|
Scope = Scope->getParent();
|
|
break;
|
|
case DeclContextKind::AbstractClosureExpr:
|
|
case DeclContextKind::Initializer:
|
|
case DeclContextKind::SerializedLocal:
|
|
case DeclContextKind::Package:
|
|
case DeclContextKind::Module:
|
|
case DeclContextKind::FileUnit:
|
|
case DeclContextKind::MacroDecl:
|
|
break;
|
|
}
|
|
|
|
Scopes.push_back(Scope);
|
|
}
|
|
|
|
static llvm::Optional<RenameRangeCollector>
|
|
localRenames(SourceFile *SF, SourceLoc startLoc, StringRef preferredName,
|
|
DiagnosticEngine &diags) {
|
|
auto cursorInfo =
|
|
evaluateOrDefault(SF->getASTContext().evaluator,
|
|
CursorInfoRequest{CursorInfoOwner(SF, startLoc)},
|
|
new ResolvedCursorInfo());
|
|
|
|
llvm::Optional<RenameInfo> info = getRenameInfo(cursorInfo);
|
|
if (!info) {
|
|
diags.diagnose(startLoc, diag::unresolved_location);
|
|
return llvm::None;
|
|
}
|
|
|
|
switch (info->Availability.AvailableKind) {
|
|
case RefactorAvailableKind::Available:
|
|
break;
|
|
case RefactorAvailableKind::Unavailable_system_symbol:
|
|
diags.diagnose(startLoc, diag::decl_is_system_symbol, info->VD->getName());
|
|
return llvm::None;
|
|
case RefactorAvailableKind::Unavailable_has_no_location:
|
|
diags.diagnose(startLoc, diag::value_decl_no_loc, info->VD->getName());
|
|
return llvm::None;
|
|
case RefactorAvailableKind::Unavailable_has_no_name:
|
|
diags.diagnose(startLoc, diag::decl_has_no_name);
|
|
return llvm::None;
|
|
case RefactorAvailableKind::Unavailable_has_no_accessibility:
|
|
diags.diagnose(startLoc, diag::decl_no_accessibility);
|
|
return llvm::None;
|
|
case RefactorAvailableKind::Unavailable_decl_from_clang:
|
|
diags.diagnose(startLoc, diag::decl_from_clang);
|
|
return llvm::None;
|
|
case RefactorAvailableKind::Unavailable_decl_in_macro:
|
|
diags.diagnose(startLoc, diag::decl_in_macro);
|
|
return llvm::None;
|
|
}
|
|
|
|
SmallVector<DeclContext *, 8> scopes;
|
|
analyzeRenameScope(info->VD, scopes);
|
|
if (scopes.empty())
|
|
return llvm::None;
|
|
|
|
RenameRangeCollector rangeCollector(info->VD, preferredName);
|
|
for (DeclContext *DC : scopes)
|
|
indexDeclContext(DC, rangeCollector);
|
|
|
|
return rangeCollector;
|
|
}
|
|
|
|
bool RefactoringActionLocalRename::performChange() {
|
|
if (StartLoc.isInvalid()) {
|
|
DiagEngine.diagnose(SourceLoc(), diag::invalid_location);
|
|
return true;
|
|
}
|
|
if (!DeclNameViewer(PreferredName).isValid()) {
|
|
DiagEngine.diagnose(SourceLoc(), diag::invalid_name, PreferredName);
|
|
return true;
|
|
}
|
|
if (!TheFile) {
|
|
DiagEngine.diagnose(StartLoc, diag::location_module_mismatch,
|
|
MD->getNameStr());
|
|
return true;
|
|
}
|
|
|
|
llvm::Optional<RenameRangeCollector> rangeCollector =
|
|
localRenames(TheFile, StartLoc, PreferredName, DiagEngine);
|
|
if (!rangeCollector)
|
|
return true;
|
|
|
|
auto consumers = DiagEngine.takeConsumers();
|
|
assert(consumers.size() == 1);
|
|
return syntacticRename(TheFile, rangeCollector->results(), EditConsumer,
|
|
*consumers[0]);
|
|
}
|
|
|
|
static std::vector<ResolvedLoc>
|
|
resolveRenameLocations(ArrayRef<RenameLoc> RenameLocs, SourceFile &SF,
|
|
DiagnosticEngine &Diags) {
|
|
SourceManager &SM = SF.getASTContext().SourceMgr;
|
|
unsigned BufferID = SF.getBufferID().value();
|
|
|
|
std::vector<UnresolvedLoc> UnresolvedLocs;
|
|
for (const RenameLoc &RenameLoc : RenameLocs) {
|
|
DeclNameViewer OldName(RenameLoc.OldName);
|
|
SourceLoc Location =
|
|
SM.getLocForLineCol(BufferID, RenameLoc.Line, RenameLoc.Column);
|
|
|
|
if (!OldName.isValid()) {
|
|
Diags.diagnose(Location, diag::invalid_name, RenameLoc.OldName);
|
|
return {};
|
|
}
|
|
|
|
if (!RenameLoc.NewName.empty()) {
|
|
DeclNameViewer NewName(RenameLoc.NewName);
|
|
ArrayRef<StringRef> ParamNames = NewName.args();
|
|
bool newOperator = Lexer::isOperator(NewName.base());
|
|
bool NewNameIsValid =
|
|
NewName.isValid() &&
|
|
(Lexer::isIdentifier(NewName.base()) || newOperator) &&
|
|
std::all_of(ParamNames.begin(), ParamNames.end(),
|
|
[](StringRef Label) {
|
|
return Label.empty() || Lexer::isIdentifier(Label);
|
|
});
|
|
|
|
if (!NewNameIsValid) {
|
|
Diags.diagnose(Location, diag::invalid_name, RenameLoc.NewName);
|
|
return {};
|
|
}
|
|
|
|
if (NewName.partsCount() != OldName.partsCount()) {
|
|
Diags.diagnose(Location, diag::arity_mismatch, RenameLoc.NewName,
|
|
RenameLoc.OldName);
|
|
return {};
|
|
}
|
|
|
|
if (RenameLoc.Usage == NameUsage::Call && !RenameLoc.IsFunctionLike) {
|
|
Diags.diagnose(Location, diag::name_not_functionlike,
|
|
RenameLoc.NewName);
|
|
return {};
|
|
}
|
|
}
|
|
|
|
bool isOperator = Lexer::isOperator(OldName.base());
|
|
UnresolvedLocs.push_back(
|
|
{Location, (RenameLoc.Usage == NameUsage::Unknown ||
|
|
(RenameLoc.Usage == NameUsage::Call && !isOperator))});
|
|
}
|
|
|
|
NameMatcher Resolver(SF);
|
|
return Resolver.resolve(UnresolvedLocs, SF.getAllTokens());
|
|
}
|
|
|
|
int swift::ide::syntacticRename(SourceFile *SF, ArrayRef<RenameLoc> RenameLocs,
|
|
SourceEditConsumer &EditConsumer,
|
|
DiagnosticConsumer &DiagConsumer) {
|
|
|
|
assert(SF && "null source file");
|
|
|
|
SourceManager &SM = SF->getASTContext().SourceMgr;
|
|
DiagnosticEngine DiagEngine(SM);
|
|
DiagEngine.addConsumer(DiagConsumer);
|
|
|
|
auto ResolvedLocs = resolveRenameLocations(RenameLocs, *SF, DiagEngine);
|
|
if (ResolvedLocs.size() != RenameLocs.size())
|
|
return true; // Already diagnosed.
|
|
|
|
size_t index = 0;
|
|
llvm::StringSet<> ReplaceTextContext;
|
|
for (const RenameLoc &Rename : RenameLocs) {
|
|
ResolvedLoc &Resolved = ResolvedLocs[index++];
|
|
TextReplacementsRenamer Renamer(SM, Rename.OldName, Rename.NewName,
|
|
ReplaceTextContext);
|
|
RegionType Type = Renamer.addSyntacticRenameRanges(Resolved, Rename);
|
|
if (Type == RegionType::Mismatch) {
|
|
DiagEngine.diagnose(Resolved.Range.getStart(), diag::mismatched_rename,
|
|
Rename.NewName);
|
|
EditConsumer.accept(SM, Type, llvm::None);
|
|
} else {
|
|
EditConsumer.accept(SM, Type, Renamer.getReplacements());
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int swift::ide::findSyntacticRenameRanges(
|
|
SourceFile *SF, ArrayRef<RenameLoc> RenameLocs,
|
|
FindRenameRangesConsumer &RenameConsumer,
|
|
DiagnosticConsumer &DiagConsumer) {
|
|
assert(SF && "null source file");
|
|
|
|
SourceManager &SM = SF->getASTContext().SourceMgr;
|
|
DiagnosticEngine DiagEngine(SM);
|
|
DiagEngine.addConsumer(DiagConsumer);
|
|
|
|
auto ResolvedLocs = resolveRenameLocations(RenameLocs, *SF, DiagEngine);
|
|
if (ResolvedLocs.size() != RenameLocs.size())
|
|
return true; // Already diagnosed.
|
|
|
|
size_t index = 0;
|
|
for (const RenameLoc &Rename : RenameLocs) {
|
|
ResolvedLoc &Resolved = ResolvedLocs[index++];
|
|
RenameRangeDetailCollector Renamer(SM, Rename.OldName);
|
|
RegionType Type = Renamer.addSyntacticRenameRanges(Resolved, Rename);
|
|
if (Type == RegionType::Mismatch) {
|
|
DiagEngine.diagnose(Resolved.Range.getStart(), diag::mismatched_rename,
|
|
Rename.NewName);
|
|
RenameConsumer.accept(SM, Type, llvm::None);
|
|
} else {
|
|
RenameConsumer.accept(SM, Type, Renamer.Ranges);
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int swift::ide::findLocalRenameRanges(SourceFile *SF, RangeConfig Range,
|
|
FindRenameRangesConsumer &RenameConsumer,
|
|
DiagnosticConsumer &DiagConsumer) {
|
|
assert(SF && "null source file");
|
|
|
|
SourceManager &SM = SF->getASTContext().SourceMgr;
|
|
DiagnosticEngine Diags(SM);
|
|
Diags.addConsumer(DiagConsumer);
|
|
|
|
auto StartLoc = Lexer::getLocForStartOfToken(SM, Range.getStart(SM));
|
|
llvm::Optional<RenameRangeCollector> RangeCollector =
|
|
localRenames(SF, StartLoc, StringRef(), Diags);
|
|
if (!RangeCollector)
|
|
return true;
|
|
|
|
return findSyntacticRenameRanges(SF, RangeCollector->results(),
|
|
RenameConsumer, DiagConsumer);
|
|
}
|