From 048750077676190a0733112d8f700e76a0553f60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 20 Dec 2023 00:37:57 +0000 Subject: [PATCH 1/6] Provide better suggestions when encountering a bare trait as a type Add the following suggestions: ``` 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(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 { | +++++++ + ``` --- .../rustc_hir_analysis/src/astconv/lint.rs | 131 +++++++++++++++--- .../ui/traits/bound/not-on-bare-trait-2021.rs | 17 +++ .../bound/not-on-bare-trait-2021.stderr | 56 ++++++++ .../ui/traits/bound/not-on-bare-trait.stderr | 14 +- 4 files changed, 197 insertions(+), 21 deletions(-) create mode 100644 tests/ui/traits/bound/not-on-bare-trait-2021.rs create mode 100644 tests/ui/traits/bound/not-on-bare-trait-2021.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs index f3b93c91ae9ab..99dbba1ecdc51 100644 --- a/compiler/rustc_hir_analysis/src/astconv/lint.rs +++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs @@ -2,6 +2,7 @@ use rustc_ast::TraitObjectSyntax; use rustc_errors::{Diagnostic, StashKey}; use rustc_hir as hir; 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 +33,120 @@ 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 Some(sugg) = self.generics_suggestion(generics, self_ty.span, &impl_trait_name) + else { + 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 generics_suggestion( + &self, + generics: &hir::Generics<'_>, + self_ty_span: Span, + impl_trait_name: &str, + ) -> Option> { + // 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}>")) + }; + Some(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) = self.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())]; + if let hir::FnRetTy::Return(ty) = sig.decl.output + && ty.hir_id == self_ty.hir_id + { + diag.multipart_suggestion_verbose( + format!("use `impl {trait_name}` to return an opaque type, as long as you return a single underlying type"), + impl_sugg, + Applicability::MachineApplicable, + ); + diag.multipart_suggestion_verbose( + "alternatively, you can return an owned trait object", + vec![ + (ty.span.shrink_to_lo(), "Box".to_string()), + ], + Applicability::MachineApplicable, + ); + return true; + } + for ty in sig.decl.inputs { + if ty.hir_id == self_ty.hir_id { + if let Some(sugg) = self.generics_suggestion(generics, self_ty.span, &trait_name) { + 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, + ); + } + 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 +187,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 +207,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/tests/ui/traits/bound/not-on-bare-trait-2021.rs b/tests/ui/traits/bound/not-on-bare-trait-2021.rs new file mode 100644 index 0000000000000..3d97bddb4a49f --- /dev/null +++ b/tests/ui/traits/bound/not-on-bare-trait-2021.rs @@ -0,0 +1,17 @@ +// edition:2021 +trait Foo { + fn dummy(&self) {} +} + +// This should emit the less confusing error, not the more confusing one. + +fn foo(_x: Foo + Send) { + //~^ ERROR trait objects must include the `dyn` keyword +} +fn bar(x: Foo) -> Foo { + //~^ ERROR trait objects must include the `dyn` keyword + //~| ERROR trait objects must include the `dyn` keyword + x +} + +fn main() {} diff --git a/tests/ui/traits/bound/not-on-bare-trait-2021.stderr b/tests/ui/traits/bound/not-on-bare-trait-2021.stderr new file mode 100644 index 0000000000000..6f41f872e4cf6 --- /dev/null +++ b/tests/ui/traits/bound/not-on-bare-trait-2021.stderr @@ -0,0 +1,56 @@ +error[E0782]: trait objects must include the `dyn` keyword + --> $DIR/not-on-bare-trait-2021.rs:8:12 + | +LL | fn foo(_x: Foo + Send) { + | ^^^^^^^^^^ + | +help: use a new generic type parameter, constrained by `Foo + Send` + | +LL | fn foo(_x: T) { + | +++++++++++++++ ~ +help: 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 + | +LL | fn foo(_x: impl Foo + Send) { + | ++++ +help: alternatively, use a trait object to accept any type that implements `Foo + Send`, accessing its methods at runtime using dynamic dispatch + | +LL | fn foo(_x: &(dyn Foo + Send)) { + | +++++ + + +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 new generic type parameter, constrained by `Foo` + | +LL | fn bar(x: T) -> Foo { + | ++++++++ ~ +help: 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 + | +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 { + | +++++++ + + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0782`. diff --git a/tests/ui/traits/bound/not-on-bare-trait.stderr b/tests/ui/traits/bound/not-on-bare-trait.stderr index 1d97bf3d8f957..976dd6a1bc532 100644 --- a/tests/ui/traits/bound/not-on-bare-trait.stderr +++ b/tests/ui/traits/bound/not-on-bare-trait.stderr @@ -7,10 +7,18 @@ LL | fn foo(_x: Foo + Send) { = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! = note: for more information, see = note: `#[warn(bare_trait_objects)]` on by default -help: use `dyn` +help: use a new generic type parameter, constrained by `Foo + Send` | -LL | fn foo(_x: dyn Foo + Send) { - | +++ +LL | fn foo(_x: T) { + | +++++++++++++++ ~ +help: 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 + | +LL | fn foo(_x: impl Foo + Send) { + | ++++ +help: alternatively, use a trait object to accept any type that implements `Foo + Send`, accessing its methods at runtime using dynamic dispatch + | +LL | fn foo(_x: &(dyn Foo + Send)) { + | +++++ + error[E0277]: the size for values of type `(dyn Foo + Send + 'static)` cannot be known at compilation time --> $DIR/not-on-bare-trait.rs:7:8 From 8551cab7b75ab8f9c51b1d5a847031093b79b546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 20 Dec 2023 01:03:05 +0000 Subject: [PATCH 2/6] Account for multiple trait bounds in bare trait object suggestion Note the parentheses in the last suggestion: ``` error[E0277]: the size for values of type `(dyn Foo + Send + 'static)` cannot be known at compilation time --> $DIR/not-on-bare-trait.rs:7:8 | LL | fn foo(_x: Foo + Send) { | ^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn Foo + Send + 'static)` = help: unsized fn params are gated as an unstable feature help: you can use `impl Trait` as the argument type | LL | fn foo(_x: impl Foo + Send) { | ++++ help: function arguments must have a statically known size, borrowed types always have a known size | LL | fn foo(_x: &(Foo + Send)) { | ++ + ``` --- .../src/traits/error_reporting/suggestions.rs | 40 ++++++++++++++++--- tests/ui/traits/bound/not-on-bare-trait.rs | 3 ++ .../ui/traits/bound/not-on-bare-trait.stderr | 21 +++++++++- 3 files changed, 56 insertions(+), 8 deletions(-) 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 f63314081d615..b768108e24ce2 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -3202,9 +3202,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { } ObligationCauseCode::SizedArgumentType(ty_span) => { if let Some(span) = ty_span { - if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder() + let trait_len = 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 ty::Dynamic(preds, ..) = trait_pred.self_ty().kind() { let span = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) @@ -3221,12 +3222,39 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { "impl ".to_string(), Applicability::MaybeIncorrect, ); - } - err.span_suggestion_verbose( - span.shrink_to_lo(), + preds + .iter() + .filter(|pred| { + // We only want to count `dyn Foo + Bar`, not `dyn Foo`, + // because the later doesn't need parentheses. + matches!( + pred.skip_binder(), + ty::ExistentialPredicate::Trait(_) + | ty::ExistentialPredicate::AutoTrait(_) + ) + }) + .count() + } else { + 1 + }; + let sugg = if trait_len == 1 { + vec![(span.shrink_to_lo(), "&".to_string())] + } else if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) + && snippet.starts_with('(') + { + // We don't want to suggest `&((dyn Foo + Bar))` when we have + // `(dyn Foo + Bar)`. + vec![(span.shrink_to_lo(), "&".to_string())] + } else { + vec![ + (span.shrink_to_lo(), "&(".to_string()), + (span.shrink_to_hi(), ")".to_string()), + ] + }; + err.multipart_suggestion_verbose( "function arguments must have a statically known size, borrowed types \ always have a known size", - "&", + sugg, Applicability::MachineApplicable, ); } else { diff --git a/tests/ui/traits/bound/not-on-bare-trait.rs b/tests/ui/traits/bound/not-on-bare-trait.rs index daf18c6702e46..9e717f3c045e3 100644 --- a/tests/ui/traits/bound/not-on-bare-trait.rs +++ b/tests/ui/traits/bound/not-on-bare-trait.rs @@ -9,5 +9,8 @@ fn foo(_x: Foo + Send) { //~| WARN trait objects without an explicit `dyn` are deprecated //~| WARN this is accepted in the current edition } +fn bar(_x: (dyn Foo + Send)) { + //~^ ERROR the size for values of type +} fn main() {} diff --git a/tests/ui/traits/bound/not-on-bare-trait.stderr b/tests/ui/traits/bound/not-on-bare-trait.stderr index 976dd6a1bc532..edb6b1d934bd4 100644 --- a/tests/ui/traits/bound/not-on-bare-trait.stderr +++ b/tests/ui/traits/bound/not-on-bare-trait.stderr @@ -34,9 +34,26 @@ LL | fn foo(_x: impl Foo + Send) { | ++++ help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn foo(_x: &Foo + Send) { +LL | fn foo(_x: &(Foo + Send)) { + | ++ + + +error[E0277]: the size for values of type `(dyn Foo + Send + 'static)` cannot be known at compilation time + --> $DIR/not-on-bare-trait.rs:12:8 + | +LL | fn bar(_x: (dyn Foo + Send)) { + | ^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `(dyn Foo + Send + 'static)` + = help: unsized fn params are gated as an unstable feature +help: you can use `impl Trait` as the argument type + | +LL | fn bar(_x: impl (dyn Foo + Send)) { + | ++++ +help: function arguments must have a statically known size, borrowed types always have a known size + | +LL | fn bar(_x: &(dyn Foo + Send)) { | + -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0277`. From 79bef72fd56c2a0f3998dceb7c08355cf3fabc4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 20 Dec 2023 19:13:24 +0000 Subject: [PATCH 3/6] Track `HirId` instead of `Span` in `ObligationCauseCode::SizedArgumentType` This gets us more accurate suggestions. --- compiler/rustc_hir_typeck/src/check.rs | 7 +- .../rustc_hir_typeck/src/gather_locals.rs | 9 +- compiler/rustc_middle/src/traits/mod.rs | 2 +- .../src/traits/error_reporting/suggestions.rs | 136 ++++++++++-------- .../clippy/tests/ui/crashes/ice-6251.stderr | 2 +- .../feature-gate-unsized_fn_params.stderr | 6 +- tests/ui/resolve/issue-5035-2.stderr | 4 - .../ui/traits/bound/not-on-bare-trait.stderr | 12 +- 8 files changed, 96 insertions(+), 82 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs index 0ca0f7d2daf92..bd7c0749171f6 100644 --- a/compiler/rustc_hir_typeck/src/check.rs +++ b/compiler/rustc_hir_typeck/src/check.rs @@ -114,7 +114,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. @@ -122,13 +123,13 @@ 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(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 0cca779b1560e..52259c6066012 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, + 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_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 09b0a0dfbf38c..af601a0d702ed 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), + SizedArgumentType(Option), /// 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 b768108e24ce2..bc53e0e57133e 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,63 +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 { - let trait_len = if let ty::PredicateKind::Clause(clause) = - predicate.kind().skip_binder() - && let ty::ClauseKind::Trait(trait_pred) = clause - && let ty::Dynamic(preds, ..) = 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, - ); - preds - .iter() - .filter(|pred| { - // We only want to count `dyn Foo + Bar`, not `dyn Foo`, - // because the later doesn't need parentheses. - matches!( - pred.skip_binder(), - ty::ExistentialPredicate::Trait(_) - | ty::ExistentialPredicate::AutoTrait(_) - ) - }) - .count() - } else { - 1 - }; - let sugg = if trait_len == 1 { - vec![(span.shrink_to_lo(), "&".to_string())] - } else if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) - && snippet.starts_with('(') - { - // We don't want to suggest `&((dyn Foo + Bar))` when we have - // `(dyn Foo + Bar)`. - vec![(span.shrink_to_lo(), "&".to_string())] - } else { - vec![ - (span.shrink_to_lo(), "&(".to_string()), - (span.shrink_to_hi(), ")".to_string()), - ] - }; - err.multipart_suggestion_verbose( - "function arguments must have a statically known size, borrowed types \ - always have a known size", - sugg, - Applicability::MachineApplicable, - ); + 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, + ); + } + _ => {} + } } else { err.note("all function arguments must have a statically known size"); } diff --git a/src/tools/clippy/tests/ui/crashes/ice-6251.stderr b/src/tools/clippy/tests/ui/crashes/ice-6251.stderr index 11081dc8087e2..0196c9923dbe6 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6251.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-6251.stderr @@ -6,7 +6,7 @@ LL | fn bug() -> impl Iterator { | = help: the trait `std::marker::Sized` is not implemented for `[u8]` = help: unsized fn params are gated as an unstable feature -help: function arguments must have a statically known size, borrowed types always have a known size +help: function arguments must have a statically known size, borrowed slices always have a known size | LL | fn bug() -> impl Iterator { | + diff --git a/tests/ui/feature-gates/feature-gate-unsized_fn_params.stderr b/tests/ui/feature-gates/feature-gate-unsized_fn_params.stderr index 92c71392672e1..b11c30eaad4b7 100644 --- a/tests/ui/feature-gates/feature-gate-unsized_fn_params.stderr +++ b/tests/ui/feature-gates/feature-gate-unsized_fn_params.stderr @@ -29,8 +29,8 @@ LL | fn bar(x: impl Foo) { | ++++ help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn bar(x: &Foo) { - | + +LL | fn bar(x: &dyn Foo) { + | ++++ error[E0277]: the size for values of type `[()]` cannot be known at compilation time --> $DIR/feature-gate-unsized_fn_params.rs:25:8 @@ -40,7 +40,7 @@ LL | fn qux(_: [()]) {} | = help: the trait `Sized` is not implemented for `[()]` = help: unsized fn params are gated as an unstable feature -help: function arguments must have a statically known size, borrowed types always have a known size +help: function arguments must have a statically known size, borrowed slices always have a known size | LL | fn qux(_: &[()]) {} | + diff --git a/tests/ui/resolve/issue-5035-2.stderr b/tests/ui/resolve/issue-5035-2.stderr index 8eeb398f077ea..30721c0a206e7 100644 --- a/tests/ui/resolve/issue-5035-2.stderr +++ b/tests/ui/resolve/issue-5035-2.stderr @@ -6,10 +6,6 @@ LL | fn foo(_x: K) {} | = help: the trait `Sized` is not implemented for `(dyn I + 'static)` = help: unsized fn params are gated as an unstable feature -help: you can use `impl Trait` as the argument type - | -LL | fn foo(_x: impl K) {} - | ++++ help: function arguments must have a statically known size, borrowed types always have a known size | LL | fn foo(_x: &K) {} diff --git a/tests/ui/traits/bound/not-on-bare-trait.stderr b/tests/ui/traits/bound/not-on-bare-trait.stderr index edb6b1d934bd4..8d0e40be788c1 100644 --- a/tests/ui/traits/bound/not-on-bare-trait.stderr +++ b/tests/ui/traits/bound/not-on-bare-trait.stderr @@ -34,8 +34,8 @@ LL | fn foo(_x: impl Foo + Send) { | ++++ help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn foo(_x: &(Foo + Send)) { - | ++ + +LL | fn foo(_x: &(dyn Foo + Send)) { + | +++++ + error[E0277]: the size for values of type `(dyn Foo + Send + 'static)` cannot be known at compilation time --> $DIR/not-on-bare-trait.rs:12:8 @@ -47,12 +47,12 @@ LL | fn bar(_x: (dyn Foo + Send)) { = help: unsized fn params are gated as an unstable feature help: you can use `impl Trait` as the argument type | -LL | fn bar(_x: impl (dyn Foo + Send)) { - | ++++ +LL | fn bar(_x: (impl Foo + Send)) { + | ~~~~ help: function arguments must have a statically known size, borrowed types always have a known size | -LL | fn bar(_x: &(dyn Foo + Send)) { - | + +LL | fn bar(_x: (&(dyn Foo + Send))) { + | ++ + error: aborting due to 2 previous errors; 1 warning emitted From 771966ba294be37e0613d97109f203393521e217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 3 Jan 2024 19:25:31 +0000 Subject: [PATCH 4/6] review comments --- .../rustc_hir_analysis/src/astconv/lint.rs | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs index 99dbba1ecdc51..2be172e8113ae 100644 --- a/compiler/rustc_hir_analysis/src/astconv/lint.rs +++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs @@ -41,8 +41,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { else { return; }; - let Some(sugg) = self.generics_suggestion(generics, self_ty.span, &impl_trait_name) - else { + let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &impl_trait_name); + if sugg.is_empty() { return; }; diag.multipart_suggestion( @@ -56,12 +56,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } } - fn generics_suggestion( + fn add_generic_param_suggestion( &self, generics: &hir::Generics<'_>, self_ty_span: Span, impl_trait_name: &str, - ) -> Option> { + ) -> Vec<(Span, String)> { // check if the trait has generics, to make a correct suggestion let param_name = generics.params.next_type_param_name(None); @@ -70,7 +70,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } else { (generics.span, format!("<{param_name}: {impl_trait_name}>")) }; - Some(vec![(self_ty_span, param_name), add_generic_sugg]) + vec![(self_ty_span, param_name), add_generic_sugg] } /// Make sure that we are in the condition to suggest `impl Trait`. @@ -86,7 +86,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { else { return false; }; - let Ok(trait_name) = self.tcx().sess.source_map().span_to_snippet(self_ty.span) else { + 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())]; @@ -98,19 +98,22 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { impl_sugg, Applicability::MachineApplicable, ); - diag.multipart_suggestion_verbose( - "alternatively, you can return an owned trait object", - vec![ - (ty.span.shrink_to_lo(), "Box".to_string()), - ], - Applicability::MachineApplicable, - ); + if tcx.check_is_object_safe(def_id) { + diag.multipart_suggestion_verbose( + "alternatively, you can return an owned trait object", + vec![ + (ty.span.shrink_to_lo(), "Box".to_string()), + ], + Applicability::MachineApplicable, + ); + } return true; } for ty in sig.decl.inputs { if ty.hir_id == self_ty.hir_id { - if let Some(sugg) = self.generics_suggestion(generics, self_ty.span, &trait_name) { + 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, @@ -124,6 +127,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Applicability::MachineApplicable, ); } + if !tcx.check_is_object_safe(def_id) { + diag.note(format!("it is not object safe, so it can't be `dyn`")); + } let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind { // There are more than one trait bound, we need surrounding parentheses. vec![ From 78ef94618b037e18d01644638eaf41ecfa5def04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 3 Jan 2024 21:00:04 +0000 Subject: [PATCH 5/6] Account for object unsafe traits Fix #119525. --- .../rustc_hir_analysis/src/astconv/lint.rs | 66 ++++++++++++------- .../bare-trait-dont-suggest-dyn.fixed | 11 ++++ .../bare-trait-dont-suggest-dyn.rs | 11 ++++ .../bare-trait-dont-suggest-dyn.stderr | 35 ++++++++++ 4 files changed, 98 insertions(+), 25 deletions(-) create mode 100644 tests/ui/object-safety/bare-trait-dont-suggest-dyn.fixed create mode 100644 tests/ui/object-safety/bare-trait-dont-suggest-dyn.rs create mode 100644 tests/ui/object-safety/bare-trait-dont-suggest-dyn.stderr diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs index 2be172e8113ae..d26f3d5ee917f 100644 --- a/compiler/rustc_hir_analysis/src/astconv/lint.rs +++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs @@ -1,6 +1,7 @@ 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; @@ -90,15 +91,29 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { 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 { - diag.multipart_suggestion_verbose( - format!("use `impl {trait_name}` to return an opaque type, as long as you return a single underlying type"), - impl_sugg, - Applicability::MachineApplicable, + 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", ); - if tcx.check_is_object_safe(def_id) { + 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![ @@ -111,25 +126,26 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return true; } for ty in sig.decl.inputs { - if ty.hir_id == self_ty.hir_id { - 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 !tcx.check_is_object_safe(def_id) { - diag.note(format!("it is not object safe, so it can't be `dyn`")); - } + 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`")); + } else { let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind { // There are more than one trait bound, we need surrounding parentheses. vec![ @@ -147,8 +163,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { sugg, Applicability::MachineApplicable, ); - return true; } + return true; } false } diff --git a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.fixed b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.fixed new file mode 100644 index 0000000000000..9720a781eb8d9 --- /dev/null +++ b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.fixed @@ -0,0 +1,11 @@ +// run-rustfix +#![deny(bare_trait_objects)] +fn ord_prefer_dot(s: String) -> impl Ord { + //~^ ERROR trait objects without an explicit `dyn` are deprecated + //~| ERROR the trait `Ord` cannot be made into an object + //~| WARNING this is accepted in the current edition (Rust 2015) + (s.starts_with("."), s) +} +fn main() { + let _ = ord_prefer_dot(String::new()); +} diff --git a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.rs b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.rs new file mode 100644 index 0000000000000..31d6e336ccfe7 --- /dev/null +++ b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.rs @@ -0,0 +1,11 @@ +// run-rustfix +#![deny(bare_trait_objects)] +fn ord_prefer_dot(s: String) -> Ord { + //~^ ERROR trait objects without an explicit `dyn` are deprecated + //~| ERROR the trait `Ord` cannot be made into an object + //~| WARNING this is accepted in the current edition (Rust 2015) + (s.starts_with("."), s) +} +fn main() { + let _ = ord_prefer_dot(String::new()); +} diff --git a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.stderr b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.stderr new file mode 100644 index 0000000000000..468a570a0f92e --- /dev/null +++ b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.stderr @@ -0,0 +1,35 @@ +error: trait objects without an explicit `dyn` are deprecated + --> $DIR/bare-trait-dont-suggest-dyn.rs:3:33 + | +LL | fn ord_prefer_dot(s: String) -> Ord { + | ^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see +note: the lint level is defined here + --> $DIR/bare-trait-dont-suggest-dyn.rs:2:9 + | +LL | #![deny(bare_trait_objects)] + | ^^^^^^^^^^^^^^^^^^ +help: `Ord` is not object safe, use `impl Ord` to return an opaque type, as long as you return a single underlying type + | +LL | fn ord_prefer_dot(s: String) -> impl Ord { + | ++++ + +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 + --> $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 + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0038`. From 698dfc322fd42fa4dff71474aba7cdf691a459a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 3 Jan 2024 23:40:49 +0000 Subject: [PATCH 6/6] Silence redundant warning when E0038 will be emitted --- .../rustc_hir_analysis/src/astconv/errors.rs | 2 +- .../rustc_hir_analysis/src/astconv/lint.rs | 5 ++++ .../src/astconv/object_safety.rs | 1 + .../src/traits/error_reporting/mod.rs | 22 ++++++++++++++++- .../error_reporting/type_err_ctxt_ext.rs | 4 ++-- tests/rustdoc-ui/issues/issue-105742.stderr | 4 ++++ .../bare-trait-dont-suggest-dyn.fixed | 4 +--- .../bare-trait-dont-suggest-dyn.rs | 4 +--- .../bare-trait-dont-suggest-dyn.stderr | 24 ++++--------------- ...ect-safety-supertrait-mentions-Self.stderr | 4 ++++ tests/ui/traits/issue-28576.stderr | 4 ++++ 11 files changed, 49 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs index f17f19bb77c70..d22c4fe201df0 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 d26f3d5ee917f..6675f517cfa15 100644 --- a/compiler/rustc_hir_analysis/src/astconv/lint.rs +++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs @@ -122,6 +122,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ], Applicability::MachineApplicable, ); + } else { + // We'll emit the object safety error already, with a structured suggestion. + diag.downgrade_to_delayed_bug(); } return true; } @@ -145,6 +148,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } 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. diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs index a614d4abf25f7..8a3df79cb2534 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_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs index d89c205da3f52..4aabe5c292237 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, 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_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 d2598b0defe7f..81e02a0b17ed7 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) => { diff --git a/tests/rustdoc-ui/issues/issue-105742.stderr b/tests/rustdoc-ui/issues/issue-105742.stderr index ad1020a1f0817..d5a9031075f8d 100644 --- a/tests/rustdoc-ui/issues/issue-105742.stderr +++ b/tests/rustdoc-ui/issues/issue-105742.stderr @@ -323,6 +323,10 @@ LL | || Output = ::Item> as SVec>::Item, LL | | LL | | > { | |__^ ...because it uses `Self` as a type parameter +help: consider using an opaque type instead + | +LL | pub fn next<'a, T>(s: &'a mut impl SVec) { + | ~~~~ error[E0107]: missing generics for associated type `SVec::Item` --> $DIR/issue-105742.rs:15:21 diff --git a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.fixed b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.fixed index 9720a781eb8d9..e95b982966d0b 100644 --- a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.fixed +++ b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.fixed @@ -1,9 +1,7 @@ // run-rustfix #![deny(bare_trait_objects)] fn ord_prefer_dot(s: String) -> impl Ord { - //~^ ERROR trait objects without an explicit `dyn` are deprecated - //~| ERROR the trait `Ord` cannot be made into an object - //~| WARNING this is accepted in the current edition (Rust 2015) + //~^ ERROR the trait `Ord` cannot be made into an object (s.starts_with("."), s) } fn main() { diff --git a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.rs b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.rs index 31d6e336ccfe7..fdf7e0a77aa34 100644 --- a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.rs +++ b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.rs @@ -1,9 +1,7 @@ // run-rustfix #![deny(bare_trait_objects)] fn ord_prefer_dot(s: String) -> Ord { - //~^ ERROR trait objects without an explicit `dyn` are deprecated - //~| ERROR the trait `Ord` cannot be made into an object - //~| WARNING this is accepted in the current edition (Rust 2015) + //~^ ERROR the trait `Ord` cannot be made into an object (s.starts_with("."), s) } fn main() { diff --git a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.stderr b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.stderr index 468a570a0f92e..2c499d240ab0b 100644 --- a/tests/ui/object-safety/bare-trait-dont-suggest-dyn.stderr +++ b/tests/ui/object-safety/bare-trait-dont-suggest-dyn.stderr @@ -1,21 +1,3 @@ -error: trait objects without an explicit `dyn` are deprecated - --> $DIR/bare-trait-dont-suggest-dyn.rs:3:33 - | -LL | fn ord_prefer_dot(s: String) -> Ord { - | ^^^ - | - = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! - = note: for more information, see -note: the lint level is defined here - --> $DIR/bare-trait-dont-suggest-dyn.rs:2:9 - | -LL | #![deny(bare_trait_objects)] - | ^^^^^^^^^^^^^^^^^^ -help: `Ord` is not object safe, use `impl Ord` to return an opaque type, as long as you return a single underlying type - | -LL | fn ord_prefer_dot(s: String) -> impl Ord { - | ++++ - error[E0038]: the trait `Ord` cannot be made into an object --> $DIR/bare-trait-dont-suggest-dyn.rs:3:33 | @@ -29,7 +11,11 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all ::: $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 { + | ++++ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/object-safety/object-safety-supertrait-mentions-Self.stderr b/tests/ui/object-safety/object-safety-supertrait-mentions-Self.stderr index 08df069275a60..fc476691d0147 100644 --- a/tests/ui/object-safety/object-safety-supertrait-mentions-Self.stderr +++ b/tests/ui/object-safety/object-safety-supertrait-mentions-Self.stderr @@ -11,6 +11,10 @@ LL | trait Baz : Bar { | --- ^^^^^^^^^ ...because it uses `Self` as a type parameter | | | this trait cannot be made into an object... +help: consider using an opaque type instead + | +LL | fn make_baz(t: &T) -> &impl Baz { + | ~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/traits/issue-28576.stderr b/tests/ui/traits/issue-28576.stderr index 9fe508646423e..3b45a510341fe 100644 --- a/tests/ui/traits/issue-28576.stderr +++ b/tests/ui/traits/issue-28576.stderr @@ -14,6 +14,10 @@ LL | pub trait Bar: Foo { | | | ...because it uses `Self` as a type parameter | | ...because it uses `Self` as a type parameter | this trait cannot be made into an object... +help: consider using an opaque type instead + | +LL | impl Bar + | ~~~~ error: aborting due to 1 previous error