diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 8d9d4123c79..245353c2e07 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1884,6 +1884,16 @@ fn clone(&self) -> Self { } } +impl Ty { + pub fn peel_refs(&self) -> &Self { + let mut final_ty = self; + while let TyKind::Rptr(_, MutTy { ty, .. }) = &final_ty.kind { + final_ty = &ty; + } + final_ty + } +} + #[derive(Clone, Encodable, Decodable, Debug)] pub struct BareFnTy { pub unsafety: Unsafe, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index b983656c423..bee05e77382 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -442,7 +442,11 @@ pub(crate) fn smart_resolve_report_errors( if !self.type_ascription_suggestion(&mut err, base_span) { let mut fallback = false; - if let PathSource::Trait(AliasPossibility::Maybe) = source { + if let ( + PathSource::Trait(AliasPossibility::Maybe), + Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)), + ) = (source, res) + { if let Some(bounds @ [_, .., _]) = self.diagnostic_metadata.current_trait_object { fallback = true; let spans: Vec = bounds @@ -580,7 +584,7 @@ fn restrict_assoc_type_in_where_clause( return false; }; - if let ast::TyKind::Path(None, type_param_path) = &ty.kind { + if let ast::TyKind::Path(None, type_param_path) = &ty.peel_refs().kind { // Confirm that the `SelfTy` is a type parameter. let partial_res = if let Ok(Some(partial_res)) = self.resolve_qpath_anywhere( bounded_ty.id, @@ -603,20 +607,24 @@ fn restrict_assoc_type_in_where_clause( return false; } if let ( - [ast::PathSegment { ident, args: None, .. }], + [ast::PathSegment { ident: constrain_ident, args: None, .. }], [ast::GenericBound::Trait(poly_trait_ref, ast::TraitBoundModifier::None)], ) = (&type_param_path.segments[..], &bounds[..]) { - if let [ast::PathSegment { ident: bound_ident, args: None, .. }] = + if let [ast::PathSegment { ident, args: None, .. }] = &poly_trait_ref.trait_ref.path.segments[..] { - if bound_ident.span == span { + if ident.span == span { err.span_suggestion_verbose( *where_span, - &format!("constrain the associated type to `{}`", bound_ident), + &format!("constrain the associated type to `{}`", ident), format!( "{}: {}<{} = {}>", - ident, + self.r + .session + .source_map() + .span_to_snippet(ty.span) // Account for `<&'a T as Foo>::Bar`. + .unwrap_or_else(|_| constrain_ident.to_string()), path.segments[..*position] .iter() .map(|segment| path_segment_to_string(segment)) @@ -627,7 +635,7 @@ fn restrict_assoc_type_in_where_clause( .map(|segment| path_segment_to_string(segment)) .collect::>() .join("::"), - bound_ident, + ident, ), Applicability::MaybeIncorrect, ); diff --git a/src/test/ui/traits/assoc_type_bound_with_struct.rs b/src/test/ui/traits/assoc_type_bound_with_struct.rs index 729996c8130..c66009fe24c 100644 --- a/src/test/ui/traits/assoc_type_bound_with_struct.rs +++ b/src/test/ui/traits/assoc_type_bound_with_struct.rs @@ -6,4 +6,14 @@ struct Foo where T: Bar, ::Baz: String { //~ ERROR expected trait, t: T, } +struct Qux<'a, T> where T: Bar, <&'a T as Bar>::Baz: String { //~ ERROR expected trait, found struct + t: &'a T, +} + +fn foo(_: T) where ::Baz: String { //~ ERROR expected trait, found struct +} + +fn qux<'a, T: Bar>(_: &'a T) where <&'a T as Bar>::Baz: String { //~ ERROR expected trait, found +} + fn main() {} diff --git a/src/test/ui/traits/assoc_type_bound_with_struct.stderr b/src/test/ui/traits/assoc_type_bound_with_struct.stderr index f0c23aecf73..7cf872eb6ac 100644 --- a/src/test/ui/traits/assoc_type_bound_with_struct.stderr +++ b/src/test/ui/traits/assoc_type_bound_with_struct.stderr @@ -18,6 +18,66 @@ help: a trait with a similar name exists LL | struct Foo where T: Bar, ::Baz: ToString { | ^^^^^^^^ -error: aborting due to previous error +error[E0404]: expected trait, found struct `String` + --> $DIR/assoc_type_bound_with_struct.rs:9:54 + | +LL | struct Qux<'a, T> where T: Bar, <&'a T as Bar>::Baz: String { + | ^^^^^^ not a trait + | + ::: $SRC_DIR/alloc/src/string.rs:LL:COL + | +LL | pub trait ToString { + | ------------------ similarly named trait `ToString` defined here + | +help: constrain the associated type to `String` + | +LL | struct Qux<'a, T> where T: Bar, &'a T: Bar { + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: a trait with a similar name exists + | +LL | struct Qux<'a, T> where T: Bar, <&'a T as Bar>::Baz: ToString { + | ^^^^^^^^ + +error[E0404]: expected trait, found struct `String` + --> $DIR/assoc_type_bound_with_struct.rs:13:45 + | +LL | fn foo(_: T) where ::Baz: String { + | ^^^^^^ not a trait + | + ::: $SRC_DIR/alloc/src/string.rs:LL:COL + | +LL | pub trait ToString { + | ------------------ similarly named trait `ToString` defined here + | +help: constrain the associated type to `String` + | +LL | fn foo(_: T) where T: Bar { + | ^^^^^^^^^^^^^^^^^^^^ +help: a trait with a similar name exists + | +LL | fn foo(_: T) where ::Baz: ToString { + | ^^^^^^^^ + +error[E0404]: expected trait, found struct `String` + --> $DIR/assoc_type_bound_with_struct.rs:16:57 + | +LL | fn qux<'a, T: Bar>(_: &'a T) where <&'a T as Bar>::Baz: String { + | ^^^^^^ not a trait + | + ::: $SRC_DIR/alloc/src/string.rs:LL:COL + | +LL | pub trait ToString { + | ------------------ similarly named trait `ToString` defined here + | +help: constrain the associated type to `String` + | +LL | fn qux<'a, T: Bar>(_: &'a T) where &'a T: Bar { + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: a trait with a similar name exists + | +LL | fn qux<'a, T: Bar>(_: &'a T) where <&'a T as Bar>::Baz: ToString { + | ^^^^^^^^ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0404`.