Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect when trait is implemented for type and suggest importing it #116862

Merged
merged 1 commit into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 40 additions & 16 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 _;
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -2464,6 +2465,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&self,
err: &mut Diagnostic,
valid_out_of_scope_traits: Vec<DefId>,
explain: bool,
) -> bool {
if !valid_out_of_scope_traits.is_empty() {
let mut candidates = valid_out_of_scope_traits;
Expand All @@ -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}:",
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -2970,29 +2974,49 @@ 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(
|| "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented.
|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,
Expand Down
21 changes: 9 additions & 12 deletions tests/ui/traits/item-privacy.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading