diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 8dccead861de7..5520b059f5678 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -21,7 +21,7 @@ use rustc_macros::{ Decodable, Encodable, StableHash, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, }; use rustc_span::def_id::{CRATE_DEF_ID, LocalDefId}; -use rustc_span::{DUMMY_SP, Span, Symbol}; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use smallvec::{SmallVec, smallvec}; use thin_vec::ThinVec; @@ -870,8 +870,8 @@ impl DynCompatibilityViolation { add_self_sugg: add_self_sugg.clone(), make_sized_sugg: make_sized_sugg.clone(), }, - Self::Method(name, MethodViolation::UndispatchableReceiver(Some(span)), _) => { - DynCompatibilityViolationSolution::ChangeToRefSelf(*name, *span) + Self::Method(name, MethodViolation::UndispatchableReceiver(Some((span, lt))), _) => { + DynCompatibilityViolationSolution::ChangeToRefSelf(*name, *span, *lt) } Self::Method(name, ..) | Self::AssocConst(name, ..) | Self::GenericAssocTy(name, _) => { DynCompatibilityViolationSolution::MoveToAnotherTrait(*name) @@ -909,7 +909,7 @@ pub enum DynCompatibilityViolationSolution { add_self_sugg: (String, Span), make_sized_sugg: (String, Span), }, - ChangeToRefSelf(Symbol, Span), + ChangeToRefSelf(Symbol, Span, Symbol), MoveToAnotherTrait(Symbol), } @@ -922,30 +922,30 @@ impl DynCompatibilityViolationSolution { add_self_sugg, make_sized_sugg, } => { - err.span_suggestion( + err.span_suggestion_verbose( add_self_sugg.1, format!( - "consider turning `{name}` into a method by giving it a `&self` \ - argument, so that it is accessible through the trait object's vtable" + "consider turning `{name}` into a method by giving it a `&self` argument, \ + so that it is accessible through the trait object's vtable", ), add_self_sugg.0, Applicability::MaybeIncorrect, ); - err.span_suggestion( + err.span_suggestion_verbose( make_sized_sugg.1, format!( - "alternatively, consider constraining `{name}` so it is explicitly \ - marked as not applying to trait objects" + "alternatively, consider constraining `{name}` so it is explicitly marked \ + as not applying to trait objects", ), make_sized_sugg.0, Applicability::MaybeIncorrect, ); } - DynCompatibilityViolationSolution::ChangeToRefSelf(name, span) => { - err.span_suggestion( + DynCompatibilityViolationSolution::ChangeToRefSelf(name, span, lt) => { + err.span_suggestion_verbose( span, format!("consider changing method `{name}`'s `self` parameter to be `&self`"), - "&Self", + format!("&{lt}{}self", if lt != sym::empty { " " } else { "" }), Applicability::MachineApplicable, ); } @@ -983,8 +983,11 @@ pub enum MethodViolation { /// e.g., `fn (mut ap: ...)` CVariadic, - /// the method's receiver (`self` argument) can't be dispatched on - UndispatchableReceiver(Option), + /// The method's receiver (`self` argument) can't be dispatched on + /// + /// The `Span` points at the receiver. The `Symbol` is the lifetime's name `'a` when we have + /// Arbitrary Self Types like `self: &'a ()`. + UndispatchableReceiver(Option<(Span, Symbol)>), } /// Reasons an associated const might not be dyn compatible. diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs index f2b221c59ee95..e964c09420b0e 100644 --- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs +++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs @@ -16,7 +16,7 @@ use rustc_middle::ty::{ TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode, Unnormalized, Upcast, elaborate, }; -use rustc_span::{DUMMY_SP, Span}; +use rustc_span::{DUMMY_SP, Span, kw, sym}; use smallvec::SmallVec; use tracing::{debug, instrument}; @@ -403,7 +403,7 @@ pub fn dyn_compatibility_violations_for_assoc_item( // Get an accurate span depending on the violation. let span = match (&v, node) { (MethodViolation::ReferencesSelfInput(Some(span)), _) => *span, - (MethodViolation::UndispatchableReceiver(Some(span)), _) => *span, + (MethodViolation::UndispatchableReceiver(Some((span, _))), _) => *span, (MethodViolation::ReferencesImplTraitInTrait(span), _) => *span, (MethodViolation::ReferencesSelfOutput, Some(node)) => { node.fn_decl().map_or(item.ident(tcx).span, |decl| decl.output.span()) @@ -519,16 +519,40 @@ fn virtual_call_violations_for_method<'tcx>( // `Receiver: Unsize dyn Trait]>`. if receiver_ty != tcx.types.self_param { if !receiver_is_dispatchable(tcx, method, receiver_ty) { - let span = if let Some(hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(sig, _), + let span_n_lt = if let Some(hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(sig, trait_fn), .. })) = tcx.hir_get_if_local(method.def_id).as_ref() { - Some(sig.decl.inputs[0].span) + // If we have `self: &'a Ty`, get `'a`, so that we can suggest `&'a self`. + let lt = match sig.decl.inputs[0].kind { + hir::TyKind::Ref(lt, _) if lt.ident.name == kw::UnderscoreLifetime => { + sym::empty + } + hir::TyKind::Ref(lt, _) => lt.ident.name, + _ => sym::empty, + }; + // Get the `Span` for all of `self: Ty`, not just `Ty`. + match trait_fn { + hir::TraitFn::Required([Some(name), ..]) + if name.span.eq_ctxt(sig.decl.inputs[0].span) => + { + Some(name.span.to(sig.decl.inputs[0].span)) + } + hir::TraitFn::Provided(body_id) + if let body = tcx.hir_body(*body_id) + && let Some(p) = body.params.get(0) + && p.span.eq_ctxt(p.ty_span) => + { + Some(p.span.to(p.ty_span)) + } + _ => None, + } + .map(|sp| (sp, lt)) } else { None }; - errors.push(MethodViolation::UndispatchableReceiver(span)); + errors.push(MethodViolation::UndispatchableReceiver(span_n_lt)); } else { // We confirm that the `receiver_is_dispatchable` is accurate later, // see `check_receiver_correct`. It should be kept in sync with this code. diff --git a/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr b/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr index 867a719e2ebfd..8048ff3e9c0c3 100644 --- a/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr +++ b/tests/ui/dyn-compatibility/undispatchable-receiver-and-wc-references-Self.stderr @@ -1,38 +1,42 @@ error[E0038]: the trait `Fetcher` is not dyn compatible --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:19:21 | -LL | fn get<'a>(self: &'a Box) -> Pin> + 'a>> - | ------------- help: consider changing method `get`'s `self` parameter to be `&self`: `&Self` -... LL | fn fetcher() -> Box { | ^^^^^^^^^^^ `Fetcher` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:11:22 + --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:11:16 | LL | pub trait Fetcher: Send + Sync { | ------- this trait is not dyn compatible... LL | fn get<'a>(self: &'a Box) -> Pin> + 'a>> - | ^^^^^^^^^^^^^ ...because method `get`'s `self` parameter cannot be dispatched on + | ^^^^^^^^^^^^^^^^^^^ ...because method `get`'s `self` parameter cannot be dispatched on +help: consider changing method `get`'s `self` parameter to be `&self` + | +LL - fn get<'a>(self: &'a Box) -> Pin> + 'a>> +LL + fn get<'a>(&'a self) -> Pin> + 'a>> + | error[E0038]: the trait `Fetcher` is not dyn compatible --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:25:19 | -LL | fn get<'a>(self: &'a Box) -> Pin> + 'a>> - | ------------- help: consider changing method `get`'s `self` parameter to be `&self`: `&Self` -... LL | let fetcher = fetcher(); | ^^^^^^^^^ `Fetcher` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:11:22 + --> $DIR/undispatchable-receiver-and-wc-references-Self.rs:11:16 | LL | pub trait Fetcher: Send + Sync { | ------- this trait is not dyn compatible... LL | fn get<'a>(self: &'a Box) -> Pin> + 'a>> - | ^^^^^^^^^^^^^ ...because method `get`'s `self` parameter cannot be dispatched on + | ^^^^^^^^^^^^^^^^^^^ ...because method `get`'s `self` parameter cannot be dispatched on +help: consider changing method `get`'s `self` parameter to be `&self` + | +LL - fn get<'a>(self: &'a Box) -> Pin> + 'a>> +LL + fn get<'a>(&'a self) -> Pin> + 'a>> + | error: aborting due to 2 previous errors diff --git a/tests/ui/dyn-compatibility/unsafe-binders-bare-trait-object-next-solver.stderr b/tests/ui/dyn-compatibility/unsafe-binders-bare-trait-object-next-solver.stderr index 6ca8ec6bd1a5a..e333fccd69f5d 100644 --- a/tests/ui/dyn-compatibility/unsafe-binders-bare-trait-object-next-solver.stderr +++ b/tests/ui/dyn-compatibility/unsafe-binders-bare-trait-object-next-solver.stderr @@ -32,20 +32,22 @@ LL | fn method(self: &unsafe<'ops> &'a dyn Bar) {} error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/unsafe-binders-bare-trait-object-next-solver.rs:17:13 | -LL | fn method(self: &unsafe<'ops> &'a Bar) {} - | --------------------- help: consider changing method `method`'s `self` parameter to be `&self`: `&Self` -... LL | fn test(x: &dyn Foo) { | ^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/unsafe-binders-bare-trait-object-next-solver.rs:10:21 + --> $DIR/unsafe-binders-bare-trait-object-next-solver.rs:10:15 | LL | trait Foo: Deref &'a dyn Bar> { | --- this trait is not dyn compatible... LL | fn method(self: &unsafe<'ops> &'a Bar) {} - | ^^^^^^^^^^^^^^^^^^^^^ ...because method `method`'s `self` parameter cannot be dispatched on + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...because method `method`'s `self` parameter cannot be dispatched on +help: consider changing method `method`'s `self` parameter to be `&self` + | +LL - fn method(self: &unsafe<'ops> &'a Bar) {} +LL + fn method(&self) {} + | error[E0599]: no method named `method` found for reference `&dyn Foo` in the current scope --> $DIR/unsafe-binders-bare-trait-object-next-solver.rs:19:7 diff --git a/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr index c70ab65aa9056..3aeb3edb8690c 100644 --- a/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr +++ b/tests/ui/feature-gates/feature-gate-dispatch-from-dyn-missing-impl.stderr @@ -1,21 +1,23 @@ error[E0038]: the trait `Trait` is not dyn compatible --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:32:33 | -LL | fn ptr(self: Ptr); - | --------- help: consider changing method `ptr`'s `self` parameter to be `&self`: `&Self` -... LL | Ptr(Box::new(4)) as Ptr; | ^^^^^ `Trait` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:25:18 + --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:25:12 | LL | trait Trait { | ----- this trait is not dyn compatible... LL | fn ptr(self: Ptr); - | ^^^^^^^^^ ...because method `ptr`'s `self` parameter cannot be dispatched on + | ^^^^^^^^^^^^^^^ ...because method `ptr`'s `self` parameter cannot be dispatched on = help: only type `i32` implements `Trait`; consider using it directly instead. +help: consider changing method `ptr`'s `self` parameter to be `&self` + | +LL - fn ptr(self: Ptr); +LL + fn ptr(&self); + | error: aborting due to 1 previous error diff --git a/tests/ui/self/arbitrary-self-types-dyn-incompatible.stderr b/tests/ui/self/arbitrary-self-types-dyn-incompatible.stderr index fe4802c9b3a47..ccc322d36acea 100644 --- a/tests/ui/self/arbitrary-self-types-dyn-incompatible.stderr +++ b/tests/ui/self/arbitrary-self-types-dyn-incompatible.stderr @@ -1,21 +1,23 @@ error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/arbitrary-self-types-dyn-incompatible.rs:29:39 | -LL | fn foo(self: &Rc) -> usize; - | --------- help: consider changing method `foo`'s `self` parameter to be `&self`: `&Self` -... LL | let x = Rc::new(5usize) as Rc; | ^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/arbitrary-self-types-dyn-incompatible.rs:4:18 + --> $DIR/arbitrary-self-types-dyn-incompatible.rs:4:12 | LL | trait Foo { | --- this trait is not dyn compatible... LL | fn foo(self: &Rc) -> usize; - | ^^^^^^^^^ ...because method `foo`'s `self` parameter cannot be dispatched on + | ^^^^^^^^^^^^^^^ ...because method `foo`'s `self` parameter cannot be dispatched on = help: only type `usize` implements `Foo`; consider using it directly instead. +help: consider changing method `foo`'s `self` parameter to be `&self` + | +LL - fn foo(self: &Rc) -> usize; +LL + fn foo(&self) -> usize; + | error: aborting due to 1 previous error diff --git a/tests/ui/self/dispatch-dyn-incompatible-that-does-not-deref.stderr b/tests/ui/self/dispatch-dyn-incompatible-that-does-not-deref.stderr index b37dd6411cab3..276da3ebaf9bc 100644 --- a/tests/ui/self/dispatch-dyn-incompatible-that-does-not-deref.stderr +++ b/tests/ui/self/dispatch-dyn-incompatible-that-does-not-deref.stderr @@ -1,20 +1,22 @@ error[E0038]: the trait `Foo` is not dyn compatible --> $DIR/dispatch-dyn-incompatible-that-does-not-deref.rs:12:13 | -LL | fn method(self: &W) {} - | -- help: consider changing method `method`'s `self` parameter to be `&self`: `&Self` -... LL | fn test(x: &dyn Foo) { | ^^^^^^^ `Foo` is not dyn compatible | note: for a trait to be dyn compatible it needs to allow building a vtable for more information, visit - --> $DIR/dispatch-dyn-incompatible-that-does-not-deref.rs:8:21 + --> $DIR/dispatch-dyn-incompatible-that-does-not-deref.rs:8:15 | LL | trait Foo: Deref { | --- this trait is not dyn compatible... LL | fn method(self: &W) {} - | ^^ ...because method `method`'s `self` parameter cannot be dispatched on + | ^^^^^^^^ ...because method `method`'s `self` parameter cannot be dispatched on +help: consider changing method `method`'s `self` parameter to be `&self` + | +LL - fn method(self: &W) {} +LL + fn method(&self) {} + | error[E0307]: invalid `self` parameter type: `&W` --> $DIR/dispatch-dyn-incompatible-that-does-not-deref.rs:8:21 diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.fixed b/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.fixed index 2b26d8cc82ee3..3bc052f127214 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.fixed +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.fixed @@ -3,7 +3,7 @@ trait Trait { fn foo(&self) where Self: Other, Self: Sized { } - fn bar(self: &Self) {} //~ ERROR invalid `self` parameter type + fn bar(&self) {} //~ ERROR invalid `self` parameter type } fn bar(x: &dyn Trait) {} //~ ERROR the trait `Trait` is not dyn compatible diff --git a/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr b/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr index 17c819660f51d..a1d5a3bd2228c 100644 --- a/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr +++ b/tests/ui/suggestions/dyn-incompatible-trait-should-use-where-sized.stderr @@ -13,7 +13,7 @@ LL | trait Trait { LL | fn foo() where Self: Other, { } | ^^^ ...because associated function `foo` has no `self` parameter LL | fn bar(self: ()) {} - | ^^ ...because method `bar`'s `self` parameter cannot be dispatched on + | ^^^^^^^^ ...because method `bar`'s `self` parameter cannot be dispatched on help: consider turning `foo` into a method by giving it a `&self` argument, so that it is accessible through the trait object's vtable | LL | fn foo(&self) where Self: Other, { } @@ -25,7 +25,7 @@ LL | fn foo() where Self: Other, Self: Sized { } help: consider changing method `bar`'s `self` parameter to be `&self` | LL - fn bar(self: ()) {} -LL + fn bar(self: &Self) {} +LL + fn bar(&self) {} | error[E0307]: invalid `self` parameter type: `()` diff --git a/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr b/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr index f29950de63798..7fb3980839e4f 100644 --- a/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr +++ b/tests/ui/traits/default_auto_traits/maybe-bounds-in-dyn-traits.stderr @@ -19,9 +19,6 @@ LL | impl LeakTr for LeakS {} error[E0038]: the trait `DynCompatCheck2` is not dyn compatible --> $DIR/maybe-bounds-in-dyn-traits.rs:90:17 | -LL | fn mut_foo(&mut self) {} - | --------- help: consider changing method `mut_foo`'s `self` parameter to be `&self`: `&Self` -... LL | let _: &dyn DynCompatCheck2 = &NonLeakS; | ^^^^^^^^^^^^^^^ `DynCompatCheck2` is not dyn compatible | @@ -34,6 +31,11 @@ LL | trait DynCompatCheck2: ?Leak { LL | fn mut_foo(&mut self) {} | ^^^^^^^^^ ...because method `mut_foo`'s `self` parameter cannot be dispatched on = help: only type `NonLeakS` implements `DynCompatCheck2`; consider using it directly instead. +help: consider changing method `mut_foo`'s `self` parameter to be `&self` + | +LL - fn mut_foo(&mut self) {} +LL + fn mut_foo(&self) {} + | error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied --> $DIR/maybe-bounds-in-dyn-traits.rs:98:26