diff options
Diffstat (limited to 'compiler/rustc_hir_typeck/src/method/suggest.rs')
-rw-r--r-- | compiler/rustc_hir_typeck/src/method/suggest.rs | 373 |
1 files changed, 203 insertions, 170 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index bd40e6c10..7595f21d9 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1,6 +1,8 @@ //! Give useful errors and suggestions to users when an item can't be //! found or is otherwise invalid. +// ignore-tidy-filelength + use crate::errors; use crate::errors::{CandidateTraitNote, NoAssociatedItem}; use crate::Expectation; @@ -228,7 +230,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } 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::Pat(b)) = self.tcx.opt_hir_node(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() @@ -260,7 +262,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rcvr_ty: Ty<'tcx>, rcvr_expr: &hir::Expr<'tcx>, ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { - let (ty_str, _ty_file) = self.tcx.short_ty_string(rcvr_ty); + let mut file = None; + let ty_str = self.tcx.short_ty_string(rcvr_ty, &mut file); let mut err = struct_span_err!( self.tcx.sess, rcvr_expr.span, @@ -278,6 +281,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "a writer is needed before this format string", ); }; + if let Some(file) = file { + err.note(format!("the full type name has been written to '{}'", file.display())); + } err } @@ -297,11 +303,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mode = no_match_data.mode; let tcx = self.tcx; let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty); - let ((mut ty_str, ty_file), short_ty_str) = + let mut ty_file = None; + let (mut ty_str, short_ty_str) = if trait_missing_method && let ty::Dynamic(predicates, _, _) = rcvr_ty.kind() { - ((predicates.to_string(), None), with_forced_trimmed_paths!(predicates.to_string())) + (predicates.to_string(), with_forced_trimmed_paths!(predicates.to_string())) } else { - (tcx.short_ty_string(rcvr_ty), with_forced_trimmed_paths!(rcvr_ty.to_string())) + ( + tcx.short_ty_string(rcvr_ty, &mut ty_file), + with_forced_trimmed_paths!(rcvr_ty.to_string()), + ) }; let is_method = mode == Mode::MethodCall; let unsatisfied_predicates = &no_match_data.unsatisfied_predicates; @@ -369,25 +379,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tcx.is_diagnostic_item(sym::write_macro, def_id) || tcx.is_diagnostic_item(sym::writeln_macro, def_id) }) && item_name.name == Symbol::intern("write_fmt"); - let mut err = - if is_write && let SelfSource::MethodCall(rcvr_expr) = source - { - self.suggest_missing_writer(rcvr_ty, rcvr_expr) - } else { - tcx.sess.create_err(NoAssociatedItem { - span, - item_kind, - item_name, - ty_prefix: if trait_missing_method { - // FIXME(mu001999) E0599 maybe not suitable here because it is for types - Cow::from("trait") - } else { - rcvr_ty.prefix_string(self.tcx) - }, - ty_str: ty_str_reported, - trait_missing_method, - }) - }; + let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source { + self.suggest_missing_writer(rcvr_ty, rcvr_expr) + } else { + tcx.sess.create_err(NoAssociatedItem { + span, + item_kind, + item_name, + ty_prefix: if trait_missing_method { + // FIXME(mu001999) E0599 maybe not suitable here because it is for types + Cow::from("trait") + } else { + rcvr_ty.prefix_string(self.tcx) + }, + ty_str: ty_str_reported, + trait_missing_method, + }) + }; if tcx.sess.source_map().is_multiline(sugg_span) { err.span_label(sugg_span.with_hi(span.lo()), ""); } @@ -484,7 +492,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.note_internal_mutation_in_method( &mut err, rcvr_expr, - expected.to_option(&self), + expected.to_option(self), rcvr_ty, ); } @@ -509,7 +517,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if static_candidates.len() == 1 { self.suggest_associated_call_syntax( &mut err, - &static_candidates, + static_candidates, rcvr_ty, source, item_name, @@ -605,16 +613,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let (ty::Param(_), ty::PredicateKind::Clause(ty::ClauseKind::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. - Some(hir.get_by_def_id(self.body_id)) - } - ty::Adt(def, _) => { - def.did().as_local().map(|def_id| hir.get_by_def_id(def_id)) + Some(self.tcx.hir_node_by_def_id(self.body_id)) } + ty::Adt(def, _) => def + .did() + .as_local() + .map(|def_id| self.tcx.hir_node_by_def_id(def_id)), _ => None, }; if let Some(hir::Node::Item(hir::Item { kind, .. })) = node @@ -813,7 +821,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span: item_span, .. })) => { - tcx.sess.delay_span_bug( + tcx.sess.span_delayed_bug( *item_span, "auto trait is invoked with no method error, but no error reported?", ); @@ -969,7 +977,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "the following trait bounds were not satisfied:\n{bound_list}" )); } - self.suggest_derive(&mut err, &unsatisfied_predicates); + self.suggest_derive(&mut err, unsatisfied_predicates); unsatisfied_bounds = true; } @@ -1170,8 +1178,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { args.map(|args| args.len() + 1), source, no_match_data.out_of_scope_traits.clone(), - &unsatisfied_predicates, - &static_candidates, + unsatisfied_predicates, + static_candidates, unsatisfied_bounds, expected.only_has_type(self), trait_missing_method, @@ -1240,20 +1248,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } // If an appropriate error source is not found, check method chain for possible candiates - if unsatisfied_predicates.is_empty() && let Mode::MethodCall = mode && let SelfSource::MethodCall(mut source_expr) = source { + if unsatisfied_predicates.is_empty() + && let Mode::MethodCall = mode + && let SelfSource::MethodCall(mut source_expr) = source + { let mut stack_methods = vec![]; while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, method_span) = - source_expr.kind + source_expr.kind { - // Pop the matching receiver, to align on it's notional span - if let Some(prev_match) = stack_methods.pop() { - err.span_label(method_span, format!("{item_kind} `{item_name}` is available on `{prev_match}`")); + // Pop the matching receiver, to align on it's notional span + if let Some(prev_match) = stack_methods.pop() { + err.span_label( + method_span, + format!("{item_kind} `{item_name}` is available on `{prev_match}`"), + ); } let rcvr_ty = self.resolve_vars_if_possible( self.typeck_results .borrow() .expr_ty_adjusted_opt(rcvr_expr) - .unwrap_or(Ty::new_misc_error(self.tcx)),); + .unwrap_or(Ty::new_misc_error(self.tcx)), + ); for _matched_method in self.probe_for_name_many( Mode::MethodCall, @@ -1262,15 +1277,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { IsSuggestion(true), rcvr_ty, source_expr.hir_id, - ProbeScope::TraitsInScope,) { - // found a match, push to stack - stack_methods.push(rcvr_ty); + ProbeScope::TraitsInScope, + ) { + // found a match, push to stack + stack_methods.push(rcvr_ty); } source_expr = rcvr_expr; } // If there is a match at the start of the chain, add a label for it too! if let Some(prev_match) = stack_methods.pop() { - err.span_label(source_expr.span, format!("{item_kind} `{item_name}` is available on `{prev_match}`")); + err.span_label( + source_expr.span, + format!("{item_kind} `{item_name}` is available on `{prev_match}`"), + ); } } self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected); @@ -1357,10 +1376,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err, self_source, args, - trait_ref.instantiate( - self.tcx, - self.fresh_args_for_item(sugg_span, impl_did) - ).with_self_ty(self.tcx, rcvr_ty), + trait_ref + .instantiate( + self.tcx, + self.fresh_args_for_item(sugg_span, impl_did), + ) + .with_self_ty(self.tcx, rcvr_ty), idx, sugg_span, item, @@ -1397,8 +1418,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::TraitRef::new( self.tcx, trait_did, - self.fresh_args_for_item(sugg_span, trait_did) - ).with_self_ty(self.tcx, rcvr_ty), + self.fresh_args_for_item(sugg_span, trait_did), + ) + .with_self_ty(self.tcx, rcvr_ty), idx, sugg_span, item, @@ -1409,7 +1431,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - if !suggs.is_empty() && let Some(span) = sugg_span { + if !suggs.is_empty() + && let Some(span) = sugg_span + { + suggs.sort(); err.span_suggestions( span.with_hi(item_name.span.lo()), "use fully-qualified syntax to disambiguate", @@ -1438,7 +1463,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .filter_map(|item| { // Only assoc fns that return `Self`, `Option<Self>` or `Result<Self, _>`. let ret_ty = self.tcx.fn_sig(item.def_id).skip_binder().output(); - let ret_ty = self.tcx.erase_late_bound_regions(ret_ty); + let ret_ty = self.tcx.instantiate_bound_regions_with_erased(ret_ty); let ty::Adt(def, args) = ret_ty.kind() else { return None; }; @@ -1574,10 +1599,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let sig = self.tcx.fn_sig(assoc.def_id).instantiate_identity(); sig.inputs().skip_binder().get(0).and_then(|first| { - if first.peel_refs() == rcvr_ty.peel_refs() { - None - } else { + let impl_ty = self.tcx.type_of(*impl_did).instantiate_identity(); + // if the type of first arg is the same as the current impl type, we should take the first arg into assoc function + if first.peel_refs() == impl_ty { Some(first.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str())) + } else { + None } }) } else { @@ -1585,39 +1612,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let mut applicability = Applicability::MachineApplicable; let args = if let SelfSource::MethodCall(receiver) = source - && let Some(args) = args - { - // The first arg is the same kind as the receiver - let explicit_args = if first_arg.is_some() { - std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>() - } else { - // There is no `Self` kind to infer the arguments from - if has_unsuggestable_args { - applicability = Applicability::HasPlaceholders; - } - args.iter().collect() - }; - format!( - "({}{})", - first_arg.unwrap_or(""), - explicit_args - .iter() - .map(|arg| self - .tcx - .sess - .source_map() - .span_to_snippet(arg.span) - .unwrap_or_else(|_| { - applicability = Applicability::HasPlaceholders; - "_".to_owned() - })) - .collect::<Vec<_>>() - .join(", "), - ) + && let Some(args) = args + { + // The first arg is the same kind as the receiver + let explicit_args = if first_arg.is_some() { + std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>() } else { - applicability = Applicability::HasPlaceholders; - "(...)".to_owned() + // There is no `Self` kind to infer the arguments from + if has_unsuggestable_args { + applicability = Applicability::HasPlaceholders; + } + args.iter().collect() }; + format!( + "({}{})", + first_arg.unwrap_or(""), + explicit_args + .iter() + .map(|arg| self + .tcx + .sess + .source_map() + .span_to_snippet(arg.span) + .unwrap_or_else(|_| { + applicability = Applicability::HasPlaceholders; + "_".to_owned() + })) + .collect::<Vec<_>>() + .join(", "), + ) + } else { + applicability = Applicability::HasPlaceholders; + "(...)".to_owned() + }; err.span_suggestion( sugg_span, "use associated function syntax instead", @@ -1705,7 +1732,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { for (_, parent) in tcx.hir().parent_iter(expr.hir_id).take(5) { if let Node::Expr(parent_expr) = parent { let lang_item = match parent_expr.kind { - ExprKind::Struct(ref qpath, _, _) => match **qpath { + ExprKind::Struct(qpath, _, _) => match *qpath { QPath::LangItem(LangItem::Range, ..) => Some(LangItem::Range), QPath::LangItem(LangItem::RangeTo, ..) => Some(LangItem::RangeTo), QPath::LangItem(LangItem::RangeToInclusive, ..) => { @@ -1713,7 +1740,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => None, }, - ExprKind::Call(ref func, _) => match func.kind { + ExprKind::Call(func, _) => match func.kind { // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. ExprKind::Path(QPath::LangItem(LangItem::RangeInclusiveNew, ..)) => { Some(LangItem::RangeInclusiveStruct) @@ -1732,7 +1759,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { eps.len() > 0 && eps.last().is_some_and(|ep| ep.span.contains(span)) } // `..=` desugars into `::std::ops::RangeInclusive::new(...)`. - hir::ExprKind::Call(ref func, ..) => func.span.contains(span), + hir::ExprKind::Call(func, ..) => func.span.contains(span), _ => false, }; @@ -1826,7 +1853,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let concrete_type = if actual.is_integral() { "i32" } else { "f32" }; match expr.kind { - ExprKind::Lit(ref lit) => { + ExprKind::Lit(lit) => { // numeric literal let snippet = tcx .sess @@ -1902,7 +1929,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; }; let Some(mut diag) = - self.tcx.sess.diagnostic().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod) + self.tcx.sess.dcx().steal_diagnostic(seg1.ident.span, StashKey::CallAssocMethod) else { return; }; @@ -1930,10 +1957,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let mut visitor = LetVisitor { result: None, ident_name: seg1.ident.name }; - visitor.visit_body(&body); + visitor.visit_body(body); let parent = self.tcx.hir().parent_id(seg1.hir_id); - if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) + if let Some(Node::Expr(call_expr)) = self.tcx.opt_hir_node(parent) && let Some(expr) = visitor.result && let Some(self_ty) = self.node_ty_opt(expr.hir_id) { @@ -1967,69 +1994,73 @@ impl<'a, 'tcx> FnCtxt<'a, '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, args)) = - self.get_field_candidates_considering_privacy(span, actual, mod_id) - { - let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().parent_id(expr.hir_id)); + if let SelfSource::MethodCall(expr) = source { + let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id(); + for (fields, args) in + self.get_field_candidates_considering_privacy(span, actual, mod_id, 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 = [ - lang_items.clone_trait(), - lang_items.deref_trait(), - lang_items.deref_mut_trait(), - self.tcx.get_diagnostic_item(sym::AsRef), - self.tcx.get_diagnostic_item(sym::AsMut), - self.tcx.get_diagnostic_item(sym::Borrow), - self.tcx.get_diagnostic_item(sym::BorrowMut), - ]; - let candidate_fields: Vec<_> = fields - .filter_map(|candidate_field| { - self.check_for_nested_field_satisfying( - span, - &|_, field_ty| { - self.lookup_probe_for_diagnostic( - item_name, - field_ty, - call_expr, - ProbeScope::TraitsInScope, - return_type, - ) - .is_ok_and(|pick| { - !never_mention_traits - .iter() - .flatten() - .any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id) - }) - }, - candidate_field, - args, - vec![], - mod_id, - ) - }) - .map(|field_path| { - field_path - .iter() - .map(|id| id.name.to_ident_string()) - .collect::<Vec<String>>() - .join(".") - }) - .collect(); + let lang_items = self.tcx.lang_items(); + let never_mention_traits = [ + lang_items.clone_trait(), + lang_items.deref_trait(), + lang_items.deref_mut_trait(), + self.tcx.get_diagnostic_item(sym::AsRef), + self.tcx.get_diagnostic_item(sym::AsMut), + self.tcx.get_diagnostic_item(sym::Borrow), + self.tcx.get_diagnostic_item(sym::BorrowMut), + ]; + let mut candidate_fields: Vec<_> = fields + .into_iter() + .filter_map(|candidate_field| { + self.check_for_nested_field_satisfying( + span, + &|_, field_ty| { + self.lookup_probe_for_diagnostic( + item_name, + field_ty, + call_expr, + ProbeScope::TraitsInScope, + return_type, + ) + .is_ok_and(|pick| { + !never_mention_traits + .iter() + .flatten() + .any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id) + }) + }, + candidate_field, + args, + vec![], + mod_id, + expr.hir_id, + ) + }) + .map(|field_path| { + field_path + .iter() + .map(|id| id.name.to_ident_string()) + .collect::<Vec<String>>() + .join(".") + }) + .collect(); + candidate_fields.sort(); - let len = candidate_fields.len(); - if len > 0 { - err.span_suggestions( - item_name.span.shrink_to_lo(), - format!( - "{} of the expressions' fields {} a method of the same name", - if len > 1 { "some" } else { "one" }, - if len > 1 { "have" } else { "has" }, - ), - candidate_fields.iter().map(|path| format!("{path}.")), - Applicability::MaybeIncorrect, - ); + let len = candidate_fields.len(); + if len > 0 { + err.span_suggestions( + item_name.span.shrink_to_lo(), + format!( + "{} of the expressions' fields {} a method of the same name", + if len > 1 { "some" } else { "one" }, + if len > 1 { "have" } else { "has" }, + ), + candidate_fields.iter().map(|path| format!("{path}.")), + Applicability::MaybeIncorrect, + ); + } } } } @@ -2267,7 +2298,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::Adt(def, _) if def.did().is_local() => { spans.push_span_label( self.tcx.def_span(def.did()), - format!("must implement `{}`", pred.trait_ref.print_only_trait_path()), + format!("must implement `{}`", pred.trait_ref.print_trait_sugared()), ); } _ => {} @@ -2278,7 +2309,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let msg = if preds.len() == 1 { format!( "an implementation of `{}` might be missing for `{}`", - preds[0].trait_ref.print_only_trait_path(), + preds[0].trait_ref.print_trait_sugared(), preds[0].self_ty() ) } else { @@ -2548,13 +2579,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.item_name(*trait_did), ) }); + let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect(); + sugg.sort(); - err.span_suggestions( - span, - msg, - path_strings.chain(glob_path_strings), - Applicability::MaybeIncorrect, - ); + err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect); } fn suggest_valid_traits( @@ -2849,11 +2877,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let id = item .def_id .as_local() - .map(|def_id| self.tcx.hir().local_def_id_to_hir_id(def_id)); + .map(|def_id| self.tcx.hir_node_by_def_id(def_id)); if let Some(hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(fn_sig, method), .. - })) = id.map(|id| self.tcx.hir().get(id)) + })) = id { let self_first_arg = match method { hir::TraitFn::Required([ident, ..]) => { @@ -2939,11 +2967,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let type_param = generics.type_param(param, self.tcx); let hir = self.tcx.hir(); if let Some(def_id) = type_param.def_id.as_local() { - let id = hir.local_def_id_to_hir_id(def_id); + let id = self.tcx.local_def_id_to_hir_id(def_id); // Get the `hir::Param` to verify whether it already has any bounds. // We do this to avoid suggesting code that ends up as `T: FooBar`, // instead we suggest `T: Foo + Bar` in that case. - match hir.get(id) { + match self.tcx.hir_node(id) { Node::GenericParam(param) => { enum Introducer { Plus, @@ -3163,7 +3191,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let parent = self.tcx.hir().parent_id(expr.hir_id); - if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) + if let Some(Node::Expr(call_expr)) = self.tcx.opt_hir_node(parent) && let hir::ExprKind::MethodCall( hir::PathSegment { ident: method_name, .. }, self_expr, @@ -3296,8 +3324,13 @@ fn print_disambiguation_help<'tcx>( { let def_kind_descr = tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id); let item_name = item.ident(tcx); - let rcvr_ref = tcx.fn_sig(item.def_id).skip_binder().skip_binder().inputs()[0] - .ref_mutability() + let rcvr_ref = tcx + .fn_sig(item.def_id) + .skip_binder() + .skip_binder() + .inputs() + .get(0) + .and_then(|ty| ty.ref_mutability()) .map_or("", |mutbl| mutbl.ref_prefix_str()); let args = format!( "({}{})", |