-
Notifications
You must be signed in to change notification settings - Fork 93
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
Improved (but possibly expensive) reference-finding #1800
Conversation
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'm a bit wary of a an analysis that needs to traverse the whole AST, although it might not be too horrible in practice, things like that tend to add up over time. It seems what we want is to have an undirected edge between a usage and a definition. Would that be too hard to just store the reverse edge alongside the definition node each time we link from a usage site to a definition site? I think the AST isn't mutated once it's built, so it shouldn't be too error-prone, as there should be only one place where we create such links (if we had to handle edits that would remove such edges it might start to be a bit more annoying).
So we do store a link between usage and definition sites, but in a way that turns out not to be useful enough. Specifically, "definition" here means a let binding, function parameter, or field in a record, and "usage" means a The problem is that in order to have a nice LSP experience, we really want "usage" to also include |
I see. Maybe I misunderstood then: the additional traversal of the AST to find all static accesses is done only once, and then is accessed through a Hashmap? In this case I believe it's reasonable (whether it can be improved or not), as it's just an additional pass. I initially thought we would have to traverse the whole AST for each and every request/renaming, which is something different really. |
Right: the full traversal to find the static accesses is done once. On each request, we look up all the potentially related static accesses in a hashmap and do some partial traversals for each one. But if you have a lot of fields with the same name, it could still be a lot of processing per request. |
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.
LGTM. I think it might be worth adding a short summary of what is discussed in this PR to #1484 (comment), for future reference.
@@ -212,6 +227,7 @@ pub struct Analysis { | |||
pub usage_lookup: UsageLookup, | |||
pub parent_lookup: ParentLookup, | |||
pub type_lookup: CollectedTypes<Type>, | |||
pub static_accesses: HashMap<Ident, Vec<RichTerm>>, |
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.
Might be worth a little description of what it is
The backstory here is that I attempted to implement renaming support in LSP but ran into some limitations with our support for finding references to record fields. Specifically, in an example like
we could tell that the definition of
y.foo
is thefoo
in{ foo = 1 }
, but we couldn't go the other way around. There are two difficulties in implementing the other direction:This PR implements reference-finding in a way that's possibly inefficient but easy to keep in sync: to find references too the first
foo
in the example above, we find all places with a.foo
static access, run the definition-finding direction that we've already implemented, and check whether thefoo
we started with is one of the definitions that was found.I definitely need to do some more performance measurements here, but at least its fast on the biggest examples I have easily available (some 2000-line json-schema-to-nickel outputs, and the organist source tree).