From ac3c18bc17eddb62aab0abf9c7c6dea9d17dd0b1 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Tue, 10 May 2022 16:54:31 +0200 Subject: [PATCH] Include assoc. types in trait signature help --- crates/ide/src/signature_help.rs | 180 ++++++++++++++++++++++++++++++- 1 file changed, 176 insertions(+), 4 deletions(-) diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index e603cb4862..32e7c59b2a 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -1,8 +1,10 @@ //! This module provides primitives for showing type and function parameter information when editing //! a call or use-site. +use std::collections::BTreeSet; + use either::Either; -use hir::{GenericParam, HasAttrs, HirDisplay, Semantics}; +use hir::{AssocItem, GenericParam, HasAttrs, HirDisplay, Semantics, Trait}; use ide_db::{active_parameter::callable_for_node, base_db::FilePosition}; use stdx::format_to; use syntax::{ @@ -316,11 +318,52 @@ fn signature_help_for_generics( format_to!(buf, "{}", param.display(db)); res.push_generic_param(&buf); } + if let hir::GenericDef::Trait(tr) = generics_def { + add_assoc_type_bindings(db, &mut res, tr, arg_list); + } res.signature.push('>'); Some(res) } +fn add_assoc_type_bindings( + db: &RootDatabase, + res: &mut SignatureHelp, + tr: Trait, + args: ast::GenericArgList, +) { + if args.syntax().ancestors().find_map(ast::TypeBound::cast).is_none() { + // Assoc type bindings are only valid in type bound position. + return; + } + + let present_bindings = args + .generic_args() + .filter_map(|arg| match arg { + ast::GenericArg::AssocTypeArg(arg) => arg.name_ref().map(|n| n.to_string()), + _ => None, + }) + .collect::>(); + + let mut buf = String::new(); + for binding in &present_bindings { + buf.clear(); + format_to!(buf, "{} = …", binding); + res.push_generic_param(&buf); + } + + for item in tr.items_with_supertraits(db) { + if let AssocItem::TypeAlias(ty) = item { + let name = ty.name(db).to_smol_str(); + if !present_bindings.contains(&*name) { + buf.clear(); + format_to!(buf, "{} = …", name); + res.push_generic_param(&buf); + } + } + } +} + #[cfg(test)] mod tests { use std::iter; @@ -368,10 +411,11 @@ mod tests { panic!("parameter ranges out of order: {:?}", sig_help.parameter_ranges()) }); rendered.extend(iter::repeat(' ').take(gap as usize)); - let width = u32::from(range.end() - range.start()); + let param_text = &sig_help.signature[*range]; + let width = param_text.chars().count(); // … let marker = if is_active { '^' } else { '-' }; - rendered.extend(iter::repeat(marker).take(width as usize)); - offset += gap + width; + rendered.extend(iter::repeat(marker).take(width)); + offset += gap + u32::from(range.len()); } if !sig_help.parameter_ranges().is_empty() { format_to!(rendered, "\n"); @@ -1124,6 +1168,134 @@ fn f() { ); } + #[test] + fn test_trait_assoc_types() { + check( + r#" +trait Trait<'a, T> { + type Assoc; +} +fn f() -> impl Trait<(), $0 + "#, + expect![[r#" + trait Trait<'a, T, Assoc = …> + -- - ^^^^^^^^^ + "#]], + ); + check( + r#" +trait Iterator { + type Item; +} +fn f() -> impl Iterator<$0 + "#, + expect![[r#" + trait Iterator + ^^^^^^^^ + "#]], + ); + check( + r#" +trait Iterator { + type Item; +} +fn f() -> impl Iterator + ^^^^^^^^ + "#]], + ); + check( + r#" +trait Tr { + type A; + type B; +} +fn f() -> impl Tr<$0 + "#, + expect![[r#" + trait Tr + ^^^^^ ----- + "#]], + ); + check( + r#" +trait Tr { + type A; + type B; +} +fn f() -> impl Tr + ^^^^^ ----- + "#]], + ); + check( + r#" +trait Tr { + type A; + type B; +} +fn f() -> impl Tr + ^^^^^ ----- + "#]], + ); + check( + r#" +trait Tr { + type A; + type B; +} +fn f() -> impl Tr + ----- ^^^^^ + "#]], + ); + } + + #[test] + fn test_supertrait_assoc() { + check( + r#" +trait Super { + type SuperTy; +} +trait Sub: Super + Super { + type SubTy; +} +fn f() -> impl Sub<$0 + "#, + expect![[r#" + trait Sub + ^^^^^^^^^ ----------- + "#]], + ); + } + + #[test] + fn no_assoc_types_outside_type_bounds() { + check( + r#" +trait Tr { + type Assoc; +} + +impl Tr<$0 + "#, + expect![[r#" + trait Tr + ^ + "#]], + ); + } + #[test] fn impl_trait() { // FIXME: Substitute type vars in impl trait (`U` -> `i8`)