use crate::check::FnCtxt;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{
+ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, Node, QPath};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::traits::util::supertraits;
-use rustc_middle::ty::fast_reject::{simplify_type, SimplifyParams};
+use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
use rustc_middle::ty::print::with_crate_prefix;
use rustc_middle::ty::ToPolyTraitRef;
use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable};
-use rustc_span::lev_distance;
use rustc_span::symbol::{kw, sym, Ident};
-use rustc_span::{source_map, FileName, MultiSpan, Span};
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
+use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, MultiSpan, Span};
+use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_trait_selection::traits::{
- FulfillmentError, Obligation, ObligationCause, ObligationCauseCode,
+ FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedNote,
};
use std::cmp::Ordering;
ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(_) => true,
// If it's not a simple function, look for things which implement `FnOnce`.
_ => {
- let fn_once = match tcx.lang_items().require(LangItem::FnOnce) {
- Ok(fn_once) => fn_once,
- Err(..) => return false,
+ let Some(fn_once) = tcx.lang_items().fn_once_trait() else {
+ return false;
};
// This conditional prevents us from asking to call errors and unresolved types.
source: SelfSource<'tcx>,
error: MethodError<'tcx>,
args: Option<&'tcx [hir::Expr<'tcx>]>,
- ) -> Option<DiagnosticBuilder<'_>> {
+ ) -> 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 DiagnosticBuilder<'_>,
+ err: &mut Diagnostic,
mut sources: Vec<CandidateSource>,
sugg_span: Span| {
sources.sort();
CandidateSource::ImplSource(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 item = match self.associated_value(impl_did, item_name).or_else(|| {
+ 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)
- }) {
- Some(item) => item,
- None => continue,
+ }) else {
+ continue;
};
let note_span = self
.tcx
}
}
CandidateSource::TraitSource(trait_did) => {
- let item = match self.associated_value(trait_did, item_name) {
- Some(item) => item,
- None => continue,
- };
+ let Some(item) = self.associated_value(trait_did, item_name) else { continue };
let item_span = self
.tcx
.sess
(None, true) => "variant",
}
};
- let mut err = if !actual.references_error() {
+ // FIXME(eddyb) this indentation is probably unnecessary.
+ let mut err = {
// Suggest clamping down the type if the method that is being attempted to
// be used exists at all, and the type is an ambiguous numeric type
// ({integer}/{float}).
.into_iter()
.filter_map(|info| self.associated_value(info.def_id, item_name));
// There are methods that are defined on the primitive types and won't be
- // found when exploring `all_traits`, but we also need them to be acurate on
+ // found when exploring `all_traits`, but we also need them to be accurate on
// our suggestions (#47759).
- let fund_assoc = |opt_def_id: Option<DefId>| {
- opt_def_id.and_then(|id| self.associated_value(id, item_name)).is_some()
+ let found_assoc = |ty: Ty<'tcx>| {
+ simplify_type(tcx, ty, TreatParams::AsPlaceholders)
+ .and_then(|simp| {
+ tcx.incoherent_impls(simp)
+ .iter()
+ .find_map(|&id| self.associated_value(id, item_name))
+ })
+ .is_some()
};
- let lang_items = tcx.lang_items();
let found_candidate = candidates.next().is_some()
- || fund_assoc(lang_items.i8_impl())
- || fund_assoc(lang_items.i16_impl())
- || fund_assoc(lang_items.i32_impl())
- || fund_assoc(lang_items.i64_impl())
- || fund_assoc(lang_items.i128_impl())
- || fund_assoc(lang_items.u8_impl())
- || fund_assoc(lang_items.u16_impl())
- || fund_assoc(lang_items.u32_impl())
- || fund_assoc(lang_items.u64_impl())
- || fund_assoc(lang_items.u128_impl())
- || fund_assoc(lang_items.f32_impl())
- || fund_assoc(lang_items.f32_runtime_impl())
- || fund_assoc(lang_items.f64_impl())
- || fund_assoc(lang_items.f64_runtime_impl());
+ || found_assoc(tcx.types.i8)
+ || found_assoc(tcx.types.i16)
+ || found_assoc(tcx.types.i32)
+ || found_assoc(tcx.types.i64)
+ || found_assoc(tcx.types.i128)
+ || found_assoc(tcx.types.u8)
+ || found_assoc(tcx.types.u16)
+ || found_assoc(tcx.types.u32)
+ || found_assoc(tcx.types.u64)
+ || found_assoc(tcx.types.u128)
+ || found_assoc(tcx.types.f32)
+ || found_assoc(tcx.types.f32);
if let (true, false, SelfSource::MethodCall(expr), true) = (
actual.is_numeric(),
actual.has_concrete_skeleton(),
let candidate_found = autoderef.any(|(ty, _)| {
if let ty::Adt(adt_deref, _) = ty.kind() {
self.tcx
- .inherent_impls(adt_deref.did)
+ .inherent_impls(adt_deref.did())
.iter()
.filter_map(|def_id| {
self.associated_value(*def_id, item_name)
actual.prefix_string(self.tcx),
ty_str_reported,
);
- if let Mode::MethodCall = mode {
- if let SelfSource::MethodCall(call) = source {
- self.suggest_await_before_method(
- &mut err, item_name, actual, call, span,
- );
- }
+ if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
+ self.suggest_await_before_method(
+ &mut err, item_name, actual, cal, span,
+ );
}
if let Some(span) =
tcx.resolutions(()).confused_type_with_std_module.get(&span)
}
err
}
- } else {
- tcx.sess.diagnostic().struct_dummy()
};
+ if actual.references_error() {
+ err.downgrade_to_delayed_bug();
+ }
+
if let Some(def) = actual.ty_adt_def() {
- if let Some(full_sp) = tcx.hir().span_if_local(def.did) {
+ if let Some(full_sp) = tcx.hir().span_if_local(def.did()) {
let def_sp = tcx.sess.source_map().guess_head_span(full_sp);
err.span_label(
def_sp,
}
}
- let mut label_span_not_found = || {
- if unsatisfied_predicates.is_empty() {
- err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
- let is_string_or_ref_str = match actual.kind() {
- ty::Ref(_, ty, _) => {
- ty.is_str()
- || matches!(
- ty.kind(),
- ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did)
- )
- }
- ty::Adt(adt, _) => self.tcx.is_diagnostic_item(sym::String, adt.did),
- _ => 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`",
- String::from("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) != actual
- && 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 {
- "".to_string()
- };
- err.note(&format!(
- "the {item_kind} was found for\n{}{}",
- type_candidates, additional_types
- ));
- }
- }
- } else {
- err.span_label(span, format!("{item_kind} cannot be called on `{ty_str}` due to unsatisfied trait bounds"));
- }
- };
-
- // 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 {
- let field_receiver =
- self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() {
- ty::Adt(def, substs) if !def.is_enum() => {
- let variant = &def.non_enum_variant();
- self.tcx.find_field_index(item_name, variant).map(|index| {
- let field = &variant.fields[index];
- let field_ty = field.ty(tcx, substs);
- (field, field_ty)
- })
- }
- _ => None,
- });
-
- if let Some((field, field_ty)) = field_receiver {
- let scope = self.tcx.parent_module(self.body_id).to_def_id();
- let is_accessible = field.vis.is_accessible_from(scope, self.tcx);
-
- if is_accessible {
- if self.is_fn_ty(field_ty, span) {
- let expr_span = expr.span.to(item_name.span);
- err.multipart_suggestion(
- &format!(
- "to call the function stored in `{}`, \
- surround the field access with parentheses",
- item_name,
- ),
- vec![
- (expr_span.shrink_to_lo(), '('.to_string()),
- (expr_span.shrink_to_hi(), ')'.to_string()),
- ],
- Applicability::MachineApplicable,
- );
- } else {
- 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(item_name.span) {
- err.span_suggestion(
- span,
- "remove the arguments",
- String::new(),
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
-
- let field_kind = if is_accessible { "field" } else { "private field" };
- err.span_label(item_name.span, format!("{}, not a method", field_kind));
- } else if lev_candidate.is_none() && static_sources.is_empty() {
- label_span_not_found();
- }
- } else {
- label_span_not_found();
- }
-
if self.is_fn_ty(rcvr_ty, span) {
- fn report_function<T: std::fmt::Display>(
- err: &mut DiagnosticBuilder<'_>,
- name: T,
- ) {
+ fn report_function<T: std::fmt::Display>(err: &mut Diagnostic, name: T) {
err.note(
&format!("`{}` is a function, perhaps you wish to call it", name,),
);
}
}
+ let mut custom_span_label = false;
+
if !static_sources.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_sources.len() == 1 {
let ty_str = if let Some(CandidateSource::ImplSource(impl_did)) =
report_candidates(span, &mut err, static_sources, 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(actual, span) {
self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id))
};
let mut type_params = FxHashMap::default();
- let mut bound_spans = vec![];
+
+ // 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::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.clone(),
+ 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::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| {
.get(self.tcx.hir().local_def_id_to_hir_id(did)),
)
}
- ty::Adt(def, _) => def.did.as_local().map(|def_id| {
+ ty::Adt(def, _) => def.did().as_local().map(|def_id| {
self.tcx
.hir()
.get(self.tcx.hir().local_def_id_to_hir_id(def_id))
);
match &self_ty.kind() {
// Point at the type that couldn't satisfy the bound.
- ty::Adt(def, _) => bound_spans.push((def_span(def.did), msg)),
+ ty::Adt(def, _) => bound_spans.push((def_span(def.did()), msg)),
// Point at the trait object that couldn't satisfy the bound.
ty::Dynamic(preds, _) => {
for pred in preds.iter() {
// 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) in unsatisfied_predicates
+ for (data, p, parent_p, impl_def_id, cause_span) 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(ref data) => {
- Some((data, p, parent))
+ Some((&data.derived, p, parent, data.impl_def_id, data.span))
}
_ => None,
})
{
let parent_trait_ref = data.parent_trait_pred;
- let parent_def_id = parent_trait_ref.def_id();
let path = parent_trait_ref.print_modifiers_and_trait_path();
let tr_self_ty = parent_trait_ref.skip_binder().self_ty();
- let mut candidates = vec![];
- self.tcx.for_each_relevant_impl(
- parent_def_id,
- parent_trait_ref.self_ty().skip_binder(),
- |impl_def_id| match self.tcx.hir().get_if_local(impl_def_id) {
- Some(Node::Item(hir::Item {
- kind: hir::ItemKind::Impl(hir::Impl { .. }),
- ..
- })) => {
- candidates.push(impl_def_id);
+ let unsatisfied_msg = "unsatisfied trait bound introduced here".to_string();
+ 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::Trait(..),
+ ident,
+ ..
+ })) if matches!(
+ ident.span.ctxt().outer_expn_data().kind,
+ ExpnKind::Macro(MacroKind::Derive, _)
+ ) =>
+ {
+ let span = ident.span.ctxt().outer_expn_data().call_site;
+ let mut spans: MultiSpan = span.into();
+ spans.push_span_label(span, derive_msg.to_string());
+ let entry = spanned_predicates.entry(spans);
+ entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p);
+ }
+
+ 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.to_string());
+ let entry = spanned_predicates.entry(spans.into());
+ entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p);
+ }
+
+ // Unmet obligation coming from a `trait`.
+ Some(Node::Item(hir::Item {
+ kind: hir::ItemKind::Trait(..),
+ ident,
+ span: item_span,
+ ..
+ })) if !matches!(
+ ident.span.ctxt().outer_expn_data().kind,
+ ExpnKind::Macro(MacroKind::Derive, _)
+ ) =>
+ {
+ if let Some(pred) = parent_p {
+ // Done to add the "doesn't satisfy" `span_label`.
+ let _ = format_pred(*pred);
}
- _ => {}
- },
- );
- if let [def_id] = &candidates[..] {
- match self.tcx.hir().get_if_local(*def_id) {
- Some(Node::Item(hir::Item {
- kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }),
- ..
- })) => {
- if let Some(pred) = parent_p {
- // Done to add the "doesn't satisfy" `span_label`.
- let _ = format_pred(*pred);
- }
- skip_list.insert(p);
+ 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 {
+ ident.span.into()
+ };
+ spans.push_span_label(ident.span, "in this trait".to_string());
+ let entry = spanned_predicates.entry(spans.into());
+ entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p);
+ }
+
+ // Unmet obligation coming from an `impl`.
+ Some(Node::Item(hir::Item {
+ kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }),
+ span: item_span,
+ ..
+ })) 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, _))
+ ) =>
+ {
+ 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);
- let entry = spanned_predicates.entry(spans.into());
- entry
- .or_insert_with(|| (path, tr_self_ty, Vec::new()))
- .2
- .push(p);
+ spans.into()
+ };
+ if let Some(trait_ref) = of_trait {
+ spans.push_span_label(trait_ref.path.span, String::new());
}
- _ => {}
+ spans.push_span_label(self_ty.span, String::new());
+
+ let entry = spanned_predicates.entry(spans.into());
+ entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p);
}
+ _ => {}
}
}
- for (span, (path, self_ty, preds)) in spanned_predicates {
- err.span_note(
- span,
- &format!(
- "the following trait bounds were not satisfied because of the \
- requirements of the implementation of `{}` for `{}`:\n{}",
- path,
- self_ty,
- preds
- .into_iter()
- // .map(|pred| format!("{:?}", pred))
- .filter_map(|pred| format_pred(*pred))
- .map(|(p, _)| format!("`{}`", p))
- .collect::<Vec<_>>()
- .join("\n"),
- ),
- );
+ 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)
+ } else {
+ format!(
+ "the following trait bounds were not satisfied:\n{}",
+ preds.join("\n"),
+ )
+ };
+ err.span_note(span, &msg);
+ unsatisfied_bounds = true;
}
// The requirements that didn't have an `impl` span to show.
let mut bound_list = unsatisfied_predicates
.iter()
- .filter(|(pred, _, _parent_pred)| !skip_list.contains(&pred))
.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) {
+ (
+ match 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)
- }
+ 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)>>();
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.
- bound_spans.sort();
- bound_spans.dedup();
- for (span, msg) in bound_spans.into_iter() {
- err.span_label(span, &msg);
- }
+
if !bound_list.is_empty() || !skip_list.is_empty() {
let bound_list = bound_list
.into_iter()
.collect::<Vec<_>>()
.join("\n");
let actual_prefix = actual.prefix_string(self.tcx);
- err.set_primary_message(&format!(
+ 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()
+ || actual.references_error()
+ {
+ // Avoid crashing.
+ return (None, None);
+ }
+ let OnUnimplementedNote { message, label, .. } =
+ self.infcx.on_unimplemented_note(trait_ref, &obligation);
+ (message, label)
+ })
+ .unwrap_or((None, None))
+ } 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}"
}
}
+ let mut label_span_not_found = || {
+ if unsatisfied_predicates.is_empty() {
+ err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
+ let is_string_or_ref_str = match actual.kind() {
+ ty::Ref(_, ty, _) => {
+ ty.is_str()
+ || matches!(
+ ty.kind(),
+ ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did())
+ )
+ }
+ ty::Adt(adt, _) => self.tcx.is_diagnostic_item(sym::String, adt.did()),
+ _ => 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`",
+ String::from("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) != actual
+ && 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 {
+ "".to_string()
+ };
+ err.note(&format!(
+ "the {item_kind} was found for\n{}{}",
+ type_candidates, additional_types
+ ));
+ }
+ }
+ } else {
+ err.span_label(span, format!("{item_kind} cannot be called on `{ty_str}` due to unsatisfied trait bounds"));
+ }
+ };
+
+ // 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 {
+ let field_receiver =
+ self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() {
+ ty::Adt(def, substs) if !def.is_enum() => {
+ let variant = &def.non_enum_variant();
+ self.tcx.find_field_index(item_name, variant).map(|index| {
+ let field = &variant.fields[index];
+ let field_ty = field.ty(tcx, substs);
+ (field, field_ty)
+ })
+ }
+ _ => None,
+ });
+
+ if let Some((field, field_ty)) = field_receiver {
+ let scope = self.tcx.parent_module(self.body_id).to_def_id();
+ let is_accessible = field.vis.is_accessible_from(scope, self.tcx);
+
+ if is_accessible {
+ if self.is_fn_ty(field_ty, span) {
+ let expr_span = expr.span.to(item_name.span);
+ err.multipart_suggestion(
+ &format!(
+ "to call the function stored in `{}`, \
+ surround the field access with parentheses",
+ item_name,
+ ),
+ vec![
+ (expr_span.shrink_to_lo(), '('.to_string()),
+ (expr_span.shrink_to_hi(), ')'.to_string()),
+ ],
+ Applicability::MachineApplicable,
+ );
+ } else {
+ 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(item_name.span) {
+ err.span_suggestion(
+ span,
+ "remove the arguments",
+ String::new(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+
+ let field_kind = if is_accessible { "field" } else { "private field" };
+ err.span_label(item_name.span, format!("{}, not a method", field_kind));
+ } else if lev_candidate.is_none() && !custom_span_label {
+ label_span_not_found();
+ }
+ } else if !custom_span_label {
+ label_span_not_found();
+ }
+
+ bound_spans.sort();
+ bound_spans.dedup();
+ for (span, msg) in bound_spans.into_iter() {
+ err.span_label(span, &msg);
+ }
+
if actual.is_numeric() && actual.is_fresh() || restrict_type_params {
} else {
self.suggest_traits_to_import(
if unsatisfied_predicates.is_empty() && actual.is_enum() {
let adt_def = actual.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<_>>(),
+ &adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(),
item_name.name,
None,
) {
crate fn note_unmet_impls_on_type(
&self,
- err: &mut rustc_errors::DiagnosticBuilder<'_>,
+ err: &mut Diagnostic,
errors: Vec<FulfillmentError<'tcx>>,
) {
let all_local_types_needing_impls =
errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() {
ty::PredicateKind::Trait(pred) => match pred.self_ty().kind() {
- ty::Adt(def, _) => def.did.is_local(),
+ ty::Adt(def, _) => def.did().is_local(),
_ => false,
},
_ => false,
let def_ids = preds
.iter()
.filter_map(|pred| match pred.self_ty().kind() {
- ty::Adt(def, _) => Some(def.did),
+ ty::Adt(def, _) => Some(def.did()),
_ => None,
})
.collect::<FxHashSet<_>>();
match pred.self_ty().kind() {
ty::Adt(def, _) => {
spans.push_span_label(
- sm.guess_head_span(self.tcx.def_span(def.did)),
+ sm.guess_head_span(self.tcx.def_span(def.did())),
format!("must implement `{}`", pred.trait_ref.print_only_trait_path()),
);
}
fn suggest_derive(
&self,
- err: &mut DiagnosticBuilder<'_>,
+ err: &mut Diagnostic,
unsatisfied_predicates: &[(
ty::Predicate<'tcx>,
Option<ty::Predicate<'tcx>>,
let mut derives = Vec::<(String, Span, String)>::new();
let mut traits = Vec::<Span>::new();
for (pred, _, _) in unsatisfied_predicates {
- let trait_pred = match pred.kind().skip_binder() {
- ty::PredicateKind::Trait(trait_pred) => trait_pred,
- _ => continue,
- };
+ let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() else { continue };
let adt = match trait_pred.self_ty().ty_adt_def() {
- Some(adt) if adt.did.is_local() => adt,
+ Some(adt) if adt.did().is_local() => adt,
_ => continue,
};
if let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) {
};
if can_derive {
let self_name = trait_pred.self_ty().to_string();
- let self_span = self.tcx.def_span(adt.did);
+ let self_span = self.tcx.def_span(adt.did());
if let Some(poly_trait_ref) = pred.to_opt_poly_trait_pred() {
for super_trait in supertraits(self.tcx, poly_trait_ref.to_poly_trait_ref())
{
/// Print out the type for use in value namespace.
fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String {
match ty.kind() {
- ty::Adt(def, substs) => format!("{}", ty::Instance::new(def.did, substs)),
+ ty::Adt(def, substs) => format!("{}", ty::Instance::new(def.did(), substs)),
_ => self.ty_to_string(ty),
}
}
fn suggest_await_before_method(
&self,
- err: &mut DiagnosticBuilder<'_>,
+ err: &mut Diagnostic,
item_name: Ident,
ty: Ty<'tcx>,
call: &hir::Expr<'_>,
fn suggest_use_candidates(
&self,
- err: &mut DiagnosticBuilder<'_>,
+ err: &mut Diagnostic,
mut msg: String,
candidates: Vec<DefId>,
) {
let additional_newline = if found_use { "" } else { "\n" };
format!(
"use {};\n{}",
- with_crate_prefix(|| self.tcx.def_path_str(*trait_did)),
+ with_crate_prefix!(self.tcx.def_path_str(*trait_did)),
additional_newline
)
});
let additional_newline = if found_use { "" } else { "\n" };
format!(
"use {}::*; // trait {}\n{}",
- with_crate_prefix(|| self.tcx.def_path_str(*parent_did)),
+ with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
self.tcx.item_name(*trait_did),
additional_newline
)
msg.push_str(&format!(
"\ncandidate #{}: `use {};`",
i + 1,
- with_crate_prefix(|| self.tcx.def_path_str(*trait_did))
+ with_crate_prefix!(self.tcx.def_path_str(*trait_did))
));
} else {
msg.push_str(&format!(
"\n`use {};`",
- with_crate_prefix(|| self.tcx.def_path_str(*trait_did))
+ with_crate_prefix!(self.tcx.def_path_str(*trait_did))
));
}
}
msg.push_str(&format!(
"\ncandidate #{}: `use {}::*; // trait {}`",
candidates.len() + i + 1,
- with_crate_prefix(|| self.tcx.def_path_str(*parent_did)),
+ with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
self.tcx.item_name(*trait_did),
));
} else {
msg.push_str(&format!(
"\n`use {}::*; // trait {}`",
- with_crate_prefix(|| self.tcx.def_path_str(*parent_did)),
+ with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
self.tcx.item_name(*trait_did),
));
}
fn suggest_valid_traits(
&self,
- err: &mut DiagnosticBuilder<'_>,
+ err: &mut Diagnostic,
valid_out_of_scope_traits: Vec<DefId>,
) -> bool {
if !valid_out_of_scope_traits.is_empty() {
if let Some(did) = edition_fix {
err.note(&format!(
"'{}' is included in the prelude starting in Edition 2021",
- with_crate_prefix(|| self.tcx.def_path_str(did))
+ with_crate_prefix!(self.tcx.def_path_str(did))
));
}
fn suggest_traits_to_import(
&self,
- err: &mut DiagnosticBuilder<'_>,
+ err: &mut Diagnostic,
span: Span,
rcvr_ty: Ty<'tcx>,
item_name: Ident,
(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), "&"),
] {
- if let Ok(pick) = self.lookup_probe(
+ match self.lookup_probe(
span,
item_name,
*rcvr_ty,
rcvr,
crate::check::method::probe::ProbeScope::AllTraits,
) {
- // 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
- // self types and rely on the suggestion to `use` the trait from
- // `suggest_valid_traits`.
- let did = Some(pick.item.container.id());
- let skip = skippable.contains(&did);
- if pick.autoderefs == 0 && !skip {
- err.span_label(
- pick.item.ident(self.tcx).span,
- &format!("the method is available for `{}` here", rcvr_ty),
- );
+ 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
+ // self types and rely on the suggestion to `use` the trait from
+ // `suggest_valid_traits`.
+ let did = Some(pick.item.container.id());
+ let skip = skippable.contains(&did);
+ if pick.autoderefs == 0 && !skip {
+ err.span_label(
+ pick.item.ident(self.tcx).span,
+ &format!("the method is available for `{}` here", rcvr_ty),
+ );
+ }
+ break;
}
- break;
+ Err(MethodError::Ambiguity(_)) => {
+ // If the method is defined (but ambiguous) for the receiver we have, it is also
+ // likely we haven't `use`d it. It may be possible that if we `Box`/`Pin`/etc.
+ // the receiver, then it might disambiguate this method, but I think these
+ // suggestions are generally misleading (see #94218).
+ break;
+ }
+ _ => {}
}
+
for (rcvr_ty, pre) in &[
(self.tcx.mk_lang_item(*rcvr_ty, LangItem::OwnedBox), "Box::new"),
(self.tcx.mk_lang_item(*rcvr_ty, LangItem::Pin), "Pin::new"),
(self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Arc), "Arc::new"),
(self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"),
] {
- if let Some(new_rcvr_t) = *rcvr_ty {
- if let Ok(pick) = self.lookup_probe(
- span,
- item_name,
- new_rcvr_t,
- rcvr,
- crate::check::method::probe::ProbeScope::AllTraits,
- ) {
- debug!("try_alt_rcvr: pick candidate {:?}", pick);
- let did = Some(pick.item.container.id());
- // We don't want to suggest a container type when the missing
- // method is `.clone()` or `.deref()` otherwise we'd suggest
- // `Arc::new(foo).clone()`, which is far from what the user wants.
- // Explicitly ignore the `Pin::as_ref()` method as `Pin` does not
- // implement the `AsRef` trait.
- let skip = skippable.contains(&did)
- || (("Pin::new" == *pre) && (sym::as_ref == item_name.name));
- // Make sure the method is defined for the *actual* receiver: we don't
- // want to treat `Box<Self>` as a receiver if it only works because of
- // an autoderef to `&self`
- if pick.autoderefs == 0 && !skip {
- err.span_label(
- pick.item.ident(self.tcx).span,
- &format!("the method is available for `{}` here", new_rcvr_t),
- );
- err.multipart_suggestion(
- "consider wrapping the receiver expression with the \
- appropriate type",
- vec![
- (rcvr.span.shrink_to_lo(), format!("{}({}", pre, post)),
- (rcvr.span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- // We don't care about the other suggestions.
- alt_rcvr_sugg = true;
- }
+ if let Some(new_rcvr_t) = *rcvr_ty && let Ok(pick) = self.lookup_probe(
+ span,
+ item_name,
+ new_rcvr_t,
+ rcvr,
+ crate::check::method::probe::ProbeScope::AllTraits,
+ ) {
+ debug!("try_alt_rcvr: pick candidate {:?}", pick);
+ let did = Some(pick.item.container.id());
+ // We don't want to suggest a container type when the missing
+ // method is `.clone()` or `.deref()` otherwise we'd suggest
+ // `Arc::new(foo).clone()`, which is far from what the user wants.
+ // Explicitly ignore the `Pin::as_ref()` method as `Pin` does not
+ // implement the `AsRef` trait.
+ let skip = skippable.contains(&did)
+ || (("Pin::new" == *pre) && (sym::as_ref == item_name.name));
+ // Make sure the method is defined for the *actual* receiver: we don't
+ // want to treat `Box<Self>` as a receiver if it only works because of
+ // an autoderef to `&self`
+ if pick.autoderefs == 0 && !skip {
+ err.span_label(
+ pick.item.ident(self.tcx).span,
+ &format!("the method is available for `{}` here", new_rcvr_t),
+ );
+ err.multipart_suggestion(
+ "consider wrapping the receiver expression with the \
+ appropriate type",
+ vec![
+ (rcvr.span.shrink_to_lo(), format!("{}({}", pre, post)),
+ (rcvr.span.shrink_to_hi(), ")".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ // We don't care about the other suggestions.
+ alt_rcvr_sugg = true;
}
}
}
// FIXME: Even though negative bounds are not implemented, we could maybe handle
// cases where a positive bound implies a negative impl.
(candidates, Vec::new())
- } else if let Some(simp_rcvr_ty) = simplify_type(self.tcx, rcvr_ty, SimplifyParams::Yes)
+ } else if let Some(simp_rcvr_ty) =
+ simplify_type(self.tcx, rcvr_ty, TreatParams::AsBoundTypes)
{
let mut potential_candidates = Vec::new();
let mut explicitly_negative = Vec::new();
.any(|imp_did| {
let imp = self.tcx.impl_trait_ref(imp_did).unwrap();
let imp_simp =
- simplify_type(self.tcx, imp.self_ty(), SimplifyParams::Yes);
+ simplify_type(self.tcx, imp.self_ty(), TreatParams::AsBoundTypes);
imp_simp.map_or(false, |s| s == simp_rcvr_ty)
})
{
) -> bool {
fn is_local(ty: Ty<'_>) -> bool {
match ty.kind() {
- ty::Adt(def, _) => def.did.is_local(),
+ ty::Adt(def, _) => def.did().is_local(),
ty::Foreign(did) => did.is_local(),
ty::Dynamic(tr, ..) => tr.principal().map_or(false, |d| d.def_id().is_local()),
ty::Param(_) => true,
}
fn find_use_placement<'tcx>(tcx: TyCtxt<'tcx>, target_module: LocalDefId) -> (Option<Span>, bool) {
+ // FIXME(#94854): this code uses an out-of-date method for inferring a span
+ // to suggest. It would be better to thread the ModSpans from the AST into
+ // the HIR, and then use that to drive the suggestion here.
+
let mut span = None;
let mut found_use = false;
let (module, _, _) = tcx.hir().get_module(target_module);
fn print_disambiguation_help<'tcx>(
item_name: Ident,
args: Option<&'tcx [hir::Expr<'tcx>]>,
- err: &mut DiagnosticBuilder<'_>,
+ err: &mut Diagnostic,
trait_name: String,
rcvr_ty: Ty<'_>,
kind: ty::AssocKind,