diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:13 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:19:13 +0000 |
commit | 218caa410aa38c29984be31a5229b9fa717560ee (patch) | |
tree | c54bd55eeb6e4c508940a30e94c0032fbd45d677 /compiler/rustc_hir_typeck/src/method/suggest.rs | |
parent | Releasing progress-linux version 1.67.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-218caa410aa38c29984be31a5229b9fa717560ee.tar.xz rustc-218caa410aa38c29984be31a5229b9fa717560ee.zip |
Merging upstream version 1.68.2+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_hir_typeck/src/method/suggest.rs')
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/suggest.rs | 1972 |
1 files changed, 1064 insertions, 908 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index db93cfab2..2e1fc4c38 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2,6 +2,7 @@ //! found or is otherwise invalid. use crate::errors; +use crate::Expectation; use crate::FnCtxt; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -25,7 +26,7 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin use rustc_middle::traits::util::supertraits; use rustc_middle::ty::fast_reject::DeepRejectCtxt; use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; -use rustc_middle::ty::print::with_crate_prefix; +use rustc_middle::ty::print::{with_crate_prefix, with_forced_trimmed_paths}; use rustc_middle::ty::{self, DefIdTree, GenericArgKind, Ty, TyCtxt, TypeVisitable}; use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; use rustc_span::symbol::{kw, sym, Ident}; @@ -100,982 +101,1099 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.autoderef(span, ty).any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) } + #[instrument(level = "debug", skip(self))] pub fn report_method_error( &self, - mut span: Span, + span: Span, rcvr_ty: Ty<'tcx>, item_name: Ident, source: SelfSource<'tcx>, error: MethodError<'tcx>, args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, + expected: Expectation<'tcx>, ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { // Avoid suggestions when we don't know what's going on. if rcvr_ty.references_error() { return None; } - let report_candidates = |span: Span, - err: &mut Diagnostic, - sources: &mut Vec<CandidateSource>, - sugg_span: Option<Span>| { - sources.sort(); - sources.dedup(); - // Dynamic limit to avoid hiding just one candidate, which is silly. - let limit = if sources.len() == 5 { 5 } else { 4 }; - - for (idx, source) in sources.iter().take(limit).enumerate() { - match *source { - CandidateSource::Impl(impl_did) => { - // Provide the best span we can. Use the item, if local to crate, else - // the impl, if local to crate (item may be defaulted), else nothing. - let Some(item) = self.associated_value(impl_did, item_name).or_else(|| { - let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?; - self.associated_value(impl_trait_ref.def_id, item_name) - }) else { - continue; - }; + let sugg_span = if let SelfSource::MethodCall(expr) = source { + // Given `foo.bar(baz)`, `expr` is `bar`, but we want to point to the whole thing. + self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)).span + } else { + span + }; - let note_span = if item.def_id.is_local() { - Some(self.tcx.def_span(item.def_id)) - } else if impl_did.is_local() { - Some(self.tcx.def_span(impl_did)) - } else { - None - }; + match error { + MethodError::NoMatch(mut no_match_data) => { + return self.report_no_match_method_error( + span, + rcvr_ty, + item_name, + source, + args, + sugg_span, + &mut no_match_data, + expected, + ); + } - let impl_ty = self.tcx.at(span).type_of(impl_did); + MethodError::Ambiguity(mut sources) => { + let mut err = struct_span_err!( + self.sess(), + item_name.span, + E0034, + "multiple applicable items in scope" + ); + err.span_label(item_name.span, format!("multiple `{}` found", item_name)); - let insertion = match self.tcx.impl_trait_ref(impl_did) { - None => String::new(), - Some(trait_ref) => format!( - " of the trait `{}`", - self.tcx.def_path_str(trait_ref.def_id) - ), - }; + self.note_candidates_on_method_error( + rcvr_ty, + item_name, + args, + span, + &mut err, + &mut sources, + Some(sugg_span), + ); + err.emit(); + } - let (note_str, idx) = if sources.len() > 1 { - ( - format!( - "candidate #{} is defined in an impl{} for the type `{}`", - idx + 1, - insertion, - impl_ty, - ), - Some(idx + 1), - ) - } else { - ( - format!( - "the candidate is defined in an impl{} for the type `{}`", - insertion, impl_ty, - ), - None, - ) - }; - if let Some(note_span) = note_span { - // We have a span pointing to the method. Show note with snippet. - err.span_note(note_span, ¬e_str); - } else { - err.note(¬e_str); - } - if let Some(sugg_span) = sugg_span - && let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) { - let path = self.tcx.def_path_str(trait_ref.def_id); - - let ty = match item.kind { - ty::AssocKind::Const | ty::AssocKind::Type => rcvr_ty, - ty::AssocKind::Fn => self - .tcx - .fn_sig(item.def_id) - .inputs() - .skip_binder() - .get(0) - .filter(|ty| ty.is_region_ptr() && !rcvr_ty.is_region_ptr()) - .copied() - .unwrap_or(rcvr_ty), - }; - print_disambiguation_help( - item_name, - args, - err, - path, - ty, - item.kind, - item.def_id, - sugg_span, - idx, - self.tcx.sess.source_map(), - item.fn_has_self_parameter, - ); + MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => { + let kind = kind.descr(def_id); + let mut err = struct_span_err!( + self.tcx.sess, + item_name.span, + E0624, + "{} `{}` is private", + kind, + item_name + ); + err.span_label(item_name.span, &format!("private {}", kind)); + let sp = self + .tcx + .hir() + .span_if_local(def_id) + .unwrap_or_else(|| self.tcx.def_span(def_id)); + err.span_label(sp, &format!("private {} defined here", kind)); + self.suggest_valid_traits(&mut err, out_of_scope_traits); + err.emit(); + } + + MethodError::IllegalSizedBound { candidates, needs_mut, bound_span, self_expr } => { + let msg = if needs_mut { + with_forced_trimmed_paths!(format!( + "the `{item_name}` method cannot be invoked on `{rcvr_ty}`" + )) + } else { + format!("the `{item_name}` method cannot be invoked on a trait object") + }; + let mut err = self.sess().struct_span_err(span, &msg); + if !needs_mut { + err.span_label(bound_span, "this has a `Sized` requirement"); + } + if !candidates.is_empty() { + let help = format!( + "{an}other candidate{s} {were} found in the following trait{s}, perhaps \ + add a `use` for {one_of_them}:", + an = if candidates.len() == 1 { "an" } else { "" }, + s = pluralize!(candidates.len()), + were = pluralize!("was", candidates.len()), + one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" }, + ); + self.suggest_use_candidates(&mut err, help, candidates); + } + if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() { + if needs_mut { + let trait_type = self.tcx.mk_ref( + *region, + ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }, + ); + let msg = format!("you need `{}` instead of `{}`", trait_type, rcvr_ty); + let mut kind = &self_expr.kind; + while let hir::ExprKind::AddrOf(_, _, expr) + | hir::ExprKind::Unary(hir::UnOp::Deref, expr) = kind + { + kind = &expr.kind; } - } - CandidateSource::Trait(trait_did) => { - let Some(item) = self.associated_value(trait_did, item_name) else { continue }; - let item_span = self.tcx.def_span(item.def_id); - let idx = if sources.len() > 1 { - let msg = &format!( - "candidate #{} is defined in the trait `{}`", - idx + 1, - self.tcx.def_path_str(trait_did) + if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = kind + && let hir::def::Res::Local(hir_id) = path.res + && let Some(hir::Node::Pat(b)) = self.tcx.hir().find(hir_id) + && let Some(hir::Node::Param(p)) = self.tcx.hir().find_parent(b.hir_id) + && let Some(node) = self.tcx.hir().find_parent(p.hir_id) + && let Some(decl) = node.fn_decl() + && let Some(ty) = decl.inputs.iter().find(|ty| ty.span == p.ty_span) + && let hir::TyKind::Ref(_, mut_ty) = &ty.kind + && let hir::Mutability::Not = mut_ty.mutbl + { + err.span_suggestion_verbose( + mut_ty.ty.span.shrink_to_lo(), + &msg, + "mut ", + Applicability::MachineApplicable, ); - err.span_note(item_span, msg); - Some(idx + 1) } else { - let msg = &format!( - "the candidate is defined in the trait `{}`", - self.tcx.def_path_str(trait_did) - ); - err.span_note(item_span, msg); - None - }; - if let Some(sugg_span) = sugg_span { - let path = self.tcx.def_path_str(trait_did); - print_disambiguation_help( - item_name, - args, - err, - path, - rcvr_ty, - item.kind, - item.def_id, - sugg_span, - idx, - self.tcx.sess.source_map(), - item.fn_has_self_parameter, - ); + err.help(&msg); } } } + err.emit(); } - if sources.len() > limit { - err.note(&format!("and {} others", sources.len() - limit)); - } - }; - let sugg_span = if let SelfSource::MethodCall(expr) = source { - // Given `foo.bar(baz)`, `expr` is `bar`, but we want to point to the whole thing. - self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)).span + MethodError::BadReturnType => bug!("no return type expectations but got BadReturnType"), + } + None + } + + pub fn report_no_match_method_error( + &self, + mut span: Span, + rcvr_ty: Ty<'tcx>, + item_name: Ident, + source: SelfSource<'tcx>, + args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, + sugg_span: Span, + no_match_data: &mut NoMatchData<'tcx>, + expected: Expectation<'tcx>, + ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { + let mode = no_match_data.mode; + let tcx = self.tcx; + let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty); + let ty_str = with_forced_trimmed_paths!(self.ty_to_string(rcvr_ty)); + let is_method = mode == Mode::MethodCall; + let unsatisfied_predicates = &no_match_data.unsatisfied_predicates; + let lev_candidate = no_match_data.lev_candidate; + let item_kind = if is_method { + "method" + } else if rcvr_ty.is_enum() { + "variant or associated item" } else { - span + match (item_name.as_str().chars().next(), rcvr_ty.is_fresh_ty()) { + (Some(name), false) if name.is_lowercase() => "function or associated item", + (Some(_), false) => "associated item", + (Some(_), true) | (None, false) => "variant or associated item", + (None, true) => "variant", + } }; - match error { - MethodError::NoMatch(NoMatchData { - mut static_candidates, - unsatisfied_predicates, - out_of_scope_traits, - lev_candidate, - mode, - }) => { - let tcx = self.tcx; - - let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty); - let ty_str = self.ty_to_string(rcvr_ty); - let is_method = mode == Mode::MethodCall; - let item_kind = if is_method { - "method" - } else if rcvr_ty.is_enum() { - "variant or associated item" - } else { - match (item_name.as_str().chars().next(), rcvr_ty.is_fresh_ty()) { - (Some(name), false) if name.is_lowercase() => "function or associated item", - (Some(_), false) => "associated item", - (Some(_), true) | (None, false) => "variant or associated item", - (None, true) => "variant", + if self.suggest_wrapping_range_with_parens(tcx, rcvr_ty, source, span, item_name, &ty_str) + || self.suggest_constraining_numerical_ty( + tcx, rcvr_ty, source, span, item_kind, item_name, &ty_str, + ) + { + return None; + } + span = item_name.span; + + // Don't show generic arguments when the method can't be found in any implementation (#81576). + let mut ty_str_reported = ty_str.clone(); + if let ty::Adt(_, generics) = rcvr_ty.kind() { + if generics.len() > 0 { + let mut autoderef = self.autoderef(span, rcvr_ty); + let candidate_found = autoderef.any(|(ty, _)| { + if let ty::Adt(adt_def, _) = ty.kind() { + self.tcx + .inherent_impls(adt_def.did()) + .iter() + .any(|def_id| self.associated_value(*def_id, item_name).is_some()) + } else { + false } - }; - - if self.suggest_wrapping_range_with_parens( - tcx, rcvr_ty, source, span, item_name, &ty_str, - ) || self.suggest_constraining_numerical_ty( - tcx, rcvr_ty, source, span, item_kind, item_name, &ty_str, - ) { - return None; - } - span = item_name.span; - - // Don't show generic arguments when the method can't be found in any implementation (#81576). - let mut ty_str_reported = ty_str.clone(); - if let ty::Adt(_, generics) = rcvr_ty.kind() { - if generics.len() > 0 { - let mut autoderef = self.autoderef(span, rcvr_ty); - let candidate_found = autoderef.any(|(ty, _)| { - if let ty::Adt(adt_def, _) = ty.kind() { - self.tcx - .inherent_impls(adt_def.did()) - .iter() - .filter_map(|def_id| self.associated_value(*def_id, item_name)) - .count() - >= 1 - } else { - false - } - }); - let has_deref = autoderef.step_count() > 0; - if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() { - if let Some((path_string, _)) = ty_str.split_once('<') { - ty_str_reported = path_string.to_string(); - } - } + }); + let has_deref = autoderef.step_count() > 0; + if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() { + if let Some((path_string, _)) = ty_str.split_once('<') { + ty_str_reported = path_string.to_string(); } } + } + } - let mut err = struct_span_err!( - tcx.sess, - span, - E0599, - "no {} named `{}` found for {} `{}` in the current scope", - item_kind, - item_name, - rcvr_ty.prefix_string(self.tcx), - ty_str_reported, - ); - if rcvr_ty.references_error() { - err.downgrade_to_delayed_bug(); - } + let mut err = struct_span_err!( + tcx.sess, + span, + E0599, + "no {} named `{}` found for {} `{}` in the current scope", + item_kind, + item_name, + rcvr_ty.prefix_string(self.tcx), + ty_str_reported, + ); + if rcvr_ty.references_error() { + err.downgrade_to_delayed_bug(); + } - if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { - self.suggest_await_before_method( - &mut err, item_name, rcvr_ty, cal, span, - ); - } - if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) { - err.span_suggestion( - span.shrink_to_lo(), - "you are looking for the module in `std`, not the primitive type", - "std::", - Applicability::MachineApplicable, - ); - } - if let ty::RawPtr(_) = &rcvr_ty.kind() { - err.note( - "try using `<*const T>::as_ref()` to get a reference to the \ - type behind the pointer: https://doc.rust-lang.org/std/\ - primitive.pointer.html#method.as_ref", - ); - err.note( - "using `<*const T>::as_ref()` on a pointer which is unaligned or points \ - to invalid or uninitialized memory is undefined behavior", - ); - } + if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { + self.suggest_await_before_method( + &mut err, item_name, rcvr_ty, cal, span, expected.only_has_type(self), + ); + } + if let Some(span) = + tcx.resolutions(()).confused_type_with_std_module.get(&span.with_parent(None)) + { + err.span_suggestion( + span.shrink_to_lo(), + "you are looking for the module in `std`, not the primitive type", + "std::", + Applicability::MachineApplicable, + ); + } + if let ty::RawPtr(_) = &rcvr_ty.kind() { + err.note( + "try using `<*const T>::as_ref()` to get a reference to the \ + type behind the pointer: https://doc.rust-lang.org/std/\ + primitive.pointer.html#method.as_ref", + ); + err.note( + "using `<*const T>::as_ref()` on a pointer which is unaligned or points \ + to invalid or uninitialized memory is undefined behavior", + ); + } - let ty_span = match rcvr_ty.kind() { - ty::Param(param_type) => Some( - param_type.span_from_generics(self.tcx, self.body_id.owner.to_def_id()), - ), - ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())), - _ => None, - }; - if let Some(span) = ty_span { - err.span_label( - span, - format!( - "{item_kind} `{item_name}` not found for this {}", - rcvr_ty.prefix_string(self.tcx) - ), - ); - } + let ty_span = match rcvr_ty.kind() { + ty::Param(param_type) => { + Some(param_type.span_from_generics(self.tcx, self.body_id.owner.to_def_id())) + } + ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())), + _ => None, + }; + if let Some(span) = ty_span { + err.span_label( + span, + format!( + "{item_kind} `{item_name}` not found for this {}", + rcvr_ty.prefix_string(self.tcx) + ), + ); + } - if let SelfSource::MethodCall(rcvr_expr) = source { - self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| { - let call_expr = self - .tcx - .hir() - .expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id)); - let probe = self.lookup_probe( - item_name, - output_ty, - call_expr, - ProbeScope::AllTraits, - ); - probe.is_ok() - }); - } + if let SelfSource::MethodCall(rcvr_expr) = source { + self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| { + let call_expr = + self.tcx.hir().expect_expr(self.tcx.hir().parent_id(rcvr_expr.hir_id)); + let probe = self.lookup_probe_for_diagnostic( + item_name, + output_ty, + call_expr, + ProbeScope::AllTraits, + expected.only_has_type(self), + ); + probe.is_ok() + }); + } - let mut custom_span_label = false; + let mut custom_span_label = false; - if !static_candidates.is_empty() { - err.note( - "found the following associated functions; to be used as methods, \ - functions must have a `self` parameter", - ); - err.span_label(span, "this is an associated function, not a method"); - custom_span_label = true; - } - if static_candidates.len() == 1 { - self.suggest_associated_call_syntax( - &mut err, - &static_candidates, - rcvr_ty, - source, - item_name, - args, - sugg_span, - ); + let static_candidates = &mut no_match_data.static_candidates; + if !static_candidates.is_empty() { + err.note( + "found the following associated functions; to be used as methods, \ + functions must have a `self` parameter", + ); + err.span_label(span, "this is an associated function, not a method"); + custom_span_label = true; + } + if static_candidates.len() == 1 { + self.suggest_associated_call_syntax( + &mut err, + &static_candidates, + rcvr_ty, + source, + item_name, + args, + sugg_span, + ); - report_candidates(span, &mut err, &mut static_candidates, None); - } else if static_candidates.len() > 1 { - report_candidates(span, &mut err, &mut static_candidates, Some(sugg_span)); - } + self.note_candidates_on_method_error( + rcvr_ty, + item_name, + args, + span, + &mut err, + static_candidates, + None, + ); + } else if static_candidates.len() > 1 { + self.note_candidates_on_method_error( + rcvr_ty, + item_name, + args, + span, + &mut err, + static_candidates, + Some(sugg_span), + ); + } - let mut bound_spans = vec![]; - let mut restrict_type_params = false; - let mut unsatisfied_bounds = false; - if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) { - let msg = "consider using `len` instead"; - if let SelfSource::MethodCall(_expr) = source { - err.span_suggestion_short( - span, - msg, - "len", - Applicability::MachineApplicable, - ); - } else { - err.span_label(span, msg); - } - if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) { - let iterator_trait = self.tcx.def_path_str(iterator_trait); - err.note(&format!("`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement")); - } - } else if !unsatisfied_predicates.is_empty() { - let mut type_params = FxHashMap::default(); - - // Pick out the list of unimplemented traits on the receiver. - // This is used for custom error messages with the `#[rustc_on_unimplemented]` attribute. - let mut unimplemented_traits = FxHashMap::default(); - let mut unimplemented_traits_only = true; - for (predicate, _parent_pred, cause) in &unsatisfied_predicates { - if let (ty::PredicateKind::Clause(ty::Clause::Trait(p)), Some(cause)) = - (predicate.kind().skip_binder(), cause.as_ref()) - { - if p.trait_ref.self_ty() != rcvr_ty { - // This is necessary, not just to keep the errors clean, but also - // because our derived obligations can wind up with a trait ref that - // requires a different param_env to be correctly compared. - continue; - } - unimplemented_traits.entry(p.trait_ref.def_id).or_insert(( - predicate.kind().rebind(p.trait_ref), - Obligation { - cause: cause.clone(), - param_env: self.param_env, - predicate: *predicate, - recursion_depth: 0, - }, - )); - } + let mut bound_spans = vec![]; + let mut restrict_type_params = false; + let mut unsatisfied_bounds = false; + if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) { + let msg = "consider using `len` instead"; + if let SelfSource::MethodCall(_expr) = source { + err.span_suggestion_short(span, msg, "len", Applicability::MachineApplicable); + } else { + err.span_label(span, msg); + } + if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) { + let iterator_trait = self.tcx.def_path_str(iterator_trait); + err.note(&format!( + "`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement" + )); + } + } else if !unsatisfied_predicates.is_empty() { + let mut type_params = FxHashMap::default(); + + // Pick out the list of unimplemented traits on the receiver. + // This is used for custom error messages with the `#[rustc_on_unimplemented]` attribute. + let mut unimplemented_traits = FxHashMap::default(); + let mut unimplemented_traits_only = true; + for (predicate, _parent_pred, cause) in unsatisfied_predicates { + if let (ty::PredicateKind::Clause(ty::Clause::Trait(p)), Some(cause)) = + (predicate.kind().skip_binder(), cause.as_ref()) + { + if p.trait_ref.self_ty() != rcvr_ty { + // This is necessary, not just to keep the errors clean, but also + // because our derived obligations can wind up with a trait ref that + // requires a different param_env to be correctly compared. + continue; } + unimplemented_traits.entry(p.trait_ref.def_id).or_insert(( + predicate.kind().rebind(p.trait_ref), + Obligation { + cause: cause.clone(), + param_env: self.param_env, + predicate: *predicate, + recursion_depth: 0, + }, + )); + } + } - // Make sure that, if any traits other than the found ones were involved, - // we don't don't report an unimplemented trait. - // We don't want to say that `iter::Cloned` is not an iterator, just - // because of some non-Clone item being iterated over. - for (predicate, _parent_pred, _cause) in &unsatisfied_predicates { - match predicate.kind().skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Trait(p)) - if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {} - _ => { - unimplemented_traits_only = false; - break; - } - } + // Make sure that, if any traits other than the found ones were involved, + // we don't don't report an unimplemented trait. + // We don't want to say that `iter::Cloned` is not an iterator, just + // because of some non-Clone item being iterated over. + for (predicate, _parent_pred, _cause) in unsatisfied_predicates { + match predicate.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Trait(p)) + if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {} + _ => { + unimplemented_traits_only = false; + break; } + } + } - let mut collect_type_param_suggestions = - |self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| { - // We don't care about regions here, so it's fine to skip the binder here. - if let (ty::Param(_), ty::PredicateKind::Clause(ty::Clause::Trait(p))) = - (self_ty.kind(), parent_pred.kind().skip_binder()) - { - let hir = self.tcx.hir(); - let node = match p.trait_ref.self_ty().kind() { - ty::Param(_) => { - // Account for `fn` items like in `issue-35677.rs` to - // suggest restricting its type params. - let parent_body = - hir.body_owner(hir::BodyId { hir_id: self.body_id }); - Some(hir.get(parent_body)) - } - ty::Adt(def, _) => { - def.did().as_local().map(|def_id| hir.get_by_def_id(def_id)) - } - _ => None, - }; - if let Some(hir::Node::Item(hir::Item { kind, .. })) = node { - if let Some(g) = kind.generics() { - let key = ( - g.tail_span_for_predicate_suggestion(), - g.add_where_or_trailing_comma(), - ); - type_params - .entry(key) - .or_insert_with(FxHashSet::default) - .insert(obligation.to_owned()); - } - } + let mut collect_type_param_suggestions = + |self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| { + // We don't care about regions here, so it's fine to skip the binder here. + if let (ty::Param(_), ty::PredicateKind::Clause(ty::Clause::Trait(p))) = + (self_ty.kind(), parent_pred.kind().skip_binder()) + { + let hir = self.tcx.hir(); + let node = match p.trait_ref.self_ty().kind() { + ty::Param(_) => { + // Account for `fn` items like in `issue-35677.rs` to + // suggest restricting its type params. + let parent_body = + hir.body_owner(hir::BodyId { hir_id: self.body_id }); + Some(hir.get(parent_body)) } - }; - let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { - let msg = format!( - "doesn't satisfy `{}`", - if obligation.len() > 50 { quiet } else { obligation } - ); - match &self_ty.kind() { - // Point at the type that couldn't satisfy the bound. ty::Adt(def, _) => { - bound_spans.push((self.tcx.def_span(def.did()), msg)) + def.did().as_local().map(|def_id| hir.get_by_def_id(def_id)) } - // Point at the trait object that couldn't satisfy the bound. - ty::Dynamic(preds, _, _) => { - for pred in preds.iter() { - match pred.skip_binder() { - ty::ExistentialPredicate::Trait(tr) => bound_spans - .push((self.tcx.def_span(tr.def_id), msg.clone())), - ty::ExistentialPredicate::Projection(_) - | ty::ExistentialPredicate::AutoTrait(_) => {} - } + _ => None, + }; + if let Some(hir::Node::Item(hir::Item { kind, .. })) = node + && let Some(g) = kind.generics() + { + let key = ( + g.tail_span_for_predicate_suggestion(), + g.add_where_or_trailing_comma(), + ); + type_params + .entry(key) + .or_insert_with(FxHashSet::default) + .insert(obligation.to_owned()); + return true; + } + } + false + }; + let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { + let msg = format!( + "doesn't satisfy `{}`", + if obligation.len() > 50 { quiet } else { obligation } + ); + match &self_ty.kind() { + // Point at the type that couldn't satisfy the bound. + ty::Adt(def, _) => bound_spans.push((self.tcx.def_span(def.did()), msg)), + // Point at the trait object that couldn't satisfy the bound. + ty::Dynamic(preds, _, _) => { + for pred in preds.iter() { + match pred.skip_binder() { + ty::ExistentialPredicate::Trait(tr) => { + bound_spans.push((self.tcx.def_span(tr.def_id), msg.clone())) } + ty::ExistentialPredicate::Projection(_) + | ty::ExistentialPredicate::AutoTrait(_) => {} } - // Point at the closure that couldn't satisfy the bound. - ty::Closure(def_id, _) => bound_spans.push(( - tcx.def_span(*def_id), - format!("doesn't satisfy `{}`", quiet), - )), - _ => {} } - }; - let mut format_pred = |pred: ty::Predicate<'tcx>| { - let bound_predicate = pred.kind(); - match bound_predicate.skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => { - let pred = bound_predicate.rebind(pred); - // `<Foo as Iterator>::Item = String`. - let projection_ty = pred.skip_binder().projection_ty; - - let substs_with_infer_self = tcx.mk_substs( - iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into()) - .chain(projection_ty.substs.iter().skip(1)), - ); + } + // Point at the closure that couldn't satisfy the bound. + ty::Closure(def_id, _) => bound_spans + .push((tcx.def_span(*def_id), format!("doesn't satisfy `{}`", quiet))), + _ => {} + } + }; + let mut format_pred = |pred: ty::Predicate<'tcx>| { + let bound_predicate = pred.kind(); + match bound_predicate.skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Projection(pred)) => { + let pred = bound_predicate.rebind(pred); + // `<Foo as Iterator>::Item = String`. + let projection_ty = pred.skip_binder().projection_ty; + + let substs_with_infer_self = tcx.mk_substs( + iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into()) + .chain(projection_ty.substs.iter().skip(1)), + ); - let quiet_projection_ty = ty::ProjectionTy { - substs: substs_with_infer_self, - item_def_id: projection_ty.item_def_id, - }; + let quiet_projection_ty = + tcx.mk_alias_ty(projection_ty.def_id, substs_with_infer_self); - let term = pred.skip_binder().term; + let term = pred.skip_binder().term; - let obligation = format!("{} = {}", projection_ty, term); - let quiet = format!("{} = {}", quiet_projection_ty, term); + let obligation = format!("{} = {}", projection_ty, term); + let quiet = with_forced_trimmed_paths!(format!( + "{} = {}", + quiet_projection_ty, term + )); - bound_span_label(projection_ty.self_ty(), &obligation, &quiet); - Some((obligation, projection_ty.self_ty())) - } - ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => { - let p = poly_trait_ref.trait_ref; - let self_ty = p.self_ty(); - let path = p.print_only_trait_path(); - let obligation = format!("{}: {}", self_ty, path); - let quiet = format!("_: {}", path); - bound_span_label(self_ty, &obligation, &quiet); - Some((obligation, self_ty)) - } - _ => None, - } - }; + bound_span_label(projection_ty.self_ty(), &obligation, &quiet); + Some((obligation, projection_ty.self_ty())) + } + ty::PredicateKind::Clause(ty::Clause::Trait(poly_trait_ref)) => { + let p = poly_trait_ref.trait_ref; + let self_ty = p.self_ty(); + let path = p.print_only_trait_path(); + let obligation = format!("{}: {}", self_ty, path); + let quiet = with_forced_trimmed_paths!(format!("_: {}", path)); + bound_span_label(self_ty, &obligation, &quiet); + Some((obligation, self_ty)) + } + _ => None, + } + }; - // Find all the requirements that come from a local `impl` block. - let mut skip_list: FxHashSet<_> = Default::default(); - let mut spanned_predicates: FxHashMap<MultiSpan, _> = Default::default(); - for (data, p, parent_p, impl_def_id, cause) in unsatisfied_predicates - .iter() - .filter_map(|(p, parent, c)| c.as_ref().map(|c| (p, parent, c))) - .filter_map(|(p, parent, c)| match c.code() { - ObligationCauseCode::ImplDerivedObligation(data) => { - Some((&data.derived, p, parent, data.impl_def_id, data)) - } - _ => None, - }) + // Find all the requirements that come from a local `impl` block. + let mut skip_list: FxHashSet<_> = Default::default(); + let mut spanned_predicates = FxHashMap::default(); + for (p, parent_p, impl_def_id, cause) in unsatisfied_predicates + .iter() + .filter_map(|(p, parent, c)| c.as_ref().map(|c| (p, parent, c))) + .filter_map(|(p, parent, c)| match c.code() { + ObligationCauseCode::ImplDerivedObligation(data) + if matches!(p.kind().skip_binder(), ty::PredicateKind::Clause(_)) => { - let parent_trait_ref = data.parent_trait_pred; - let path = parent_trait_ref.print_modifiers_and_trait_path(); - let tr_self_ty = parent_trait_ref.skip_binder().self_ty(); - let unsatisfied_msg = "unsatisfied trait bound introduced here"; - let derive_msg = - "unsatisfied trait bound introduced in this `derive` macro"; - match self.tcx.hir().get_if_local(impl_def_id) { - // Unmet obligation comes from a `derive` macro, point at it once to - // avoid multiple span labels pointing at the same place. - Some(Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), - .. - })) if matches!( - self_ty.span.ctxt().outer_expn_data().kind, - ExpnKind::Macro(MacroKind::Derive, _) - ) || matches!( - of_trait.as_ref().map(|t| t - .path - .span - .ctxt() - .outer_expn_data() - .kind), - Some(ExpnKind::Macro(MacroKind::Derive, _)) - ) => - { - let span = self_ty.span.ctxt().outer_expn_data().call_site; - let mut spans: MultiSpan = span.into(); - spans.push_span_label(span, derive_msg); - let entry = spanned_predicates.entry(spans); - entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); - } + Some((p, parent, data.impl_def_id, data)) + } + _ => None, + }) + { + match self.tcx.hir().get_if_local(impl_def_id) { + // Unmet obligation comes from a `derive` macro, point at it once to + // avoid multiple span labels pointing at the same place. + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), + .. + })) if matches!( + self_ty.span.ctxt().outer_expn_data().kind, + ExpnKind::Macro(MacroKind::Derive, _) + ) || matches!( + of_trait.as_ref().map(|t| t.path.span.ctxt().outer_expn_data().kind), + Some(ExpnKind::Macro(MacroKind::Derive, _)) + ) => + { + let span = self_ty.span.ctxt().outer_expn_data().call_site; + let entry = spanned_predicates.entry(span); + let entry = entry.or_insert_with(|| { + (FxHashSet::default(), FxHashSet::default(), Vec::new()) + }); + entry.0.insert(span); + entry.1.insert(( + span, + "unsatisfied trait bound introduced in this `derive` macro", + )); + entry.2.push(p); + skip_list.insert(p); + } - // Unmet obligation coming from an `impl`. - Some(Node::Item(hir::Item { - kind: - hir::ItemKind::Impl(hir::Impl { - of_trait, self_ty, generics, .. - }), - span: item_span, - .. - })) => { - let sized_pred = - unsatisfied_predicates.iter().any(|(pred, _, _)| { - match pred.kind().skip_binder() { - ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => { - Some(pred.def_id()) - == self.tcx.lang_items().sized_trait() - && pred.polarity == ty::ImplPolarity::Positive - } - _ => false, - } - }); - for param in generics.params { - if param.span == cause.span && sized_pred { - let (sp, sugg) = match param.colon_span { - Some(sp) => (sp.shrink_to_hi(), " ?Sized +"), - None => (param.span.shrink_to_hi(), ": ?Sized"), - }; - err.span_suggestion_verbose( - sp, - "consider relaxing the type parameter's implicit \ - `Sized` bound", - sugg, - Applicability::MachineApplicable, - ); + // Unmet obligation coming from an `impl`. + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, generics, .. }), + span: item_span, + .. + })) => { + let sized_pred = + unsatisfied_predicates.iter().any(|(pred, _, _)| { + match pred.kind().skip_binder() { + ty::PredicateKind::Clause(ty::Clause::Trait(pred)) => { + Some(pred.def_id()) == self.tcx.lang_items().sized_trait() + && pred.polarity == ty::ImplPolarity::Positive } + _ => false, } - if let Some(pred) = parent_p { - // Done to add the "doesn't satisfy" `span_label`. - let _ = format_pred(*pred); - } - skip_list.insert(p); - let mut spans = if cause.span != *item_span { - let mut spans: MultiSpan = cause.span.into(); - spans.push_span_label(cause.span, unsatisfied_msg); - spans - } else { - let mut spans = Vec::with_capacity(2); - if let Some(trait_ref) = of_trait { - spans.push(trait_ref.path.span); - } - spans.push(self_ty.span); - spans.into() + }); + for param in generics.params { + if param.span == cause.span && sized_pred { + let (sp, sugg) = match param.colon_span { + Some(sp) => (sp.shrink_to_hi(), " ?Sized +"), + None => (param.span.shrink_to_hi(), ": ?Sized"), }; - if let Some(trait_ref) = of_trait { - spans.push_span_label(trait_ref.path.span, ""); - } - spans.push_span_label(self_ty.span, ""); - - let entry = spanned_predicates.entry(spans); - entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); + err.span_suggestion_verbose( + sp, + "consider relaxing the type parameter's implicit `Sized` bound", + sugg, + Applicability::MachineApplicable, + ); } - Some(_) => unreachable!(), - None => (), } - } - let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); - spanned_predicates.sort_by_key(|(span, (_, _, _))| span.primary_span()); - for (span, (_path, _self_ty, preds)) in spanned_predicates { - let mut preds: Vec<_> = preds - .into_iter() - .filter_map(|pred| format_pred(*pred)) - .map(|(p, _)| format!("`{}`", p)) - .collect(); - preds.sort(); - preds.dedup(); - let msg = if let [pred] = &preds[..] { - format!("trait bound {} was not satisfied", pred) + if let Some(pred) = parent_p { + // Done to add the "doesn't satisfy" `span_label`. + let _ = format_pred(*pred); + } + skip_list.insert(p); + let entry = spanned_predicates.entry(self_ty.span); + let entry = entry.or_insert_with(|| { + (FxHashSet::default(), FxHashSet::default(), Vec::new()) + }); + entry.2.push(p); + if cause.span != *item_span { + entry.0.insert(cause.span); + entry.1.insert((cause.span, "unsatisfied trait bound introduced here")); } else { - format!( - "the following trait bounds were not satisfied:\n{}", - preds.join("\n"), - ) + if let Some(trait_ref) = of_trait { + entry.0.insert(trait_ref.path.span); + } + entry.0.insert(self_ty.span); }; - err.span_note(span, &msg); - unsatisfied_bounds = true; + if let Some(trait_ref) = of_trait { + entry.1.insert((trait_ref.path.span, "")); + } + entry.1.insert((self_ty.span, "")); } - - // The requirements that didn't have an `impl` span to show. - let mut bound_list = unsatisfied_predicates - .iter() - .filter_map(|(pred, parent_pred, _cause)| { - format_pred(*pred).map(|(p, self_ty)| { - collect_type_param_suggestions(self_ty, *pred, &p); - ( - match parent_pred { - None => format!("`{}`", &p), - Some(parent_pred) => match format_pred(*parent_pred) { - None => format!("`{}`", &p), - Some((parent_p, _)) => { - collect_type_param_suggestions( - self_ty, - *parent_pred, - &p, - ); - format!( - "`{}`\nwhich is required by `{}`", - p, parent_p - ) - } - }, - }, - *pred, - ) - }) - }) - .filter(|(_, pred)| !skip_list.contains(&pred)) - .map(|(t, _)| t) - .enumerate() - .collect::<Vec<(usize, String)>>(); - - for ((span, add_where_or_comma), obligations) in type_params.into_iter() { - restrict_type_params = true; - // #74886: Sort here so that the output is always the same. - let mut obligations = obligations.into_iter().collect::<Vec<_>>(); - obligations.sort(); - err.span_suggestion_verbose( - span, - &format!( - "consider restricting the type parameter{s} to satisfy the \ - trait bound{s}", - s = pluralize!(obligations.len()) - ), - format!("{} {}", add_where_or_comma, obligations.join(", ")), - Applicability::MaybeIncorrect, + Some(Node::Item(hir::Item { + kind: hir::ItemKind::Trait(rustc_ast::ast::IsAuto::Yes, ..), + span: item_span, + .. + })) => { + tcx.sess.delay_span_bug( + *item_span, + "auto trait is invoked with no method error, but no error reported?", ); } - - bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically. - bound_list.dedup_by(|(_, a), (_, b)| a == b); // #35677 - bound_list.sort_by_key(|(pos, _)| *pos); // Keep the original predicate order. - - if !bound_list.is_empty() || !skip_list.is_empty() { - let bound_list = bound_list - .into_iter() - .map(|(_, path)| path) - .collect::<Vec<_>>() - .join("\n"); - let actual_prefix = rcvr_ty.prefix_string(self.tcx); - info!("unimplemented_traits.len() == {}", unimplemented_traits.len()); - let (primary_message, label) = - if unimplemented_traits.len() == 1 && unimplemented_traits_only { - unimplemented_traits - .into_iter() - .next() - .map(|(_, (trait_ref, obligation))| { - if trait_ref.self_ty().references_error() - || rcvr_ty.references_error() - { - // Avoid crashing. - return (None, None); - } - let OnUnimplementedNote { message, label, .. } = self - .err_ctxt() - .on_unimplemented_note(trait_ref, &obligation); - (message, label) - }) - .unwrap() - } else { - (None, None) - }; - let primary_message = primary_message.unwrap_or_else(|| format!( - "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, but its trait bounds were not satisfied" - )); - err.set_primary_message(&primary_message); - if let Some(label) = label { - custom_span_label = true; - err.span_label(span, label); - } - if !bound_list.is_empty() { - err.note(&format!( - "the following trait bounds were not satisfied:\n{bound_list}" - )); - } - self.suggest_derive(&mut err, &unsatisfied_predicates); - - unsatisfied_bounds = true; + Some(Node::Item(hir::Item { + ident, kind: hir::ItemKind::Trait(..), .. + })) => { + skip_list.insert(p); + let entry = spanned_predicates.entry(ident.span); + let entry = entry.or_insert_with(|| { + (FxHashSet::default(), FxHashSet::default(), Vec::new()) + }); + entry.0.insert(cause.span); + entry.1.insert((ident.span, "")); + entry.1.insert((cause.span, "unsatisfied trait bound introduced here")); + entry.2.push(p); } + Some(node) => unreachable!("encountered `{node:?}`"), + None => (), + } + } + let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); + spanned_predicates.sort_by_key(|(span, _)| *span); + for (_, (primary_spans, span_labels, predicates)) in spanned_predicates { + let mut preds: Vec<_> = predicates + .iter() + .filter_map(|pred| format_pred(**pred)) + .map(|(p, _)| format!("`{}`", p)) + .collect(); + preds.sort(); + preds.dedup(); + let msg = if let [pred] = &preds[..] { + format!("trait bound {} was not satisfied", pred) + } else { + format!("the following trait bounds were not satisfied:\n{}", preds.join("\n"),) + }; + let mut span: MultiSpan = primary_spans.into_iter().collect::<Vec<_>>().into(); + for (sp, label) in span_labels { + span.push_span_label(sp, label); } + err.span_note(span, &msg); + unsatisfied_bounds = true; + } - let label_span_not_found = |err: &mut Diagnostic| { - if unsatisfied_predicates.is_empty() { - err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); - let is_string_or_ref_str = match rcvr_ty.kind() { - ty::Ref(_, ty, _) => { - ty.is_str() - || matches!( - ty.kind(), - ty::Adt(adt, _) if Some(adt.did()) == self.tcx.lang_items().string() - ) + let mut suggested_bounds = FxHashSet::default(); + // The requirements that didn't have an `impl` span to show. + let mut bound_list = unsatisfied_predicates + .iter() + .filter_map(|(pred, parent_pred, _cause)| { + let mut suggested = false; + format_pred(*pred).map(|(p, self_ty)| { + if let Some(parent) = parent_pred && suggested_bounds.contains(parent) { + // We don't suggest `PartialEq` when we already suggest `Eq`. + } else if !suggested_bounds.contains(pred) { + if collect_type_param_suggestions(self_ty, *pred, &p) { + suggested = true; + suggested_bounds.insert(pred); } - ty::Adt(adt, _) => Some(adt.did()) == self.tcx.lang_items().string(), - _ => false, - }; - if is_string_or_ref_str && item_name.name == sym::iter { - err.span_suggestion_verbose( - item_name.span, - "because of the in-memory representation of `&str`, to obtain \ - an `Iterator` over each of its codepoint use method `chars`", - "chars", - Applicability::MachineApplicable, - ); } - if let ty::Adt(adt, _) = rcvr_ty.kind() { - let mut inherent_impls_candidate = self - .tcx - .inherent_impls(adt.did()) - .iter() - .copied() - .filter(|def_id| { - if let Some(assoc) = self.associated_value(*def_id, item_name) { - // Check for both mode is the same so we avoid suggesting - // incorrect associated item. - match (mode, assoc.fn_has_self_parameter, source) { - (Mode::MethodCall, true, SelfSource::MethodCall(_)) => { - // We check that the suggest type is actually - // different from the received one - // So we avoid suggestion method with Box<Self> - // for instance - self.tcx.at(span).type_of(*def_id) != rcvr_ty - && self.tcx.at(span).type_of(*def_id) != rcvr_ty + ( + match parent_pred { + None => format!("`{}`", &p), + Some(parent_pred) => match format_pred(*parent_pred) { + None => format!("`{}`", &p), + Some((parent_p, _)) => { + if !suggested + && !suggested_bounds.contains(pred) + && !suggested_bounds.contains(parent_pred) + { + if collect_type_param_suggestions( + self_ty, + *parent_pred, + &p, + ) { + suggested_bounds.insert(pred); } - (Mode::Path, false, _) => true, - _ => false, } - } else { - false + format!("`{}`\nwhich is required by `{}`", p, parent_p) } - }) - .collect::<Vec<_>>(); - if !inherent_impls_candidate.is_empty() { - inherent_impls_candidate.sort(); - inherent_impls_candidate.dedup(); - - // number of type to shows at most. - let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 }; - let type_candidates = inherent_impls_candidate - .iter() - .take(limit) - .map(|impl_item| { - format!("- `{}`", self.tcx.at(span).type_of(*impl_item)) - }) - .collect::<Vec<_>>() - .join("\n"); - let additional_types = if inherent_impls_candidate.len() > limit { - format!( - "\nand {} more types", - inherent_impls_candidate.len() - limit - ) - } else { - "".to_string() - }; - err.note(&format!( - "the {item_kind} was found for\n{}{}", - type_candidates, additional_types - )); + }, + }, + *pred, + ) + }) + }) + .filter(|(_, pred)| !skip_list.contains(&pred)) + .map(|(t, _)| t) + .enumerate() + .collect::<Vec<(usize, String)>>(); + + for ((span, add_where_or_comma), obligations) in type_params.into_iter() { + restrict_type_params = true; + // #74886: Sort here so that the output is always the same. + let mut obligations = obligations.into_iter().collect::<Vec<_>>(); + obligations.sort(); + err.span_suggestion_verbose( + span, + &format!( + "consider restricting the type parameter{s} to satisfy the \ + trait bound{s}", + s = pluralize!(obligations.len()) + ), + format!("{} {}", add_where_or_comma, obligations.join(", ")), + Applicability::MaybeIncorrect, + ); + } + + bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically. + bound_list.dedup_by(|(_, a), (_, b)| a == b); // #35677 + bound_list.sort_by_key(|(pos, _)| *pos); // Keep the original predicate order. + + if !bound_list.is_empty() || !skip_list.is_empty() { + let bound_list = + bound_list.into_iter().map(|(_, path)| path).collect::<Vec<_>>().join("\n"); + let actual_prefix = rcvr_ty.prefix_string(self.tcx); + info!("unimplemented_traits.len() == {}", unimplemented_traits.len()); + let (primary_message, label) = if unimplemented_traits.len() == 1 + && unimplemented_traits_only + { + unimplemented_traits + .into_iter() + .next() + .map(|(_, (trait_ref, obligation))| { + if trait_ref.self_ty().references_error() || rcvr_ty.references_error() + { + // Avoid crashing. + return (None, None); } - } - } else { - err.span_label(span, format!("{item_kind} cannot be called on `{ty_str}` due to unsatisfied trait bounds")); - } + let OnUnimplementedNote { message, label, .. } = + self.err_ctxt().on_unimplemented_note(trait_ref, &obligation); + (message, label) + }) + .unwrap() + } else { + (None, None) }; - - // If the method name is the name of a field with a function or closure type, - // give a helping note that it has to be called as `(x.f)(...)`. - if let SelfSource::MethodCall(expr) = source { - if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_name, &mut err) - && lev_candidate.is_none() - && !custom_span_label - { - label_span_not_found(&mut err); - } - } else if !custom_span_label { - label_span_not_found(&mut err); - } - - // Don't suggest (for example) `expr.field.clone()` if `expr.clone()` - // can't be called due to `typeof(expr): Clone` not holding. - if unsatisfied_predicates.is_empty() { - self.suggest_calling_method_on_field( - &mut err, source, span, rcvr_ty, item_name, - ); + let primary_message = primary_message.unwrap_or_else(|| { + format!( + "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, \ + but its trait bounds were not satisfied" + ) + }); + err.set_primary_message(&primary_message); + if let Some(label) = label { + custom_span_label = true; + err.span_label(span, label); } - - self.check_for_inner_self(&mut err, source, rcvr_ty, item_name); - - bound_spans.sort(); - bound_spans.dedup(); - for (span, msg) in bound_spans.into_iter() { - err.span_label(span, &msg); + if !bound_list.is_empty() { + err.note(&format!( + "the following trait bounds were not satisfied:\n{bound_list}" + )); } + self.suggest_derive(&mut err, &unsatisfied_predicates); - if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params { - } else { - self.suggest_traits_to_import( - &mut err, - span, - rcvr_ty, - item_name, - args.map(|(_, args)| args.len() + 1), - source, - out_of_scope_traits, - &unsatisfied_predicates, - &static_candidates, - unsatisfied_bounds, - ); - } + unsatisfied_bounds = true; + } + } - // Don't emit a suggestion if we found an actual method - // that had unsatisfied trait bounds - if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() { - let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT"); - if let Some(suggestion) = lev_distance::find_best_match_for_name( - &adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(), - item_name.name, - None, - ) { - err.span_suggestion( - span, - "there is a variant with a similar name", - suggestion, - Applicability::MaybeIncorrect, - ); + let label_span_not_found = |err: &mut Diagnostic| { + if unsatisfied_predicates.is_empty() { + err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); + let is_string_or_ref_str = match rcvr_ty.kind() { + ty::Ref(_, ty, _) => { + ty.is_str() + || matches!( + ty.kind(), + ty::Adt(adt, _) if Some(adt.did()) == self.tcx.lang_items().string() + ) } + ty::Adt(adt, _) => Some(adt.did()) == self.tcx.lang_items().string(), + _ => false, + }; + if is_string_or_ref_str && item_name.name == sym::iter { + err.span_suggestion_verbose( + item_name.span, + "because of the in-memory representation of `&str`, to obtain \ + an `Iterator` over each of its codepoint use method `chars`", + "chars", + Applicability::MachineApplicable, + ); } - - if item_name.name == sym::as_str && rcvr_ty.peel_refs().is_str() { - let msg = "remove this method call"; - let mut fallback_span = true; - if let SelfSource::MethodCall(expr) = source { - let call_expr = - self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); - if let Some(span) = call_expr.span.trim_start(expr.span) { - err.span_suggestion(span, msg, "", Applicability::MachineApplicable); - fallback_span = false; - } - } - if fallback_span { - err.span_label(span, msg); - } - } else if let Some(lev_candidate) = lev_candidate { - // Don't emit a suggestion if we found an actual method - // that had unsatisfied trait bounds - if unsatisfied_predicates.is_empty() { - let def_kind = lev_candidate.kind.as_def_kind(); - // Methods are defined within the context of a struct and their first parameter is always self, - // which represents the instance of the struct the method is being called on - // Associated functions don’t take self as a parameter and - // they are not methods because they don’t have an instance of the struct to work with. - if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter { - err.span_suggestion( - span, - &format!("there is a method with a similar name",), - lev_candidate.name, - Applicability::MaybeIncorrect, - ); + if let ty::Adt(adt, _) = rcvr_ty.kind() { + let mut inherent_impls_candidate = self + .tcx + .inherent_impls(adt.did()) + .iter() + .copied() + .filter(|def_id| { + if let Some(assoc) = self.associated_value(*def_id, item_name) { + // Check for both mode is the same so we avoid suggesting + // incorrect associated item. + match (mode, assoc.fn_has_self_parameter, source) { + (Mode::MethodCall, true, SelfSource::MethodCall(_)) => { + // We check that the suggest type is actually + // different from the received one + // So we avoid suggestion method with Box<Self> + // for instance + self.tcx.at(span).type_of(*def_id) != rcvr_ty + && self.tcx.at(span).type_of(*def_id) != rcvr_ty + } + (Mode::Path, false, _) => true, + _ => false, + } + } else { + false + } + }) + .collect::<Vec<_>>(); + if !inherent_impls_candidate.is_empty() { + inherent_impls_candidate.sort(); + inherent_impls_candidate.dedup(); + + // number of type to shows at most. + let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 }; + let type_candidates = inherent_impls_candidate + .iter() + .take(limit) + .map(|impl_item| { + format!("- `{}`", self.tcx.at(span).type_of(*impl_item)) + }) + .collect::<Vec<_>>() + .join("\n"); + let additional_types = if inherent_impls_candidate.len() > limit { + format!("\nand {} more types", inherent_impls_candidate.len() - limit) } else { - err.span_suggestion( - span, - &format!( - "there is {} {} with a similar name", - def_kind.article(), - def_kind.descr(lev_candidate.def_id), - ), - lev_candidate.name, - Applicability::MaybeIncorrect, - ); - } + "".to_string() + }; + err.note(&format!( + "the {item_kind} was found for\n{}{}", + type_candidates, additional_types + )); } } + } else { + let ty_str = + if ty_str.len() > 50 { String::new() } else { format!("on `{ty_str}` ") }; + err.span_label( + span, + format!("{item_kind} cannot be called {ty_str}due to unsatisfied trait bounds"), + ); + } + }; - self.check_for_deref_method(&mut err, source, rcvr_ty, item_name); - - return Some(err); + // If the method name is the name of a field with a function or closure type, + // give a helping note that it has to be called as `(x.f)(...)`. + if let SelfSource::MethodCall(expr) = source { + if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_name, &mut err) + && lev_candidate.is_none() + && !custom_span_label + { + label_span_not_found(&mut err); } + } else if !custom_span_label { + label_span_not_found(&mut err); + } - MethodError::Ambiguity(mut sources) => { - let mut err = struct_span_err!( - self.sess(), - item_name.span, - E0034, - "multiple applicable items in scope" - ); - err.span_label(item_name.span, format!("multiple `{}` found", item_name)); + // Don't suggest (for example) `expr.field.clone()` if `expr.clone()` + // can't be called due to `typeof(expr): Clone` not holding. + if unsatisfied_predicates.is_empty() { + self.suggest_calling_method_on_field( + &mut err, + source, + span, + rcvr_ty, + item_name, + expected.only_has_type(self), + ); + } - report_candidates(span, &mut err, &mut sources, Some(sugg_span)); - err.emit(); - } + self.check_for_inner_self(&mut err, source, rcvr_ty, item_name); - MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => { - let kind = kind.descr(def_id); - let mut err = struct_span_err!( - self.tcx.sess, - item_name.span, - E0624, - "{} `{}` is private", - kind, - item_name + bound_spans.sort(); + bound_spans.dedup(); + for (span, msg) in bound_spans.into_iter() { + err.span_label(span, &msg); + } + + if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params { + } else { + self.suggest_traits_to_import( + &mut err, + span, + rcvr_ty, + item_name, + args.map(|(_, args)| args.len() + 1), + source, + no_match_data.out_of_scope_traits.clone(), + &unsatisfied_predicates, + &static_candidates, + unsatisfied_bounds, + expected.only_has_type(self), + ); + } + + // Don't emit a suggestion if we found an actual method + // that had unsatisfied trait bounds + if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() { + let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT"); + if let Some(suggestion) = lev_distance::find_best_match_for_name( + &adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(), + item_name.name, + None, + ) { + err.span_suggestion( + span, + "there is a variant with a similar name", + suggestion, + Applicability::MaybeIncorrect, ); - err.span_label(item_name.span, &format!("private {}", kind)); - let sp = self - .tcx - .hir() - .span_if_local(def_id) - .unwrap_or_else(|| self.tcx.def_span(def_id)); - err.span_label(sp, &format!("private {} defined here", kind)); - self.suggest_valid_traits(&mut err, out_of_scope_traits); - err.emit(); } + } - MethodError::IllegalSizedBound(candidates, needs_mut, bound_span) => { - let msg = format!("the `{}` method cannot be invoked on a trait object", item_name); - let mut err = self.sess().struct_span_err(span, &msg); - err.span_label(bound_span, "this has a `Sized` requirement"); - if !candidates.is_empty() { - let help = format!( - "{an}other candidate{s} {were} found in the following trait{s}, perhaps \ - add a `use` for {one_of_them}:", - an = if candidates.len() == 1 { "an" } else { "" }, - s = pluralize!(candidates.len()), - were = pluralize!("was", candidates.len()), - one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" }, + if item_name.name == sym::as_str && rcvr_ty.peel_refs().is_str() { + let msg = "remove this method call"; + let mut fallback_span = true; + if let SelfSource::MethodCall(expr) = source { + let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)); + if let Some(span) = call_expr.span.trim_start(expr.span) { + err.span_suggestion(span, msg, "", Applicability::MachineApplicable); + fallback_span = false; + } + } + if fallback_span { + err.span_label(span, msg); + } + } else if let Some(lev_candidate) = lev_candidate { + // Don't emit a suggestion if we found an actual method + // that had unsatisfied trait bounds + if unsatisfied_predicates.is_empty() { + let def_kind = lev_candidate.kind.as_def_kind(); + // Methods are defined within the context of a struct and their first parameter is always self, + // which represents the instance of the struct the method is being called on + // Associated functions don’t take self as a parameter and + // they are not methods because they don’t have an instance of the struct to work with. + if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter { + err.span_suggestion( + span, + "there is a method with a similar name", + lev_candidate.name, + Applicability::MaybeIncorrect, + ); + } else { + err.span_suggestion( + span, + &format!( + "there is {} {} with a similar name", + def_kind.article(), + def_kind.descr(lev_candidate.def_id), + ), + lev_candidate.name, + Applicability::MaybeIncorrect, ); - self.suggest_use_candidates(&mut err, help, candidates); } - if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() { - if needs_mut { - let trait_type = self.tcx.mk_ref( - *region, - ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }, + } + } + + self.check_for_deref_method(&mut err, source, rcvr_ty, item_name, expected); + return Some(err); + } + + fn note_candidates_on_method_error( + &self, + rcvr_ty: Ty<'tcx>, + item_name: Ident, + args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, + span: Span, + err: &mut Diagnostic, + sources: &mut Vec<CandidateSource>, + sugg_span: Option<Span>, + ) { + sources.sort(); + sources.dedup(); + // Dynamic limit to avoid hiding just one candidate, which is silly. + let limit = if sources.len() == 5 { 5 } else { 4 }; + + for (idx, source) in sources.iter().take(limit).enumerate() { + match *source { + CandidateSource::Impl(impl_did) => { + // Provide the best span we can. Use the item, if local to crate, else + // the impl, if local to crate (item may be defaulted), else nothing. + let Some(item) = self.associated_value(impl_did, item_name).or_else(|| { + let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?; + self.associated_value(impl_trait_ref.skip_binder().def_id, item_name) + }) else { + continue; + }; + + let note_span = if item.def_id.is_local() { + Some(self.tcx.def_span(item.def_id)) + } else if impl_did.is_local() { + Some(self.tcx.def_span(impl_did)) + } else { + None + }; + + let impl_ty = self.tcx.at(span).type_of(impl_did); + + let insertion = match self.tcx.impl_trait_ref(impl_did) { + None => String::new(), + Some(trait_ref) => { + format!( + " of the trait `{}`", + self.tcx.def_path_str(trait_ref.skip_binder().def_id) + ) + } + }; + + let (note_str, idx) = if sources.len() > 1 { + ( + format!( + "candidate #{} is defined in an impl{} for the type `{}`", + idx + 1, + insertion, + impl_ty, + ), + Some(idx + 1), + ) + } else { + ( + format!( + "the candidate is defined in an impl{} for the type `{}`", + insertion, impl_ty, + ), + None, + ) + }; + if let Some(note_span) = note_span { + // We have a span pointing to the method. Show note with snippet. + err.span_note(note_span, ¬e_str); + } else { + err.note(¬e_str); + } + if let Some(sugg_span) = sugg_span + && let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) { + let path = self.tcx.def_path_str(trait_ref.skip_binder().def_id); + + let ty = match item.kind { + ty::AssocKind::Const | ty::AssocKind::Type => rcvr_ty, + ty::AssocKind::Fn => self + .tcx + .fn_sig(item.def_id) + .inputs() + .skip_binder() + .get(0) + .filter(|ty| ty.is_region_ptr() && !rcvr_ty.is_region_ptr()) + .copied() + .unwrap_or(rcvr_ty), + }; + print_disambiguation_help( + item_name, + args, + err, + path, + ty, + item.kind, + item.def_id, + sugg_span, + idx, + self.tcx.sess.source_map(), + item.fn_has_self_parameter, + ); + } + } + CandidateSource::Trait(trait_did) => { + let Some(item) = self.associated_value(trait_did, item_name) else { continue }; + let item_span = self.tcx.def_span(item.def_id); + let idx = if sources.len() > 1 { + let msg = &format!( + "candidate #{} is defined in the trait `{}`", + idx + 1, + self.tcx.def_path_str(trait_did) + ); + err.span_note(item_span, msg); + Some(idx + 1) + } else { + let msg = &format!( + "the candidate is defined in the trait `{}`", + self.tcx.def_path_str(trait_did) + ); + err.span_note(item_span, msg); + None + }; + if let Some(sugg_span) = sugg_span { + let path = self.tcx.def_path_str(trait_did); + print_disambiguation_help( + item_name, + args, + err, + path, + rcvr_ty, + item.kind, + item.def_id, + sugg_span, + idx, + self.tcx.sess.source_map(), + item.fn_has_self_parameter, ); - err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty)); } } - err.emit(); } - - MethodError::BadReturnType => bug!("no return type expectations but got BadReturnType"), } - None + if sources.len() > limit { + err.note(&format!("and {} others", sources.len() - limit)); + } } /// Suggest calling `Ty::method` if `.method()` isn't found because the method @@ -1244,7 +1362,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } else { - let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); + let call_expr = tcx.hir().expect_expr(tcx.hir().parent_id(expr.hir_id)); if let Some(span) = call_expr.span.trim_start(item_name.span) { err.span_suggestion( @@ -1318,13 +1436,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let range_ty = self.tcx.bound_type_of(range_def_id).subst(self.tcx, &[actual.into()]); - let pick = self.probe_for_name( - Mode::MethodCall, + let pick = self.lookup_probe_for_diagnostic( item_name, - IsSuggestion(true), range_ty, - expr.hir_id, + expr, ProbeScope::AllTraits, + None, ); if pick.is_ok() { let range_span = parent_expr.span.with_hi(expr.span.hi()); @@ -1426,7 +1543,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let filename = tcx.sess.source_map().span_to_filename(span); let parent_node = - self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)); + self.tcx.hir().get_parent(hir_id); let msg = format!( "you must specify a type for this binding, like `{}`", concrete_type, @@ -1499,16 +1616,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name }; visitor.visit_body(&body); - let parent = self.tcx.hir().get_parent_node(seg1.hir_id); + let parent = self.tcx.hir().parent_id(seg1.hir_id); if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) && let Some(expr) = visitor.result && let Some(self_ty) = self.node_ty_opt(expr.hir_id) { - let probe = self.lookup_probe( + let probe = self.lookup_probe_for_diagnostic( seg2.ident, self_ty, call_expr, ProbeScope::TraitsInScope, + None, ); if probe.is_ok() { let sm = self.infcx.tcx.sess.source_map(); @@ -1531,13 +1649,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: Span, actual: Ty<'tcx>, item_name: Ident, + return_type: Option<Ty<'tcx>>, ) { if let SelfSource::MethodCall(expr) = source && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id() && let Some((fields, substs)) = self.get_field_candidates_considering_privacy(span, actual, mod_id) { - let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); + let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)); let lang_items = self.tcx.lang_items(); let never_mention_traits = [ @@ -1554,11 +1673,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_for_nested_field_satisfying( span, &|_, field_ty| { - self.lookup_probe( + self.lookup_probe_for_diagnostic( item_name, field_ty, call_expr, ProbeScope::TraitsInScope, + return_type, ) .map_or(false, |pick| { !never_mention_traits @@ -1607,7 +1727,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let tcx = self.tcx; let SelfSource::MethodCall(expr) = source else { return; }; - let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); + let call_expr = tcx.hir().expect_expr(tcx.hir().parent_id(expr.hir_id)); let ty::Adt(kind, substs) = actual.kind() else { return; }; match kind.adt_kind() { @@ -1624,9 +1744,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return None; } - self.lookup_probe(item_name, field_ty, call_expr, ProbeScope::TraitsInScope) - .ok() - .map(|pick| (variant, field, pick)) + self.lookup_probe_for_diagnostic( + item_name, + field_ty, + call_expr, + ProbeScope::TraitsInScope, + None, + ) + .ok() + .map(|pick| (variant, field, pick)) }) .collect(); @@ -1690,11 +1816,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::AdtKind::Struct | ty::AdtKind::Union => { let [first] = ***substs else { return; }; let ty::GenericArgKind::Type(ty) = first.unpack() else { return; }; - let Ok(pick) = self.lookup_probe( + let Ok(pick) = self.lookup_probe_for_diagnostic( item_name, ty, call_expr, ProbeScope::TraitsInScope, + None, ) else { return; }; let name = self.ty_to_value_string(actual); @@ -1843,7 +1970,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_derive(err, &preds); } - fn suggest_derive( + pub fn suggest_derive( &self, err: &mut Diagnostic, unsatisfied_predicates: &[( @@ -1853,7 +1980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )], ) { let mut derives = Vec::<(String, Span, Symbol)>::new(); - let mut traits = Vec::<Span>::new(); + let mut traits = Vec::new(); for (pred, _, _) in unsatisfied_predicates { let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = pred.kind().skip_binder() else { continue }; let adt = match trait_pred.self_ty().ty_adt_def() { @@ -1892,10 +2019,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } derives.push((self_name, self_span, diagnostic_name)); } else { - traits.push(self.tcx.def_span(trait_pred.def_id())); + traits.push(trait_pred.def_id()); } } else { - traits.push(self.tcx.def_span(trait_pred.def_id())); + traits.push(trait_pred.def_id()); } } traits.sort(); @@ -1918,10 +2045,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let len = traits.len(); if len > 0 { - let span: MultiSpan = traits.into(); + let span = + MultiSpan::from_spans(traits.iter().map(|&did| self.tcx.def_span(did)).collect()); + let mut names = format!("`{}`", self.tcx.def_path_str(traits[0])); + for (i, &did) in traits.iter().enumerate().skip(1) { + if len > 2 { + names.push_str(", "); + } + if i == len - 1 { + names.push_str(" and "); + } + names.push('`'); + names.push_str(&self.tcx.def_path_str(did)); + names.push('`'); + } err.span_note( span, - &format!("the following trait{} must be implemented", pluralize!(len),), + &format!("the trait{} {} must be implemented", pluralize!(len), names), ); } @@ -1941,12 +2081,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_source: SelfSource<'tcx>, rcvr_ty: Ty<'tcx>, item_name: Ident, + expected: Expectation<'tcx>, ) { let SelfSource::QPath(ty) = self_source else { return; }; for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) { if let Ok(pick) = self.probe_for_name( Mode::Path, item_name, + expected.only_has_type(self), IsSuggestion(true), deref_ty, ty.hir_id, @@ -1969,7 +2111,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | ty::Float(_) | ty::Adt(_, _) | ty::Str - | ty::Projection(_) + | ty::Alias(ty::Projection, _) | ty::Param(_) => format!("{deref_ty}"), // we need to test something like <&[_]>::len or <(&[u32])>::len // and Vec::function(); @@ -2011,12 +2153,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty: Ty<'tcx>, call: &hir::Expr<'_>, span: Span, + return_type: Option<Ty<'tcx>>, ) { let output_ty = match self.get_impl_future_output_ty(ty) { Some(output_ty) => self.resolve_vars_if_possible(output_ty), _ => return, }; - let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true); + let method_exists = + self.method_exists(item_name, output_ty, call.hir_id, true, return_type); debug!("suggest_await_before_method: is_method_exist={}", method_exists); if method_exists { err.span_suggestion_verbose( @@ -2130,6 +2274,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )], static_candidates: &[CandidateSource], unsatisfied_bounds: bool, + return_type: Option<Ty<'tcx>>, ) { let mut alt_rcvr_sugg = false; if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) { @@ -2152,7 +2297,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "), (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"), ] { - match self.lookup_probe(item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) { + match self.lookup_probe_for_diagnostic( + item_name, + *rcvr_ty, + rcvr, + ProbeScope::AllTraits, + return_type, + ) { Ok(pick) => { // If the method is defined for the receiver we have, it likely wasn't `use`d. // We point at the method, but we just skip the rest of the check for arbitrary @@ -2185,11 +2336,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"), ] { if let Some(new_rcvr_t) = *rcvr_ty - && let Ok(pick) = self.lookup_probe( + && let Ok(pick) = self.lookup_probe_for_diagnostic( item_name, new_rcvr_t, rcvr, ProbeScope::AllTraits, + return_type, ) { debug!("try_alt_rcvr: pick candidate {:?}", pick); @@ -2269,11 +2421,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { t.def_id() == info.def_id } ty::PredicateKind::Clause(ty::Clause::Projection(p)) => { - p.projection_ty.item_def_id == info.def_id + p.projection_ty.def_id == info.def_id } _ => false, } }) && (type_is_local || info.def_id.is_local()) + && !self.tcx.trait_is_auto(info.def_id) && self .associated_value(info.def_id, item_name) .filter(|item| { @@ -2466,7 +2619,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.impl_polarity(*imp_did) == ty::ImplPolarity::Negative }) .any(|imp_did| { - let imp = self.tcx.impl_trait_ref(imp_did).unwrap(); + let imp = self.tcx.impl_trait_ref(imp_did).unwrap().subst_identity(); let imp_simp = simplify_type(self.tcx, imp.self_ty(), TreatParams::AsPlaceholder); imp_simp.map_or(false, |s| s == simp_rcvr_ty) @@ -2547,14 +2700,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { found: Ty<'tcx>, expected: Ty<'tcx>, ) -> bool { - let Some((_def_id_or_name, output, _inputs)) = self.extract_callable_info(expr, found) - else { return false; }; + let Some((_def_id_or_name, output, _inputs)) = + self.extract_callable_info(found) else { + return false; + }; if !self.can_coerce(output, expected) { return false; } - let parent = self.tcx.hir().get_parent_node(expr.hir_id); + let parent = self.tcx.hir().parent_id(expr.hir_id); if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) && let hir::ExprKind::MethodCall( hir::PathSegment { ident: method_name, .. }, @@ -2567,11 +2722,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { name: Symbol::intern(&format!("{}_else", method_name.as_str())), span: method_name.span, }; - let probe = self.lookup_probe( + let probe = self.lookup_probe_for_diagnostic( new_name, self_ty, self_expr, ProbeScope::TraitsInScope, + Some(expected), ); // check the method arguments number |