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
Conversation
89bc4fe
to
5e630fb
Compare
Some(assets_for_path) | ||
} | ||
|
||
fn is_expected_type(ctx: &CompletionContext<'_>, located_import: &LocatedImport) -> bool { |
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.
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 Adt
s, 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).
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.
Awesome that makes a lot more sense haha.
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 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,
},
]
"#]],
);
}
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.
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).
crates/ide-completion/src/render.rs
Outdated
@@ -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 { |
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.
@Veykril is this the sort of thing you had in mind?
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.
Yes, that should be what I had in mind
crates/ide-completion/src/render.rs
Outdated
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, |
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 the rest of CompletionRelevance
be configured here? I assume options like is_name_already_imported
are important.
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.
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)
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.
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.)
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 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() { |
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.
What do you think of the tests? I will add similar tests for the remaining cases as well if these look good.
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.
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)
636fe55
to
b8b1f2c
Compare
item.set_relevance(CompletionRelevance { | ||
type_match: compute_type_match(completion, &ret_type), |
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.
Looks like functions were only being type matched based on the return type. I am not sure if there was a reason for this 🤔
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.
This should be done conditionally, by return type if we have a call / complete call, otherwise by signature.
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 :) |
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. |
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 |
☀️ Test successful - checks-actions |
Awesome thanks :) |
Hi, this is a draft PR to solve #15384.
Adt
types work and now I have a few questions :)ModeuleDef
andhir::Type
in the rustanalyzer code base?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.