Rollup merge of #116862 - estebank:issue-57457, r=oli-obk

Detect when trait is implemented for type and suggest importing it

Fix #57457.
This commit is contained in:
Matthias Krüger 2023-10-30 21:03:37 +01:00 committed by GitHub
commit c299595379
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 49 additions and 28 deletions

View File

@ -35,6 +35,7 @@
use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Symbol; use rustc_span::Symbol;
use rustc_span::{edit_distance, source_map, ExpnKind, FileName, MacroKind, Span}; 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::OnUnimplementedNote;
use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _; use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@ -192,7 +193,7 @@ pub fn report_method_error(
.span_if_local(def_id) .span_if_local(def_id)
.unwrap_or_else(|| self.tcx.def_span(def_id)); .unwrap_or_else(|| self.tcx.def_span(def_id));
err.span_label(sp, format!("private {kind} defined here")); 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(); err.emit();
} }
@ -2464,6 +2465,7 @@ fn suggest_valid_traits(
&self, &self,
err: &mut Diagnostic, err: &mut Diagnostic,
valid_out_of_scope_traits: Vec<DefId>, valid_out_of_scope_traits: Vec<DefId>,
explain: bool,
) -> bool { ) -> bool {
if !valid_out_of_scope_traits.is_empty() { if !valid_out_of_scope_traits.is_empty() {
let mut candidates = valid_out_of_scope_traits; let mut candidates = valid_out_of_scope_traits;
@ -2476,7 +2478,9 @@ fn suggest_valid_traits(
.find(|did| self.tcx.is_diagnostic_item(sym::TryInto, **did)) .find(|did| self.tcx.is_diagnostic_item(sym::TryInto, **did))
.copied(); .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!( let msg = format!(
"the following {traits_are} implemented but not in scope; \ "the following {traits_are} implemented but not in scope; \
perhaps add a `use` for {one_of_them}:", perhaps add a `use` for {one_of_them}:",
@ -2693,7 +2697,7 @@ fn suggest_traits_to_import(
} }
} }
} }
if self.suggest_valid_traits(err, valid_out_of_scope_traits) { if self.suggest_valid_traits(err, valid_out_of_scope_traits, true) {
return; return;
} }
@ -2970,22 +2974,39 @@ enum Introducer {
(candidates, Vec::new()) (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[..] { match &potential_candidates[..] {
[] => {} [] => {}
[trait_info] if trait_info.def_id.is_local() => { [trait_info] if trait_info.def_id.is_local() => {
err.subdiagnostic(CandidateTraitNote { if impls_trait(trait_info.def_id) {
span: self.tcx.def_span(trait_info.def_id), self.suggest_valid_traits(err, vec![trait_info.def_id], false);
trait_name: self.tcx.def_path_str(trait_info.def_id), } else {
item_name, err.subdiagnostic(CandidateTraitNote {
action_or_ty: if trait_missing_method { span: self.tcx.def_span(trait_info.def_id),
"NONE".to_string() trait_name: self.tcx.def_path_str(trait_info.def_id),
} else { item_name,
param_type.map_or_else( action_or_ty: if trait_missing_method {
|| "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented. "NONE".to_string()
ToString::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 => { trait_infos => {
let mut msg = message(param_type.map_or_else( let mut msg = message(param_type.map_or_else(
@ -2993,6 +3014,9 @@ enum Introducer {
|param| format!("restrict type parameter `{param}` with"), |param| format!("restrict type parameter `{param}` with"),
)); ));
for (i, trait_info) in trait_infos.iter().enumerate() { 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!( msg.push_str(&format!(
"\ncandidate #{}: `{}`", "\ncandidate #{}: `{}`",
i + 1, i + 1,

View File

@ -8,11 +8,10 @@ LL | S.a();
| ^ method not found in `S` | ^ method not found in `S`
| |
= help: items from traits can only be used if the trait is implemented and in scope = 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 help: the following trait is implemented but not in scope; perhaps add a `use` for it:
--> $DIR/item-privacy.rs:6:5 |
LL + use method::A;
| |
LL | trait A {
| ^^^^^^^
error[E0599]: no method named `b` found for struct `S` in the current scope error[E0599]: no method named `b` found for struct `S` in the current scope
--> $DIR/item-privacy.rs:68:7 --> $DIR/item-privacy.rs:68:7
@ -51,11 +50,10 @@ LL | S::a(&S);
| ^ function or associated item not found in `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 = 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 help: the following trait is implemented but not in scope; perhaps add a `use` for it:
--> $DIR/item-privacy.rs:6:5 |
LL + use method::A;
| |
LL | trait A {
| ^^^^^^^
error[E0599]: no function or associated item named `b` found for struct `S` in the current scope error[E0599]: no function or associated item named `b` found for struct `S` in the current scope
--> $DIR/item-privacy.rs:80:8 --> $DIR/item-privacy.rs:80:8
@ -91,11 +89,10 @@ LL | S::A;
| ^ associated item not found in `S` | ^ associated item not found in `S`
| |
= help: items from traits can only be used if the trait is implemented and in scope = 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 help: the following trait is implemented but not in scope; perhaps add a `use` for it:
--> $DIR/item-privacy.rs:24:5 |
LL + use assoc_const::A;
| |
LL | trait A {
| ^^^^^^^
error[E0599]: no associated item named `B` found for struct `S` in the current scope error[E0599]: no associated item named `B` found for struct `S` in the current scope
--> $DIR/item-privacy.rs:98:8 --> $DIR/item-privacy.rs:98:8