Skip to content

Commit 3cabf61

Browse files
committed
tip for inaccessible traits
1 parent 466be51 commit 3cabf61

File tree

2 files changed

+128
-51
lines changed

2 files changed

+128
-51
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

+122-45
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
282282
}
283283
if !candidates.is_empty() {
284284
let help = format!(
285-
"{an}other candidate{s} {were} found in the following trait{s}, perhaps \
286-
add a `use` for {one_of_them}:",
285+
"{an}other candidate{s} {were} found in the following trait{s},",
287286
an = if candidates.len() == 1 { "an" } else { "" },
288287
s = pluralize!(candidates.len()),
289288
were = pluralize!("was", candidates.len()),
290-
one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" },
291289
);
292-
self.suggest_use_candidates(&mut err, help, candidates);
290+
self.suggest_use_candidates(
291+
candidates,
292+
|accessible_sugg, inaccessible_sugg, span| {
293+
let suggest_for_access =
294+
|err: &mut Diag<'_>, mut msg: String, sugg: Vec<_>| {
295+
msg += &format!(
296+
" perhaps add a `use` for {one_of_them}:",
297+
one_of_them =
298+
if sugg.len() == 1 { "it" } else { "one_of_them" },
299+
);
300+
err.span_suggestions(
301+
span,
302+
msg,
303+
sugg,
304+
Applicability::MaybeIncorrect,
305+
);
306+
};
307+
let suggest_for_privacy =
308+
|err: &mut Diag<'_>, mut msg: String, sugg: Vec<_>| {
309+
msg += &format!(
310+
" perhaps you want to modify {one_of}the visibility and use it",
311+
one_of = if sugg.len() == 1 { "" } else { "one of " },
312+
);
313+
err.span_suggestions(
314+
span,
315+
msg,
316+
sugg,
317+
Applicability::MaybeIncorrect,
318+
);
319+
};
320+
if accessible_sugg.is_empty() {
321+
// `inaccessible_sugg` must not be empty
322+
suggest_for_privacy(&mut err, help, inaccessible_sugg);
323+
} else if inaccessible_sugg.is_empty() {
324+
suggest_for_access(&mut err, help, accessible_sugg);
325+
} else {
326+
suggest_for_access(&mut err, help.clone(), accessible_sugg);
327+
suggest_for_privacy(&mut err, help, inaccessible_sugg);
328+
}
329+
},
330+
);
293331
}
294332
if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() {
295333
if needs_mut {
@@ -3051,49 +3089,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
30513089
}
30523090
}
30533091

3054-
fn suggest_use_candidates(&self, err: &mut Diag<'_>, msg: String, candidates: Vec<DefId>) {
3092+
fn suggest_use_candidates<F>(&self, candidates: Vec<DefId>, f: F)
3093+
where
3094+
F: FnOnce(Vec<String>, Vec<String>, Span),
3095+
{
30553096
let parent_map = self.tcx.visible_parent_map(());
30563097

3057-
// Separate out candidates that must be imported with a glob, because they are named `_`
3058-
// and cannot be referred with their identifier.
3059-
let (candidates, globs): (Vec<_>, Vec<_>) = candidates.into_iter().partition(|trait_did| {
3060-
if let Some(parent_did) = parent_map.get(trait_did) {
3061-
// If the item is re-exported as `_`, we should suggest a glob-import instead.
3062-
if *parent_did != self.tcx.parent(*trait_did)
3063-
&& self
3064-
.tcx
3065-
.module_children(*parent_did)
3066-
.iter()
3067-
.filter(|child| child.res.opt_def_id() == Some(*trait_did))
3068-
.all(|child| child.ident.name == kw::Underscore)
3069-
{
3070-
return false;
3071-
}
3072-
}
3098+
let scope = self.tcx.parent_module_from_def_id(self.body_id);
3099+
let (accessible_candidates, inaccessible_candidates): (Vec<_>, Vec<_>) =
3100+
candidates.into_iter().partition(|id| {
3101+
let vis = self.tcx.visibility(*id);
3102+
vis.is_accessible_from(scope, self.tcx)
3103+
});
30733104

3074-
true
3075-
});
3105+
let sugg = |candidates: Vec<_>, visible| {
3106+
// Separate out candidates that must be imported with a glob, because they are named `_`
3107+
// and cannot be referred with their identifier.
3108+
let (candidates, globs): (Vec<_>, Vec<_>) =
3109+
candidates.into_iter().partition(|trait_did| {
3110+
if let Some(parent_did) = parent_map.get(trait_did) {
3111+
// If the item is re-exported as `_`, we should suggest a glob-import instead.
3112+
if *parent_did != self.tcx.parent(*trait_did)
3113+
&& self
3114+
.tcx
3115+
.module_children(*parent_did)
3116+
.iter()
3117+
.filter(|child| child.res.opt_def_id() == Some(*trait_did))
3118+
.all(|child| child.ident.name == kw::Underscore)
3119+
{
3120+
return false;
3121+
}
3122+
}
30763123

3077-
let module_did = self.tcx.parent_module_from_def_id(self.body_id);
3078-
let (module, _, _) = self.tcx.hir().get_module(module_did);
3079-
let span = module.spans.inject_use_span;
3124+
true
3125+
});
30803126

3081-
let path_strings = candidates.iter().map(|trait_did| {
3082-
format!("use {};\n", with_crate_prefix!(self.tcx.def_path_str(*trait_did)),)
3083-
});
3127+
let prefix = if visible { "use " } else { "" };
3128+
let path_strings = candidates.iter().map(|trait_did| {
3129+
format!("{prefix}{};\n", with_crate_prefix!(self.tcx.def_path_str(*trait_did)),)
3130+
});
30843131

3085-
let glob_path_strings = globs.iter().map(|trait_did| {
3086-
let parent_did = parent_map.get(trait_did).unwrap();
3087-
format!(
3088-
"use {}::*; // trait {}\n",
3089-
with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
3090-
self.tcx.item_name(*trait_did),
3091-
)
3092-
});
3093-
let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect();
3094-
sugg.sort();
3132+
let glob_path_strings = globs.iter().map(|trait_did| {
3133+
let parent_did = parent_map.get(trait_did).unwrap();
3134+
format!(
3135+
"{prefix}{}::*; // trait {}\n",
3136+
with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
3137+
self.tcx.item_name(*trait_did),
3138+
)
3139+
});
3140+
let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect();
3141+
sugg.sort();
3142+
sugg
3143+
};
3144+
3145+
let accessible_sugg = sugg(accessible_candidates, true);
3146+
let inaccessible_sugg = sugg(inaccessible_candidates, false);
30953147

3096-
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
3148+
let (module, _, _) = self.tcx.hir().get_module(scope);
3149+
let span = module.spans.inject_use_span;
3150+
f(accessible_sugg, inaccessible_sugg, span);
30973151
}
30983152

30993153
fn suggest_valid_traits(
@@ -3118,20 +3172,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
31183172
err.help("items from traits can only be used if the trait is in scope");
31193173
}
31203174
let msg = format!(
3121-
"{this_trait_is} implemented but not in scope; perhaps you want to import \
3122-
{one_of_them}",
3175+
"{this_trait_is} implemented but not in scope;",
31233176
this_trait_is = if candidates.len() == 1 {
31243177
format!(
31253178
"trait `{}` which provides `{item_name}` is",
31263179
self.tcx.item_name(candidates[0]),
31273180
)
31283181
} else {
31293182
format!("the following traits which provide `{item_name}` are")
3130-
},
3131-
one_of_them = if candidates.len() == 1 { "it" } else { "one of them" },
3183+
}
31323184
);
3185+
self.suggest_use_candidates(candidates, |accessible_sugg, inaccessible_sugg, span| {
3186+
let suggest_for_access = |err: &mut Diag<'_>, mut msg: String, sugg: Vec<_>| {
3187+
msg += &format!(
3188+
" perhaps you want to import {one_of}",
3189+
one_of = if sugg.len() == 1 { "it" } else { "one of them" },
3190+
);
3191+
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
3192+
};
3193+
let suggest_for_privacy = |err: &mut Diag<'_>, mut msg: String, sugg: Vec<_>| {
3194+
msg += &format!(
3195+
" perhaps you want to use {one_of}the inaccessible trait{s}",
3196+
one_of = if sugg.len() == 1 { "" } else { "one of " },
3197+
s = pluralize!(sugg.len())
3198+
);
3199+
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
3200+
};
3201+
if accessible_sugg.is_empty() {
3202+
// `inaccessible_sugg` must not be empty
3203+
suggest_for_privacy(err, msg, inaccessible_sugg);
3204+
} else if inaccessible_sugg.is_empty() {
3205+
suggest_for_access(err, msg, accessible_sugg);
3206+
} else {
3207+
suggest_for_access(err, msg.clone(), accessible_sugg);
3208+
suggest_for_privacy(err, msg, inaccessible_sugg);
3209+
}
3210+
});
31333211

3134-
self.suggest_use_candidates(err, msg, candidates);
31353212
if let Some(did) = edition_fix {
31363213
err.note(format!(
31373214
"'{}' is included in the prelude starting in Edition 2021",

tests/ui/traits/item-privacy.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ 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
11+
help: trait `A` which provides `a` is implemented but not in scope; perhaps you want to use the inaccessible trait
1212
|
13-
LL + use method::A;
13+
LL + method::A;
1414
|
1515
help: there is a method `b` with a similar name
1616
|
@@ -63,9 +63,9 @@ help: there is an associated constant `B` with a similar name
6363
|
6464
LL | const B: u8 = 0;
6565
| ^^^^^^^^^^^
66-
help: trait `A` which provides `a` is implemented but not in scope; perhaps you want to import it
66+
help: trait `A` which provides `a` is implemented but not in scope; perhaps you want to use the inaccessible trait
6767
|
68-
LL + use method::A;
68+
LL + method::A;
6969
|
7070

7171
error[E0599]: no function or associated item named `b` found for struct `S` in the current scope
@@ -107,9 +107,9 @@ LL | S::A;
107107
| ^ associated item not found in `S`
108108
|
109109
= 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
110+
help: trait `A` which provides `A` is implemented but not in scope; perhaps you want to use the inaccessible trait
111111
|
112-
LL + use assoc_const::A;
112+
LL + assoc_const::A;
113113
|
114114
help: there is an associated constant `B` with a similar name
115115
|

0 commit comments

Comments
 (0)