Skip to content
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

Swiftinterface symbol lookup #747

Merged
merged 10 commits into from
May 23, 2023

Conversation

adam-fowler
Copy link
Contributor

On calling definition if a symbol returns it is in a file with a .swiftinterface extension it will use OpenInterface to create the generated interface file. It will then call sourcekitd source.request.editor.find_usr to find the location of the symbol in the generated file.

  • OpenInterfaceRequest has been extended to include an optional symbol name.
  • Broke out openInterface code in SourceKitServer.definition into a separate function: respondWithInterface, as it is called from multiple sites now
  • If first occurrence of symbol definition occurs inside a swiftInterface file then use respondWithInterface to build generated interface and then find symbol location in generated interface.
  • Use DocumentManager to store instances of generated interface files, to avoid generating them on every call to openInterface and also cache the line table.
  • Added _findUSR function that calls source.request.editor.find_usr

When a symbol definition returns it is in a swiftinterface file, create textual version of swiftinterface and return that in response.
skreq[keys.usr] = symbol

if let compileCommand = self.commandsByFile[uri] {
skreq[keys.compilerargs] = compileCommand.compilerArgs
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wasn't sure if this was necessary

Copy link
Member

Choose a reason for hiding this comment

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

// textual interface
if let firstResolved = resolved.first,
let moduleName = firstResolved.occurrence?.location.moduleName,
firstResolved.location.uri.fileURL?.pathExtension == "swiftinterface" {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is checking the extension of location uri enough here?

@bnbarham
Copy link
Contributor

bnbarham commented May 19, 2023

I thought I might give some extra background here.

The cursor info request returns the group name (which is where the module splitting is coming from) as well as whether it's "dynamic", ie. whether it could be overridden. In the dynamic case we will also provide the "receiver" USRs which are the statically known types of the member that's being called.

If the returned symbol isn't dynamic, then there's no need to do an index lookup - we can jump straight to the location provided by the result (*). In the case that location is a header we can make a choice:

  1. Jump to the header
  2. Do an index lookup for the definition and jump to that (if we have the definition)
  3. Jump to the generated interface for that module
  4. Provide the user a choice for one of these

If it is dynamic, we should always do an index lookup - looking up and down the hierarchy from the given receiver types. This is basically to avoid going off into a separate subtree, eg. if we had D: C, C: B, B: A, E: B and a call on C.foo() we wouldn't want to provide E.foo() as a result, but we would want to give D.foo() since it's possible that's the actual call.

(*) This result wouldn't be any more/less up to date than the index since we only index-while-building. Though if we instead had background indexing, it might be better to consider the index result all the time. We could also background build though so... 🤷The dynamic/receiver USR data is also in the index.


With that said...

Is checking the extension of location uri enough here?

Depends on what we actually want to do regarding the header case above. Another case to consider is what happens when library evolution isn't enabled, though I'm not sure we really want to to support that in general. I believe (but haven't checked) that we don't return a path at all in that case, but ideally we'd still jump to the generated interface. The index wouldn't contain any results from such modules anyway.

If this is just about providing the generated interface for swift modules, then this check works well enough.

Is there a better way to do this? Are there other modules that might be split up like this?

Technically any library could provide this via -group-info-path, though it really only matters for when there's no source. The index records group names as if they were submodules for the stdlib, but for all other modules that's not the case. So the only accurate place to get this from is the cursor info request.

But considering only the stdlib is likely good enough here. In that case passing the group name to the generated interface request would also be a good thing to do. Splitting works if we can't get it from the cursor info request.

Comment on lines 37 to 44
self._findUSR(request: request, uri: interfaceDocURI, snapshot: snapshot, symbol: symbol) { result in
switch result {
case .success(let info):
request.reply(.success(InterfaceDetails(uri: interfaceDocURI, position: info.position)))
case .failure:
request.reply(.success(InterfaceDetails(uri: interfaceDocURI, position: nil)))
}
}
Copy link
Member

Choose a reason for hiding this comment

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

I wonder whether we can share this code between the two if branches

skreq[keys.usr] = symbol

if let compileCommand = self.commandsByFile[uri] {
skreq[keys.compilerargs] = compileCommand.compilerArgs
Copy link
Member

Choose a reason for hiding this comment

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

Comment on lines 138 to 143
if let offset: Int = dict[keys.offset],
let position = snapshot.positionOf(utf8Offset: offset) {
return completion(.success(FindUSRInfo(position: position)))
} else {
return completion(.success(FindUSRInfo(position: nil)))
}
Copy link
Member

Choose a reason for hiding this comment

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

Should be indented by 2 spaces, I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you use one of the swift formats to format the code?

Copy link
Member

Choose a reason for hiding this comment

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

No, SourceKit-LSP is manual 2 space indentation

rename symbol to symbolUSR
Cleanup OpenInterfaceRequest.init
Use SwiftPMPackage test module
Added version of buildAndIndex that includes system symbols
Copy link
Member

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

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

Thank you so much for this

@ahoppen
Copy link
Member

ahoppen commented May 22, 2023

@swift-ci Please test

Add multiple tests for various system modules
@adam-fowler
Copy link
Contributor Author

@ahoppen Hopefully fixed the tests. Had to create a new test SwiftPM project for the swift interface tests as my edits of an existing one broke another test. Also added tests for symbols from multiple libs, with submodules and not.

Copy link
Member

@ahoppen ahoppen left a comment

Choose a reason for hiding this comment

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

Thanks

@ahoppen
Copy link
Member

ahoppen commented May 23, 2023

@swift-ci Please test

@ahoppen
Copy link
Member

ahoppen commented May 23, 2023

@swift-ci Please test Windows

@ahoppen ahoppen merged commit ff59c1a into swiftlang:main May 23, 2023
@ahoppen
Copy link
Member

ahoppen commented May 23, 2023

@adam-fowler Could you also create a cherry-pick PR for release/5.9? I think it would be worth to include this improvement in the 5.9 release.

@adam-fowler
Copy link
Contributor Author

ok will do

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.

3 participants