diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 9ea5d1ed5fa27..356b578d51802 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -1593,44 +1593,98 @@ fn deny_equality_constraints( } } } - // Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo`. - if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind { - if let [potential_param, potential_assoc] = &full_path.segments[..] { - for param in &generics.params { - if param.ident == potential_param.ident { - for bound in ¶m.bounds { - if let ast::GenericBound::Trait(trait_ref, TraitBoundModifiers::NONE) = - bound + + let mut suggest = + |poly: &PolyTraitRef, potential_assoc: &PathSegment, predicate: &WhereEqPredicate| { + if let [trait_segment] = &poly.trait_ref.path.segments[..] { + let assoc = pprust::path_to_string(&ast::Path::from_ident(potential_assoc.ident)); + let ty = pprust::ty_to_string(&predicate.rhs_ty); + let (args, span) = match &trait_segment.args { + Some(args) => match args.deref() { + ast::GenericArgs::AngleBracketed(args) => { + let Some(arg) = args.args.last() else { + return; + }; + (format!(", {assoc} = {ty}"), arg.span().shrink_to_hi()) + } + _ => return, + }, + None => (format!("<{assoc} = {ty}>"), trait_segment.span().shrink_to_hi()), + }; + let removal_span = if generics.where_clause.predicates.len() == 1 { + // We're removing th eonly where bound left, remove the whole thing. + generics.where_clause.span + } else { + let mut span = predicate.span; + let mut prev: Option = None; + let mut preds = generics.where_clause.predicates.iter().peekable(); + // Find the predicate that shouldn't have been in the where bound list. + while let Some(pred) = preds.next() { + if let WherePredicate::EqPredicate(pred) = pred + && pred.span == predicate.span { - if let [trait_segment] = &trait_ref.trait_ref.path.segments[..] { - let assoc = pprust::path_to_string(&ast::Path::from_ident( - potential_assoc.ident, - )); - let ty = pprust::ty_to_string(&predicate.rhs_ty); - let (args, span) = match &trait_segment.args { - Some(args) => match args.deref() { - ast::GenericArgs::AngleBracketed(args) => { - let Some(arg) = args.args.last() else { - continue; - }; - (format!(", {assoc} = {ty}"), arg.span().shrink_to_hi()) - } - _ => continue, - }, - None => ( - format!("<{assoc} = {ty}>"), - trait_segment.span().shrink_to_hi(), - ), - }; - err.assoc2 = Some(errors::AssociatedSuggestion2 { - span, - args, - predicate: predicate.span, - trait_segment: trait_segment.ident, - potential_assoc: potential_assoc.ident, - }); + if let Some(next) = preds.peek() { + // This is the first predicate, remove the trailing comma as well. + span = span.with_hi(next.span().lo()); + } else if let Some(prev) = prev { + // Remove the previous comma as well. + span = span.with_lo(prev.hi()); } } + prev = Some(pred.span()); + } + span + }; + err.assoc2 = Some(errors::AssociatedSuggestion2 { + span, + args, + predicate: removal_span, + trait_segment: trait_segment.ident, + potential_assoc: potential_assoc.ident, + }); + } + }; + + if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind { + // Given `A: Foo, Foo::Bar = RhsTy`, suggest `A: Foo`. + for bounds in generics.params.iter().map(|p| &p.bounds).chain( + generics.where_clause.predicates.iter().filter_map(|pred| match pred { + WherePredicate::BoundPredicate(p) => Some(&p.bounds), + _ => None, + }), + ) { + for bound in bounds { + if let GenericBound::Trait(poly, TraitBoundModifiers::NONE) = bound { + if full_path.segments[..full_path.segments.len() - 1] + .iter() + .map(|segment| segment.ident.name) + .zip(poly.trait_ref.path.segments.iter().map(|segment| segment.ident.name)) + .all(|(a, b)| a == b) + && let Some(potential_assoc) = full_path.segments.iter().last() + { + suggest(poly, potential_assoc, predicate); + } + } + } + } + // Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo`. + if let [potential_param, potential_assoc] = &full_path.segments[..] { + for (ident, bounds) in generics.params.iter().map(|p| (p.ident, &p.bounds)).chain( + generics.where_clause.predicates.iter().filter_map(|pred| match pred { + WherePredicate::BoundPredicate(p) + if let ast::TyKind::Path(None, path) = &p.bounded_ty.kind + && let [segment] = &path.segments[..] => + { + Some((segment.ident, &p.bounds)) + } + _ => None, + }), + ) { + if ident == potential_param.ident { + for bound in bounds { + if let ast::GenericBound::Trait(poly, TraitBoundModifiers::NONE) = bound { + suggest(poly, potential_assoc, predicate); + } } } } diff --git a/tests/ui/generic-associated-types/equality-bound.rs b/tests/ui/generic-associated-types/equality-bound.rs index fcc2da8014f87..be05181f5d09e 100644 --- a/tests/ui/generic-associated-types/equality-bound.rs +++ b/tests/ui/generic-associated-types/equality-bound.rs @@ -12,4 +12,71 @@ fn sum3(i: J) -> i32 where I::Item = i32 { panic!() } +use std::iter::FromIterator; + +struct X {} + +impl FromIterator for X { + fn from_iter(_: T) -> Self where T: IntoIterator, IntoIterator::Item = A, + //~^ ERROR equality constraints are not yet supported in `where` clauses + //~| ERROR cannot find type `A` in this scope + { + todo!() + } +} + +struct Y {} + +impl FromIterator for Y { + fn from_iter(_: T) -> Self where T: IntoIterator, T::Item = A, + //~^ ERROR equality constraints are not yet supported in `where` clauses + //~| ERROR cannot find type `A` in this scope + { + todo!() + } +} + +struct Z {} + +impl FromIterator for Z { + fn from_iter(_: T) -> Self where IntoIterator::Item = A, + //~^ ERROR equality constraints are not yet supported in `where` clauses + //~| ERROR cannot find type `A` in this scope + { + todo!() + } +} + +struct K {} + +impl FromIterator for K { + fn from_iter(_: T) -> Self where T::Item = A, + //~^ ERROR equality constraints are not yet supported in `where` clauses + //~| ERROR cannot find type `A` in this scope + { + todo!() + } +} + +struct L {} + +impl FromIterator for L { + fn from_iter(_: T) -> Self where IntoIterator::Item = A, T: IntoIterator, + //~^ ERROR equality constraints are not yet supported in `where` clauses + //~| ERROR cannot find type `A` in this scope + { + todo!() + } +} + +struct M {} + +impl FromIterator for M { + fn from_iter(_: T) -> Self where T::Item = A, T: IntoIterator, + //~^ ERROR equality constraints are not yet supported in `where` clauses + //~| ERROR cannot find type `A` in this scope + { + todo!() + } +} fn main() {} diff --git a/tests/ui/generic-associated-types/equality-bound.stderr b/tests/ui/generic-associated-types/equality-bound.stderr index b21ff30a27da9..a054c06caebde 100644 --- a/tests/ui/generic-associated-types/equality-bound.stderr +++ b/tests/ui/generic-associated-types/equality-bound.stderr @@ -8,7 +8,7 @@ LL | fn sum>(i: I) -> i32 where I::Item = i32 { help: if `Iterator::Item` is an associated type you're trying to set, use the associated type binding syntax | LL - fn sum>(i: I) -> i32 where I::Item = i32 { -LL + fn sum>(i: I) -> i32 where { +LL + fn sum>(i: I) -> i32 { | error: equality constraints are not yet supported in `where` clauses @@ -21,7 +21,7 @@ LL | fn sum2(i: I) -> i32 where I::Item = i32 { help: if `Iterator::Item` is an associated type you're trying to set, use the associated type binding syntax | LL - fn sum2(i: I) -> i32 where I::Item = i32 { -LL + fn sum2>(i: I) -> i32 where { +LL + fn sum2>(i: I) -> i32 { | error: equality constraints are not yet supported in `where` clauses @@ -32,6 +32,138 @@ LL | fn sum3(i: J) -> i32 where I::Item = i32 { | = note: see issue #20041 for more information +error: equality constraints are not yet supported in `where` clauses + --> $DIR/equality-bound.rs:20:58 + | +LL | fn from_iter(_: T) -> Self where T: IntoIterator, IntoIterator::Item = A, + | ^^^^^^^^^^^^^^^^^^^^^^ not supported + | + = note: see issue #20041 for more information +help: if `IntoIterator::Item` is an associated type you're trying to set, use the associated type binding syntax + | +LL - fn from_iter(_: T) -> Self where T: IntoIterator, IntoIterator::Item = A, +LL + fn from_iter(_: T) -> Self where T: IntoIterator, + | + +error: equality constraints are not yet supported in `where` clauses + --> $DIR/equality-bound.rs:31:58 + | +LL | fn from_iter(_: T) -> Self where T: IntoIterator, T::Item = A, + | ^^^^^^^^^^^ not supported + | + = note: see issue #20041 for more information +help: if `IntoIterator::Item` is an associated type you're trying to set, use the associated type binding syntax + | +LL - fn from_iter(_: T) -> Self where T: IntoIterator, T::Item = A, +LL + fn from_iter(_: T) -> Self where T: IntoIterator, + | + +error: equality constraints are not yet supported in `where` clauses + --> $DIR/equality-bound.rs:42:55 + | +LL | fn from_iter(_: T) -> Self where IntoIterator::Item = A, + | ^^^^^^^^^^^^^^^^^^^^^^ not supported + | + = note: see issue #20041 for more information +help: if `IntoIterator::Item` is an associated type you're trying to set, use the associated type binding syntax + | +LL - fn from_iter(_: T) -> Self where IntoIterator::Item = A, +LL + fn from_iter>(_: T) -> Self + | + +error: equality constraints are not yet supported in `where` clauses + --> $DIR/equality-bound.rs:53:55 + | +LL | fn from_iter(_: T) -> Self where T::Item = A, + | ^^^^^^^^^^^ not supported + | + = note: see issue #20041 for more information +help: if `IntoIterator::Item` is an associated type you're trying to set, use the associated type binding syntax + | +LL - fn from_iter(_: T) -> Self where T::Item = A, +LL + fn from_iter>(_: T) -> Self + | + +error: equality constraints are not yet supported in `where` clauses + --> $DIR/equality-bound.rs:64:41 + | +LL | fn from_iter(_: T) -> Self where IntoIterator::Item = A, T: IntoIterator, + | ^^^^^^^^^^^^^^^^^^^^^^ not supported + | + = note: see issue #20041 for more information +help: if `IntoIterator::Item` is an associated type you're trying to set, use the associated type binding syntax + | +LL - fn from_iter(_: T) -> Self where IntoIterator::Item = A, T: IntoIterator, +LL + fn from_iter(_: T) -> Self where T: IntoIterator, + | + +error: equality constraints are not yet supported in `where` clauses + --> $DIR/equality-bound.rs:75:41 + | +LL | fn from_iter(_: T) -> Self where T::Item = A, T: IntoIterator, + | ^^^^^^^^^^^ not supported + | + = note: see issue #20041 for more information +help: if `IntoIterator::Item` is an associated type you're trying to set, use the associated type binding syntax + | +LL - fn from_iter(_: T) -> Self where T::Item = A, T: IntoIterator, +LL + fn from_iter(_: T) -> Self where T: IntoIterator, + | + +error[E0412]: cannot find type `A` in this scope + --> $DIR/equality-bound.rs:20:79 + | +LL | fn from_iter(_: T) -> Self where T: IntoIterator, IntoIterator::Item = A, + | ^ help: a struct with a similar name exists: `K` +... +LL | struct K {} + | -------- similarly named struct `K` defined here + +error[E0412]: cannot find type `A` in this scope + --> $DIR/equality-bound.rs:31:68 + | +LL | fn from_iter(_: T) -> Self where T: IntoIterator, T::Item = A, + | ^ help: a struct with a similar name exists: `K` +... +LL | struct K {} + | -------- similarly named struct `K` defined here + +error[E0412]: cannot find type `A` in this scope + --> $DIR/equality-bound.rs:42:76 + | +LL | fn from_iter(_: T) -> Self where IntoIterator::Item = A, + | ^ help: a struct with a similar name exists: `K` +... +LL | struct K {} + | -------- similarly named struct `K` defined here + +error[E0412]: cannot find type `A` in this scope + --> $DIR/equality-bound.rs:53:65 + | +LL | struct K {} + | -------- similarly named struct `K` defined here +... +LL | fn from_iter(_: T) -> Self where T::Item = A, + | ^ help: a struct with a similar name exists: `K` + +error[E0412]: cannot find type `A` in this scope + --> $DIR/equality-bound.rs:64:62 + | +LL | struct K {} + | -------- similarly named struct `K` defined here +... +LL | fn from_iter(_: T) -> Self where IntoIterator::Item = A, T: IntoIterator, + | ^ help: a struct with a similar name exists: `K` + +error[E0412]: cannot find type `A` in this scope + --> $DIR/equality-bound.rs:75:51 + | +LL | struct K {} + | -------- similarly named struct `K` defined here +... +LL | fn from_iter(_: T) -> Self where T::Item = A, T: IntoIterator, + | ^ help: a struct with a similar name exists: `K` + error[E0433]: failed to resolve: use of undeclared type `I` --> $DIR/equality-bound.rs:9:41 | @@ -41,6 +173,7 @@ LL | fn sum3(i: J) -> i32 where I::Item = i32 { | use of undeclared type `I` | help: a type parameter with a similar name exists: `J` -error: aborting due to 4 previous errors +error: aborting due to 16 previous errors -For more information about this error, try `rustc --explain E0433`. +Some errors have detailed explanations: E0412, E0433. +For more information about an error, try `rustc --explain E0412`.