From 2a4b8fcc13e636cd476b03ad984577c8775c3af5 Mon Sep 17 00:00:00 2001 From: maple Date: Mon, 20 Oct 2025 16:43:14 +0530 Subject: [PATCH] Improve diagnostic when using generic type as type param --- compiler/rustc_parse/src/parser/item.rs | 38 +++++++++++++++++++ tests/ui/impl-trait/type-as-type-param.rs | 1 + tests/ui/impl-trait/type-as-type-param.stderr | 18 +++++++++ 3 files changed, 57 insertions(+) create mode 100644 tests/ui/impl-trait/type-as-type-param.rs create mode 100644 tests/ui/impl-trait/type-as-type-param.stderr diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 6f0e3b81cf2a6..e7e6bb69a26f0 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -573,6 +573,44 @@ impl<'a> Parser<'a> { let mut generics = if self.choose_generics_over_qpath(0) { self.parse_generics()? } else { + // We might be mistakenly trying to use a generic type as a generic parameter. + // impl> Trait for Y { ... } + if self.look_ahead(0, |t| t == &token::Lt) + && self.look_ahead(1, |t| t.is_ident()) + && self.look_ahead(2, |t| t == &token::Lt) + { + self.bump(); // < + let ident = self.parse_ident()?; + let generics = self.parse_generics()?; + let span = ident.span.to(generics.span); + let snippet = self.span_to_snippet(span).unwrap(); + + let msg = format!("expected type parameter, found path `{}`", snippet); + let mut err = self.dcx().struct_span_err(span, msg); + err.span_label(span, "expected type parameter, found path"); + err.span_suggestion( + span, + "you might have meant to bind a type parameter to a trait", + format!("T: {snippet}"), + Applicability::Unspecified, + ); + + let mut mapped = String::new(); + for i in 0..generics.params.len() { + mapped += &format!("{}: {}", generics.params[i].ident, ident); + if i != (generics.params.len() - 1) { + mapped += ", "; + } + } + err.span_suggestion(span, if generics.params.len() == 1 { + format!("alternatively, you might have meant to bind type parameter `{}` to trait `{}`", generics.params[0].ident.name.as_str(), ident.name.as_str()) + } else { + format!("alternatively, you might have meant to bind type parameters to trait `{}`", ident.name.as_str()) + }, mapped, Applicability::Unspecified); + + return Err(err); + } + let mut generics = Generics::default(); // impl A for B {} // /\ this is where `generics.span` should point when there are no type params. diff --git a/tests/ui/impl-trait/type-as-type-param.rs b/tests/ui/impl-trait/type-as-type-param.rs new file mode 100644 index 0000000000000..7e71d4d2f283a --- /dev/null +++ b/tests/ui/impl-trait/type-as-type-param.rs @@ -0,0 +1 @@ +impl> Z for X {} //~ ERROR: expected type parameter, found path `Y` diff --git a/tests/ui/impl-trait/type-as-type-param.stderr b/tests/ui/impl-trait/type-as-type-param.stderr new file mode 100644 index 0000000000000..9e735a1186f2c --- /dev/null +++ b/tests/ui/impl-trait/type-as-type-param.stderr @@ -0,0 +1,18 @@ +error: expected type parameter, found path `Y` + --> $DIR/type-as-type-param.rs:1:6 + | +LL | impl> Z for X {} + | ^^^^^^^^^^ expected type parameter, found path + | +help: you might have meant to bind a type parameter to a trait + | +LL | impl> Z for X {} + | ++ +help: alternatively, you might have meant to bind type parameters to trait `Y` + | +LL - impl> Z for X {} +LL + impl Z for X {} + | + +error: aborting due to 1 previous error +