Merge #3574
3574: Fix completion of HashMap::new r=matklad a=flodiebold The `ty` function in code_model returned the type with placeholders for type parameters. That's nice for printing, but not good for completion, because placeholders won't unify with anything else: So the type we got for `HashMap` was `HashMap<K, V, T>`, which doesn't unify with `HashMap<?, ?, RandomState>`, so the `new` method wasn't shown. Now we instead return `HashMap<{unknown}, {unknown}, {unknown}>`, which does unify with the impl type. Maybe we should just expose this properly as variables though, i.e. we'd return something like `exists<type, type, type> HashMap<?0, ?1, ?2>` (in Chalk notation). It'll make the API more complicated, but harder to misuse. (And it would handle cases like `type TypeAlias<T> = HashMap<T, T>` more correctly.) The `ty` function for fields was used for signature help, so there we want placeholders so that it looks nicer, I think. Hence I renamed it to `signature_ty`. Co-authored-by: Florian Diebold <florian.diebold@freiheit.com>
This commit is contained in:
commit
93527b5f19
@ -311,7 +311,11 @@ impl StructField {
|
||||
self.parent.variant_data(db).fields()[self.id].name.clone()
|
||||
}
|
||||
|
||||
pub fn ty(&self, db: &impl HirDatabase) -> Type {
|
||||
/// Returns the type as in the signature of the struct (i.e., with
|
||||
/// placeholder types for type parameters). This is good for showing
|
||||
/// signature help, but not so good to actually get the type of the field
|
||||
/// when you actually have a variable of the struct.
|
||||
pub fn signature_ty(&self, db: &impl HirDatabase) -> Type {
|
||||
let var_id = self.parent.into();
|
||||
let generic_def_id: GenericDefId = match self.parent {
|
||||
VariantDef::Struct(it) => it.id.into(),
|
||||
@ -485,6 +489,10 @@ impl Adt {
|
||||
let subst = db.generic_defaults(self.into());
|
||||
subst.iter().any(|ty| ty == &Ty::Unknown)
|
||||
}
|
||||
|
||||
/// Turns this ADT into a type. Any type parameters of the ADT will be
|
||||
/// turned into unknown types, which is good for e.g. finding the most
|
||||
/// general set of completions, but will not look very nice when printed.
|
||||
pub fn ty(self, db: &impl HirDatabase) -> Type {
|
||||
let id = AdtId::from(self);
|
||||
Type::from_def(db, id.module(db).krate, id)
|
||||
@ -1031,7 +1039,7 @@ impl Type {
|
||||
krate: CrateId,
|
||||
def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>,
|
||||
) -> Type {
|
||||
let substs = Substs::type_params(db, def);
|
||||
let substs = Substs::build_for_def(db, def).fill_with_unknown().build();
|
||||
let ty = db.ty(def.into()).subst(&substs);
|
||||
Type::new(db, krate, def, ty)
|
||||
}
|
||||
|
@ -543,6 +543,20 @@ fn main() {
|
||||
assert_eq!(info.active_parameter, Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generic_struct() {
|
||||
let info = call_info(
|
||||
r#"
|
||||
struct TS<T>(T);
|
||||
fn main() {
|
||||
let s = TS(<|>);
|
||||
}"#,
|
||||
);
|
||||
|
||||
assert_eq!(info.label(), "struct TS<T>(T) -> TS");
|
||||
assert_eq!(info.active_parameter, Some(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn cant_call_named_structs() {
|
||||
|
@ -1005,4 +1005,36 @@ mod tests {
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_hashmap_new() {
|
||||
assert_debug_snapshot!(
|
||||
do_reference_completion(
|
||||
r"
|
||||
struct RandomState;
|
||||
struct HashMap<K, V, S = RandomState> {}
|
||||
|
||||
impl<K, V> HashMap<K, V, RandomState> {
|
||||
pub fn new() -> HashMap<K, V, RandomState> { }
|
||||
}
|
||||
fn foo() {
|
||||
HashMap::<|>
|
||||
}
|
||||
"
|
||||
),
|
||||
@r###"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "new()",
|
||||
source_range: [292; 292),
|
||||
delete: [292; 292),
|
||||
insert: "new()$0",
|
||||
kind: Function,
|
||||
lookup: "new",
|
||||
detail: "pub fn new() -> HashMap<K, V, RandomState>",
|
||||
},
|
||||
]
|
||||
"###
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -273,8 +273,10 @@ impl Completions {
|
||||
pub(crate) fn add_enum_variant(&mut self, ctx: &CompletionContext, variant: hir::EnumVariant) {
|
||||
let is_deprecated = is_deprecated(variant, ctx.db);
|
||||
let name = variant.name(ctx.db);
|
||||
let detail_types =
|
||||
variant.fields(ctx.db).into_iter().map(|field| (field.name(ctx.db), field.ty(ctx.db)));
|
||||
let detail_types = variant
|
||||
.fields(ctx.db)
|
||||
.into_iter()
|
||||
.map(|field| (field.name(ctx.db), field.signature_ty(ctx.db)));
|
||||
let detail = match variant.kind(ctx.db) {
|
||||
StructKind::Tuple | StructKind::Unit => {
|
||||
join(detail_types.map(|(_, t)| t.display(ctx.db).to_string()))
|
||||
|
@ -64,7 +64,7 @@ impl FunctionSignature {
|
||||
.fields(db)
|
||||
.into_iter()
|
||||
.map(|field: hir::StructField| {
|
||||
let ty = field.ty(db);
|
||||
let ty = field.signature_ty(db);
|
||||
format!("{}", ty.display(db))
|
||||
})
|
||||
.collect();
|
||||
@ -102,7 +102,7 @@ impl FunctionSignature {
|
||||
.into_iter()
|
||||
.map(|field: hir::StructField| {
|
||||
let name = field.name(db);
|
||||
let ty = field.ty(db);
|
||||
let ty = field.signature_ty(db);
|
||||
format!("{}: {}", name, ty.display(db))
|
||||
})
|
||||
.collect();
|
||||
|
Loading…
x
Reference in New Issue
Block a user