Skip to content

Show overriden functions when performing jump-to-definition on a dynamic call #977

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 4 commits into from
Dec 6, 2023

Conversation

ahoppen
Copy link
Member

@ahoppen ahoppen commented Nov 23, 2023

Fixes #809
rdar://114864256

@ahoppen
Copy link
Member Author

ahoppen commented Nov 23, 2023

@swift-ci Please test

@ahoppen ahoppen force-pushed the ahoppen/jump-to-override branch from 5e89ed3 to a3f7f11 Compare November 27, 2023 23:37
@ahoppen ahoppen changed the title Show overriden functions when performing jump-to-definition on a dynamic call 🚥 #975 Show overriden functions when performing jump-to-definition on a dynamic call Nov 27, 2023
@ahoppen
Copy link
Member Author

ahoppen commented Nov 27, 2023

@swift-ci Please test

@ahoppen
Copy link
Member Author

ahoppen commented Nov 27, 2023

@swift-ci Please test Windows

logger.info("performing indexed jump-to-def with usr \(usr)")
var occurs = index.occurrences(ofUSR: usr, roles: [.definition])
if occurs.isEmpty {
occurs = index.occurrences(ofUSR: usr, roles: [.declaration])
}
if symbolDetails.isDynamic {
occurs += occurs.flatMap {
index.occurrences(relatedToUSR: $0.symbol.usr, roles: .overrideOf)
Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally we should filter by receivers here, ie.

  • Get the set of types to filter (receivers + their subtypes)
  • For each override check if it's a child of one of the types in the filter

Copy link
Member Author

Choose a reason for hiding this comment

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

Added a filter by receiver types.

@@ -1725,12 +1725,18 @@ extension SourceKitServer {
}

let resolved = self.extractIndexedOccurrences(symbols: symbols, index: index, useLocalFallback: true) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We could just skip this if the symbol isn't dynamic - the index will be at most up to date as the location in the module, or worse. Though that would change if background indexing was added (or preparation, if we go that route instead).

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I just noticed this is on SourceKitServer and not the language service. Would still be nice to skip for Swift IMO. As for clangd not returning dynamic, that's something we could change (the request we're using is one we specifically added for this use case - https://reviews.llvm.org/D54799). But in the meantime, I'd default to returning overrides if isDynamic isn't set.

Copy link
Member Author

Choose a reason for hiding this comment

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

Changed it so that we don’t do an index lookup if SymbolRequest returns a location.

@ahoppen ahoppen force-pushed the ahoppen/jump-to-override branch from a3f7f11 to 1ccccc3 Compare November 28, 2023 03:56
@ahoppen
Copy link
Member Author

ahoppen commented Nov 29, 2023

@swift-ci Please test

`extractIndexedOccurences` mostly dealt with how to create a fallback value and we didn’t support fallback values in 3/4 cases. Remove it, simplifying the callers of `extractIndexedOccurances` along the way.
@ahoppen ahoppen force-pushed the ahoppen/jump-to-override branch from 9aa0257 to 84fdea9 Compare November 29, 2023 18:20
@ahoppen
Copy link
Member Author

ahoppen commented Nov 29, 2023

@swift-ci Please test

@ahoppen
Copy link
Member Author

ahoppen commented Nov 29, 2023

@swift-ci Please test Windows

return []
}

if let symbol = symbols.first, let bestLocalDeclaration = symbol.bestLocalDeclaration, !(symbol.isDynamic ?? false)
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't we default isDynamic to true so that it's always doing an index lookup (this would be the clang case)?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, you’re right. I added a test case for this.

Comment on lines +1681 to +1683
guard let symbol = symbols.first else {
return []
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I realize the old code only handled a single result, but is there any reason not to just handle multiple for all of these?

Copy link
Member Author

Choose a reason for hiding this comment

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

There is not. I thought we had an issue for this but looks like we don’t. Filed rdar://119163908

Comment on lines 1729 to 1730
// if first resolved location is in `.swiftinterface` file. Use moduleName to return
// textual interface
Copy link
Contributor

Choose a reason for hiding this comment

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

What does "first" here mean?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, it used to refer to the fact that everything was based on symbols.first. Rephrased the comment slightly.

Comment on lines 1751 to 1753
do {
return try await languageService.definition(req)
} catch {}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we ignoring the error here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Changed to orLog and added the following comment:

We are on only calling the language service's definition function if your index-based lookup failed. If this fallback request fails, its error is usually not very enlightening. For example the SwiftLanguageServer will always respond with unsupported method. Thus, only log such a failure instead of returning it to the client.

// Only contain overrides that are children of one of the receiver types or their subtypes.
return overrides.filter { override in
override.relations.contains(where: {
$0.roles.contains(.childOf) && transitiveReceiverUsrs.contains($0.symbol.usr)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
$0.roles.contains(.childOf) && transitiveReceiverUsrs.contains($0.symbol.usr)
$0.roles.contains(.childOf) && (transitiveReceiverUsrs.isEmpty || transitiveReceiverUsrs.contains($0.symbol.usr))

Copy link
Member Author

Choose a reason for hiding this comment

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

Just noticed that as well. Changed it slightly so that transitiveReceiverUsrs is nil if not receivers were passed.

@ahoppen
Copy link
Member Author

ahoppen commented Dec 5, 2023

@swift-ci Please test

1 similar comment
@ahoppen
Copy link
Member Author

ahoppen commented Dec 5, 2023

@swift-ci Please test

@ahoppen ahoppen merged commit ed410e6 into swiftlang:main Dec 6, 2023
@ahoppen ahoppen deleted the ahoppen/jump-to-override branch December 6, 2023 00:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Better jump to definition for implementations of a protocol
3 participants