Skip to content

Commit 3c0f019

Browse files
committed
Auto merge of rust-lang#125852 - bvanjoi:improve-tip-for-invisible-trait, r=compiler-errors
improve tip for inaccessible traits Improve the tips when the candidate method is from an inaccessible trait. For example: ```rs mod m { trait Trait { fn f() {} } impl<T> Trait for T {} } fn main() { struct S; S::f(); } ``` The difference between before and now is: ```diff error[E0599]: no function or associated item named `f` found for struct `S` in the current scope --> ./src/main.rs:88:6 | LL | struct S; | -------- function or associated item `f` not found for this struct LL | S::f(); | ^ 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: trait `Trait` which provides `f` is implemented but not in scope; perhaps you want to import it + help: trait `crate::m::Trait` which provides `f` is implemented but not reachable | - LL + use crate::m::Trait; | ```
2 parents 9889a6f + 586821e commit 3c0f019

File tree

2 files changed

+143
-57
lines changed

2 files changed

+143
-57
lines changed

Diff for: compiler/rustc_hir_typeck/src/method/suggest.rs

+140-45
Original file line numberDiff line numberDiff line change
@@ -303,14 +303,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
303303
}
304304
if !candidates.is_empty() {
305305
let help = format!(
306-
"{an}other candidate{s} {were} found in the following trait{s}, perhaps \
307-
add a `use` for {one_of_them}:",
306+
"{an}other candidate{s} {were} found in the following trait{s}",
308307
an = if candidates.len() == 1 { "an" } else { "" },
309308
s = pluralize!(candidates.len()),
310309
were = pluralize!("was", candidates.len()),
311-
one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" },
312310
);
313-
self.suggest_use_candidates(&mut err, help, candidates);
311+
self.suggest_use_candidates(
312+
candidates,
313+
|accessible_sugg, inaccessible_sugg, span| {
314+
let suggest_for_access =
315+
|err: &mut Diag<'_>, mut msg: String, sugg: Vec<_>| {
316+
msg += &format!(
317+
", perhaps add a `use` for {one_of_them}:",
318+
one_of_them =
319+
if sugg.len() == 1 { "it" } else { "one_of_them" },
320+
);
321+
err.span_suggestions(
322+
span,
323+
msg,
324+
sugg,
325+
Applicability::MaybeIncorrect,
326+
);
327+
};
328+
let suggest_for_privacy =
329+
|err: &mut Diag<'_>, mut msg: String, sugg: Vec<String>| {
330+
if sugg.len() == 1 {
331+
let msg = format!("\
332+
trait `{}` provides `{item_name}` is implemented but not reachable",
333+
sugg[0].trim()
334+
);
335+
err.help(msg);
336+
} else {
337+
msg += &format!(" but {} not reachable", pluralize!("is", sugg.len()));
338+
err.span_suggestions(
339+
span,
340+
msg,
341+
sugg,
342+
Applicability::MaybeIncorrect,
343+
);
344+
}
345+
};
346+
if accessible_sugg.is_empty() {
347+
// `inaccessible_sugg` must not be empty
348+
suggest_for_privacy(&mut err, help, inaccessible_sugg);
349+
} else if inaccessible_sugg.is_empty() {
350+
suggest_for_access(&mut err, help, accessible_sugg);
351+
} else {
352+
suggest_for_access(&mut err, help.clone(), accessible_sugg);
353+
suggest_for_privacy(&mut err, help, inaccessible_sugg);
354+
}
355+
},
356+
);
314357
}
315358
if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() {
316359
if needs_mut {
@@ -3089,49 +3132,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
30893132
}
30903133
}
30913134

3092-
fn suggest_use_candidates(&self, err: &mut Diag<'_>, msg: String, candidates: Vec<DefId>) {
3135+
fn suggest_use_candidates<F>(&self, candidates: Vec<DefId>, handle_candidates: F)
3136+
where
3137+
F: FnOnce(Vec<String>, Vec<String>, Span),
3138+
{
30933139
let parent_map = self.tcx.visible_parent_map(());
30943140

3095-
// Separate out candidates that must be imported with a glob, because they are named `_`
3096-
// and cannot be referred with their identifier.
3097-
let (candidates, globs): (Vec<_>, Vec<_>) = candidates.into_iter().partition(|trait_did| {
3098-
if let Some(parent_did) = parent_map.get(trait_did) {
3099-
// If the item is re-exported as `_`, we should suggest a glob-import instead.
3100-
if *parent_did != self.tcx.parent(*trait_did)
3101-
&& self
3102-
.tcx
3103-
.module_children(*parent_did)
3104-
.iter()
3105-
.filter(|child| child.res.opt_def_id() == Some(*trait_did))
3106-
.all(|child| child.ident.name == kw::Underscore)
3107-
{
3108-
return false;
3109-
}
3110-
}
3141+
let scope = self.tcx.parent_module_from_def_id(self.body_id);
3142+
let (accessible_candidates, inaccessible_candidates): (Vec<_>, Vec<_>) =
3143+
candidates.into_iter().partition(|id| {
3144+
let vis = self.tcx.visibility(*id);
3145+
vis.is_accessible_from(scope, self.tcx)
3146+
});
31113147

3112-
true
3113-
});
3148+
let sugg = |candidates: Vec<_>, visible| {
3149+
// Separate out candidates that must be imported with a glob, because they are named `_`
3150+
// and cannot be referred with their identifier.
3151+
let (candidates, globs): (Vec<_>, Vec<_>) =
3152+
candidates.into_iter().partition(|trait_did| {
3153+
if let Some(parent_did) = parent_map.get(trait_did) {
3154+
// If the item is re-exported as `_`, we should suggest a glob-import instead.
3155+
if *parent_did != self.tcx.parent(*trait_did)
3156+
&& self
3157+
.tcx
3158+
.module_children(*parent_did)
3159+
.iter()
3160+
.filter(|child| child.res.opt_def_id() == Some(*trait_did))
3161+
.all(|child| child.ident.name == kw::Underscore)
3162+
{
3163+
return false;
3164+
}
3165+
}
31143166

3115-
let module_did = self.tcx.parent_module_from_def_id(self.body_id);
3116-
let (module, _, _) = self.tcx.hir().get_module(module_did);
3117-
let span = module.spans.inject_use_span;
3167+
true
3168+
});
31183169

3119-
let path_strings = candidates.iter().map(|trait_did| {
3120-
format!("use {};\n", with_crate_prefix!(self.tcx.def_path_str(*trait_did)),)
3121-
});
3170+
let prefix = if visible { "use " } else { "" };
3171+
let postfix = if visible { ";" } else { "" };
3172+
let path_strings = candidates.iter().map(|trait_did| {
3173+
format!(
3174+
"{prefix}{}{postfix}\n",
3175+
with_crate_prefix!(self.tcx.def_path_str(*trait_did)),
3176+
)
3177+
});
31223178

3123-
let glob_path_strings = globs.iter().map(|trait_did| {
3124-
let parent_did = parent_map.get(trait_did).unwrap();
3125-
format!(
3126-
"use {}::*; // trait {}\n",
3127-
with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
3128-
self.tcx.item_name(*trait_did),
3129-
)
3130-
});
3131-
let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect();
3132-
sugg.sort();
3179+
let glob_path_strings = globs.iter().map(|trait_did| {
3180+
let parent_did = parent_map.get(trait_did).unwrap();
3181+
format!(
3182+
"{prefix}{}::*{postfix} // trait {}\n",
3183+
with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
3184+
self.tcx.item_name(*trait_did),
3185+
)
3186+
});
3187+
let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect();
3188+
sugg.sort();
3189+
sugg
3190+
};
31333191

3134-
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
3192+
let accessible_sugg = sugg(accessible_candidates, true);
3193+
let inaccessible_sugg = sugg(inaccessible_candidates, false);
3194+
3195+
let (module, _, _) = self.tcx.hir().get_module(scope);
3196+
let span = module.spans.inject_use_span;
3197+
handle_candidates(accessible_sugg, inaccessible_sugg, span);
31353198
}
31363199

31373200
fn suggest_valid_traits(
@@ -3155,21 +3218,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
31553218
if explain {
31563219
err.help("items from traits can only be used if the trait is in scope");
31573220
}
3221+
31583222
let msg = format!(
3159-
"{this_trait_is} implemented but not in scope; perhaps you want to import \
3160-
{one_of_them}",
3223+
"{this_trait_is} implemented but not in scope",
31613224
this_trait_is = if candidates.len() == 1 {
31623225
format!(
31633226
"trait `{}` which provides `{item_name}` is",
31643227
self.tcx.item_name(candidates[0]),
31653228
)
31663229
} else {
31673230
format!("the following traits which provide `{item_name}` are")
3168-
},
3169-
one_of_them = if candidates.len() == 1 { "it" } else { "one of them" },
3231+
}
31703232
);
31713233

3172-
self.suggest_use_candidates(err, msg, candidates);
3234+
self.suggest_use_candidates(candidates, |accessible_sugg, inaccessible_sugg, span| {
3235+
let suggest_for_access = |err: &mut Diag<'_>, mut msg: String, sugg: Vec<_>| {
3236+
msg += &format!(
3237+
"; perhaps you want to import {one_of}",
3238+
one_of = if sugg.len() == 1 { "it" } else { "one of them" },
3239+
);
3240+
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
3241+
};
3242+
let suggest_for_privacy = |err: &mut Diag<'_>, sugg: Vec<String>| {
3243+
let msg = format!(
3244+
"{this_trait_is} implemented but not reachable",
3245+
this_trait_is = if sugg.len() == 1 {
3246+
format!("trait `{}` which provides `{item_name}` is", sugg[0].trim())
3247+
} else {
3248+
format!("the following traits which provide `{item_name}` are")
3249+
}
3250+
);
3251+
if sugg.len() == 1 {
3252+
err.help(msg);
3253+
} else {
3254+
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
3255+
}
3256+
};
3257+
if accessible_sugg.is_empty() {
3258+
// `inaccessible_sugg` must not be empty
3259+
suggest_for_privacy(err, inaccessible_sugg);
3260+
} else if inaccessible_sugg.is_empty() {
3261+
suggest_for_access(err, msg, accessible_sugg);
3262+
} else {
3263+
suggest_for_access(err, msg, accessible_sugg);
3264+
suggest_for_privacy(err, inaccessible_sugg);
3265+
}
3266+
});
3267+
31733268
if let Some(did) = edition_fix {
31743269
err.note(format!(
31753270
"'{}' is included in the prelude starting in Edition 2021",

Diff for: tests/ui/traits/item-privacy.stderr

+3-12
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@ LL | S.a();
88
| ^
99
|
1010
= help: items from traits can only be used if the trait is implemented and in scope
11-
help: trait `A` which provides `a` is implemented but not in scope; perhaps you want to import it
12-
|
13-
LL + use method::A;
14-
|
11+
= help: trait `method::A` which provides `a` is implemented but not reachable
1512
help: there is a method `b` with a similar name
1613
|
1714
LL | S.b();
@@ -58,15 +55,12 @@ LL | S::a(&S);
5855
| ^ function or associated item not found in `S`
5956
|
6057
= help: items from traits can only be used if the trait is implemented and in scope
58+
= help: trait `method::A` which provides `a` is implemented but not reachable
6159
help: there is an associated constant `B` with a similar name
6260
--> $DIR/item-privacy.rs:29:9
6361
|
6462
LL | const B: u8 = 0;
6563
| ^^^^^^^^^^^
66-
help: trait `A` which provides `a` is implemented but not in scope; perhaps you want to import it
67-
|
68-
LL + use method::A;
69-
|
7064

7165
error[E0599]: no function or associated item named `b` found for struct `S` in the current scope
7266
--> $DIR/item-privacy.rs:80:8
@@ -107,10 +101,7 @@ LL | S::A;
107101
| ^ associated item not found in `S`
108102
|
109103
= help: items from traits can only be used if the trait is implemented and in scope
110-
help: trait `A` which provides `A` is implemented but not in scope; perhaps you want to import it
111-
|
112-
LL + use assoc_const::A;
113-
|
104+
= help: trait `assoc_const::A` which provides `A` is implemented but not reachable
114105
help: there is an associated constant `B` with a similar name
115106
|
116107
LL | S::B;

0 commit comments

Comments
 (0)