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

Completion context expected type #8008

Merged
merged 1 commit into from Mar 15, 2021

Conversation

JoshMcguigan
Copy link
Contributor

@JoshMcguigan JoshMcguigan commented Mar 14, 2021

Currently there are two ways completions use to determine the expected type. There is the expected_type field on the CompletionContext, as well as the expected_name_and_type method on the RenderContext. These two things returned slightly different results, and their results were only valid if you had pre-checked some (undocumented) invariants. A simple combination of the two approaches doesn't work because they are both too willing to go far up the syntax tree to find something that fits what they are looking for.

This PR makes the following changes:

  1. Updates the algorithm that sets expected_type on CompletionContext
  2. Adds expected_name field to CompletionContext
  3. Re-writes the expected_name_and_type method to simply return the underlying fields from CompletionContext (I'd like to save actually removing this method for a follow up PR just to keep the scope of the changes down)
  4. Adds unit tests for the expected_type/expected_name fields

All the existing unit tests still pass (unmodified), but this new algorithm certainly has some gaps (although I believe all the FIXME introduced in this PR are also flaws in the current code). I wanted to stop here and get some feedback though - is this approach fundamentally sound?

@matklad
Copy link
Member

matklad commented Mar 14, 2021

I'd like to save actually removing this method for a follow up PR just to keep the scope of the changes down

👍

The overall approach here does seem sound to me!

Comment on lines 336 to 341
// Is there some way to directly look up the type of the
// NameRef `a`?
Copy link
Member

Choose a reason for hiding this comment

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

You might be looking for https://github.com/rust-analyzer/rust-analyzer/blob/0c2f570151749d14e351f30fa234693b36bc4e72/crates/syntax/src/ast/node_ext.rs#L275-L282

Due to field shorthand, the AST situation here is non-trivial.

also, a bunch of map flattens can be converted to and_thens

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You might be looking for

I don't think this is what I need. I already have some code which gets the ast::RecordExprField, but then Semantics::resolve_record_field returns None because in this case my record field has no expr.

This is when the source code looks like:

struct A { a: u32 }

fn foo() {
    A { a: $0 };
}

I'm fairly certain the existing implementation (before this PR) has the same problem here, so I'd be happy to push this off to a future PR if the solution is complex.

Copy link
Member

Choose a reason for hiding this comment

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

yeah, plus one to postponing this!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This has been resolved after rebasing since #8025 merged 🎉

I've updated the tests and removed the FIXME comments in both places!

Comment on lines +369 to +354
cov_mark::hit!(expected_type_if_let_with_leading_char);
cov_mark::hit!(expected_type_if_let_without_leading_char);
cov_mark::hit!(expected_type_match_arm_with_leading_char);
Copy link
Member

Choose a reason for hiding this comment

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

I am a bit wary about several hit!s at the same place. To my mind, the difference between zero and one mark is huge, because that lets you to quickly identify the interesting test. Additional marks generally don't add as much value (other tests are probably nearby anyway), but they make the code more verbose.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I wasn't sure about this. I went forward with it because:

  1. These marks were genuinely useful in helping me reason about this code during development, and I figure if they are useful now as I am writing this code they will be at least 2x as useful when myself or someone else needs to change it in the future.
  2. I considered perhaps only having marks on some of the tests but then I'd need to arbitrarily draw the line somewhere, which seems harder than putting them in each test.

That said, I can remove them if you prefer that. Although if we want to remove them, I'd prefer to remove them all rather than only some of them (as discussed in point 2 above).

Copy link
Member

Choose a reason for hiding this comment

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

I am fine with keeping them all! I'd be strongly against removing all, as having just one mark is hugely helpful.

In the future, if someone sees this logic, they might wonder "ok, what's the actual rust code that causes us to hit this case?" By grepping for a mark, they can quickly identify one example.

Anyway, that's not really all that important

bors r+

@JoshMcguigan JoshMcguigan force-pushed the completion-expected-type branch 2 times, most recently from 6eb09ab to 7a521e6 Compare March 14, 2021 18:18
@matklad
Copy link
Member

matklad commented Mar 14, 2021 via email

@JoshMcguigan
Copy link
Contributor Author

I usually just not print anything, so that the expected output is an empty string in case the expectations are empty

This seems tricky if we want the expect test to be one line, which seems nice since it is so short. The problem is that in theory either field could be None, so if you just put both fields on one line without some labels or placeholders reading the tests could be difficult.

I think the current approach provides enough context to make the tests readable, without overwhelming with the amount of output. But of course this format is now easy to change, so I'm open to alternatives here.

@JoshMcguigan
Copy link
Contributor Author

@matklad is there anything else you'd for sure want to see before this merges? I know there is probably a lot that can be done to improve it, but I believe at this point it meets or exceeds the existing behavior in all cases.

Merging this sooner than later allows parallelizing two streams of work - one is further improvements of the calculations for expected_type and expected_name, and the other is further completion work (such as #7992) which would build on this.

@matklad
Copy link
Member

matklad commented Mar 15, 2021

is there anything else you'd for sure want to see before this merges?

Ah, sorry, I though I did give an d+ here, but apparently I didn't. Guess it's easier to invite you as a colaborator, so that you have r+ :)

@matklad
Copy link
Member

matklad commented Mar 15, 2021

bors r+

?

@bors
Copy link
Contributor

bors bot commented Mar 15, 2021

Already running a review

@lnicola
Copy link
Member

lnicola commented Mar 15, 2021

changelog internal clean up CompletionContext::expected_type

@bors
Copy link
Contributor

bors bot commented Mar 15, 2021

@bors bors bot merged commit 0ac7a19 into rust-lang:master Mar 15, 2021
JoshMcguigan added a commit to JoshMcguigan/rust-analyzer that referenced this pull request Mar 15, 2021
bors bot added a commit that referenced this pull request Mar 15, 2021
8027: Completion context remove exact match method in favor of fields r=JoshMcguigan a=JoshMcguigan

This is a minor cleanup PR following #8008. It removes the `expected_name_and_type` method on completion context in favor of using the fields. 

I thought this method was used in more places, or else it may have just made sense to make this change directly in #8008 🤷 

Co-authored-by: Josh Mcguigan <joshmcg88@gmail.com>
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.

None yet

3 participants