Skip to content

Commit 162443b

Browse files
committed
Detect when trait is implemented for type and suggest importing it
Fix #57457.
1 parent 6d1fc53 commit 162443b

File tree

2 files changed

+49
-28
lines changed

2 files changed

+49
-28
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

+40-16
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ use rustc_span::def_id::DefIdSet;
3535
use rustc_span::symbol::{kw, sym, Ident};
3636
use rustc_span::Symbol;
3737
use rustc_span::{edit_distance, source_map, ExpnKind, FileName, MacroKind, Span};
38+
use rustc_trait_selection::infer::InferCtxtExt;
3839
use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote;
3940
use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _;
4041
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -192,7 +193,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
192193
.span_if_local(def_id)
193194
.unwrap_or_else(|| self.tcx.def_span(def_id));
194195
err.span_label(sp, format!("private {kind} defined here"));
195-
self.suggest_valid_traits(&mut err, out_of_scope_traits);
196+
self.suggest_valid_traits(&mut err, out_of_scope_traits, true);
196197
err.emit();
197198
}
198199

@@ -2464,6 +2465,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24642465
&self,
24652466
err: &mut Diagnostic,
24662467
valid_out_of_scope_traits: Vec<DefId>,
2468+
explain: bool,
24672469
) -> bool {
24682470
if !valid_out_of_scope_traits.is_empty() {
24692471
let mut candidates = valid_out_of_scope_traits;
@@ -2476,7 +2478,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24762478
.find(|did| self.tcx.is_diagnostic_item(sym::TryInto, **did))
24772479
.copied();
24782480

2479-
err.help("items from traits can only be used if the trait is in scope");
2481+
if explain {
2482+
err.help("items from traits can only be used if the trait is in scope");
2483+
}
24802484
let msg = format!(
24812485
"the following {traits_are} implemented but not in scope; \
24822486
perhaps add a `use` for {one_of_them}:",
@@ -2693,7 +2697,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26932697
}
26942698
}
26952699
}
2696-
if self.suggest_valid_traits(err, valid_out_of_scope_traits) {
2700+
if self.suggest_valid_traits(err, valid_out_of_scope_traits, true) {
26972701
return;
26982702
}
26992703

@@ -2970,29 +2974,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
29702974
(candidates, Vec::new())
29712975
};
29722976

2977+
let impls_trait = |def_id: DefId| {
2978+
let args = ty::GenericArgs::for_item(self.tcx, def_id, |param, _| {
2979+
if param.index == 0 {
2980+
rcvr_ty.into()
2981+
} else {
2982+
self.infcx.var_for_def(span, param)
2983+
}
2984+
});
2985+
self.infcx
2986+
.type_implements_trait(def_id, args, self.param_env)
2987+
.must_apply_modulo_regions()
2988+
&& param_type.is_none()
2989+
};
29732990
match &potential_candidates[..] {
29742991
[] => {}
29752992
[trait_info] if trait_info.def_id.is_local() => {
2976-
err.subdiagnostic(CandidateTraitNote {
2977-
span: self.tcx.def_span(trait_info.def_id),
2978-
trait_name: self.tcx.def_path_str(trait_info.def_id),
2979-
item_name,
2980-
action_or_ty: if trait_missing_method {
2981-
"NONE".to_string()
2982-
} else {
2983-
param_type.map_or_else(
2984-
|| "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented.
2985-
ToString::to_string,
2986-
)
2987-
},
2988-
});
2993+
if impls_trait(trait_info.def_id) {
2994+
self.suggest_valid_traits(err, vec![trait_info.def_id], false);
2995+
} else {
2996+
err.subdiagnostic(CandidateTraitNote {
2997+
span: self.tcx.def_span(trait_info.def_id),
2998+
trait_name: self.tcx.def_path_str(trait_info.def_id),
2999+
item_name,
3000+
action_or_ty: if trait_missing_method {
3001+
"NONE".to_string()
3002+
} else {
3003+
param_type.map_or_else(
3004+
|| "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented.
3005+
ToString::to_string,
3006+
)
3007+
},
3008+
});
3009+
}
29893010
}
29903011
trait_infos => {
29913012
let mut msg = message(param_type.map_or_else(
29923013
|| "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented.
29933014
|param| format!("restrict type parameter `{param}` with"),
29943015
));
29953016
for (i, trait_info) in trait_infos.iter().enumerate() {
3017+
if impls_trait(trait_info.def_id) {
3018+
self.suggest_valid_traits(err, vec![trait_info.def_id], false);
3019+
}
29963020
msg.push_str(&format!(
29973021
"\ncandidate #{}: `{}`",
29983022
i + 1,

tests/ui/traits/item-privacy.stderr

+9-12
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ LL | S.a();
88
| ^ method not found in `S`
99
|
1010
= help: items from traits can only be used if the trait is implemented and in scope
11-
note: `method::A` defines an item `a`, perhaps you need to implement it
12-
--> $DIR/item-privacy.rs:6:5
11+
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
12+
|
13+
LL + use method::A;
1314
|
14-
LL | trait A {
15-
| ^^^^^^^
1615

1716
error[E0599]: no method named `b` found for struct `S` in the current scope
1817
--> $DIR/item-privacy.rs:68:7
@@ -51,11 +50,10 @@ LL | S::a(&S);
5150
| ^ function or associated item not found in `S`
5251
|
5352
= help: items from traits can only be used if the trait is implemented and in scope
54-
note: `method::A` defines an item `a`, perhaps you need to implement it
55-
--> $DIR/item-privacy.rs:6:5
53+
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
54+
|
55+
LL + use method::A;
5656
|
57-
LL | trait A {
58-
| ^^^^^^^
5957

6058
error[E0599]: no function or associated item named `b` found for struct `S` in the current scope
6159
--> $DIR/item-privacy.rs:80:8
@@ -91,11 +89,10 @@ LL | S::A;
9189
| ^ associated item not found in `S`
9290
|
9391
= help: items from traits can only be used if the trait is implemented and in scope
94-
note: `assoc_const::A` defines an item `A`, perhaps you need to implement it
95-
--> $DIR/item-privacy.rs:24:5
92+
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
93+
|
94+
LL + use assoc_const::A;
9695
|
97-
LL | trait A {
98-
| ^^^^^^^
9996

10097
error[E0599]: no associated item named `B` found for struct `S` in the current scope
10198
--> $DIR/item-privacy.rs:98:8

0 commit comments

Comments
 (0)