Skip to content

Commit 4d94cf3

Browse files
committed
Auto merge of rust-lang#12208 - jonas-schievink:assoc-ty-signature-info, r=jonas-schievink
feat: include associated types in trait signature help Fixes rust-lang/rust-analyzer#12141 ![screenshot-2022-05-10-16:55:19](https://user-images.githubusercontent.com/1786438/167658642-8df42fba-523a-46fe-a0f6-e0e041b3659d.png)
2 parents 254bfdd + ac3c18b commit 4d94cf3

File tree

2 files changed

+177
-5
lines changed

2 files changed

+177
-5
lines changed

Diff for: crates/ide/src/signature_help.rs

+176-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
//! This module provides primitives for showing type and function parameter information when editing
22
//! a call or use-site.
33
4+
use std::collections::BTreeSet;
5+
46
use either::Either;
5-
use hir::{GenericParam, HasAttrs, HirDisplay, Semantics};
7+
use hir::{AssocItem, GenericParam, HasAttrs, HirDisplay, Semantics, Trait};
68
use ide_db::{active_parameter::callable_for_node, base_db::FilePosition};
79
use stdx::format_to;
810
use syntax::{
@@ -316,11 +318,52 @@ fn signature_help_for_generics(
316318
format_to!(buf, "{}", param.display(db));
317319
res.push_generic_param(&buf);
318320
}
321+
if let hir::GenericDef::Trait(tr) = generics_def {
322+
add_assoc_type_bindings(db, &mut res, tr, arg_list);
323+
}
319324
res.signature.push('>');
320325

321326
Some(res)
322327
}
323328

329+
fn add_assoc_type_bindings(
330+
db: &RootDatabase,
331+
res: &mut SignatureHelp,
332+
tr: Trait,
333+
args: ast::GenericArgList,
334+
) {
335+
if args.syntax().ancestors().find_map(ast::TypeBound::cast).is_none() {
336+
// Assoc type bindings are only valid in type bound position.
337+
return;
338+
}
339+
340+
let present_bindings = args
341+
.generic_args()
342+
.filter_map(|arg| match arg {
343+
ast::GenericArg::AssocTypeArg(arg) => arg.name_ref().map(|n| n.to_string()),
344+
_ => None,
345+
})
346+
.collect::<BTreeSet<_>>();
347+
348+
let mut buf = String::new();
349+
for binding in &present_bindings {
350+
buf.clear();
351+
format_to!(buf, "{} = …", binding);
352+
res.push_generic_param(&buf);
353+
}
354+
355+
for item in tr.items_with_supertraits(db) {
356+
if let AssocItem::TypeAlias(ty) = item {
357+
let name = ty.name(db).to_smol_str();
358+
if !present_bindings.contains(&*name) {
359+
buf.clear();
360+
format_to!(buf, "{} = …", name);
361+
res.push_generic_param(&buf);
362+
}
363+
}
364+
}
365+
}
366+
324367
#[cfg(test)]
325368
mod tests {
326369
use std::iter;
@@ -368,10 +411,11 @@ mod tests {
368411
panic!("parameter ranges out of order: {:?}", sig_help.parameter_ranges())
369412
});
370413
rendered.extend(iter::repeat(' ').take(gap as usize));
371-
let width = u32::from(range.end() - range.start());
414+
let param_text = &sig_help.signature[*range];
415+
let width = param_text.chars().count(); // …
372416
let marker = if is_active { '^' } else { '-' };
373-
rendered.extend(iter::repeat(marker).take(width as usize));
374-
offset += gap + width;
417+
rendered.extend(iter::repeat(marker).take(width));
418+
offset += gap + u32::from(range.len());
375419
}
376420
if !sig_help.parameter_ranges().is_empty() {
377421
format_to!(rendered, "\n");
@@ -1124,6 +1168,134 @@ fn f() {
11241168
);
11251169
}
11261170

1171+
#[test]
1172+
fn test_trait_assoc_types() {
1173+
check(
1174+
r#"
1175+
trait Trait<'a, T> {
1176+
type Assoc;
1177+
}
1178+
fn f() -> impl Trait<(), $0
1179+
"#,
1180+
expect![[r#"
1181+
trait Trait<'a, T, Assoc = …>
1182+
-- - ^^^^^^^^^
1183+
"#]],
1184+
);
1185+
check(
1186+
r#"
1187+
trait Iterator {
1188+
type Item;
1189+
}
1190+
fn f() -> impl Iterator<$0
1191+
"#,
1192+
expect![[r#"
1193+
trait Iterator<Item = …>
1194+
^^^^^^^^
1195+
"#]],
1196+
);
1197+
check(
1198+
r#"
1199+
trait Iterator {
1200+
type Item;
1201+
}
1202+
fn f() -> impl Iterator<Item = $0
1203+
"#,
1204+
expect![[r#"
1205+
trait Iterator<Item = …>
1206+
^^^^^^^^
1207+
"#]],
1208+
);
1209+
check(
1210+
r#"
1211+
trait Tr {
1212+
type A;
1213+
type B;
1214+
}
1215+
fn f() -> impl Tr<$0
1216+
"#,
1217+
expect![[r#"
1218+
trait Tr<A = …, B = …>
1219+
^^^^^ -----
1220+
"#]],
1221+
);
1222+
check(
1223+
r#"
1224+
trait Tr {
1225+
type A;
1226+
type B;
1227+
}
1228+
fn f() -> impl Tr<B$0
1229+
"#,
1230+
expect![[r#"
1231+
trait Tr<A = …, B = …>
1232+
^^^^^ -----
1233+
"#]],
1234+
);
1235+
check(
1236+
r#"
1237+
trait Tr {
1238+
type A;
1239+
type B;
1240+
}
1241+
fn f() -> impl Tr<B = $0
1242+
"#,
1243+
expect![[r#"
1244+
trait Tr<B = …, A = …>
1245+
^^^^^ -----
1246+
"#]],
1247+
);
1248+
check(
1249+
r#"
1250+
trait Tr {
1251+
type A;
1252+
type B;
1253+
}
1254+
fn f() -> impl Tr<B = (), $0
1255+
"#,
1256+
expect![[r#"
1257+
trait Tr<B = …, A = …>
1258+
----- ^^^^^
1259+
"#]],
1260+
);
1261+
}
1262+
1263+
#[test]
1264+
fn test_supertrait_assoc() {
1265+
check(
1266+
r#"
1267+
trait Super {
1268+
type SuperTy;
1269+
}
1270+
trait Sub: Super + Super {
1271+
type SubTy;
1272+
}
1273+
fn f() -> impl Sub<$0
1274+
"#,
1275+
expect![[r#"
1276+
trait Sub<SubTy = …, SuperTy = …>
1277+
^^^^^^^^^ -----------
1278+
"#]],
1279+
);
1280+
}
1281+
1282+
#[test]
1283+
fn no_assoc_types_outside_type_bounds() {
1284+
check(
1285+
r#"
1286+
trait Tr<T> {
1287+
type Assoc;
1288+
}
1289+
1290+
impl Tr<$0
1291+
"#,
1292+
expect![[r#"
1293+
trait Tr<T>
1294+
^
1295+
"#]],
1296+
);
1297+
}
1298+
11271299
#[test]
11281300
fn impl_trait() {
11291301
// FIXME: Substitute type vars in impl trait (`U` -> `i8`)

Diff for: crates/rust-analyzer/src/to_proto.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ pub(crate) fn signature_help(
341341
config: CallInfoConfig,
342342
label_offsets: bool,
343343
) -> lsp_types::SignatureHelp {
344-
let (label, parameters) = match (!config.params_only, label_offsets) {
344+
let (label, parameters) = match (config.params_only, label_offsets) {
345345
(concise, false) => {
346346
let params = call_info
347347
.parameter_labels()

0 commit comments

Comments
 (0)