summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir_typeck/src/method/suggest.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_hir_typeck/src/method/suggest.rs')
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs1972
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, &note_str);
- } else {
- err.note(&note_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, &note_str);
+ } else {
+ err.note(&note_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