-
Notifications
You must be signed in to change notification settings - Fork 304
Don’t repeat a function in incomingCalls
if it contains multiple calls to the same function
#1166
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
Don’t repeat a function in incomingCalls
if it contains multiple calls to the same function
#1166
Conversation
5053fc7
to
4d80ff6
Compare
@@ -2128,27 +2128,44 @@ extension SourceKitLSPServer { | |||
callableUsrs += index.occurrences(ofUSR: data.usr, roles: .overrideOf).flatMap { occurrence in | |||
occurrence.relations.filter { $0.roles.contains(.overrideOf) }.map(\.symbol.usr) | |||
} | |||
// callOccurrences are all the places that any of the USRs in callableUsrs is called. | |||
// We also load the `calledBy` roles to get the method that contains the reference to this call. |
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.
Sort of unrelated to this PR, but it'd be good to add a bunch of test cases for properties:
struct Foo {
var member: Int = {
return bar()
}();
func bar() {}
}
Call hierarchy for bar
probably should give member
there. member
isn't really the caller, but... I think it's what people would expect to see in the UI. Though probably want to extend that to something accessing member
, as I wouldn't necessarily say the call hierarchy should continue after member
(or if it did, it should go to the init
).
Also just a regular getter:
struct Foo {
var member: Int {
return bar()
}
func bar() {}
}
Plus explicit getter/setter/etc:
struct Foo {
var member: Int {
get {
return bar()
}
}
func bar() {}
}
Also CC @hamishknight as he's been looking at this a bit recently (both to comment on the above and also a review of this PR in general).
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.
We do already have a test for valuable getters FWIW: https://github.com/apple/sourcekit-lsp/blob/4ed62392e7b4f889d44c09e4d6857a0eb28975c3/Tests/SourceKitLSPTests/CallHierarchyTests.swift#L414-L452
I agree that the rest would be good to test
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.
Extending that with a use of foo
seems like a good thing to do as well.
for call in callOccurrences { | ||
// Callers are all `calledBy` relations of a call to a USR in `callableUsrs`, ie. all the functions that contain a | ||
// call to a USR in callableUSRs. In practice, this should always be a single item. | ||
let callers = call.relations.filter { $0.symbol.kind.isCallable }.map(\.symbol) |
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.
I think it would be better to check $0.roles
here, with swiftlang/swift#72930 it ought to be sufficient to check for .containedBy
, which would handle the case where the parent is a property (note that isCallable
would be false
in that case). Though for compatibility with older toolchains it may be better to check if the role contains .calledBy
, and if that's empty, fallback to checking for .containedBy
.
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.
Good suggestion with checking $0.roles
.
There is a subtle difference between calledBy
and containedBy
relations.
func foo() {}
var testVar: Int {
get {
let myVar = foo()
return 2
}
}
func test() {
_ = testVar
}
The call to foo
in let myVar = foo()
is called by s:4test0A3VarSivg
(demangled test.testVar.getter : Swift.Int
, display name getter:testVar
) while it is contained by s:4test0A3VarSivg02myB0L_ytvp
(demangled myVar #1 : () in test.testVar.getter : Swift.Int
/ display name testVar
). But s:4test0A3VarSivg02myB0L_ytvp
doesn’t have any callers while s:4test0A3VarSivg
does show the call inside test()
. So I’ll go with just checking the calledBy
relation. Or do you see any advantage of containedBy
over calledBy
?
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.
while it is contained by
s:4test0A3VarSivg02myB0L_ytvp
That should have been fixed by swiftlang/swift#72930, is it still an issue? The main advantage of containedBy
is that it works for cases like:
struct Foo {
var member: Int = {
return bar()
}()
func bar() {}
}
Even though member
isn't a callable symbol, it probably makes sense to consider it the caller (as we don't really have anything else to anchor it on). Generally, I think the right check is probably "called by, or container as a fallback"
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.
Oh, right, sorry, you mentioned that. I was using an open source toolchain snapshot.
Let me try again once use have a toolchain snapshot with swiftlang/swift#72930 because I can’t be bothered to build a toolchain myself.
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.
Should we stop the traversal if we use the container instead? Does it make sense to continue from member
there? Or should we have a calledBy to the init
?
incomingCalls
if it contains multiple calls to the same function 🚥 #1165incomingCalls
if it contains multiple calls to the same function
…lls to the same function Eg. if we have the following, and we get the call hierarchy of `foo`, we only want to show `bar` once, with multiple `fromRanges` instead of having two entries for `bar` in the call hierarchy. ```swift func foo() {} func bar() { foo() foo() } ```
4d80ff6
to
1e3488b
Compare
@swift-ci Please test |
@swift-ci Please test Linux |
Eg. if we have the following, and we get the call hierarchy of
foo
, we only want to showbar
once, with multiplefromRanges
instead of having two entries forbar
in the call hierarchy.