Conversation
With this change, API graphs will recognise reads of unqualified constants
inside a modules as members of that module, provided a matching constant
definition exists. For example:
module A
class B
end
B # getMember("A").getMember("B")
end
c418419 to
4814103
Compare
|
If the class module A
B # is this ::B or A::B?
endI think it's important to keep in mind that API graphs are intended to reason about how the codebase interacts with external code. It's not meant as a tool for name binding or call resolution internally in the codebase. (Or at least if that's what you're doing, I think you're diverging from JS/Python in a way that's hard to reconcile later). It's all about the interaction across the library boundary. In Ruby it's hard to see what comes from a library, so we have to conservatively build up the API graph for anything that could reasonably come from a library, such as the So I look at the situation like this:
I think the proper way to model this is in the API graph:
My rationale here is that it works out in both of the following cases:
OK so spurious edges in the API graph can actually matter in a few ways.
But I think this is the direction we need to go in. Note that the graph representation I mentioned (with a use-node for |
|
Hey, thanks for the detailed response! I haven't gone through it entirely yet, but I'll flesh out this comment as I do. First off:
We recommend to external query writers that they should use API Graphs to find uses of classes defined in their own code, so at least for Ruby, they have a wider use case than just finding uses of external code.
I think we need to be careful about terminology here, so we don't get confused. "extends" has a specific meaning in Ruby, and unless we're literally talking about # inside library
module SomeLibraryModule
...
end
# inside codebase
module SomeLibraryModule
# some new methods etc.
...
endIs that right? So then for the two scenarios you outlined:
This is true only if we don't care about using API Graphs to find uses of codebase-defined stuff. As I mentioned above, I think we do care about this (though it's less important than finding uses of library-defined stuff).
Yep, totally agree. This change doesn't help at all for that scenario.
I like this approach, but I'm not sure about having a use-node for |
Yes, thanks, it sounds like that's the correct term for what I was talking about. 👍
In order to open up the pre-existing module, the
I think this is fine for one-off recommendations where it helps a user make progress, but it might not be the best idea long-term. We've talked about wanting to share and reconcile more between language implementations and this would be a case of Ruby sort of doing its own thing. But this is largely out of scope for this PR -- I'm hoping we can have a wider team discussion about API graphs soon. But actually there's another concrete reason I'm mentioning this. When the declaration of |
Just following up on this, sorry for the long delay. This is a compelling argument to me, and it's clear that there's a lot of improvements we could make to our call resolution in general, so it fits in well with that. I'll close this PR and perhaps we can revisit it another time if the need arises. |
In the following code:
we currently fail to identify
(1)as a result ofAPI::getTopLevelMember("A").getMember("B"). We do identify(2).This PR fixes that, so we identify both
(1)and(2).The implementation is naive and, I think, may have performance issues. Evaluation shows a small but significant increase in analysis time. I'd be keen to hear advice on how to make this faster.