Skip to content

Commit 586821e

Browse files
committed
tip for inaccessible traits
1 parent 865eaf9 commit 586821e

File tree

2 files changed

+143
-57
lines changed

2 files changed

+143
-57
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

+140-45
Original file line numberDiff line numberDiff line change
@@ -284,14 +284,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
284284
}
285285
if !candidates.is_empty() {
286286
let help = format!(
287-
"{an}other candidate{s} {were} found in the following trait{s}, perhaps \
288-
add a `use` for {one_of_them}:",
287+
"{an}other candidate{s} {were} found in the following trait{s}",
289288
an = if candidates.len() == 1 { "an" } else { "" },
290289
s = pluralize!(candidates.len()),
291290
were = pluralize!("was", candidates.len()),
292-
one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" },
293291
);
294-
self.suggest_use_candidates(&mut err, help, candidates);
292+
self.suggest_use_candidates(
293+
candidates,
294+
|accessible_sugg, inaccessible_sugg, span| {
295+
let suggest_for_access =
296+
|err: &mut Diag<'_>, mut msg: String, sugg: Vec<_>| {
297+
msg += &format!(
298+
", perhaps add a `use` for {one_of_them}:",
299+
one_of_them =
300+
if sugg.len() == 1 { "it" } else { "one_of_them" },
301+
);
302+
err.span_suggestions(
303+
span,
304+
msg,
305+
sugg,
306+
Applicability::MaybeIncorrect,
307+
);
308+
};
309+
let suggest_for_privacy =
310+
|err: &mut Diag<'_>, mut msg: String, sugg: Vec<String>| {
311+
if sugg.len() == 1 {
312+
let msg = format!("\
313+
trait `{}` provides `{item_name}` is implemented but not reachable",
314+
sugg[0].trim()
315+
);
316+
err.help(msg);
317+
} else {
318+
msg += &format!(" but {} not reachable", pluralize!("is", sugg.len()));
319+
err.span_suggestions(
320+
span,
321+
msg,
322+
sugg,
323+
Applicability::MaybeIncorrect,
324+
);
325+
}
326+
};
327+
if accessible_sugg.is_empty() {
328+
// `inaccessible_sugg` must not be empty
329+
suggest_for_privacy(&mut err, help, inaccessible_sugg);
330+
} else if inaccessible_sugg.is_empty() {
331+
suggest_for_access(&mut err, help, accessible_sugg);
332+
} else {
333+
suggest_for_access(&mut err, help.clone(), accessible_sugg);
334+
suggest_for_privacy(&mut err, help, inaccessible_sugg);
335+
}
336+
},
337+
);
295338
}
296339
if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() {
297340
if needs_mut {
@@ -3069,49 +3112,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
30693112
}
30703113
}
30713114

3072-
fn suggest_use_candidates(&self, err: &mut Diag<'_>, msg: String, candidates: Vec<DefId>) {
3115+
fn suggest_use_candidates<F>(&self, candidates: Vec<DefId>, handle_candidates: F)
3116+
where
3117+
F: FnOnce(Vec<String>, Vec<String>, Span),
3118+
{
30733119
let parent_map = self.tcx.visible_parent_map(());
30743120

3075-
// Separate out candidates that must be imported with a glob, because they are named `_`
3076-
// and cannot be referred with their identifier.
3077-
let (candidates, globs): (Vec<_>, Vec<_>) = candidates.into_iter().partition(|trait_did| {
3078-
if let Some(parent_did) = parent_map.get(trait_did) {
3079-
// If the item is re-exported as `_`, we should suggest a glob-import instead.
3080-
if *parent_did != self.tcx.parent(*trait_did)
3081-
&& self
3082-
.tcx
3083-
.module_children(*parent_did)
3084-
.iter()
3085-
.filter(|child| child.res.opt_def_id() == Some(*trait_did))
3086-
.all(|child| child.ident.name == kw::Underscore)
3087-
{
3088-
return false;
3089-
}
3090-
}
3121+
let scope = self.tcx.parent_module_from_def_id(self.body_id);
3122+
let (accessible_candidates, inaccessible_candidates): (Vec<_>, Vec<_>) =
3123+
candidates.into_iter().partition(|id| {
3124+
let vis = self.tcx.visibility(*id);
3125+
vis.is_accessible_from(scope, self.tcx)
3126+
});
30913127

3092-
true
3093-
});
3128+
let sugg = |candidates: Vec<_>, visible| {
3129+
// Separate out candidates that must be imported with a glob, because they are named `_`
3130+
// and cannot be referred with their identifier.
3131+
let (candidates, globs): (Vec<_>, Vec<_>) =
3132+
candidates.into_iter().partition(|trait_did| {
3133+
if let Some(parent_did) = parent_map.get(trait_did) {
3134+
// If the item is re-exported as `_`, we should suggest a glob-import instead.
3135+
if *parent_did != self.tcx.parent(*trait_did)
3136+
&& self
3137+
.tcx
3138+
.module_children(*parent_did)
3139+
.iter()
3140+
.filter(|child| child.res.opt_def_id() == Some(*trait_did))
3141+
.all(|child| child.ident.name == kw::Underscore)
3142+
{
3143+
return false;
3144+
}
3145+
}
30943146

3095-
let module_did = self.tcx.parent_module_from_def_id(self.body_id);
3096-
let (module, _, _) = self.tcx.hir().get_module(module_did);
3097-
let span = module.spans.inject_use_span;
3147+
true
3148+
});
30983149

3099-
let path_strings = candidates.iter().map(|trait_did| {
3100-
format!("use {};\n", with_crate_prefix!(self.tcx.def_path_str(*trait_did)),)
3101-
});
3150+
let prefix = if visible { "use " } else { "" };
3151+
let postfix = if visible { ";" } else { "" };
3152+
let path_strings = candidates.iter().map(|trait_did| {
3153+
format!(
3154+
"{prefix}{}{postfix}\n",
3155+
with_crate_prefix!(self.tcx.def_path_str(*trait_did)),
3156+
)
3157+
});
31023158

3103-
let glob_path_strings = globs.iter().map(|trait_did| {
3104-
let parent_did = parent_map.get(trait_did).unwrap();
3105-
format!(
3106-
"use {}::*; // trait {}\n",
3107-
with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
3108-
self.tcx.item_name(*trait_did),
3109-
)
3110-
});
3111-
let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect();
3112-
sugg.sort();
3159+
let glob_path_strings = globs.iter().map(|trait_did| {
3160+
let parent_did = parent_map.get(trait_did).unwrap();
3161+
format!(
3162+
"{prefix}{}::*{postfix} // trait {}\n",
3163+
with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
3164+
self.tcx.item_name(*trait_did),
3165+
)
3166+
});
3167+
let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect();
3168+
sugg.sort();
3169+
sugg
3170+
};
31133171

3114-
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
3172+
let accessible_sugg = sugg(accessible_candidates, true);
3173+
let inaccessible_sugg = sugg(inaccessible_candidates, false);
3174+
3175+
let (module, _, _) = self.tcx.hir().get_module(scope);
3176+
let span = module.spans.inject_use_span;
3177+
handle_candidates(accessible_sugg, inaccessible_sugg, span);
31153178
}
31163179

31173180
fn suggest_valid_traits(
@@ -3135,21 +3198,53 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
31353198
if explain {
31363199
err.help("items from traits can only be used if the trait is in scope");
31373200
}
3201+
31383202
let msg = format!(
3139-
"{this_trait_is} implemented but not in scope; perhaps you want to import \
3140-
{one_of_them}",
3203+
"{this_trait_is} implemented but not in scope",
31413204
this_trait_is = if candidates.len() == 1 {
31423205
format!(
31433206
"trait `{}` which provides `{item_name}` is",
31443207
self.tcx.item_name(candidates[0]),
31453208
)
31463209
} else {
31473210
format!("the following traits which provide `{item_name}` are")
3148-
},
3149-
one_of_them = if candidates.len() == 1 { "it" } else { "one of them" },
3211+
}
31503212
);
31513213

3152-
self.suggest_use_candidates(err, msg, candidates);
3214+
self.suggest_use_candidates(candidates, |accessible_sugg, inaccessible_sugg, span| {
3215+
let suggest_for_access = |err: &mut Diag<'_>, mut msg: String, sugg: Vec<_>| {
3216+
msg += &format!(
3217+
"; perhaps you want to import {one_of}",
3218+
one_of = if sugg.len() == 1 { "it" } else { "one of them" },
3219+
);
3220+
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
3221+
};
3222+
let suggest_for_privacy = |err: &mut Diag<'_>, sugg: Vec<String>| {
3223+
let msg = format!(
3224+
"{this_trait_is} implemented but not reachable",
3225+
this_trait_is = if sugg.len() == 1 {
3226+
format!("trait `{}` which provides `{item_name}` is", sugg[0].trim())
3227+
} else {
3228+
format!("the following traits which provide `{item_name}` are")
3229+
}
3230+
);
3231+
if sugg.len() == 1 {
3232+
err.help(msg);
3233+
} else {
3234+
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
3235+
}
3236+
};
3237+
if accessible_sugg.is_empty() {
3238+
// `inaccessible_sugg` must not be empty
3239+
suggest_for_privacy(err, inaccessible_sugg);
3240+
} else if inaccessible_sugg.is_empty() {
3241+
suggest_for_access(err, msg, accessible_sugg);
3242+
} else {
3243+
suggest_for_access(err, msg, accessible_sugg);
3244+
suggest_for_privacy(err, inaccessible_sugg);
3245+
}
3246+
});
3247+
31533248
if let Some(did) = edition_fix {
31543249
err.note(format!(
31553250
"'{}' is included in the prelude starting in Edition 2021",

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)