about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2024-01-05 10:57:20 -0500
committerGitHub <noreply@github.com>2024-01-05 10:57:20 -0500
commitf41ad1bc9c54824073142cc19c5eafef4893d86b (patch)
tree3acb4afe95c85b80f3ca3aa21cbbaa9392cce334 /compiler
parentd180e9101dd9cd08ed1f25de5dbab83d7f964e12 (diff)
parent698dfc322fd42fa4dff71474aba7cdf691a459a6 (diff)
downloadrust-f41ad1bc9c54824073142cc19c5eafef4893d86b.tar.gz
rust-f41ad1bc9c54824073142cc19c5eafef4893d86b.zip
Rollup merge of #119148 - estebank:bare-traits, r=davidtwco
Tweak suggestions for bare trait used as a type

```
error[E0782]: trait objects must include the `dyn` keyword
  --> $DIR/not-on-bare-trait-2021.rs:11:11
   |
LL | fn bar(x: Foo) -> Foo {
   |           ^^^
   |
help: use a generic type parameter, constrained by the trait `Foo`
   |
LL | fn bar<T: Foo>(x: T) -> Foo {
   |       ++++++++    ~
help: you can also use `impl Foo`, but users won't be able to specify the type paramer when calling the `fn`, having to rely exclusively on type inference
   |
LL | fn bar(x: impl Foo) -> Foo {
   |           ++++
help: alternatively, use a trait object to accept any type that implements `Foo`, accessing its methods at runtime using dynamic dispatch
   |
LL | fn bar(x: &dyn Foo) -> Foo {
   |           ++++

error[E0782]: trait objects must include the `dyn` keyword
  --> $DIR/not-on-bare-trait-2021.rs:11:19
   |
LL | fn bar(x: Foo) -> Foo {
   |                   ^^^
   |
help: use `impl Foo` to return an opaque type, as long as you return a single underlying type
   |
LL | fn bar(x: Foo) -> impl Foo {
   |                   ++++
help: alternatively, you can return an owned trait object
   |
LL | fn bar(x: Foo) -> Box<dyn Foo> {
   |                   +++++++    +
```

Fix #119525:

```

error[E0038]: the trait `Ord` cannot be made into an object
  --> $DIR/bare-trait-dont-suggest-dyn.rs:3:33
   |
LL | fn ord_prefer_dot(s: String) -> Ord {
   |                                 ^^^ `Ord` cannot be made into an object
   |
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
  --> $SRC_DIR/core/src/cmp.rs:LL:COL
   |
   = note: the trait cannot be made into an object because it uses `Self` as a type parameter
  ::: $SRC_DIR/core/src/cmp.rs:LL:COL
   |
   = note: the trait cannot be made into an object because it uses `Self` as a type parameter
help: consider using an opaque type instead
   |
LL | fn ord_prefer_dot(s: String) -> impl Ord {
   |                                 ++++
```
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/errors.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/lint.rs158
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/object_safety.rs1
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/gather_locals.rs9
-rw-r--r--compiler/rustc_infer/src/traits/error_reporting/mod.rs22
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs106
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs4
9 files changed, 250 insertions, 61 deletions
diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs
index f17f19bb77c..d22c4fe201d 100644
--- a/compiler/rustc_hir_analysis/src/astconv/errors.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs
@@ -605,7 +605,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 let violations =
                     object_safety_violations_for_assoc_item(tcx, trait_def_id, *assoc_item);
                 if !violations.is_empty() {
-                    report_object_safety_error(tcx, *span, trait_def_id, &violations).emit();
+                    report_object_safety_error(tcx, *span, None, trait_def_id, &violations).emit();
                     object_safety_violations = true;
                 }
             }
diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs
index f3b93c91ae9..6675f517cfa 100644
--- a/compiler/rustc_hir_analysis/src/astconv/lint.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs
@@ -1,7 +1,9 @@
 use rustc_ast::TraitObjectSyntax;
 use rustc_errors::{Diagnostic, StashKey};
 use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
 use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability};
+use rustc_span::Span;
 use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
 
 use super::AstConv;
@@ -32,32 +34,146 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             }
             let of_trait_span = of_trait_ref.path.span;
             // make sure that we are not calling unwrap to abort during the compilation
-            let Ok(impl_trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else {
-                return;
-            };
             let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else {
                 return;
             };
-            // check if the trait has generics, to make a correct suggestion
-            let param_name = generics.params.next_type_param_name(None);
 
-            let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() {
-                (span, format!(", {param_name}: {impl_trait_name}"))
-            } else {
-                (generics.span, format!("<{param_name}: {impl_trait_name}>"))
+            let Ok(impl_trait_name) = self.tcx().sess.source_map().span_to_snippet(self_ty.span)
+            else {
+                return;
+            };
+            let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &impl_trait_name);
+            if sugg.is_empty() {
+                return;
             };
             diag.multipart_suggestion(
                 format!(
-                    "alternatively use a blanket \
-                     implementation to implement `{of_trait_name}` for \
+                    "alternatively use a blanket implementation to implement `{of_trait_name}` for \
                      all types that also implement `{impl_trait_name}`"
                 ),
-                vec![(self_ty.span, param_name), add_generic_sugg],
+                sugg,
                 Applicability::MaybeIncorrect,
             );
         }
     }
 
+    fn add_generic_param_suggestion(
+        &self,
+        generics: &hir::Generics<'_>,
+        self_ty_span: Span,
+        impl_trait_name: &str,
+    ) -> Vec<(Span, String)> {
+        // check if the trait has generics, to make a correct suggestion
+        let param_name = generics.params.next_type_param_name(None);
+
+        let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() {
+            (span, format!(", {param_name}: {impl_trait_name}"))
+        } else {
+            (generics.span, format!("<{param_name}: {impl_trait_name}>"))
+        };
+        vec![(self_ty_span, param_name), add_generic_sugg]
+    }
+
+    /// Make sure that we are in the condition to suggest `impl Trait`.
+    fn maybe_lint_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diagnostic) -> bool {
+        let tcx = self.tcx();
+        let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id;
+        let (hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. })
+        | hir::Node::TraitItem(hir::TraitItem {
+            kind: hir::TraitItemKind::Fn(sig, _),
+            generics,
+            ..
+        })) = tcx.hir_node_by_def_id(parent_id)
+        else {
+            return false;
+        };
+        let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else {
+            return false;
+        };
+        let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())];
+        let is_object_safe = match self_ty.kind {
+            hir::TyKind::TraitObject(objects, ..) => {
+                objects.iter().all(|o| match o.trait_ref.path.res {
+                    Res::Def(DefKind::Trait, id) => tcx.check_is_object_safe(id),
+                    _ => false,
+                })
+            }
+            _ => false,
+        };
+        if let hir::FnRetTy::Return(ty) = sig.decl.output
+            && ty.hir_id == self_ty.hir_id
+        {
+            let pre = if !is_object_safe {
+                format!("`{trait_name}` is not object safe, ")
+            } else {
+                String::new()
+            };
+            let msg = format!(
+                "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \
+                 single underlying type",
+            );
+            diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable);
+            if is_object_safe {
+                diag.multipart_suggestion_verbose(
+                    "alternatively, you can return an owned trait object",
+                    vec![
+                        (ty.span.shrink_to_lo(), "Box<dyn ".to_string()),
+                        (ty.span.shrink_to_hi(), ">".to_string()),
+                    ],
+                    Applicability::MachineApplicable,
+                );
+            } else {
+                // We'll emit the object safety error already, with a structured suggestion.
+                diag.downgrade_to_delayed_bug();
+            }
+            return true;
+        }
+        for ty in sig.decl.inputs {
+            if ty.hir_id != self_ty.hir_id {
+                continue;
+            }
+            let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &trait_name);
+            if !sugg.is_empty() {
+                diag.multipart_suggestion_verbose(
+                    format!("use a new generic type parameter, constrained by `{trait_name}`"),
+                    sugg,
+                    Applicability::MachineApplicable,
+                );
+                diag.multipart_suggestion_verbose(
+                    "you can also use an opaque type, but users won't be able to specify the type \
+                     parameter when calling the `fn`, having to rely exclusively on type inference",
+                    impl_sugg,
+                    Applicability::MachineApplicable,
+                );
+            }
+            if !is_object_safe {
+                diag.note(format!("`{trait_name}` it is not object safe, so it can't be `dyn`"));
+                // We'll emit the object safety error already, with a structured suggestion.
+                diag.downgrade_to_delayed_bug();
+            } else {
+                let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind {
+                    // There are more than one trait bound, we need surrounding parentheses.
+                    vec![
+                        (self_ty.span.shrink_to_lo(), "&(dyn ".to_string()),
+                        (self_ty.span.shrink_to_hi(), ")".to_string()),
+                    ]
+                } else {
+                    vec![(self_ty.span.shrink_to_lo(), "&dyn ".to_string())]
+                };
+                diag.multipart_suggestion_verbose(
+                    format!(
+                        "alternatively, use a trait object to accept any type that implements \
+                         `{trait_name}`, accessing its methods at runtime using dynamic dispatch",
+                    ),
+                    sugg,
+                    Applicability::MachineApplicable,
+                );
+            }
+            return true;
+        }
+        false
+    }
+
     pub(super) fn maybe_lint_bare_trait(&self, self_ty: &hir::Ty<'_>, in_path: bool) {
         let tcx = self.tcx();
         if let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) =
@@ -98,7 +214,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 let label = "add `dyn` keyword before this trait";
                 let mut diag =
                     rustc_errors::struct_span_err!(tcx.dcx(), self_ty.span, E0782, "{}", msg);
-                if self_ty.span.can_be_used_for_suggestions() {
+                if self_ty.span.can_be_used_for_suggestions()
+                    && !self.maybe_lint_impl_trait(self_ty, &mut diag)
+                {
                     diag.multipart_suggestion_verbose(
                         label,
                         sugg,
@@ -116,11 +234,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     self_ty.span,
                     msg,
                     |lint| {
-                        lint.multipart_suggestion_verbose(
-                            "use `dyn`",
-                            sugg,
-                            Applicability::MachineApplicable,
-                        );
+                        if self_ty.span.can_be_used_for_suggestions()
+                            && !self.maybe_lint_impl_trait(self_ty, lint)
+                        {
+                            lint.multipart_suggestion_verbose(
+                                "use `dyn`",
+                                sugg,
+                                Applicability::MachineApplicable,
+                            );
+                        }
                         self.maybe_lint_blanket_trait_impl(self_ty, lint);
                     },
                 );
diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
index a614d4abf25..8a3df79cb25 100644
--- a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
@@ -140,6 +140,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 let reported = report_object_safety_error(
                     tcx,
                     span,
+                    Some(hir_id),
                     item.trait_ref().def_id(),
                     &object_safety_violations,
                 )
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index a852c3d2be3..c887368b2a2 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -73,7 +73,8 @@ pub(super) fn check_fn<'a, 'tcx>(
     let inputs_fn = fn_sig.inputs().iter().copied();
     for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
         // Check the pattern.
-        let ty_span = try { inputs_hir?.get(idx)?.span };
+        let ty: Option<&hir::Ty<'_>> = try { inputs_hir?.get(idx)? };
+        let ty_span = ty.map(|ty| ty.span);
         fcx.check_pat_top(param.pat, param_ty, ty_span, None, None);
 
         // Check that argument is Sized.
@@ -81,14 +82,14 @@ pub(super) fn check_fn<'a, 'tcx>(
             fcx.require_type_is_sized(
                 param_ty,
                 param.pat.span,
-                // ty_span == binding_span iff this is a closure parameter with no type ascription,
+                // ty.span == binding_span iff this is a closure parameter with no type ascription,
                 // or if it's an implicit `self` parameter
                 traits::SizedArgumentType(
                     if ty_span == Some(param.span) && tcx.is_closure_or_coroutine(fn_def_id.into())
                     {
                         None
                     } else {
-                        ty_span
+                        ty.map(|ty| ty.hir_id)
                     },
                 ),
             );
diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs
index e169b45d725..52dc1e85916 100644
--- a/compiler/rustc_hir_typeck/src/gather_locals.rs
+++ b/compiler/rustc_hir_typeck/src/gather_locals.rs
@@ -60,7 +60,7 @@ pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
     // parameters are special cases of patterns, but we want to handle them as
     // *distinct* cases. so track when we are hitting a pattern *within* an fn
     // parameter.
-    outermost_fn_param_pat: Option<Span>,
+    outermost_fn_param_pat: Option<(Span, hir::HirId)>,
 }
 
 impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
@@ -131,7 +131,8 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
     }
 
     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
-        let old_outermost_fn_param_pat = self.outermost_fn_param_pat.replace(param.ty_span);
+        let old_outermost_fn_param_pat =
+            self.outermost_fn_param_pat.replace((param.ty_span, param.hir_id));
         intravisit::walk_param(self, param);
         self.outermost_fn_param_pat = old_outermost_fn_param_pat;
     }
@@ -141,7 +142,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
         if let PatKind::Binding(_, _, ident, _) = p.kind {
             let var_ty = self.assign(p.span, p.hir_id, None);
 
-            if let Some(ty_span) = self.outermost_fn_param_pat {
+            if let Some((ty_span, hir_id)) = self.outermost_fn_param_pat {
                 if !self.fcx.tcx.features().unsized_fn_params {
                     self.fcx.require_type_is_sized(
                         var_ty,
@@ -154,7 +155,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
                             {
                                 None
                             } else {
-                                Some(ty_span)
+                                Some(hir_id)
                             },
                         ),
                     );
diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
index d89c205da3f..4aabe5c2922 100644
--- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
@@ -2,9 +2,10 @@ use super::ObjectSafetyViolation;
 
 use crate::infer::InferCtxt;
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{struct_span_err, DiagnosticBuilder, MultiSpan};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::Map;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::Span;
@@ -42,6 +43,7 @@ impl<'tcx> InferCtxt<'tcx> {
 pub fn report_object_safety_error<'tcx>(
     tcx: TyCtxt<'tcx>,
     span: Span,
+    hir_id: Option<hir::HirId>,
     trait_def_id: DefId,
     violations: &[ObjectSafetyViolation],
 ) -> DiagnosticBuilder<'tcx> {
@@ -59,6 +61,24 @@ pub fn report_object_safety_error<'tcx>(
     );
     err.span_label(span, format!("`{trait_str}` cannot be made into an object"));
 
+    if let Some(hir_id) = hir_id
+        && let Some(hir::Node::Ty(ty)) = tcx.hir().find(hir_id)
+        && let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind
+    {
+        let mut hir_id = hir_id;
+        while let hir::Node::Ty(ty) = tcx.hir().get_parent(hir_id) {
+            hir_id = ty.hir_id;
+        }
+        if tcx.hir().get_parent(hir_id).fn_sig().is_some() {
+            // Do not suggest `impl Trait` when dealing with things like super-traits.
+            err.span_suggestion_verbose(
+                ty.span.until(trait_ref.span),
+                "consider using an opaque type instead",
+                "impl ",
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
     let mut reported_violations = FxIndexSet::default();
     let mut multi_span = vec![];
     let mut messages = vec![];
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 09b0a0dfbf3..af601a0d702 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -289,7 +289,7 @@ pub enum ObligationCauseCode<'tcx> {
     /// Type of each variable must be `Sized`.
     VariableType(hir::HirId),
     /// Argument type must be `Sized`.
-    SizedArgumentType(Option<Span>),
+    SizedArgumentType(Option<hir::HirId>),
     /// Return type must be `Sized`.
     SizedReturnType,
     /// Yield type must be `Sized`.
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index f63314081d6..bc53e0e5713 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -19,11 +19,10 @@ use rustc_errors::{
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::Visitor;
+use rustc_hir::intravisit::{Map, Visitor};
 use rustc_hir::is_range_literal;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Node};
-use rustc_hir::{Expr, HirId};
+use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node};
 use rustc_infer::infer::error_reporting::TypeErrCtxt;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk};
@@ -3200,35 +3199,80 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     err.help("unsized locals are gated as an unstable feature");
                 }
             }
-            ObligationCauseCode::SizedArgumentType(ty_span) => {
-                if let Some(span) = ty_span {
-                    if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder()
-                        && let ty::ClauseKind::Trait(trait_pred) = clause
-                        && let ty::Dynamic(..) = trait_pred.self_ty().kind()
-                    {
-                        let span = if let Ok(snippet) =
-                            self.tcx.sess.source_map().span_to_snippet(span)
-                            && snippet.starts_with("dyn ")
-                        {
-                            let pos = snippet.len() - snippet[3..].trim_start().len();
-                            span.with_hi(span.lo() + BytePos(pos as u32))
-                        } else {
-                            span.shrink_to_lo()
-                        };
-                        err.span_suggestion_verbose(
-                            span,
-                            "you can use `impl Trait` as the argument type",
-                            "impl ".to_string(),
-                            Applicability::MaybeIncorrect,
-                        );
+            ObligationCauseCode::SizedArgumentType(hir_id) => {
+                let mut ty = None;
+                let borrowed_msg = "function arguments must have a statically known size, borrowed \
+                                    types always have a known size";
+                if let Some(hir_id) = hir_id
+                    && let Some(hir::Node::Param(param)) = self.tcx.hir().find(hir_id)
+                    && let Some(item) = self.tcx.hir().find_parent(hir_id)
+                    && let Some(decl) = item.fn_decl()
+                    && let Some(t) = decl.inputs.iter().find(|t| param.ty_span.contains(t.span))
+                {
+                    // We use `contains` because the type might be surrounded by parentheses,
+                    // which makes `ty_span` and `t.span` disagree with each other, but one
+                    // fully contains the other: `foo: (dyn Foo + Bar)`
+                    //                                 ^-------------^
+                    //                                 ||
+                    //                                 |t.span
+                    //                                 param._ty_span
+                    ty = Some(t);
+                } else if let Some(hir_id) = hir_id
+                    && let Some(hir::Node::Ty(t)) = self.tcx.hir().find(hir_id)
+                {
+                    ty = Some(t);
+                }
+                if let Some(ty) = ty {
+                    match ty.kind {
+                        hir::TyKind::TraitObject(traits, _, _) => {
+                            let (span, kw) = match traits {
+                                [first, ..] if first.span.lo() == ty.span.lo() => {
+                                    // Missing `dyn` in front of trait object.
+                                    (ty.span.shrink_to_lo(), "dyn ")
+                                }
+                                [first, ..] => (ty.span.until(first.span), ""),
+                                [] => span_bug!(ty.span, "trait object with no traits: {ty:?}"),
+                            };
+                            let needs_parens = traits.len() != 1;
+                            err.span_suggestion_verbose(
+                                span,
+                                "you can use `impl Trait` as the argument type",
+                                "impl ".to_string(),
+                                Applicability::MaybeIncorrect,
+                            );
+                            let sugg = if !needs_parens {
+                                vec![(span.shrink_to_lo(), format!("&{kw}"))]
+                            } else {
+                                vec![
+                                    (span.shrink_to_lo(), format!("&({kw}")),
+                                    (ty.span.shrink_to_hi(), ")".to_string()),
+                                ]
+                            };
+                            err.multipart_suggestion_verbose(
+                                borrowed_msg,
+                                sugg,
+                                Applicability::MachineApplicable,
+                            );
+                        }
+                        hir::TyKind::Slice(_ty) => {
+                            err.span_suggestion_verbose(
+                                ty.span.shrink_to_lo(),
+                                "function arguments must have a statically known size, borrowed \
+                                 slices always have a known size",
+                                "&",
+                                Applicability::MachineApplicable,
+                            );
+                        }
+                        hir::TyKind::Path(_) => {
+                            err.span_suggestion_verbose(
+                                ty.span.shrink_to_lo(),
+                                borrowed_msg,
+                                "&",
+                                Applicability::MachineApplicable,
+                            );
+                        }
+                        _ => {}
                     }
-                    err.span_suggestion_verbose(
-                        span.shrink_to_lo(),
-                        "function arguments must have a statically known size, borrowed types \
-                         always have a known size",
-                        "&",
-                        Applicability::MachineApplicable,
-                    );
                 } else {
                     err.note("all function arguments must have a statically known size");
                 }
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index d2598b0defe..81e02a0b17e 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -816,7 +816,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
                     ty::PredicateKind::ObjectSafe(trait_def_id) => {
                         let violations = self.tcx.object_safety_violations(trait_def_id);
-                        report_object_safety_error(self.tcx, span, trait_def_id, violations)
+                        report_object_safety_error(self.tcx, span, None, trait_def_id, violations)
                     }
 
                     ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty)) => {
@@ -924,7 +924,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
             TraitNotObjectSafe(did) => {
                 let violations = self.tcx.object_safety_violations(did);
-                report_object_safety_error(self.tcx, span, did, violations)
+                report_object_safety_error(self.tcx, span, None, did, violations)
             }
 
             SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsInfer) => {