about summary refs log tree commit diff
path: root/compiler/rustc_ast_passes/src
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2024-02-13 06:27:37 +0100
committerGitHub <noreply@github.com>2024-02-13 06:27:37 +0100
commit1b396913a99759c6b92da2acac7e8a0ed349d773 (patch)
treedd6da74ef3ee80dbb94110db34883e76862aaf02 /compiler/rustc_ast_passes/src
parent020e84652dbcf660a4983aa460a29f0267b5f590 (diff)
parent535c64336d6dcd588dd794d526047a4425e6e402 (diff)
downloadrust-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.rs124
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 &param.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);
+                        }
                     }
                 }
             }