Skip to content

Commit 3bd6f62

Browse files
committed
tip for inaccessible traits
1 parent 06d99cd commit 3bd6f62

File tree

2 files changed

+123
-51
lines changed

2 files changed

+123
-51
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

+117-45
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,48 @@ 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<'_>, msg: String, sugg: Vec<_>| {
309+
err.span_suggestions(
310+
span,
311+
msg,
312+
sugg,
313+
Applicability::MaybeIncorrect,
314+
);
315+
};
316+
if accessible_sugg.is_empty() {
317+
// `inaccessible_sugg` must not be empty
318+
suggest_for_privacy(&mut err, help, inaccessible_sugg);
319+
} else if inaccessible_sugg.is_empty() {
320+
suggest_for_access(&mut err, help, accessible_sugg);
321+
} else {
322+
suggest_for_access(&mut err, help.clone(), accessible_sugg);
323+
suggest_for_privacy(&mut err, help, inaccessible_sugg);
324+
}
325+
},
326+
);
293327
}
294328
if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() {
295329
if needs_mut {
@@ -3051,49 +3085,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
30513085
}
30523086
}
30533087

3054-
fn suggest_use_candidates(&self, err: &mut Diag<'_>, msg: String, candidates: Vec<DefId>) {
3088+
fn suggest_use_candidates<F>(&self, candidates: Vec<DefId>, handle_candidates: F)
3089+
where
3090+
F: FnOnce(Vec<String>, Vec<String>, Span),
3091+
{
30553092
let parent_map = self.tcx.visible_parent_map(());
30563093

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-
}
3094+
let scope = self.tcx.parent_module_from_def_id(self.body_id);
3095+
let (accessible_candidates, inaccessible_candidates): (Vec<_>, Vec<_>) =
3096+
candidates.into_iter().partition(|id| {
3097+
let vis = self.tcx.visibility(*id);
3098+
vis.is_accessible_from(scope, self.tcx)
3099+
});
30733100

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

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;
3120+
true
3121+
});
30803122

3081-
let path_strings = candidates.iter().map(|trait_did| {
3082-
format!("use {};\n", with_crate_prefix!(self.tcx.def_path_str(*trait_did)),)
3083-
});
3123+
let prefix = if visible { "use " } else { "" };
3124+
let postfix = if visible { ";" } else { "" };
3125+
let path_strings = candidates.iter().map(|trait_did| {
3126+
format!(
3127+
"{prefix}{}{postfix}\n",
3128+
with_crate_prefix!(self.tcx.def_path_str(*trait_did)),
3129+
)
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}{}::*{postfix} // 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+
};
30953144

3096-
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
3145+
let accessible_sugg = sugg(accessible_candidates, true);
3146+
let inaccessible_sugg = sugg(inaccessible_candidates, false);
3147+
3148+
let (module, _, _) = self.tcx.hir().get_module(scope);
3149+
let span = module.spans.inject_use_span;
3150+
handle_candidates(accessible_sugg, inaccessible_sugg, span);
30973151
}
30983152

30993153
fn suggest_valid_traits(
@@ -3118,20 +3172,38 @@ 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<'_>, msg: String, sugg: Vec<_>| {
3194+
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
3195+
};
3196+
if accessible_sugg.is_empty() {
3197+
// `inaccessible_sugg` must not be empty
3198+
suggest_for_privacy(err, msg, inaccessible_sugg);
3199+
} else if inaccessible_sugg.is_empty() {
3200+
suggest_for_access(err, msg, accessible_sugg);
3201+
} else {
3202+
suggest_for_access(err, msg.clone(), accessible_sugg);
3203+
suggest_for_privacy(err, msg, inaccessible_sugg);
3204+
}
3205+
});
31333206

3134-
self.suggest_use_candidates(err, msg, candidates);
31353207
if let Some(did) = edition_fix {
31363208
err.note(format!(
31373209
"'{}' 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
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
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
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)