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

feat: Prioritize import suggestions based on the expected type #15627

Merged
merged 3 commits into from Dec 8, 2023

Conversation

jmintb
Copy link
Contributor

@jmintb jmintb commented Sep 18, 2023

Hi, this is a draft PR to solve #15384. Adt types work and now I have a few questions :)

  1. What other types make sense in this context? Looking at ModuleDef I am thinking everything except Modules.
  2. Is there an existing way of converting between ModeuleDef and hir::Type in the rustanalyzer code base?
  3. Does this approach seem sound to you?

Ups: Upon writing this I just realised that the enum test is invalided as there are no enum variants and this no variant is passed as a function argument.

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 18, 2023
Some(assets_for_path)
}

fn is_expected_type(ctx: &CompletionContext<'_>, located_import: &LocatedImport) -> bool {
Copy link
Member

Choose a reason for hiding this comment

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

This is the wrong way to approach it, our CompletionItem structure has a type_match field which is what we need to populate here. According to that, we are later going to sort the completions already. The render_resolution_with_import(RenderContext::new(ctx), path_ctx, import) call already does this for locals being completed in render_resolution_path. Now, the reason this therefore doesn't work here in flyimport already is that we are completin importable things which are never locals! See
https://github.com/Veykril/rust-analyzer/blob/994df3d6a31d39f11600f30a6df0b744b13937c1/crates/ide-completion/src/render.rs#L337-L351

We should instead generalize that part I believe, making it work for more scopedefs, mainly
ScopeDef::ImplSelfType, ScopeDef::AdtSelfType and reklevant ScopeDef::ModuleDef. For ModuleDef, all 3 Adts, Function, Variant, Const, Static and BuiltinType are relevant. (Some of those might not have a function allowing you to easily turn them into a hir version yet. It's fine to skip them in that case (or (ask about how) to implement them).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Awesome that makes a lot more sense haha.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I used UPDATE_EXPECT=1 to see what the new code produces. It looks more or less correct to me. I am not sure why there are two completion items though. Is it because we can choose to either import or use the fully qualified name? Also should type_match: Some(CouldUnify)actually be type_match: Some(Exact)? as this is the exact type we are expecting.

// crates/ide-completion/src/render.rs
 #[test]
    fn sort_suggested_structs_by_types() {
        check(
            r#"
//- /lib.rs crate:dep

pub mod test_mod_b {
    pub struct Struct {}
}

//- /main.rs crate:main deps:dep

fn test(input: dep::test_mod_b::Struct) { }

fn main() {
    test(Struct$0);
}
"#,
            SymbolKind::Struct,
            expect![[r#"
                [
                    CompletionItem {
                        label: "Struct (use dep::test_mod_b::Struct)",
                        source_range: 67..73,
                        delete: 67..73,
                        insert: "Struct",
                        kind: SymbolKind(
                            Struct,
                        ),
                        lookup: "Struct",
                        detail: "Struct",
                        relevance: CompletionRelevance {
                            exact_name_match: false,
                            type_match: Some(
                                CouldUnify,
                            ),
                            is_local: false,
                            is_item_from_trait: false,
                            is_name_already_imported: false,
                            requires_import: false,
                            is_op_method: false,
                            is_private_editable: false,
                            postfix_match: None,
                            is_definite: false,
                        },
                    },
                    CompletionItem {
                        label: "dep::test_mod_b::Struct {…}",
                        source_range: 67..73,
                        delete: 67..73,
                        insert: "dep::test_mod_b::Struct {  }$0",
                        kind: SymbolKind(
                            Struct,
                        ),
                        lookup: "test_mod_b::Struct{}",
                        detail: "dep::test_mod_b::Struct {  }",
                        relevance: CompletionRelevance {
                            exact_name_match: false,
                            type_match: Some(
                                CouldUnify,
                            ),
                            is_local: false,
                            is_item_from_trait: false,
                            is_name_already_imported: false,
                            requires_import: false,
                            is_op_method: false,
                            is_private_editable: false,
                            postfix_match: None,
                            is_definite: false,
                        },
                        trigger_call_info: true,
                    },
                ]
            "#]],
        );
    }

Copy link
Member

Choose a reason for hiding this comment

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

The qualified completion is from another code path, you can ignore that one. Exact doesn't work in this case, since imported things might have generics that we aren't filling out when constructing their type (since we don't know the concrete generics for them).

@Veykril Veykril added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 20, 2023
@@ -349,6 +349,23 @@ fn render_resolution_path(

path_ref_match(completion, path_ctx, &ty, &mut item);
};

if let ScopeDef::ModuleDef(ModuleDef::Adt(adt)) = resolution {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@Veykril is this the sort of thing you had in mind?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, that should be what I had in mind

if !ty.is_unknown() {
item.detail(ty.display(db).to_string());
}

item.set_relevance(CompletionRelevance {
type_match: compute_type_match(completion, &ty),
exact_name_match: compute_exact_name_match(completion, &name),
is_local: true,
is_local,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should the rest of CompletionRelevance be configured here? I assume options like is_name_already_imported are important.

Copy link
Member

Choose a reason for hiding this comment

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

is_name_already_imported is only used by use item completions, so we can ignore this here as its being set somewhere else already (in this case its overwriting the relevance calculated here)

Copy link
Member

Choose a reason for hiding this comment

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

Also instead of passing is_local as a param to the closre we can put is_local: matches!(resolution, ScopeDef::Local(_)) here. That simplifies things a bit. (I noticed you also set it to true for ImplSelfType but that property is specifically meant for local variables, the doc comment on that isn't clear on that.)

Copy link
Member

Choose a reason for hiding this comment

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

We should set the requires_import field for it here as well if import_to_add (the param of the function we are in right now is Some). The rest we can ignore I think.

@@ -595,6 +621,206 @@ mod tests {
}
}

#[test]
fn set_struct_type_completion_info() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you think of the tests? I will add similar tests for the remaining cases as well if these look good.

Copy link
Member

Choose a reason for hiding this comment

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

Instead of doing mere check tests here, let's make use of check_relevance instead, as that is what we are mainly interested in checking here (those are used a bit further down in this module)

@Veykril Veykril added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Oct 9, 2023
@jmintb jmintb force-pushed the sort_imports branch 5 times, most recently from 636fe55 to b8b1f2c Compare November 4, 2023 15:32
item.set_relevance(CompletionRelevance {
type_match: compute_type_match(completion, &ret_type),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looks like functions were only being type matched based on the return type. I am not sure if there was a reason for this 🤔

Copy link
Member

Choose a reason for hiding this comment

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

This should be done conditionally, by return type if we have a call / complete call, otherwise by signature.

@jmintb
Copy link
Contributor Author

jmintb commented Nov 4, 2023

I ended up going down a small rabbit hole getting the function type matching to work, but it should be good now.

Let me know what you think :)

@Veykril Veykril marked this pull request as ready for review December 8, 2023 12:31
@Veykril Veykril changed the title Draft: Prioritize import suggestions based on the expected type feat: Prioritize import suggestions based on the expected type Dec 8, 2023
@Veykril
Copy link
Member

Veykril commented Dec 8, 2023

I went ahead and cleaned up the remaining things, I hope you don't mind, I wanna get our PR backlog down and I imagine you might not have the PR in your head at this point anymore.
Thanks a lot for the work on this!
@bors r+

@bors
Copy link
Collaborator

bors commented Dec 8, 2023

📌 Commit 1475848 has been approved by Veykril

It is now in the queue for this repository.

@Veykril
Copy link
Member

Veykril commented Dec 8, 2023

I ended up going down a small rabbit hole getting the function type matching to work, but it should be good now.

I removed the signature match function again, as that is the wrong way to approach it (but you are right we need to do this ideally!) I'll open an issue on the matter and fill that out with details later. #16053

@bors
Copy link
Collaborator

bors commented Dec 8, 2023

⌛ Testing commit 1475848 with merge b03a0bd...

@bors
Copy link
Collaborator

bors commented Dec 8, 2023

☀️ Test successful - checks-actions
Approved by: Veykril
Pushing b03a0bd to master...

@bors bors merged commit b03a0bd into rust-lang:master Dec 8, 2023
10 checks passed
@jmintb
Copy link
Contributor Author

jmintb commented Dec 8, 2023

Awesome thanks :)

@lnicola
Copy link
Member

lnicola commented Dec 11, 2023

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants