-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[SourceKit] Update related identifiers request to serve local rename #70287
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
ahoppen
merged 5 commits into
swiftlang:main
from
ahoppen:ahoppen/related-identifiers-for-rename
Dec 8, 2023
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
143dccb
[SourceKit] Return `RenameLocUsage` in related identifiers response
ahoppen ddf4a2e
[SourceKit] Return the old name from the related identifiers response
ahoppen a24c563
[SourceKit] Allow references to non-editable base names to be returne…
ahoppen 2e806b1
[SourceKit] Return operator results from related identifiers request
ahoppen 516836c
[SourceKit] Don't handle labels when renaming operators
ahoppen File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// RUN: %empty-directory(%t) | ||
// RUN: split-file --leading-lines %s %t | ||
|
||
//--- a.swift | ||
|
||
struct Foo {} | ||
// RUN: %sourcekitd-test -req=related-idents -pos=%(line + 1):6 %t/a.swift -- %t/a.swift | %FileCheck %s | ||
func +(x: Foo, y: Foo) {} | ||
Foo() + Foo() | ||
|
||
//--- dummy.swift | ||
|
||
// CHECK: START RANGES | ||
// CHECK: 8:6 - 1 - source.syntacticrename.definition | ||
// CHECK: 9:7 - 1 - source.syntacticrename.call | ||
// CHECK: END RANGES | ||
// CHECK: NAME: +(_:_:) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// RUN: %empty-directory(%t) | ||
// RUN: split-file %s %t | ||
// RUN: %refactor -find-rename-ranges -source-filename %t/input.swift -pos="test" -old-name "+(x:y:)" -new-name "-(x:y:)" > %t/output.txt | ||
// RUN: diff -u %t/expected.swift %t/output.txt | ||
|
||
//--- input.swift | ||
|
||
struct Foo {} | ||
func /*test:def*/+(x: Foo, y: Foo) {} | ||
Foo() /*test:ref*/+ Foo() | ||
|
||
//--- expected.swift | ||
|
||
struct Foo {} | ||
func /*test:def*/<base>+</base>(x: Foo, y: Foo) {} | ||
Foo() /*test:ref*/<base>+</base> Foo() | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2491,126 +2491,139 @@ SwiftLangSupport::findUSRRange(StringRef DocumentName, StringRef USR) { | |
|
||
void SwiftLangSupport::findRelatedIdentifiersInFile( | ||
StringRef PrimaryFilePath, StringRef InputBufferName, unsigned Offset, | ||
bool CancelOnSubsequentRequest, ArrayRef<const char *> Args, | ||
SourceKitCancellationToken CancellationToken, | ||
std::function<void(const RequestResult<ArrayRef<RelatedIdentInfo>> &)> | ||
Receiver) { | ||
bool IncludeNonEditableBaseNames, bool CancelOnSubsequentRequest, | ||
ArrayRef<const char *> Args, SourceKitCancellationToken CancellationToken, | ||
std::function<void(const RequestResult<RelatedIdentsResult> &)> Receiver) { | ||
|
||
std::string Error; | ||
SwiftInvocationRef Invok = | ||
ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error); | ||
if (!Invok) { | ||
LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error); | ||
Receiver(RequestResult<ArrayRef<RelatedIdentInfo>>::fromError(Error)); | ||
Receiver(RequestResult<RelatedIdentsResult>::fromError(Error)); | ||
return; | ||
} | ||
|
||
class RelatedIdConsumer : public SwiftASTConsumer { | ||
std::string InputFile; | ||
unsigned Offset; | ||
std::function<void(const RequestResult<ArrayRef<RelatedIdentInfo>> &)> | ||
Receiver; | ||
bool IncludeNonEditableBaseNames; | ||
std::function<void(const RequestResult<RelatedIdentsResult> &)> Receiver; | ||
SwiftInvocationRef Invok; | ||
|
||
#if SWIFT_BUILD_SWIFT_SYNTAX | ||
// FIXME: Don't silently eat errors here. | ||
RelatedIdentsResult getRelatedIdents(SourceFile *SrcFile, | ||
CompilerInstance &CompInst) { | ||
unsigned BufferID = SrcFile->getBufferID().value(); | ||
SourceLoc Loc = Lexer::getLocForStartOfToken(CompInst.getSourceMgr(), | ||
BufferID, Offset); | ||
if (Loc.isInvalid()) | ||
return RelatedIdentsResult::empty(); | ||
|
||
SourceManager &SrcMgr = CompInst.getASTContext().SourceMgr; | ||
|
||
ResolvedCursorInfoPtr CursorInfo = | ||
evaluateOrDefault(CompInst.getASTContext().evaluator, | ||
CursorInfoRequest{CursorInfoOwner(SrcFile, Loc)}, | ||
new ResolvedCursorInfo()); | ||
auto ValueRefCursorInfo = | ||
dyn_cast<ResolvedValueRefCursorInfo>(CursorInfo); | ||
if (!ValueRefCursorInfo) | ||
return RelatedIdentsResult::empty(); | ||
if (ValueRefCursorInfo->isKeywordArgument()) | ||
return RelatedIdentsResult::empty(); | ||
|
||
ValueDecl *VD = ValueRefCursorInfo->typeOrValue(); | ||
if (!VD) | ||
return RelatedIdentsResult::empty(); // This was a module reference. | ||
|
||
// Only accept pointing to an identifier. | ||
if (!IncludeNonEditableBaseNames && !ValueRefCursorInfo->isRef() && | ||
(isa<ConstructorDecl>(VD) || isa<DestructorDecl>(VD) || | ||
isa<SubscriptDecl>(VD))) | ||
return RelatedIdentsResult::empty(); | ||
|
||
llvm::Optional<RenameInfo> Info = getRenameInfo(CursorInfo); | ||
|
||
if (!Info) { | ||
return RelatedIdentsResult::empty(); | ||
} | ||
|
||
RenameLocs Locs = localRenameLocs(SrcFile, Info->VD); | ||
|
||
std::string OldName = Locs.getLocations().front().OldName.str(); | ||
#ifndef NDEBUG | ||
for (auto loc : Locs.getLocations()) { | ||
assert(loc.OldName == OldName && | ||
"Found related identfiers with different names?"); | ||
} | ||
#endif | ||
|
||
// Ignore any errors produced by `resolveRenameLocations` since, if some | ||
// symbol failed to resolve, we still want to return all the other | ||
// symbols. This makes related idents more fault-tolerant. | ||
DiagnosticEngine Diags(SrcMgr); | ||
|
||
std::vector<ResolvedLoc> ResolvedLocs = resolveRenameLocations( | ||
Locs.getLocations(), /*NewName=*/StringRef(), *SrcFile, Diags); | ||
|
||
SmallVector<RelatedIdentInfo, 8> Ranges; | ||
assert(ResolvedLocs.size() == Locs.getLocations().size()); | ||
for (auto [RenameLoc, ResolvedLoc] : | ||
llvm::zip_equal(Locs.getLocations(), ResolvedLocs)) { | ||
Comment on lines
+2573
to
+2574
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TIL about structured bindings, neat! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I only found them by searching for |
||
if (ResolvedLoc.range.isInvalid()) { | ||
continue; | ||
} | ||
unsigned Offset = | ||
SrcMgr.getLocOffsetInBuffer(ResolvedLoc.range.getStart(), BufferID); | ||
Ranges.push_back( | ||
{Offset, ResolvedLoc.range.getByteLength(), RenameLoc.Usage}); | ||
} | ||
|
||
return RelatedIdentsResult(Ranges, OldName); | ||
} | ||
#endif | ||
|
||
public: | ||
RelatedIdConsumer( | ||
StringRef InputFile, unsigned Offset, | ||
std::function<void(const RequestResult<ArrayRef<RelatedIdentInfo>> &)> | ||
StringRef InputFile, unsigned Offset, bool IncludeNonEditableBaseNames, | ||
std::function<void(const RequestResult<RelatedIdentsResult> &)> | ||
Receiver, | ||
SwiftInvocationRef Invok) | ||
: InputFile(InputFile.str()), Offset(Offset), | ||
IncludeNonEditableBaseNames(IncludeNonEditableBaseNames), | ||
Receiver(std::move(Receiver)), Invok(Invok) {} | ||
|
||
// FIXME: Don't silently eat errors here. | ||
void handlePrimaryAST(ASTUnitRef AstUnit) override { | ||
using ResultType = RequestResult<ArrayRef<RelatedIdentInfo>>; | ||
using ResultType = RequestResult<RelatedIdentsResult>; | ||
#if !SWIFT_BUILD_SWIFT_SYNTAX | ||
Receiver( | ||
ResultType::fromError("relatedidents is not supported because " | ||
"sourcekitd was built without swift-syntax")); | ||
ResultType::fromError( | ||
"relatedidents is not supported because sourcekitd was built without " | ||
"swift-syntax"); | ||
return; | ||
#else | ||
auto &CompInst = AstUnit->getCompilerInstance(); | ||
|
||
auto *SrcFile = retrieveInputFile(InputFile, CompInst); | ||
if (!SrcFile) { | ||
Receiver(RequestResult<ArrayRef<RelatedIdentInfo>>::fromError( | ||
Receiver(RequestResult<RelatedIdentsResult>::fromError( | ||
"Unable to find input file")); | ||
return; | ||
} | ||
|
||
SmallVector<RelatedIdentInfo, 8> Ranges; | ||
|
||
auto Action = [&]() { | ||
unsigned BufferID = SrcFile->getBufferID().value(); | ||
SourceLoc Loc = | ||
Lexer::getLocForStartOfToken(CompInst.getSourceMgr(), BufferID, Offset); | ||
if (Loc.isInvalid()) | ||
return; | ||
|
||
SourceManager &SrcMgr = CompInst.getASTContext().SourceMgr; | ||
|
||
ResolvedCursorInfoPtr CursorInfo = | ||
evaluateOrDefault(CompInst.getASTContext().evaluator, | ||
CursorInfoRequest{CursorInfoOwner(SrcFile, Loc)}, | ||
new ResolvedCursorInfo()); | ||
auto ValueRefCursorInfo = | ||
dyn_cast<ResolvedValueRefCursorInfo>(CursorInfo); | ||
if (!ValueRefCursorInfo) | ||
return; | ||
if (ValueRefCursorInfo->isKeywordArgument()) | ||
return; | ||
|
||
ValueDecl *VD = ValueRefCursorInfo->typeOrValue(); | ||
if (!VD) | ||
return; // This was a module reference. | ||
|
||
// Only accept pointing to an identifier. | ||
if (!ValueRefCursorInfo->isRef() && | ||
(isa<ConstructorDecl>(VD) || isa<DestructorDecl>(VD) || | ||
isa<SubscriptDecl>(VD))) | ||
return; | ||
if (VD->isOperator()) | ||
return; | ||
|
||
llvm::Optional<RenameInfo> Info = getRenameInfo(CursorInfo); | ||
|
||
if (!Info) { | ||
return; | ||
} | ||
|
||
RenameLocs Locs = localRenameLocs(SrcFile, Info->VD); | ||
|
||
// Ignore any errors produced by `resolveRenameLocations` since, if some | ||
// symbol failed to resolve, we still want to return all the other | ||
// symbols. This makes related idents more fault-tolerant. | ||
DiagnosticEngine Diags(SrcMgr); | ||
|
||
std::vector<ResolvedLoc> ResolvedLocs = resolveRenameLocations( | ||
Locs.getLocations(), /*NewName=*/StringRef(), *SrcFile, Diags); | ||
|
||
assert(ResolvedLocs.size() == Locs.getLocations().size()); | ||
for (auto ResolvedLoc : ResolvedLocs) { | ||
if (ResolvedLoc.range.isInvalid()) { | ||
continue; | ||
} | ||
unsigned Offset = SrcMgr.getLocOffsetInBuffer( | ||
ResolvedLoc.range.getStart(), BufferID); | ||
Ranges.push_back({Offset, ResolvedLoc.range.getByteLength()}); | ||
} | ||
}; | ||
Action(); | ||
Receiver(ResultType::fromResult(Ranges)); | ||
RelatedIdentsResult Result = getRelatedIdents(SrcFile, CompInst); | ||
Receiver(ResultType::fromResult(Result)); | ||
#endif | ||
} | ||
|
||
void cancelled() override { | ||
Receiver(RequestResult<ArrayRef<RelatedIdentInfo>>::cancelled()); | ||
Receiver(RequestResult<RelatedIdentsResult>::cancelled()); | ||
} | ||
|
||
void failed(StringRef Error) override { | ||
LOG_WARN_FUNC("related idents failed: " << Error); | ||
Receiver(RequestResult<ArrayRef<RelatedIdentInfo>>::fromError(Error)); | ||
Receiver(RequestResult<RelatedIdentsResult>::fromError(Error)); | ||
} | ||
|
||
static CaseStmt *getCaseStmtOfCanonicalVar(Decl *D) { | ||
|
@@ -2624,8 +2637,8 @@ void SwiftLangSupport::findRelatedIdentifiersInFile( | |
} | ||
}; | ||
|
||
auto Consumer = std::make_shared<RelatedIdConsumer>(InputBufferName, Offset, | ||
Receiver, Invok); | ||
auto Consumer = std::make_shared<RelatedIdConsumer>( | ||
InputBufferName, Offset, IncludeNonEditableBaseNames, Receiver, Invok); | ||
/// FIXME: When request cancellation is implemented and Xcode adopts it, | ||
/// don't use 'OncePerASTToken'. | ||
static const char OncePerASTToken = 0; | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably optimized out, but I'd surround in
#ifndef NDEBUG
if you want this. This would be the main case for a separate request I suppose, since ideally we wouldn't needOldName
on every single returned location.