mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 14:03:35 +00:00
Include assoc. types in trait signature help
This commit is contained in:
parent
92e56e0c70
commit
ac3c18bc17
1 changed files with 176 additions and 4 deletions
|
@ -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::<BTreeSet<_>>();
|
||||
|
||||
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<Item = …>
|
||||
^^^^^^^^
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
trait Iterator {
|
||||
type Item;
|
||||
}
|
||||
fn f() -> impl Iterator<Item = $0
|
||||
"#,
|
||||
expect![[r#"
|
||||
trait Iterator<Item = …>
|
||||
^^^^^^^^
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
trait Tr {
|
||||
type A;
|
||||
type B;
|
||||
}
|
||||
fn f() -> impl Tr<$0
|
||||
"#,
|
||||
expect![[r#"
|
||||
trait Tr<A = …, B = …>
|
||||
^^^^^ -----
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
trait Tr {
|
||||
type A;
|
||||
type B;
|
||||
}
|
||||
fn f() -> impl Tr<B$0
|
||||
"#,
|
||||
expect![[r#"
|
||||
trait Tr<A = …, B = …>
|
||||
^^^^^ -----
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
trait Tr {
|
||||
type A;
|
||||
type B;
|
||||
}
|
||||
fn f() -> impl Tr<B = $0
|
||||
"#,
|
||||
expect![[r#"
|
||||
trait Tr<B = …, A = …>
|
||||
^^^^^ -----
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
trait Tr {
|
||||
type A;
|
||||
type B;
|
||||
}
|
||||
fn f() -> impl Tr<B = (), $0
|
||||
"#,
|
||||
expect![[r#"
|
||||
trait Tr<B = …, A = …>
|
||||
----- ^^^^^
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[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<SubTy = …, SuperTy = …>
|
||||
^^^^^^^^^ -----------
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_assoc_types_outside_type_bounds() {
|
||||
check(
|
||||
r#"
|
||||
trait Tr<T> {
|
||||
type Assoc;
|
||||
}
|
||||
|
||||
impl Tr<$0
|
||||
"#,
|
||||
expect![[r#"
|
||||
trait Tr<T>
|
||||
^
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn impl_trait() {
|
||||
// FIXME: Substitute type vars in impl trait (`U` -> `i8`)
|
||||
|
|
Loading…
Reference in a new issue