Skip to content

[SourceKit] Merge local refactoring implementations #64298

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions include/swift/AST/DiagnosticsRefactoring.def
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,19 @@ ERROR(arity_mismatch, none, "the given new name '%0' does not match the arity of

ERROR(name_not_functionlike, none, "the 'call' name usage cannot be used with a non-function-like name '%0'", (StringRef))

ERROR(unresolved_location, none, "cannot resolve location as name", ())
ERROR(unresolved_location, none, "cannot rename due to unresolved location", ())

ERROR(location_module_mismatch, none, "given location does not belong to module '%0'", (StringRef))

ERROR(value_decl_no_loc, none, "value decl '%0' has no declaration location", (DeclName))
ERROR(value_decl_no_loc, none, "cannot rename %0 as it has no declaration location", (DeclName))

ERROR(decl_is_system_symbol, none, "cannot rename system symbol %0", (DeclName))

ERROR(decl_has_no_name, none, "cannot rename as no declaration name was found", ())

ERROR(decl_no_accessibility, none, "cannot rename as accessibility could not be determined", ())

ERROR(decl_from_clang, none, "cannot rename a Clang symbol from its Swift reference", ())

ERROR(value_decl_referenced_out_of_range, none, "value decl '%0' is referenced out of range", (DeclName))

Expand Down
5 changes: 2 additions & 3 deletions include/swift/Refactoring/Refactoring.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,8 @@ struct RenameRefInfo {
bool IsArgLabel; ///< Whether Loc is on an arg label, rather than base name.
};

void collectRenameAvailabilityInfo(
const ValueDecl *VD, Optional<RenameRefInfo> RefInfo,
llvm::SmallVectorImpl<RenameAvailabilityInfo> &Infos);
Optional<RenameAvailabilityInfo>
renameAvailabilityInfo(const ValueDecl *VD, Optional<RenameRefInfo> RefInfo);

} // namespace ide
} // namespace swift
Expand Down
247 changes: 121 additions & 126 deletions lib/Refactoring/Refactoring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -616,29 +616,54 @@ static const ValueDecl *getRelatedSystemDecl(const ValueDecl *VD) {
return nullptr;
}

static Optional<RefactoringKind>
getAvailableRenameForDecl(const ValueDecl *VD,
Optional<RenameRefInfo> RefInfo) {
SmallVector<RenameAvailabilityInfo, 2> Infos;
collectRenameAvailabilityInfo(VD, RefInfo, Infos);
for (auto &Info : Infos) {
if (Info.AvailableKind == RenameAvailableKind::Available)
return Info.Kind;
struct RenameInfo {
ValueDecl *VD;
RenameAvailabilityInfo Availability;
};

/// 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.
static Optional<RenameInfo> getRenameInfo(ResolvedCursorInfoPtr cursorInfo) {
auto valueCursor = dyn_cast<ResolvedValueRefCursorInfo>(cursorInfo);
if (!valueCursor)
return None;

ValueDecl *VD = valueCursor->typeOrValue();
if (!VD)
return None;

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()};
}
return None;

Optional<RenameAvailabilityInfo> info = renameAvailabilityInfo(VD, refInfo);
if (!info)
return None;

return RenameInfo{VD, *info};
}

class RenameRangeCollector : public IndexDataConsumer {
public:
RenameRangeCollector(StringRef USR, StringRef newName)
: USR(USR.str()), newName(newName.str()) {}
: USR(USR), newName(newName) {}

RenameRangeCollector(const ValueDecl *D, StringRef newName)
: newName(newName.str()) {
llvm::raw_string_ostream OS(USR);
: 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:
Expand Down Expand Up @@ -680,8 +705,8 @@ class RenameRangeCollector : public IndexDataConsumer {
StringRef NewName);

private:
std::string USR;
std::string newName;
StringRef USR;
StringRef newName;
StringScratchSpace stringStorage;
std::vector<RenameLoc> locations;
};
Expand Down Expand Up @@ -836,29 +861,14 @@ class RefactoringAction##KIND: public RangeBasedRefactoringAction { \

bool RefactoringActionLocalRename::isApplicable(
ResolvedCursorInfoPtr CursorInfo, DiagnosticEngine &Diag) {
auto ValueRefInfo = dyn_cast<ResolvedValueRefCursorInfo>(CursorInfo);
if (!ValueRefInfo)
return false;

Optional<RenameRefInfo> RefInfo;
if (ValueRefInfo->isRef())
RefInfo = {CursorInfo->getSourceFile(), CursorInfo->getLoc(),
ValueRefInfo->isKeywordArgument()};

auto RenameOp = getAvailableRenameForDecl(ValueRefInfo->getValueD(), RefInfo);
return RenameOp.has_value() &&
RenameOp.value() == RefactoringKind::LocalRename;
Optional<RenameInfo> Info = getRenameInfo(CursorInfo);
return Info &&
Info->Availability.AvailableKind == RenameAvailableKind::Available &&
Info->Availability.Kind == RefactoringKind::LocalRename;
}

static void analyzeRenameScope(ValueDecl *VD, Optional<RenameRefInfo> RefInfo,
DiagnosticEngine &Diags,
static void analyzeRenameScope(ValueDecl *VD,
SmallVectorImpl<DeclContext *> &Scopes) {
Scopes.clear();
if (!getAvailableRenameForDecl(VD, RefInfo).has_value()) {
Diags.diagnose(SourceLoc(), diag::value_decl_no_loc, VD->getName());
return;
}

auto *Scope = VD->getDeclContext();
// There may be sibling decls that the renamed symbol is visible from.
switch (Scope->getContextKind()) {
Expand All @@ -883,6 +893,53 @@ static void analyzeRenameScope(ValueDecl *VD, Optional<RenameRefInfo> RefInfo,
Scopes.push_back(Scope);
}

static Optional<RenameRangeCollector> localRenames(SourceFile *SF,
SourceLoc startLoc,
StringRef preferredName,
DiagnosticEngine &diags) {
auto cursorInfo =
evaluateOrDefault(SF->getASTContext().evaluator,
CursorInfoRequest{CursorInfoOwner(SF, startLoc)},
new ResolvedCursorInfo());

Optional<RenameInfo> info = getRenameInfo(cursorInfo);
if (!info) {
diags.diagnose(startLoc, diag::unresolved_location);
return None;
}

switch (info->Availability.AvailableKind) {
case RenameAvailableKind::Available:
break;
case RenameAvailableKind::Unavailable_system_symbol:
diags.diagnose(startLoc, diag::decl_is_system_symbol, info->VD->getName());
return None;
case RenameAvailableKind::Unavailable_has_no_location:
diags.diagnose(startLoc, diag::value_decl_no_loc, info->VD->getName());
return None;
case RenameAvailableKind::Unavailable_has_no_name:
diags.diagnose(startLoc, diag::decl_has_no_name);
return None;
case RenameAvailableKind::Unavailable_has_no_accessibility:
diags.diagnose(startLoc, diag::decl_no_accessibility);
return None;
case RenameAvailableKind::Unavailable_decl_from_clang:
diags.diagnose(startLoc, diag::decl_from_clang);
return None;
}

SmallVector<DeclContext *, 8> scopes;
analyzeRenameScope(info->VD, scopes);
if (scopes.empty())
return 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);
Expand All @@ -897,40 +954,16 @@ bool RefactoringActionLocalRename::performChange() {
MD->getNameStr());
return true;
}
CursorInfo =
evaluateOrDefault(TheFile->getASTContext().evaluator,
CursorInfoRequest{CursorInfoOwner(TheFile, StartLoc)},
new ResolvedCursorInfo());
auto ValueRefCursorInfo = dyn_cast<ResolvedValueRefCursorInfo>(CursorInfo);
if (ValueRefCursorInfo && ValueRefCursorInfo->getValueD()) {
ValueDecl *VD = ValueRefCursorInfo->typeOrValue();
// The index always uses the outermost shadow for references
if (!ValueRefCursorInfo->getShorthandShadowedDecls().empty()) {
VD = ValueRefCursorInfo->getShorthandShadowedDecls().back();
}

SmallVector<DeclContext *, 8> Scopes;

Optional<RenameRefInfo> RefInfo;
if (ValueRefCursorInfo->isRef())
RefInfo = {CursorInfo->getSourceFile(), CursorInfo->getLoc(),
ValueRefCursorInfo->isKeywordArgument()};

analyzeRenameScope(VD, RefInfo, DiagEngine, Scopes);
if (Scopes.empty())
return true;
RenameRangeCollector rangeCollector(VD, PreferredName);
for (DeclContext *DC : Scopes)
indexDeclContext(DC, rangeCollector);

auto consumers = DiagEngine.takeConsumers();
assert(consumers.size() == 1);
return syntacticRename(TheFile, rangeCollector.results(), EditConsumer,
*consumers[0]);
} else {
DiagEngine.diagnose(StartLoc, diag::unresolved_location);
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]);
}

StringRef getDefaultPreferredName(RefactoringKind Kind) {
Expand Down Expand Up @@ -8783,9 +8816,9 @@ accept(SourceManager &SM, RegionType RegionType,
}
}

void swift::ide::collectRenameAvailabilityInfo(
const ValueDecl *VD, Optional<RenameRefInfo> RefInfo,
SmallVectorImpl<RenameAvailabilityInfo> &Infos) {
Optional<RenameAvailabilityInfo>
swift::ide::renameAvailabilityInfo(const ValueDecl *VD,
Optional<RenameRefInfo> RefInfo) {
RenameAvailableKind AvailKind = RenameAvailableKind::Available;
if (getRelatedSystemDecl(VD)){
AvailKind = RenameAvailableKind::Unavailable_system_symbol;
Expand All @@ -8800,22 +8833,22 @@ void swift::ide::collectRenameAvailabilityInfo(
if (isa<AbstractFunctionDecl>(VD)) {
// Disallow renaming accessors.
if (isa<AccessorDecl>(VD))
return;
return None;

// Disallow renaming deinit.
if (isa<DestructorDecl>(VD))
return;
return None;

// Disallow renaming init with no arguments.
if (auto CD = dyn_cast<ConstructorDecl>(VD)) {
if (!CD->getParameters()->size())
return;
return None;

if (RefInfo && !RefInfo->IsArgLabel) {
NameMatcher Matcher(*(RefInfo->SF));
auto Resolved = Matcher.resolve({RefInfo->Loc, /*ResolveArgs*/true});
if (Resolved.LabelRanges.empty())
return;
return None;
}
}

Expand All @@ -8825,30 +8858,27 @@ void swift::ide::collectRenameAvailabilityInfo(
// 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;
return None;

if (RefInfo && !RefInfo->IsArgLabel) {
NameMatcher Matcher(*(RefInfo->SF));
auto Resolved = Matcher.resolve({RefInfo->Loc, /*ResolveArgs*/true});
if (Resolved.LabelRanges.empty())
return;
return None;
}
}
}
}

// Always return local rename for parameters.
// FIXME: if the cursor is on the argument, we should return global rename.
if (isa<ParamDecl>(VD)) {
Infos.emplace_back(RefactoringKind::LocalRename, AvailKind);
return;
}
if (isa<ParamDecl>(VD))
return RenameAvailabilityInfo{RefactoringKind::LocalRename, AvailKind};

// If the indexer considers VD a global symbol, then we apply global rename.
if (index::isLocalSymbol(VD))
Infos.emplace_back(RefactoringKind::LocalRename, AvailKind);
else
Infos.emplace_back(RefactoringKind::GlobalRename, AvailKind);
return RenameAvailabilityInfo{RefactoringKind::LocalRename, AvailKind};
return RenameAvailabilityInfo{RefactoringKind::GlobalRename, AvailKind};
}

void swift::ide::collectAvailableRefactorings(
Expand All @@ -8858,27 +8888,10 @@ void swift::ide::collectAvailableRefactorings(
CursorInfo->getSourceFile()->getASTContext().SourceMgr);

if (!ExcludeRename) {
if (RefactoringActionLocalRename::isApplicable(CursorInfo, DiagEngine))
Kinds.push_back(RefactoringKind::LocalRename);

switch (CursorInfo->getKind()) {
case CursorInfoKind::ModuleRef:
case CursorInfoKind::Invalid:
case CursorInfoKind::StmtStart:
case CursorInfoKind::ExprStart:
break;
case CursorInfoKind::ValueRef: {
auto ValueRefInfo = cast<ResolvedValueRefCursorInfo>(CursorInfo);
Optional<RenameRefInfo> RefInfo;
if (ValueRefInfo->isRef())
RefInfo = {CursorInfo->getSourceFile(), CursorInfo->getLoc(),
ValueRefInfo->isKeywordArgument()};
auto RenameOp =
getAvailableRenameForDecl(ValueRefInfo->getValueD(), RefInfo);
if (RenameOp.has_value() &&
RenameOp.value() == RefactoringKind::GlobalRename)
Kinds.push_back(RenameOp.value());
}
if (auto Info = getRenameInfo(CursorInfo)) {
if (Info->Availability.AvailableKind == RenameAvailableKind::Available) {
Kinds.push_back(Info->Availability.Kind);
}
}
}

Expand Down Expand Up @@ -9084,29 +9097,11 @@ int swift::ide::findLocalRenameRanges(
Diags.addConsumer(DiagConsumer);

auto StartLoc = Lexer::getLocForStartOfToken(SM, Range.getStart(SM));
ResolvedCursorInfoPtr CursorInfo =
evaluateOrDefault(SF->getASTContext().evaluator,
CursorInfoRequest{CursorInfoOwner(SF, StartLoc)},
new ResolvedCursorInfo());
auto ValueRefCursorInfo = dyn_cast<ResolvedValueRefCursorInfo>(CursorInfo);
if (!ValueRefCursorInfo || !ValueRefCursorInfo->getValueD()) {
Diags.diagnose(StartLoc, diag::unresolved_location);
return true;
}
ValueDecl *VD = ValueRefCursorInfo->typeOrValue();
Optional<RenameRefInfo> RefInfo;
if (ValueRefCursorInfo->isRef())
RefInfo = {CursorInfo->getSourceFile(), CursorInfo->getLoc(),
ValueRefCursorInfo->isKeywordArgument()};

llvm::SmallVector<DeclContext *, 8> Scopes;
analyzeRenameScope(VD, RefInfo, Diags, Scopes);
if (Scopes.empty())
Optional<RenameRangeCollector> RangeCollector =
localRenames(SF, StartLoc, StringRef(), Diags);
Comment on lines +9100 to +9101
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findLocalRenameRanges and the rename refactoring itself were both doing this. Back when I added shorthand shadow handling, I updated one and not the other (didn't see this one).

The two refactorings here are just merging those + changing collectRenameAvailabilityInfo to just give back the availability info since it was always just a single value, never both.

if (!RangeCollector)
return true;
RenameRangeCollector RangeCollector(VD, StringRef());
for (DeclContext *DC : Scopes)
indexDeclContext(DC, RangeCollector);

return findSyntacticRenameRanges(SF, RangeCollector.results(), RenameConsumer,
DiagConsumer);
return findSyntacticRenameRanges(SF, RangeCollector->results(),
RenameConsumer, DiagConsumer);
}
Loading