Skip to content

Commit 3c7465b

Browse files
committed
avoid overlapping privacy suggestion for single nested imports
1 parent ae9d7b0 commit 3c7465b

File tree

7 files changed

+114
-45
lines changed

7 files changed

+114
-45
lines changed

compiler/rustc_resolve/src/diagnostics.rs

+45-24
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
474474

475475
pub(crate) fn lint_if_path_starts_with_module(
476476
&mut self,
477-
finalize: Option<Finalize>,
477+
finalize: Option<Finalize<'_>>,
478478
path: &[Segment],
479479
second_binding: Option<NameBinding<'_>>,
480480
) {
@@ -1694,7 +1694,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
16941694
}
16951695

16961696
fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'a>) {
1697-
let PrivacyError { ident, binding, outermost_res, parent_scope, dedup_span } =
1697+
let PrivacyError { ident, binding, outermost_res, parent_scope, imported, dedup_span } =
16981698
*privacy_error;
16991699

17001700
let res = binding.res();
@@ -1733,7 +1733,11 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
17331733
&import_suggestions,
17341734
Instead::Yes,
17351735
FoundUse::Yes,
1736-
DiagnosticMode::Import,
1736+
DiagnosticMode::Import {
1737+
single_nested: imported.is_some_and(|imported| {
1738+
matches!(imported.kind, ImportKind::Single { nested: true, .. })
1739+
}),
1740+
},
17371741
vec![],
17381742
"",
17391743
);
@@ -2659,7 +2663,10 @@ pub(crate) enum DiagnosticMode {
26592663
/// The binding is part of a pattern
26602664
Pattern,
26612665
/// The binding is part of a use statement
2662-
Import,
2666+
Import {
2667+
/// Is the format `use a::{b,c}`
2668+
single_nested: bool,
2669+
},
26632670
}
26642671

26652672
pub(crate) fn import_candidates(
@@ -2684,6 +2691,8 @@ pub(crate) fn import_candidates(
26842691
);
26852692
}
26862693

2694+
type PathString<'a> = (String, &'a str, Option<DefId>, &'a Option<String>, bool);
2695+
26872696
/// When an entity with a given name is not available in scope, we search for
26882697
/// entities with that name in all crates. This method allows outputting the
26892698
/// results of this search in a programmer-friendly way. If any entities are
@@ -2704,10 +2713,8 @@ fn show_candidates(
27042713
return false;
27052714
}
27062715

2707-
let mut accessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>, bool)> =
2708-
Vec::new();
2709-
let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>, &Option<String>, bool)> =
2710-
Vec::new();
2716+
let mut accessible_path_strings: Vec<PathString<'_>> = Vec::new();
2717+
let mut inaccessible_path_strings: Vec<PathString<'_>> = Vec::new();
27112718

27122719
candidates.iter().for_each(|c| {
27132720
if c.accessible {
@@ -2769,6 +2776,15 @@ fn show_candidates(
27692776
err.note(note.clone());
27702777
}
27712778

2779+
let append_candidates = |msg: &mut String, accessible_path_strings: Vec<PathString<'_>>| {
2780+
msg.push(':');
2781+
2782+
for candidate in accessible_path_strings {
2783+
msg.push('\n');
2784+
msg.push_str(&candidate.0);
2785+
}
2786+
};
2787+
27722788
if let Some(span) = use_placement_span {
27732789
let (add_use, trailing) = match mode {
27742790
DiagnosticMode::Pattern => {
@@ -2780,7 +2796,7 @@ fn show_candidates(
27802796
);
27812797
return true;
27822798
}
2783-
DiagnosticMode::Import => ("", ""),
2799+
DiagnosticMode::Import { .. } => ("", ""),
27842800
DiagnosticMode::Normal => ("use ", ";\n"),
27852801
};
27862802
for candidate in &mut accessible_path_strings {
@@ -2797,13 +2813,22 @@ fn show_candidates(
27972813
format!("{add_use}{}{append}{trailing}{additional_newline}", &candidate.0);
27982814
}
27992815

2800-
err.span_suggestions_with_style(
2801-
span,
2802-
msg,
2803-
accessible_path_strings.into_iter().map(|a| a.0),
2804-
Applicability::MaybeIncorrect,
2805-
SuggestionStyle::ShowAlways,
2806-
);
2816+
match mode {
2817+
DiagnosticMode::Import { single_nested: true, .. } => {
2818+
append_candidates(&mut msg, accessible_path_strings);
2819+
err.span_help(span, msg);
2820+
}
2821+
_ => {
2822+
err.span_suggestions_with_style(
2823+
span,
2824+
msg,
2825+
accessible_path_strings.into_iter().map(|a| a.0),
2826+
Applicability::MaybeIncorrect,
2827+
SuggestionStyle::ShowAlways,
2828+
);
2829+
}
2830+
}
2831+
28072832
if let [first, .., last] = &path[..] {
28082833
let sp = first.ident.span.until(last.ident.span);
28092834
// Our suggestion is empty, so make sure the span is not empty (or we'd ICE).
@@ -2818,17 +2843,13 @@ fn show_candidates(
28182843
}
28192844
}
28202845
} else {
2821-
msg.push(':');
2822-
2823-
for candidate in accessible_path_strings {
2824-
msg.push('\n');
2825-
msg.push_str(&candidate.0);
2826-
}
2827-
2846+
append_candidates(&mut msg, accessible_path_strings);
28282847
err.help(msg);
28292848
}
28302849
true
2831-
} else if !(inaccessible_path_strings.is_empty() || matches!(mode, DiagnosticMode::Import)) {
2850+
} else if !(inaccessible_path_strings.is_empty()
2851+
|| matches!(mode, DiagnosticMode::Import { .. }))
2852+
{
28322853
let prefix = if let DiagnosticMode::Pattern = mode {
28332854
"you might have meant to match on "
28342855
} else {

compiler/rustc_resolve/src/ident.rs

+10-9
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
276276
mut ident: Ident,
277277
ns: Namespace,
278278
parent_scope: &ParentScope<'a>,
279-
finalize: Option<Finalize>,
279+
finalize: Option<Finalize<'a>>,
280280
ribs: &[Rib<'a>],
281281
ignore_binding: Option<NameBinding<'a>>,
282282
) -> Option<LexicalScopeBinding<'a>> {
@@ -370,7 +370,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
370370
orig_ident: Ident,
371371
scope_set: ScopeSet<'a>,
372372
parent_scope: &ParentScope<'a>,
373-
finalize: Option<Finalize>,
373+
finalize: Option<Finalize<'a>>,
374374
force: bool,
375375
ignore_binding: Option<NameBinding<'a>>,
376376
) -> Result<NameBinding<'a>, Determinacy> {
@@ -711,7 +711,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
711711
ident: Ident,
712712
ns: Namespace,
713713
parent_scope: &ParentScope<'a>,
714-
finalize: Option<Finalize>,
714+
finalize: Option<Finalize<'a>>,
715715
ignore_binding: Option<NameBinding<'a>>,
716716
) -> Result<NameBinding<'a>, Determinacy> {
717717
self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize, ignore_binding)
@@ -725,7 +725,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
725725
mut ident: Ident,
726726
ns: Namespace,
727727
parent_scope: &ParentScope<'a>,
728-
finalize: Option<Finalize>,
728+
finalize: Option<Finalize<'a>>,
729729
ignore_binding: Option<NameBinding<'a>>,
730730
) -> Result<NameBinding<'a>, (Determinacy, Weak)> {
731731
let tmp_parent_scope;
@@ -763,7 +763,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
763763
ident: Ident,
764764
ns: Namespace,
765765
parent_scope: &ParentScope<'a>,
766-
finalize: Option<Finalize>,
766+
finalize: Option<Finalize<'a>>,
767767
ignore_binding: Option<NameBinding<'a>>,
768768
) -> Result<NameBinding<'a>, Determinacy> {
769769
self.resolve_ident_in_module_unadjusted_ext(
@@ -788,7 +788,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
788788
ns: Namespace,
789789
parent_scope: &ParentScope<'a>,
790790
restricted_shadowing: bool,
791-
finalize: Option<Finalize>,
791+
finalize: Option<Finalize<'a>>,
792792
// This binding should be ignored during in-module resolution, so that we don't get
793793
// "self-confirming" import resolutions during import validation and checking.
794794
ignore_binding: Option<NameBinding<'a>>,
@@ -857,7 +857,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
857857
.into_iter()
858858
.find_map(|binding| if binding == ignore_binding { None } else { binding });
859859

860-
if let Some(Finalize { path_span, report_private, .. }) = finalize {
860+
if let Some(Finalize { path_span, report_private, imported, .. }) = finalize {
861861
let Some(binding) = binding else {
862862
return Err((Determined, Weak::No));
863863
};
@@ -870,6 +870,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
870870
dedup_span: path_span,
871871
outermost_res: None,
872872
parent_scope: *parent_scope,
873+
imported,
873874
});
874875
} else {
875876
return Err((Determined, Weak::No));
@@ -1332,7 +1333,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
13321333
path: &[Segment],
13331334
opt_ns: Option<Namespace>, // `None` indicates a module path in import
13341335
parent_scope: &ParentScope<'a>,
1335-
finalize: Option<Finalize>,
1336+
finalize: Option<Finalize<'a>>,
13361337
ignore_binding: Option<NameBinding<'a>>,
13371338
) -> PathResult<'a> {
13381339
self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, ignore_binding)
@@ -1343,7 +1344,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
13431344
path: &[Segment],
13441345
opt_ns: Option<Namespace>, // `None` indicates a module path in import
13451346
parent_scope: &ParentScope<'a>,
1346-
finalize: Option<Finalize>,
1347+
finalize: Option<Finalize<'a>>,
13471348
ribs: Option<&PerNS<Vec<Rib<'a>>>>,
13481349
ignore_binding: Option<NameBinding<'a>>,
13491350
) -> PathResult<'a> {

compiler/rustc_resolve/src/imports.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
716716
&mut diag,
717717
Some(err.span),
718718
candidates,
719-
DiagnosticMode::Import,
719+
DiagnosticMode::Import { single_nested: false },
720720
(source != target)
721721
.then(|| format!(" as {target}"))
722722
.as_deref()
@@ -857,7 +857,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
857857
_ => None,
858858
};
859859
let prev_ambiguity_errors_len = self.ambiguity_errors.len();
860-
let finalize = Finalize::with_root_span(import.root_id, import.span, import.root_span);
860+
let finalize = Finalize::with_imported(import);
861861

862862
// We'll provide more context to the privacy errors later, up to `len`.
863863
let privacy_errors_len = self.privacy_errors.len();

compiler/rustc_resolve/src/late.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1324,7 +1324,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
13241324
&mut self,
13251325
ident: Ident,
13261326
ns: Namespace,
1327-
finalize: Option<Finalize>,
1327+
finalize: Option<Finalize<'a>>,
13281328
ignore_binding: Option<NameBinding<'a>>,
13291329
) -> Option<LexicalScopeBinding<'a>> {
13301330
self.r.resolve_ident_in_lexical_scope(
@@ -1341,7 +1341,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
13411341
&mut self,
13421342
path: &[Segment],
13431343
opt_ns: Option<Namespace>, // `None` indicates a module path in import
1344-
finalize: Option<Finalize>,
1344+
finalize: Option<Finalize<'a>>,
13451345
) -> PathResult<'a> {
13461346
self.r.resolve_path_with_ribs(
13471347
path,
@@ -3725,7 +3725,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
37253725
qself: &Option<P<QSelf>>,
37263726
path: &[Segment],
37273727
source: PathSource<'ast>,
3728-
finalize: Finalize,
3728+
finalize: Finalize<'a>,
37293729
record_partial_res: RecordPartialRes,
37303730
) -> PartialRes {
37313731
let ns = source.namespace();
@@ -3982,7 +3982,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
39823982
primary_ns: Namespace,
39833983
span: Span,
39843984
defer_to_typeck: bool,
3985-
finalize: Finalize,
3985+
finalize: Finalize<'a>,
39863986
) -> Result<Option<PartialRes>, Spanned<ResolutionError<'a>>> {
39873987
let mut fin_res = None;
39883988

@@ -4024,7 +4024,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
40244024
qself: &Option<P<QSelf>>,
40254025
path: &[Segment],
40264026
ns: Namespace,
4027-
finalize: Finalize,
4027+
finalize: Finalize<'a>,
40284028
) -> Result<Option<PartialRes>, Spanned<ResolutionError<'a>>> {
40294029
debug!(
40304030
"resolve_qpath(qself={:?}, path={:?}, ns={:?}, finalize={:?})",

compiler/rustc_resolve/src/lib.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,7 @@ struct PrivacyError<'a> {
712712
dedup_span: Span,
713713
outermost_res: Option<(Res, Ident)>,
714714
parent_scope: ParentScope<'a>,
715+
imported: Option<Import<'a>>,
715716
}
716717

717718
#[derive(Debug)]
@@ -2164,7 +2165,7 @@ fn module_to_string(module: Module<'_>) -> Option<String> {
21642165
}
21652166

21662167
#[derive(Copy, Clone, Debug)]
2167-
struct Finalize {
2168+
struct Finalize<'a> {
21682169
/// Node ID for linting.
21692170
node_id: NodeId,
21702171
/// Span of the whole path or some its characteristic fragment.
@@ -2176,15 +2177,26 @@ struct Finalize {
21762177
/// Whether to report privacy errors or silently return "no resolution" for them,
21772178
/// similarly to speculative resolution.
21782179
report_private: bool,
2180+
imported: Option<Import<'a>>,
21792181
}
21802182

2181-
impl Finalize {
2182-
fn new(node_id: NodeId, path_span: Span) -> Finalize {
2183+
impl<'a> Finalize<'a> {
2184+
fn new(node_id: NodeId, path_span: Span) -> Finalize<'a> {
21832185
Finalize::with_root_span(node_id, path_span, path_span)
21842186
}
21852187

2186-
fn with_root_span(node_id: NodeId, path_span: Span, root_span: Span) -> Finalize {
2187-
Finalize { node_id, path_span, root_span, report_private: true }
2188+
fn with_root_span(node_id: NodeId, path_span: Span, root_span: Span) -> Finalize<'a> {
2189+
Finalize { node_id, path_span, root_span, report_private: true, imported: None }
2190+
}
2191+
2192+
fn with_imported(imported: Import<'a>) -> Finalize<'a> {
2193+
Finalize {
2194+
node_id: imported.root_id,
2195+
path_span: imported.span,
2196+
root_span: imported.root_span,
2197+
report_private: true,
2198+
imported: Some(imported),
2199+
}
21882200
}
21892201
}
21902202

tests/ui/imports/issue-114884.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
mod mod1 {
2+
pub trait TraitA {}
3+
}
4+
5+
mod mod2 {
6+
mod sub_mod {
7+
use super::super::mod1::TraitA;
8+
}
9+
}
10+
11+
use mod2::{sub_mod::TraitA};
12+
//~^ ERROR: module `sub_mod` is private
13+
14+
fn main() {}

tests/ui/imports/issue-114884.stderr

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0603]: module `sub_mod` is private
2+
--> $DIR/issue-114884.rs:11:12
3+
|
4+
LL | use mod2::{sub_mod::TraitA};
5+
| ^^^^^^^ private module
6+
|
7+
help: consider importing this trait instead:
8+
mod1::TraitA
9+
--> $DIR/issue-114884.rs:11:12
10+
|
11+
LL | use mod2::{sub_mod::TraitA};
12+
| ^^^^^^^^^^^^^^^
13+
note: the module `sub_mod` is defined here
14+
--> $DIR/issue-114884.rs:6:5
15+
|
16+
LL | mod sub_mod {
17+
| ^^^^^^^^^^^
18+
19+
error: aborting due to 1 previous error
20+
21+
For more information about this error, try `rustc --explain E0603`.

0 commit comments

Comments
 (0)