From 162443b32e49bfc30ef57e2e9f9c3ede0edea5aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 17 Oct 2023 22:06:58 +0000 Subject: [PATCH] Detect when trait is implemented for type and suggest importing it Fix #57457. --- .../rustc_hir_typeck/src/method/suggest.rs | 56 +++++++++++++------ tests/ui/traits/item-privacy.stderr | 21 +++---- 2 files changed, 49 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 3ed22e095e89c..12cc5ed2f1a55 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -35,6 +35,7 @@ use rustc_span::def_id::DefIdSet; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::Symbol; use rustc_span::{edit_distance, source_map, ExpnKind, FileName, MacroKind, Span}; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote; use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -192,7 +193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .span_if_local(def_id) .unwrap_or_else(|| self.tcx.def_span(def_id)); err.span_label(sp, format!("private {kind} defined here")); - self.suggest_valid_traits(&mut err, out_of_scope_traits); + self.suggest_valid_traits(&mut err, out_of_scope_traits, true); err.emit(); } @@ -2464,6 +2465,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, err: &mut Diagnostic, valid_out_of_scope_traits: Vec, + explain: bool, ) -> bool { if !valid_out_of_scope_traits.is_empty() { let mut candidates = valid_out_of_scope_traits; @@ -2476,7 +2478,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .find(|did| self.tcx.is_diagnostic_item(sym::TryInto, **did)) .copied(); - err.help("items from traits can only be used if the trait is in scope"); + if explain { + err.help("items from traits can only be used if the trait is in scope"); + } let msg = format!( "the following {traits_are} implemented but not in scope; \ perhaps add a `use` for {one_of_them}:", @@ -2693,7 +2697,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - if self.suggest_valid_traits(err, valid_out_of_scope_traits) { + if self.suggest_valid_traits(err, valid_out_of_scope_traits, true) { return; } @@ -2970,22 +2974,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (candidates, Vec::new()) }; + let impls_trait = |def_id: DefId| { + let args = ty::GenericArgs::for_item(self.tcx, def_id, |param, _| { + if param.index == 0 { + rcvr_ty.into() + } else { + self.infcx.var_for_def(span, param) + } + }); + self.infcx + .type_implements_trait(def_id, args, self.param_env) + .must_apply_modulo_regions() + && param_type.is_none() + }; match &potential_candidates[..] { [] => {} [trait_info] if trait_info.def_id.is_local() => { - err.subdiagnostic(CandidateTraitNote { - span: self.tcx.def_span(trait_info.def_id), - trait_name: self.tcx.def_path_str(trait_info.def_id), - item_name, - action_or_ty: if trait_missing_method { - "NONE".to_string() - } else { - param_type.map_or_else( - || "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented. - ToString::to_string, - ) - }, - }); + if impls_trait(trait_info.def_id) { + self.suggest_valid_traits(err, vec![trait_info.def_id], false); + } else { + err.subdiagnostic(CandidateTraitNote { + span: self.tcx.def_span(trait_info.def_id), + trait_name: self.tcx.def_path_str(trait_info.def_id), + item_name, + action_or_ty: if trait_missing_method { + "NONE".to_string() + } else { + param_type.map_or_else( + || "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented. + ToString::to_string, + ) + }, + }); + } } trait_infos => { let mut msg = message(param_type.map_or_else( @@ -2993,6 +3014,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |param| format!("restrict type parameter `{param}` with"), )); for (i, trait_info) in trait_infos.iter().enumerate() { + if impls_trait(trait_info.def_id) { + self.suggest_valid_traits(err, vec![trait_info.def_id], false); + } msg.push_str(&format!( "\ncandidate #{}: `{}`", i + 1, diff --git a/tests/ui/traits/item-privacy.stderr b/tests/ui/traits/item-privacy.stderr index af2e076321219..9382673afe04a 100644 --- a/tests/ui/traits/item-privacy.stderr +++ b/tests/ui/traits/item-privacy.stderr @@ -8,11 +8,10 @@ LL | S.a(); | ^ method not found in `S` | = help: items from traits can only be used if the trait is implemented and in scope -note: `method::A` defines an item `a`, perhaps you need to implement it - --> $DIR/item-privacy.rs:6:5 +help: the following trait is implemented but not in scope; perhaps add a `use` for it: + | +LL + use method::A; | -LL | trait A { - | ^^^^^^^ error[E0599]: no method named `b` found for struct `S` in the current scope --> $DIR/item-privacy.rs:68:7 @@ -51,11 +50,10 @@ LL | S::a(&S); | ^ function or associated item not found in `S` | = help: items from traits can only be used if the trait is implemented and in scope -note: `method::A` defines an item `a`, perhaps you need to implement it - --> $DIR/item-privacy.rs:6:5 +help: the following trait is implemented but not in scope; perhaps add a `use` for it: + | +LL + use method::A; | -LL | trait A { - | ^^^^^^^ error[E0599]: no function or associated item named `b` found for struct `S` in the current scope --> $DIR/item-privacy.rs:80:8 @@ -91,11 +89,10 @@ LL | S::A; | ^ associated item not found in `S` | = help: items from traits can only be used if the trait is implemented and in scope -note: `assoc_const::A` defines an item `A`, perhaps you need to implement it - --> $DIR/item-privacy.rs:24:5 +help: the following trait is implemented but not in scope; perhaps add a `use` for it: + | +LL + use assoc_const::A; | -LL | trait A { - | ^^^^^^^ error[E0599]: no associated item named `B` found for struct `S` in the current scope --> $DIR/item-privacy.rs:98:8