diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index bb21b027cc9fb..92501fb0a83cf 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -13,7 +13,7 @@ use crate::errors::{ YieldExprOutsideOfCoroutine, }; use crate::fatally_break_rust; -use crate::method::{MethodCallComponents, SelfSource}; +use crate::method::SelfSource; use crate::type_error_struct; use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; use crate::{ @@ -512,7 +512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) -> Ty<'tcx> { let tcx = self.tcx; let (res, opt_ty, segs) = - self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span); + self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span, Some(args)); let ty = match res { Res::Err => { self.suggest_assoc_method_call(segs); @@ -1332,7 +1332,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { segment.ident, SelfSource::MethodCall(rcvr), error, - Some(MethodCallComponents { receiver: rcvr, args, full_expr: expr }), + Some(args), expected, false, ) { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index b5a07f0d3e9ec..750ed2c34913f 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -797,6 +797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { qpath: &'tcx QPath<'tcx>, hir_id: hir::HirId, span: Span, + args: Option<&'tcx [hir::Expr<'tcx>]>, ) -> (Res, Option>, &'tcx [hir::PathSegment<'tcx>]) { debug!( "resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}", @@ -898,7 +899,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name, SelfSource::QPath(qself), error, - None, + args, Expectation::NoExpectation, trait_missing_method && span.edition().at_least_rust_2021(), // emits missing method for trait only after edition 2021 ) { diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 86a0e95de1dab..d69d2529b18d8 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::{MethodCallComponents, SelfSource}; +pub use self::suggest::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 87dfcf09cdd3e..426cec11d0e60 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -50,15 +50,6 @@ 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; @@ -124,7 +115,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { item_name: Ident, source: SelfSource<'tcx>, error: MethodError<'tcx>, - args: Option>, + args: Option<&'tcx [hir::Expr<'tcx>]>, expected: Expectation<'tcx>, trait_missing_method: bool, ) -> Option> { @@ -167,6 +158,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.note_candidates_on_method_error( rcvr_ty, item_name, + source, args, span, &mut err, @@ -266,23 +258,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn suggest_missing_writer( &self, rcvr_ty: Ty<'tcx>, - args: MethodCallComponents<'tcx>, + rcvr_expr: &hir::Expr<'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.receiver.span, + rcvr_expr.span, E0599, "cannot write into `{}`", ty_str ); err.span_note( - args.receiver.span, + rcvr_expr.span, "must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method", ); - if let ExprKind::Lit(_) = args.receiver.kind { + if let ExprKind::Lit(_) = rcvr_expr.kind { err.span_help( - args.receiver.span.shrink_to_lo(), + rcvr_expr.span.shrink_to_lo(), "a writer is needed before this format string", ); }; @@ -296,7 +288,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty: Ty<'tcx>, item_name: Ident, source: SelfSource<'tcx>, - args: Option>, + args: Option<&'tcx [hir::Expr<'tcx>]>, sugg_span: Span, no_match_data: &mut NoMatchData<'tcx>, expected: Expectation<'tcx>, @@ -377,23 +369,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.is_diagnostic_item(sym::write_macro, def_id) || tcx.is_diagnostic_item(sym::writeln_macro, def_id) }) && item_name.name == Symbol::intern("write_fmt"); - let mut err = if is_write && let Some(args) = args { - self.suggest_missing_writer(rcvr_ty, args) - } else { - tcx.sess.create_err(NoAssociatedItem { - span, - item_kind, - item_name, - ty_prefix: if trait_missing_method { - // FIXME(mu001999) E0599 maybe not suitable here because it is for types - Cow::from("trait") - } else { - rcvr_ty.prefix_string(self.tcx) - }, - ty_str: ty_str_reported, - trait_missing_method, - }) - }; + let mut err = + if is_write && let SelfSource::MethodCall(rcvr_expr) = source + { + self.suggest_missing_writer(rcvr_ty, rcvr_expr) + } else { + tcx.sess.create_err(NoAssociatedItem { + span, + item_kind, + item_name, + ty_prefix: if trait_missing_method { + // FIXME(mu001999) E0599 maybe not suitable here because it is for types + Cow::from("trait") + } else { + rcvr_ty.prefix_string(self.tcx) + }, + ty_str: ty_str_reported, + trait_missing_method, + }) + }; if tcx.sess.source_map().is_multiline(sugg_span) { err.span_label(sugg_span.with_hi(span.lo()), ""); } @@ -409,7 +403,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.downgrade_to_delayed_bug(); } - self.find_builder_fn(&mut err, rcvr_ty, source); + if matches!(source, SelfSource::QPath(_)) && args.is_some() { + self.find_builder_fn(&mut err, rcvr_ty); + } + if tcx.ty_is_opaque_future(rcvr_ty) && item_name.name == sym::poll { err.help(format!( "method `poll` found on `Pin<&mut {ty_str}>`, \ @@ -523,6 +520,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.note_candidates_on_method_error( rcvr_ty, item_name, + source, args, span, &mut err, @@ -533,6 +531,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.note_candidates_on_method_error( rcvr_ty, item_name, + source, args, span, &mut err, @@ -976,7 +975,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { unsatisfied_bounds = true; } } else if let ty::Adt(def, targs) = rcvr_ty.kind() - && let Some(args) = args + && let SelfSource::MethodCall(rcvr_expr) = source { // 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 @@ -999,8 +998,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty, &item_segment, span, - args.full_expr, - args.receiver, + tcx.hir().get_parent(rcvr_expr.hir_id).expect_expr(), + rcvr_expr, ) { err.span_note( tcx.def_span(method.def_id), @@ -1169,7 +1168,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, rcvr_ty, item_name, - args.map(|MethodCallComponents { args, .. }| args.len() + 1), + args.map(|args| args.len() + 1), source, no_match_data.out_of_scope_traits.clone(), &unsatisfied_predicates, @@ -1250,7 +1249,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, rcvr_ty: Ty<'tcx>, item_name: Ident, - args: Option>, + self_source: SelfSource<'tcx>, + args: Option<&'tcx [hir::Expr<'tcx>]>, span: Span, err: &mut Diagnostic, sources: &mut Vec, @@ -1339,6 +1339,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(sugg) = print_disambiguation_help( item_name, args, + self_source, err, path, ty, @@ -1378,6 +1379,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(sugg) = print_disambiguation_help( item_name, args, + self_source, err, path, rcvr_ty, @@ -1410,18 +1412,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Look at all the associated functions without receivers in the type's inherent impls /// to look for builders that return `Self`, `Option` or `Result`. - fn find_builder_fn(&self, err: &mut Diagnostic, rcvr_ty: Ty<'tcx>, source: SelfSource<'tcx>) { + fn find_builder_fn(&self, err: &mut Diagnostic, rcvr_ty: Ty<'tcx>) { let ty::Adt(adt_def, _) = rcvr_ty.kind() else { return; }; - let SelfSource::QPath(ty) = source else { - return; - }; - let hir = self.tcx.hir(); - if let Some(Node::Pat(_)) = hir.find(hir.parent_id(ty.hir_id)) { - // Do not suggest a fn call when a pattern is expected. - return; - } let mut items = self .tcx .inherent_impls(adt_def.did()) @@ -1504,7 +1498,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty: Ty<'tcx>, source: SelfSource<'tcx>, item_name: Ident, - args: Option>, + args: Option<&'tcx [hir::Expr<'tcx>]>, sugg_span: Span, ) { let mut has_unsuggestable_args = false; @@ -1578,38 +1572,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None }; let mut applicability = Applicability::MachineApplicable; - 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::>() + let args = if let SelfSource::MethodCall(receiver) = source + && let Some(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::>() + } else { + // There is no `Self` kind to infer the arguments from + if has_unsuggestable_args { + applicability = Applicability::HasPlaceholders; + } + args.iter().collect() + }; + format!( + "({}{})", + first_arg.unwrap_or(""), + explicit_args + .iter() + .map(|arg| self + .tcx + .sess + .source_map() + .span_to_snippet(arg.span) + .unwrap_or_else(|_| { + applicability = Applicability::HasPlaceholders; + "_".to_owned() + })) + .collect::>() + .join(", "), + ) } else { - // There is no `Self` kind to infer the arguments from - if has_unsuggestable_args { - applicability = Applicability::HasPlaceholders; - } - args.iter().collect() + applicability = Applicability::HasPlaceholders; + "(...)".to_owned() }; - format!( - "({}{})", - first_arg.unwrap_or(""), - explicit_args - .iter() - .map(|arg| self - .tcx - .sess - .source_map() - .span_to_snippet(arg.span) - .unwrap_or_else(|_| { - applicability = Applicability::HasPlaceholders; - "_".to_owned() - })) - .collect::>() - .join(", "), - ) - } else { - applicability = Applicability::HasPlaceholders; - "(...)".to_owned() - }; err.span_suggestion( sugg_span, "use associated function syntax instead", @@ -3268,7 +3264,8 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec { fn print_disambiguation_help<'tcx>( item_name: Ident, - args: Option>, + args: Option<&'tcx [hir::Expr<'tcx>]>, + source: SelfSource<'tcx>, err: &mut Diagnostic, trait_name: String, rcvr_ty: Ty<'_>, @@ -3281,7 +3278,9 @@ fn print_disambiguation_help<'tcx>( fn_has_self_parameter: bool, ) -> Option { Some( - if let (ty::AssocKind::Fn, Some(MethodCallComponents { receiver, args, .. })) = (kind, args) + if matches!(kind, ty::AssocKind::Fn) + && let SelfSource::MethodCall(receiver) = source + && let Some(args) = args { let args = format!( "({}{})", diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 50684482c0d55..b30f9b82fbbf7 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -166,9 +166,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'tcx, '_>) { let PatInfo { binding_mode: def_bm, top_info: ti, .. } = pat_info; let path_res = match &pat.kind { - PatKind::Path(qpath) => { - Some(self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span)) - } + PatKind::Path(qpath) => Some( + self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span, None), + ), _ => None, }; let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); @@ -1060,7 +1060,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Resolve the path and check the definition for errors. let (res, opt_ty, segments) = - self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span); + self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span, None); if res == Res::Err { let e = tcx.sess.delay_span_bug(pat.span, "`Res::Err` but no error emitted"); self.set_tainted_by_errors(e);