about summary refs log tree commit diff
path: root/compiler/rustc_middle/src/ty/diagnostics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_middle/src/ty/diagnostics.rs')
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs398
1 files changed, 134 insertions, 264 deletions
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 3b044b19259..f53dc0000ca 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -3,15 +3,15 @@
 use crate::ty::subst::{GenericArg, GenericArgKind};
 use crate::ty::TyKind::*;
 use crate::ty::{
-    ConstKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, InferTy,
-    ProjectionTy, Term, Ty, TyCtxt, TypeAndMut,
+    ConstKind, DefIdTree, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef,
+    InferTy, ProjectionTy, Term, Ty, TyCtxt, TypeAndMut,
 };
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
+use rustc_hir::WherePredicate;
 use rustc_span::Span;
 
 impl<'tcx> IntoDiagnosticArg for Ty<'tcx> {
@@ -74,10 +74,10 @@ impl<'tcx> Ty<'tcx> {
     }
 
     /// Whether the type can be safely suggested during error recovery.
-    pub fn is_suggestable(self) -> bool {
-        fn generic_arg_is_suggestible(arg: GenericArg<'_>) -> bool {
+    pub fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool {
+        fn generic_arg_is_suggestible<'tcx>(arg: GenericArg<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
             match arg.unpack() {
-                GenericArgKind::Type(ty) => ty.is_suggestable(),
+                GenericArgKind::Type(ty) => ty.is_suggestable(tcx),
                 GenericArgKind::Const(c) => const_is_suggestable(c.val()),
                 _ => true,
             }
@@ -99,8 +99,7 @@ impl<'tcx> Ty<'tcx> {
         // temporary, so I'll leave this as a fixme.
 
         match self.kind() {
-            Opaque(..)
-            | FnDef(..)
+            FnDef(..)
             | Closure(..)
             | Infer(..)
             | Generator(..)
@@ -108,27 +107,38 @@ impl<'tcx> Ty<'tcx> {
             | Bound(_, _)
             | Placeholder(_)
             | Error(_) => false,
+            Opaque(did, substs) => {
+                let parent = tcx.parent(*did).expect("opaque types always have a parent");
+                if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = tcx.def_kind(parent)
+                    && let Opaque(parent_did, _) = tcx.type_of(parent).kind()
+                    && parent_did == did
+                {
+                    substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
+                } else {
+                    false
+                }
+            }
             Dynamic(dty, _) => dty.iter().all(|pred| match pred.skip_binder() {
                 ExistentialPredicate::Trait(ExistentialTraitRef { substs, .. }) => {
-                    substs.iter().all(generic_arg_is_suggestible)
+                    substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
                 }
                 ExistentialPredicate::Projection(ExistentialProjection {
                     substs, term, ..
                 }) => {
                     let term_is_suggestable = match term {
-                        Term::Ty(ty) => ty.is_suggestable(),
+                        Term::Ty(ty) => ty.is_suggestable(tcx),
                         Term::Const(c) => const_is_suggestable(c.val()),
                     };
-                    term_is_suggestable && substs.iter().all(generic_arg_is_suggestible)
+                    term_is_suggestable && substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
                 }
                 _ => true,
             }),
             Projection(ProjectionTy { substs: args, .. }) | Adt(_, args) => {
-                args.iter().all(generic_arg_is_suggestible)
+                args.iter().all(|a| generic_arg_is_suggestible(a, tcx))
             }
-            Tuple(args) => args.iter().all(|ty| ty.is_suggestable()),
-            Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(),
-            Array(ty, c) => ty.is_suggestable() && const_is_suggestable(c.val()),
+            Tuple(args) => args.iter().all(|ty| ty.is_suggestable(tcx)),
+            Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(tcx),
+            Array(ty, c) => ty.is_suggestable(tcx) && const_is_suggestable(c.val()),
             _ => true,
         }
     }
@@ -146,13 +156,13 @@ pub fn suggest_arbitrary_trait_bound(
         _ => {}
     }
     // Suggest a where clause bound for a non-type parameter.
-    let (action, prefix) = if generics.where_clause.predicates.is_empty() {
-        ("introducing a", " where ")
-    } else {
+    let (action, prefix) = if generics.has_where_clause {
         ("extending the", ", ")
+    } else {
+        ("introducing a", " where ")
     };
     err.span_suggestion_verbose(
-        generics.where_clause.tail_span_for_suggestion(),
+        generics.tail_span_for_predicate_suggestion(),
         &format!(
             "consider {} `where` bound, but there might be an alternative better way to express \
              this requirement",
@@ -173,104 +183,37 @@ enum SuggestChangingConstraintsMessage<'a> {
 }
 
 fn suggest_removing_unsized_bound(
+    tcx: TyCtxt<'_>,
     generics: &hir::Generics<'_>,
     suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
-    param_name: &str,
     param: &hir::GenericParam<'_>,
     def_id: Option<DefId>,
 ) {
     // See if there's a `?Sized` bound that can be removed to suggest that.
     // First look at the `where` clause because we can have `where T: ?Sized`,
     // then look at params.
-    for (where_pos, predicate) in generics.where_clause.predicates.iter().enumerate() {
-        match predicate {
-            WherePredicate::BoundPredicate(WhereBoundPredicate {
-                bounded_ty:
-                    hir::Ty {
-                        kind:
-                            hir::TyKind::Path(hir::QPath::Resolved(
-                                None,
-                                hir::Path {
-                                    segments: [segment],
-                                    res: hir::def::Res::Def(hir::def::DefKind::TyParam, _),
-                                    ..
-                                },
-                            )),
-                        ..
-                    },
-                bounds,
-                span,
-                ..
-            }) if segment.ident.as_str() == param_name => {
-                for (pos, bound) in bounds.iter().enumerate() {
-                    match bound {
-                        hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
-                            if poly.trait_ref.trait_def_id() == def_id => {}
-                        _ => continue,
-                    }
-                    let sp = match (
-                        bounds.len(),
-                        pos,
-                        generics.where_clause.predicates.len(),
-                        where_pos,
-                    ) {
-                        // where T: ?Sized
-                        // ^^^^^^^^^^^^^^^
-                        (1, _, 1, _) => generics.where_clause.span,
-                        // where Foo: Bar, T: ?Sized,
-                        //               ^^^^^^^^^^^
-                        (1, _, len, pos) if pos == len - 1 => generics.where_clause.predicates
-                            [pos - 1]
-                            .span()
-                            .shrink_to_hi()
-                            .to(*span),
-                        // where T: ?Sized, Foo: Bar,
-                        //       ^^^^^^^^^^^
-                        (1, _, _, pos) => {
-                            span.until(generics.where_clause.predicates[pos + 1].span())
-                        }
-                        // where T: ?Sized + Bar, Foo: Bar,
-                        //          ^^^^^^^^^
-                        (_, 0, _, _) => bound.span().to(bounds[1].span().shrink_to_lo()),
-                        // where T: Bar + ?Sized, Foo: Bar,
-                        //             ^^^^^^^^^
-                        (_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
-                    };
+    let param_def_id = tcx.hir().local_def_id(param.hir_id);
+    for (where_pos, predicate) in generics.predicates.iter().enumerate() {
+        let WherePredicate::BoundPredicate(predicate) = predicate else {
+            continue;
+        };
+        if !predicate.is_param_bound(param_def_id.to_def_id()) {
+            continue;
+        };
 
-                    suggestions.push((
-                        sp,
-                        String::new(),
-                        SuggestChangingConstraintsMessage::RemovingQSized,
-                    ));
-                }
-            }
-            _ => {}
-        }
-    }
-    for (pos, bound) in param.bounds.iter().enumerate() {
-        match bound {
-            hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
-                if poly.trait_ref.trait_def_id() == def_id =>
-            {
-                let sp = match (param.bounds.len(), pos) {
-                    // T: ?Sized,
-                    //  ^^^^^^^^
-                    (1, _) => param.span.shrink_to_hi().to(bound.span()),
-                    // T: ?Sized + Bar,
-                    //    ^^^^^^^^^
-                    (_, 0) => bound.span().to(param.bounds[1].span().shrink_to_lo()),
-                    // T: Bar + ?Sized,
-                    //       ^^^^^^^^^
-                    (_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
-                };
-
-                suggestions.push((
-                    sp,
-                    String::new(),
-                    SuggestChangingConstraintsMessage::RemovingQSized,
-                ));
+        for (pos, bound) in predicate.bounds.iter().enumerate() {
+            let    hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe) = bound else {
+                continue;
+            };
+            if poly.trait_ref.trait_def_id() != def_id {
+                continue;
             }
-            _ => {}
+            let sp = generics.span_for_bound_removal(where_pos, pos);
+            suggestions.push((
+                sp,
+                String::new(),
+                SuggestChangingConstraintsMessage::RemovingQSized,
+            ));
         }
     }
 }
@@ -321,13 +264,7 @@ pub fn suggest_constraining_type_params<'a>(
                     param.span,
                     &format!("this type parameter needs to be `{}`", constraint),
                 );
-                suggest_removing_unsized_bound(
-                    generics,
-                    &mut suggestions,
-                    param_name,
-                    param,
-                    def_id,
-                );
+                suggest_removing_unsized_bound(tcx, generics, &mut suggestions, param, def_id);
             }
         }
 
@@ -348,76 +285,45 @@ pub fn suggest_constraining_type_params<'a>(
             ))
         };
 
-        if param_name.starts_with("impl ") {
-            // If there's an `impl Trait` used in argument position, suggest
-            // restricting it:
-            //
-            //   fn foo(t: impl Foo) { ... }
-            //             --------
-            //             |
-            //             help: consider further restricting this bound with `+ Bar`
-            //
-            // Suggestion for tools in this case is:
-            //
-            //   fn foo(t: impl Foo) { ... }
-            //             --------
-            //             |
-            //             replace with: `impl Foo + Bar`
-
-            // `impl Trait` must have at least one trait in the list
-            let bound_list_non_empty = true;
-
-            suggest_restrict(param.span.shrink_to_hi(), bound_list_non_empty);
+        // When the type parameter has been provided bounds
+        //
+        //    Message:
+        //      fn foo<T>(t: T) where T: Foo { ... }
+        //                            ^^^^^^
+        //                            |
+        //                            help: consider further restricting this bound with `+ Bar`
+        //
+        //    Suggestion:
+        //      fn foo<T>(t: T) where T: Foo { ... }
+        //                                  ^
+        //                                  |
+        //                                  replace with: ` + Bar`
+        //
+        // Or, if user has provided some bounds, suggest restricting them:
+        //
+        //   fn foo<T: Foo>(t: T) { ... }
+        //             ---
+        //             |
+        //             help: consider further restricting this bound with `+ Bar`
+        //
+        // Suggestion for tools in this case is:
+        //
+        //   fn foo<T: Foo>(t: T) { ... }
+        //          --
+        //          |
+        //          replace with: `T: Bar +`
+        let param_def_id = tcx.hir().local_def_id(param.hir_id);
+        if let Some(span) = generics.bounds_span_for_suggestions(param_def_id) {
+            suggest_restrict(span, true);
             continue;
         }
 
-        if generics.where_clause.predicates.is_empty()
-        // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
-        // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
-        && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
-        {
-            if let Some(span) = param.bounds_span_for_suggestions() {
-                // If user has provided some bounds, suggest restricting them:
-                //
-                //   fn foo<T: Foo>(t: T) { ... }
-                //             ---
-                //             |
-                //             help: consider further restricting this bound with `+ Bar`
-                //
-                // Suggestion for tools in this case is:
-                //
-                //   fn foo<T: Foo>(t: T) { ... }
-                //          --
-                //          |
-                //          replace with: `T: Bar +`
-
-                // `bounds_span_for_suggestions` returns `None` if the list is empty
-                let bound_list_non_empty = true;
-
-                suggest_restrict(span, bound_list_non_empty);
-            } else {
-                let (colon, span) = match param.colon_span_for_suggestions(tcx.sess.source_map()) {
-                    // If there is already a colon after generic, do not suggest adding it again
-                    Some(sp) => ("", sp.shrink_to_hi()),
-                    None => (":", param.span.shrink_to_hi()),
-                };
-
-                // If user hasn't provided any bounds, suggest adding a new one:
-                //
-                //   fn foo<T>(t: T) { ... }
-                //          - help: consider restricting this type parameter with `T: Foo`
-                suggestions.push((
-                    span,
-                    format!("{colon} {constraint}"),
-                    SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
-                ));
-            }
-        } else {
+        if generics.has_where_clause {
             // This part is a bit tricky, because using the `where` clause user can
             // provide zero, one or many bounds for the same type parameter, so we
             // have following cases to consider:
             //
-            // 1) When the type parameter has been provided zero bounds
+            // When the type parameter has been provided zero bounds
             //
             //    Message:
             //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
@@ -426,95 +332,59 @@ pub fn suggest_constraining_type_params<'a>(
             //    Suggestion:
             //      fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
             //                                           - insert: `, X: Bar`
-            //
-            //
-            // 2) When the type parameter has been provided one bound
-            //
-            //    Message:
-            //      fn foo<T>(t: T) where T: Foo { ... }
-            //                            ^^^^^^
-            //                            |
-            //                            help: consider further restricting this bound with `+ Bar`
-            //
-            //    Suggestion:
-            //      fn foo<T>(t: T) where T: Foo { ... }
-            //                            ^^
-            //                            |
-            //                            replace with: `T: Bar +`
-            //
-            //
-            // 3) When the type parameter has been provided many bounds
-            //
-            //    Message:
-            //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
-            //             - help: consider further restricting this type parameter with `where T: Zar`
-            //
-            //    Suggestion:
-            //      fn foo<T>(t: T) where T: Foo, T: Bar {... }
-            //                                          - insert: `, T: Zar`
-            //
-            // Additionally, there may be no `where` clause whatsoever in the case that this was
-            // reached because the generic parameter has a default:
-            //
-            //    Message:
-            //      trait Foo<T=()> {... }
-            //             - help: consider further restricting this type parameter with `where T: Zar`
-            //
-            //    Suggestion:
-            //      trait Foo<T=()> where T: Zar {... }
-            //                     - insert: `where T: Zar`
-
-            if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
-                && generics.where_clause.predicates.len() == 0
-            {
-                // Suggest a bound, but there is no existing `where` clause *and* the type param has a
-                // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
-                suggestions.push((
-                    generics.where_clause.tail_span_for_suggestion(),
-                    format!(" where {}: {}", param_name, constraint),
-                    SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
-                ));
-            } else {
-                let mut param_spans = Vec::new();
-                let mut non_empty = false;
-
-                for predicate in generics.where_clause.predicates {
-                    if let WherePredicate::BoundPredicate(WhereBoundPredicate {
-                        span,
-                        bounded_ty,
-                        bounds,
-                        ..
-                    }) = predicate
-                    {
-                        if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
-                            if let Some(segment) = path.segments.first() {
-                                if segment.ident.to_string() == param_name {
-                                    non_empty = !bounds.is_empty();
-
-                                    param_spans.push(span);
-                                }
-                            }
-                        }
-                    }
-                }
+            suggestions.push((
+                generics.tail_span_for_predicate_suggestion(),
+                constraints
+                    .iter()
+                    .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint))
+                    .collect::<String>(),
+                SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
+            ));
+            continue;
+        }
 
-                match param_spans[..] {
-                    [&param_span] => suggest_restrict(param_span.shrink_to_hi(), non_empty),
-                    _ => {
-                        suggestions.push((
-                            generics.where_clause.tail_span_for_suggestion(),
-                            constraints
-                                .iter()
-                                .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint))
-                                .collect::<String>(),
-                            SuggestChangingConstraintsMessage::RestrictTypeFurther {
-                                ty: param_name,
-                            },
-                        ));
-                    }
-                }
-            }
+        // Additionally, there may be no `where` clause but the generic parameter has a default:
+        //
+        //    Message:
+        //      trait Foo<T=()> {... }
+        //                - help: consider further restricting this type parameter with `where T: Zar`
+        //
+        //    Suggestion:
+        //      trait Foo<T=()> {... }
+        //                     - insert: `where T: Zar`
+        if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) {
+            // Suggest a bound, but there is no existing `where` clause *and* the type param has a
+            // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
+            suggestions.push((
+                generics.tail_span_for_predicate_suggestion(),
+                format!(" where {}: {}", param_name, constraint),
+                SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
+            ));
+            continue;
         }
+
+        // If user has provided a colon, don't suggest adding another:
+        //
+        //   fn foo<T:>(t: T) { ... }
+        //            - insert: consider restricting this type parameter with `T: Foo`
+        if let Some(colon_span) = param.colon_span {
+            suggestions.push((
+                colon_span.shrink_to_hi(),
+                format!(" {}", constraint),
+                SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
+            ));
+            continue;
+        }
+
+        // If user hasn't provided any bounds, suggest adding a new one:
+        //
+        //   fn foo<T>(t: T) { ... }
+        //          - help: consider restricting this type parameter with `T: Foo`
+        suggestions.push((
+            param.span.shrink_to_hi(),
+            format!(": {}", constraint),
+            SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
+        ));
     }
 
     if suggestions.len() == 1 {