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:
bors[bot] 2020-03-13 12:05:02 +00:00 committed by GitHub
commit 93527b5f19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 62 additions and 6 deletions

View File

@ -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)
}

View File

@ -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() {

View File

@ -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>",
},
]
"###
);
}
}

View File

@ -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()))

View File

@ -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();