diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 2af8648455bd3..7cea40fdd64c2 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -13,7 +13,7 @@ use crate::errors::{ YieldExprOutsideOfGenerator, }; use crate::fatally_break_rust; -use crate::method::SelfSource; +use crate::method::{MethodCallComponents, SelfSource}; use crate::type_error_struct; use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; use crate::{ @@ -1281,7 +1281,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { segment.ident, SelfSource::MethodCall(rcvr), error, - Some((rcvr, args)), + Some(MethodCallComponents { receiver: rcvr, args, full_expr: expr }), expected, false, ) { diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 597696843c40f..6dd131aa28397 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -7,7 +7,7 @@ mod prelude2021; pub mod probe; mod suggest; -pub use self::suggest::SelfSource; +pub use self::suggest::{MethodCallComponents, SelfSource}; pub use self::MethodError::*; use crate::errors::OpMethodGenericParams; diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index b9d8c5a7540a3..2d64c5667a89e 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -50,6 +50,15 @@ use rustc_hir::intravisit::Visitor; use std::cmp::{self, Ordering}; use std::iter; +/// After identifying that `full_expr` is a method call, we use this type to keep the expression's +/// components readily available to us to point at the right place in diagnostics. +#[derive(Debug, Clone, Copy)] +pub struct MethodCallComponents<'tcx> { + pub receiver: &'tcx hir::Expr<'tcx>, + pub args: &'tcx [hir::Expr<'tcx>], + pub full_expr: &'tcx hir::Expr<'tcx>, +} + impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { let tcx = self.tcx; @@ -115,7 +124,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name: Ident, source: SelfSource<'tcx>, error: MethodError<'tcx>, - args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, + args: Option>, expected: Expectation<'tcx>, trait_missing_method: bool, ) -> Option> { @@ -257,18 +266,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_missing_writer( &self, rcvr_ty: Ty<'tcx>, - args: (&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>]), + args: MethodCallComponents<'tcx>, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { let (ty_str, _ty_file) = self.tcx.short_ty_string(rcvr_ty); - let mut err = - struct_span_err!(self.tcx.sess, args.0.span, E0599, "cannot write into `{}`", ty_str); + let mut err = struct_span_err!( + self.tcx.sess, + args.receiver.span, + E0599, + "cannot write into `{}`", + ty_str + ); err.span_note( - args.0.span, + args.receiver.span, "must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method", ); - if let ExprKind::Lit(_) = args.0.kind { + if let ExprKind::Lit(_) = args.receiver.kind { err.span_help( - args.0.span.shrink_to_lo(), + args.receiver.span.shrink_to_lo(), "a writer is needed before this format string", ); }; @@ -282,7 +296,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty: Ty<'tcx>, item_name: Ident, source: SelfSource<'tcx>, - args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, + args: Option>, sugg_span: Span, no_match_data: &mut NoMatchData<'tcx>, expected: Expectation<'tcx>, @@ -953,6 +967,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { unsatisfied_bounds = true; } + } else if let ty::Adt(def, targs) = rcvr_ty.kind() && let Some(args) = args { + // This is useful for methods on arbitrary self types that might have a simple + // mutability difference, like calling a method on `Pin<&mut Self>` that is on + // `Pin<&Self>`. + if targs.len() == 1 { + let mut item_segment = hir::PathSegment::invalid(); + item_segment.ident = item_name; + for t in [Ty::new_mut_ref, Ty::new_imm_ref, |_, _, t| t] { + let new_args = tcx.mk_args_from_iter( + targs + .iter() + .map(|arg| match arg.as_type() { + Some(ty) => ty::GenericArg::from( + t(tcx, tcx.lifetimes.re_erased, ty.peel_refs()), + ), + _ => arg, + }) + ); + let rcvr_ty = Ty::new_adt(tcx, *def, new_args); + if let Ok(method) = self.lookup_method_for_diagnostic( + rcvr_ty, + &item_segment, + span, + args.full_expr, + args.receiver, + ) { + err.span_note( + tcx.def_span(method.def_id), + format!("{item_kind} is available for `{rcvr_ty}`"), + ); + } + } + } } let label_span_not_found = |err: &mut Diagnostic| { @@ -1111,7 +1158,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, rcvr_ty, item_name, - args.map(|(_, args)| args.len() + 1), + args.map(|MethodCallComponents { args, .. }| args.len() + 1), source, no_match_data.out_of_scope_traits.clone(), &unsatisfied_predicates, @@ -1192,7 +1239,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, rcvr_ty: Ty<'tcx>, item_name: Ident, - args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, + args: Option>, span: Span, err: &mut Diagnostic, sources: &mut Vec, @@ -1343,7 +1390,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty: Ty<'tcx>, source: SelfSource<'tcx>, item_name: Ident, - args: Option<(&hir::Expr<'tcx>, &[hir::Expr<'tcx>])>, + args: Option>, sugg_span: Span, ) { let mut has_unsuggestable_args = false; @@ -1415,7 +1462,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None }; let mut applicability = Applicability::MachineApplicable; - let args = if let Some((receiver, args)) = args { + let args = if let Some(MethodCallComponents { receiver, args, .. }) = args { // The first arg is the same kind as the receiver let explicit_args = if first_arg.is_some() { std::iter::once(receiver).chain(args.iter()).collect::>() @@ -2995,7 +3042,7 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec { fn print_disambiguation_help<'tcx>( item_name: Ident, - args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, + args: Option>, err: &mut Diagnostic, trait_name: String, rcvr_ty: Ty<'_>, @@ -3007,7 +3054,11 @@ fn print_disambiguation_help<'tcx>( fn_has_self_parameter: bool, ) { let mut applicability = Applicability::MachineApplicable; - let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) { + let (span, sugg) = if let ( + ty::AssocKind::Fn, + Some(MethodCallComponents { receiver, args, .. }), + ) = (kind, args) + { let args = format!( "({}{})", rcvr_ty.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()), diff --git a/tests/ui/self/arbitrary_self_type_mut_difference.rs b/tests/ui/self/arbitrary_self_type_mut_difference.rs new file mode 100644 index 0000000000000..e75c00ae956b3 --- /dev/null +++ b/tests/ui/self/arbitrary_self_type_mut_difference.rs @@ -0,0 +1,13 @@ +// Related to #57994. +use std::pin::Pin; +struct S; + +impl S { + fn x(self: Pin<&mut Self>) {} //~ NOTE method is available for `Pin<&mut S>` + fn y(self: Pin<&Self>) {} //~ NOTE method is available for `Pin<&S>` +} + +fn main() { + Pin::new(&S).x(); //~ ERROR no method named `x` found for struct `Pin<&S>` in the current scope + Pin::new(&mut S).y(); //~ ERROR no method named `y` found for struct `Pin<&mut S>` in the current scope +} diff --git a/tests/ui/self/arbitrary_self_type_mut_difference.stderr b/tests/ui/self/arbitrary_self_type_mut_difference.stderr new file mode 100644 index 0000000000000..a56d58694aa35 --- /dev/null +++ b/tests/ui/self/arbitrary_self_type_mut_difference.stderr @@ -0,0 +1,27 @@ +error[E0599]: no method named `x` found for struct `Pin<&S>` in the current scope + --> $DIR/arbitrary_self_type_mut_difference.rs:11:18 + | +LL | Pin::new(&S).x(); + | ^ help: there is a method with a similar name: `y` + | +note: method is available for `Pin<&mut S>` + --> $DIR/arbitrary_self_type_mut_difference.rs:6:5 + | +LL | fn x(self: Pin<&mut Self>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0599]: no method named `y` found for struct `Pin<&mut S>` in the current scope + --> $DIR/arbitrary_self_type_mut_difference.rs:12:22 + | +LL | Pin::new(&mut S).y(); + | ^ help: there is a method with a similar name: `x` + | +note: method is available for `Pin<&S>` + --> $DIR/arbitrary_self_type_mut_difference.rs:7:5 + | +LL | fn y(self: Pin<&Self>) {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/self/arbitrary_self_types_needing_box_or_arc_wrapping.fixed b/tests/ui/self/arbitrary_self_types_needing_box_or_arc_wrapping.fixed new file mode 100644 index 0000000000000..6a94b85b9ba59 --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_needing_box_or_arc_wrapping.fixed @@ -0,0 +1,17 @@ +// run-rustfix +#![allow(dead_code)] +mod first { + trait Foo { fn m(self: Box); } + fn foo(a: T) { + Box::new(a).m(); //~ ERROR no method named `m` found + } +} +mod second { + use std::sync::Arc; + trait Bar { fn m(self: Arc); } + fn bar(b: impl Bar) { + Arc::new(b).m(); //~ ERROR no method named `m` found + } +} + +fn main() {} diff --git a/tests/ui/self/arbitrary_self_types_needing_box_or_arc_wrapping.rs b/tests/ui/self/arbitrary_self_types_needing_box_or_arc_wrapping.rs new file mode 100644 index 0000000000000..fa480b1f72b2f --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_needing_box_or_arc_wrapping.rs @@ -0,0 +1,17 @@ +// run-rustfix +#![allow(dead_code)] +mod first { + trait Foo { fn m(self: Box); } + fn foo(a: T) { + a.m(); //~ ERROR no method named `m` found + } +} +mod second { + use std::sync::Arc; + trait Bar { fn m(self: Arc); } + fn bar(b: impl Bar) { + b.m(); //~ ERROR no method named `m` found + } +} + +fn main() {} diff --git a/tests/ui/self/arbitrary_self_types_needing_box_or_arc_wrapping.stderr b/tests/ui/self/arbitrary_self_types_needing_box_or_arc_wrapping.stderr new file mode 100644 index 0000000000000..2ab634ad3e87b --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_needing_box_or_arc_wrapping.stderr @@ -0,0 +1,43 @@ +error[E0599]: no method named `m` found for type parameter `T` in the current scope + --> $DIR/arbitrary_self_types_needing_box_or_arc_wrapping.rs:6:11 + | +LL | trait Foo { fn m(self: Box); } + | - --------- the method might not be found because of this arbitrary self type + | | + | the method is available for `Box` here +LL | fn foo(a: T) { + | - method `m` not found for this type parameter +LL | a.m(); + | ^ method not found in `T` +... +LL | trait Bar { fn m(self: Arc); } + | --------- the method might not be found because of this arbitrary self type + | +help: consider wrapping the receiver expression with the appropriate type + | +LL | Box::new(a).m(); + | +++++++++ + + +error[E0599]: no method named `m` found for type parameter `impl Bar` in the current scope + --> $DIR/arbitrary_self_types_needing_box_or_arc_wrapping.rs:13:11 + | +LL | trait Foo { fn m(self: Box); } + | --------- the method might not be found because of this arbitrary self type +... +LL | trait Bar { fn m(self: Arc); } + | - --------- the method might not be found because of this arbitrary self type + | | + | the method is available for `Arc` here +LL | fn bar(b: impl Bar) { + | -------- method `m` not found for this type parameter +LL | b.m(); + | ^ method not found in `impl Bar` + | +help: consider wrapping the receiver expression with the appropriate type + | +LL | Arc::new(b).m(); + | +++++++++ + + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/self/arbitrary_self_types_needing_mut_pin.fixed b/tests/ui/self/arbitrary_self_types_needing_mut_pin.fixed new file mode 100644 index 0000000000000..ccd65ff409138 --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_needing_mut_pin.fixed @@ -0,0 +1,12 @@ +// run-rustfix +use std::pin::Pin; +struct S; + +impl S { + fn x(self: Pin<&mut Self>) { + } +} + +fn main() { + Pin::new(&mut S).x(); //~ ERROR no method named `x` found +} diff --git a/tests/ui/self/arbitrary_self_types_needing_mut_pin.rs b/tests/ui/self/arbitrary_self_types_needing_mut_pin.rs new file mode 100644 index 0000000000000..d15676a623d39 --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_needing_mut_pin.rs @@ -0,0 +1,12 @@ +// run-rustfix +use std::pin::Pin; +struct S; + +impl S { + fn x(self: Pin<&mut Self>) { + } +} + +fn main() { + S.x(); //~ ERROR no method named `x` found +} diff --git a/tests/ui/self/arbitrary_self_types_needing_mut_pin.stderr b/tests/ui/self/arbitrary_self_types_needing_mut_pin.stderr new file mode 100644 index 0000000000000..f34ce4dce490f --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_needing_mut_pin.stderr @@ -0,0 +1,20 @@ +error[E0599]: no method named `x` found for struct `S` in the current scope + --> $DIR/arbitrary_self_types_needing_mut_pin.rs:11:7 + | +LL | struct S; + | -------- method `x` not found for this struct +... +LL | fn x(self: Pin<&mut Self>) { + | - the method is available for `Pin<&mut S>` here +... +LL | S.x(); + | ^ method not found in `S` + | +help: consider wrapping the receiver expression with the appropriate type + | +LL | Pin::new(&mut S).x(); + | +++++++++++++ + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/tests/ui/self/arbitrary_self_types_pin_needing_borrow.rs b/tests/ui/self/arbitrary_self_types_pin_needing_borrow.rs new file mode 100644 index 0000000000000..d877dbe60754d --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_pin_needing_borrow.rs @@ -0,0 +1,13 @@ +use std::pin::Pin; +struct S; + +impl S { + fn x(self: Pin<&mut Self>) { + } +} + +fn main() { + Pin::new(S).x(); + //~^ ERROR the trait bound `S: Deref` is not satisfied + //~| ERROR no method named `x` found for struct `Pin` in the current scope +} diff --git a/tests/ui/self/arbitrary_self_types_pin_needing_borrow.stderr b/tests/ui/self/arbitrary_self_types_pin_needing_borrow.stderr new file mode 100644 index 0000000000000..ec985b254b340 --- /dev/null +++ b/tests/ui/self/arbitrary_self_types_pin_needing_borrow.stderr @@ -0,0 +1,33 @@ +error[E0277]: the trait bound `S: Deref` is not satisfied + --> $DIR/arbitrary_self_types_pin_needing_borrow.rs:10:14 + | +LL | Pin::new(S).x(); + | -------- ^ the trait `Deref` is not implemented for `S` + | | + | required by a bound introduced by this call + | +note: required by a bound in `Pin::

::new` + --> $SRC_DIR/core/src/pin.rs:LL:COL +help: consider borrowing here + | +LL | Pin::new(&S).x(); + | + +LL | Pin::new(&mut S).x(); + | ++++ + +error[E0599]: no method named `x` found for struct `Pin` in the current scope + --> $DIR/arbitrary_self_types_pin_needing_borrow.rs:10:17 + | +LL | Pin::new(S).x(); + | ^ method not found in `Pin` + | +note: method is available for `Pin<&mut S>` + --> $DIR/arbitrary_self_types_pin_needing_borrow.rs:5:5 + | +LL | fn x(self: Pin<&mut Self>) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0277, E0599. +For more information about an error, try `rustc --explain E0277`.