diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2024-02-13 06:27:37 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-02-13 06:27:37 +0100 |
| commit | 1b396913a99759c6b92da2acac7e8a0ed349d773 (patch) | |
| tree | dd6da74ef3ee80dbb94110db34883e76862aaf02 /compiler/rustc_ast_passes/src | |
| parent | 020e84652dbcf660a4983aa460a29f0267b5f590 (diff) | |
| parent | 535c64336d6dcd588dd794d526047a4425e6e402 (diff) | |
| download | rust-1b396913a99759c6b92da2acac7e8a0ed349d773.tar.gz rust-1b396913a99759c6b92da2acac7e8a0ed349d773.zip | |
Rollup merge of #120751 - estebank:issue-68982, r=nnethercote
Provide more suggestions on invalid equality where bounds ``` error: equality constraints are not yet supported in `where` clauses --> $DIR/equality-bound.rs:50:9 | LL | IntoIterator::Item = A | ^^^^^^^^^^^^^^^^^^^^^^ not supported | = note: see issue #20041 <https://github.com/rust-lang/rust/issues/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: IntoIterator<Item = A>>(_: T) -> Self LL ~ | error: equality constraints are not yet supported in `where` clauses --> $DIR/equality-bound.rs:63:9 | LL | T::Item = A | ^^^^^^^^^^^ not supported | = note: see issue #20041 <https://github.com/rust-lang/rust/issues/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: IntoIterator<Item = A>>(_: T) -> Self LL ~ | ``` Fix #68982.
Diffstat (limited to 'compiler/rustc_ast_passes/src')
| -rw-r--r-- | compiler/rustc_ast_passes/src/ast_validation.rs | 124 |
1 files changed, 89 insertions, 35 deletions
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index d24dc44d2c5..ebc5c17a4c4 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<Bar = RhsTy>`. - 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<Span> = 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<Bar = RhsTy>`. + 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<Bar = RhsTy>`. + 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); + } } } } |
