Skip to content

Commit 9593e61

Browse files
authored
Rollup merge of #88838 - FabianWolff:issue-88472, r=estebank
Do not suggest importing inaccessible items Fixes #88472. For this example: ```rust mod a { struct Foo; } mod b { type Bar = Foo; } ``` rustc currently emits: ``` error[E0412]: cannot find type `Foo` in this scope --> test.rs:6:16 | 6 | type Bar = Foo; | ^^^ not found in this scope | help: consider importing this struct | 6 | use a::Foo; | ``` this is incorrect, as applying this suggestion leads to ``` error[E0603]: struct `Foo` is private --> test.rs:6:12 | 6 | use a::Foo; | ^^^ private struct | note: the struct `Foo` is defined here --> test.rs:2:5 | 2 | struct Foo; | ^^^^^^^^^^^ ``` With my changes, I get: ``` error[E0412]: cannot find type `Foo` in this scope --> test.rs:6:16 | 6 | type Bar = Foo; | ^^^ not found in this scope | = note: this struct exists but is inaccessible: a::Foo ``` As for the wildcard mentioned in #88472, I would argue that the warning is actually correct, since the import _is_ unused. I think the real issue is the wrong suggestion, which I have fixed here.
2 parents 598d89b + 750018e commit 9593e61

11 files changed

+264
-112
lines changed

compiler/rustc_resolve/src/diagnostics.rs

+109-31
Original file line numberDiff line numberDiff line change
@@ -973,7 +973,15 @@ impl<'a> Resolver<'a> {
973973

974974
let import_suggestions =
975975
self.lookup_import_candidates(ident, Namespace::MacroNS, parent_scope, is_expected);
976-
show_candidates(err, None, &import_suggestions, false, true);
976+
show_candidates(
977+
&self.definitions,
978+
self.session,
979+
err,
980+
None,
981+
&import_suggestions,
982+
false,
983+
true,
984+
);
977985

978986
if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
979987
let msg = format!("unsafe traits like `{}` should be implemented explicitly", ident);
@@ -1713,6 +1721,8 @@ fn find_span_immediately_after_crate_name(
17131721
/// entities with that name in all crates. This method allows outputting the
17141722
/// results of this search in a programmer-friendly way
17151723
crate fn show_candidates(
1724+
definitions: &rustc_hir::definitions::Definitions,
1725+
session: &Session,
17161726
err: &mut DiagnosticBuilder<'_>,
17171727
// This is `None` if all placement locations are inside expansions
17181728
use_placement_span: Option<Span>,
@@ -1724,43 +1734,111 @@ crate fn show_candidates(
17241734
return;
17251735
}
17261736

1737+
let mut accessible_path_strings: Vec<(String, &str, Option<DefId>)> = Vec::new();
1738+
let mut inaccessible_path_strings: Vec<(String, &str, Option<DefId>)> = Vec::new();
1739+
1740+
candidates.iter().for_each(|c| {
1741+
(if c.accessible { &mut accessible_path_strings } else { &mut inaccessible_path_strings })
1742+
.push((path_names_to_string(&c.path), c.descr, c.did))
1743+
});
1744+
17271745
// we want consistent results across executions, but candidates are produced
17281746
// by iterating through a hash map, so make sure they are ordered:
1729-
let mut path_strings: Vec<_> =
1730-
candidates.iter().map(|c| path_names_to_string(&c.path)).collect();
1747+
for path_strings in [&mut accessible_path_strings, &mut inaccessible_path_strings] {
1748+
path_strings.sort_by(|a, b| a.0.cmp(&b.0));
1749+
let core_path_strings =
1750+
path_strings.drain_filter(|p| p.0.starts_with("core::")).collect::<Vec<_>>();
1751+
path_strings.extend(core_path_strings);
1752+
path_strings.dedup_by(|a, b| a.0 == b.0);
1753+
}
1754+
1755+
if !accessible_path_strings.is_empty() {
1756+
let (determiner, kind) = if accessible_path_strings.len() == 1 {
1757+
("this", accessible_path_strings[0].1)
1758+
} else {
1759+
("one of these", "items")
1760+
};
17311761

1732-
path_strings.sort();
1733-
let core_path_strings =
1734-
path_strings.drain_filter(|p| p.starts_with("core::")).collect::<Vec<String>>();
1735-
path_strings.extend(core_path_strings);
1736-
path_strings.dedup();
1762+
let instead = if instead { " instead" } else { "" };
1763+
let mut msg = format!("consider importing {} {}{}", determiner, kind, instead);
17371764

1738-
let (determiner, kind) = if candidates.len() == 1 {
1739-
("this", candidates[0].descr)
1740-
} else {
1741-
("one of these", "items")
1742-
};
1743-
1744-
let instead = if instead { " instead" } else { "" };
1745-
let mut msg = format!("consider importing {} {}{}", determiner, kind, instead);
1746-
1747-
if let Some(span) = use_placement_span {
1748-
for candidate in &mut path_strings {
1749-
// produce an additional newline to separate the new use statement
1750-
// from the directly following item.
1751-
let additional_newline = if found_use { "" } else { "\n" };
1752-
*candidate = format!("use {};\n{}", candidate, additional_newline);
1753-
}
1765+
if let Some(span) = use_placement_span {
1766+
for candidate in &mut accessible_path_strings {
1767+
// produce an additional newline to separate the new use statement
1768+
// from the directly following item.
1769+
let additional_newline = if found_use { "" } else { "\n" };
1770+
candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline);
1771+
}
17541772

1755-
err.span_suggestions(span, &msg, path_strings.into_iter(), Applicability::Unspecified);
1756-
} else {
1757-
msg.push(':');
1773+
err.span_suggestions(
1774+
span,
1775+
&msg,
1776+
accessible_path_strings.into_iter().map(|a| a.0),
1777+
Applicability::Unspecified,
1778+
);
1779+
} else {
1780+
msg.push(':');
17581781

1759-
for candidate in path_strings {
1760-
msg.push('\n');
1761-
msg.push_str(&candidate);
1782+
for candidate in accessible_path_strings {
1783+
msg.push('\n');
1784+
msg.push_str(&candidate.0);
1785+
}
1786+
1787+
err.note(&msg);
17621788
}
1789+
} else {
1790+
assert!(!inaccessible_path_strings.is_empty());
1791+
1792+
if inaccessible_path_strings.len() == 1 {
1793+
let (name, descr, def_id) = &inaccessible_path_strings[0];
1794+
let msg = format!("{} `{}` exists but is inaccessible", descr, name);
1795+
1796+
if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
1797+
let span = definitions.def_span(local_def_id);
1798+
let span = session.source_map().guess_head_span(span);
1799+
let mut multi_span = MultiSpan::from_span(span);
1800+
multi_span.push_span_label(span, "not accessible".to_string());
1801+
err.span_note(multi_span, &msg);
1802+
} else {
1803+
err.note(&msg);
1804+
}
1805+
} else {
1806+
let (_, descr_first, _) = &inaccessible_path_strings[0];
1807+
let descr = if inaccessible_path_strings
1808+
.iter()
1809+
.skip(1)
1810+
.all(|(_, descr, _)| descr == descr_first)
1811+
{
1812+
format!("{}", descr_first)
1813+
} else {
1814+
"item".to_string()
1815+
};
1816+
1817+
let mut msg = format!("these {}s exist but are inaccessible", descr);
1818+
let mut has_colon = false;
17631819

1764-
err.note(&msg);
1820+
let mut spans = Vec::new();
1821+
for (name, _, def_id) in &inaccessible_path_strings {
1822+
if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
1823+
let span = definitions.def_span(local_def_id);
1824+
let span = session.source_map().guess_head_span(span);
1825+
spans.push((name, span));
1826+
} else {
1827+
if !has_colon {
1828+
msg.push(':');
1829+
has_colon = true;
1830+
}
1831+
msg.push('\n');
1832+
msg.push_str(name);
1833+
}
1834+
}
1835+
1836+
let mut multi_span = MultiSpan::from_spans(spans.iter().map(|(_, sp)| *sp).collect());
1837+
for (name, span) in spans {
1838+
multi_span.push_span_label(span, format!("`{}`: not accessible", name));
1839+
}
1840+
1841+
err.span_note(multi_span, &msg);
1842+
}
17651843
}
17661844
}

compiler/rustc_resolve/src/lib.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -2969,7 +2969,15 @@ impl<'a> Resolver<'a> {
29692969
(None, false)
29702970
};
29712971
if !candidates.is_empty() {
2972-
diagnostics::show_candidates(&mut err, span, &candidates, instead, found_use);
2972+
diagnostics::show_candidates(
2973+
&self.definitions,
2974+
self.session,
2975+
&mut err,
2976+
span,
2977+
&candidates,
2978+
instead,
2979+
found_use,
2980+
);
29732981
} else if let Some((span, msg, sugg, appl)) = suggestion {
29742982
err.span_suggestion(span, msg, sugg, appl);
29752983
}

src/test/ui/hygiene/globs.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0425]: cannot find function `f` in this scope
44
LL | f();
55
| ^ not found in this scope
66
|
7-
help: consider importing one of these items
7+
help: consider importing this function
88
|
99
LL | use foo::f;
1010
|
@@ -37,7 +37,7 @@ LL | n!(f);
3737
LL | n!(f);
3838
| ^ not found in this scope
3939
|
40-
= note: consider importing one of these items:
40+
= note: consider importing this function:
4141
foo::f
4242
= note: this error originates in the macro `n` (in Nightly builds, run with -Z macro-backtrace for more info)
4343

@@ -50,7 +50,7 @@ LL | n!(f);
5050
LL | f
5151
| ^ not found in this scope
5252
|
53-
= note: consider importing one of these items:
53+
= note: consider importing this function:
5454
foo::f
5555
= note: this error originates in the macro `n` (in Nightly builds, run with -Z macro-backtrace for more info)
5656

src/test/ui/imports/glob-resolve1.stderr

+27-33
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,23 @@ error[E0425]: cannot find function `fpriv` in this scope
44
LL | fpriv();
55
| ^^^^^ not found in this scope
66
|
7-
help: consider importing this function
8-
|
9-
LL | use bar::fpriv;
7+
note: function `bar::fpriv` exists but is inaccessible
8+
--> $DIR/glob-resolve1.rs:7:5
109
|
10+
LL | fn fpriv() {}
11+
| ^^^^^^^^^^ not accessible
1112

1213
error[E0425]: cannot find function `epriv` in this scope
1314
--> $DIR/glob-resolve1.rs:27:5
1415
|
1516
LL | epriv();
1617
| ^^^^^ not found in this scope
1718
|
18-
help: consider importing this function
19-
|
20-
LL | use bar::epriv;
19+
note: function `bar::epriv` exists but is inaccessible
20+
--> $DIR/glob-resolve1.rs:9:9
2121
|
22+
LL | fn epriv();
23+
| ^^^^^^^^^^^ not accessible
2224

2325
error[E0423]: expected value, found enum `B`
2426
--> $DIR/glob-resolve1.rs:28:5
@@ -44,10 +46,11 @@ error[E0425]: cannot find value `C` in this scope
4446
LL | C;
4547
| ^ not found in this scope
4648
|
47-
help: consider importing this unit struct
48-
|
49-
LL | use bar::C;
49+
note: unit struct `bar::C` exists but is inaccessible
50+
--> $DIR/glob-resolve1.rs:18:5
5051
|
52+
LL | struct C;
53+
| ^^^^^^^^^ not accessible
5154

5255
error[E0425]: cannot find function `import` in this scope
5356
--> $DIR/glob-resolve1.rs:30:5
@@ -67,16 +70,13 @@ LL | pub enum B {
6770
| ---------- similarly named enum `B` defined here
6871
...
6972
LL | foo::<A>();
70-
| ^
71-
|
72-
help: an enum with a similar name exists
73+
| ^ help: an enum with a similar name exists: `B`
7374
|
74-
LL | foo::<B>();
75-
| ~
76-
help: consider importing this enum
77-
|
78-
LL | use bar::A;
75+
note: enum `bar::A` exists but is inaccessible
76+
--> $DIR/glob-resolve1.rs:11:5
7977
|
78+
LL | enum A {
79+
| ^^^^^^ not accessible
8080

8181
error[E0412]: cannot find type `C` in this scope
8282
--> $DIR/glob-resolve1.rs:33:11
@@ -85,16 +85,13 @@ LL | pub enum B {
8585
| ---------- similarly named enum `B` defined here
8686
...
8787
LL | foo::<C>();
88-
| ^
89-
|
90-
help: an enum with a similar name exists
91-
|
92-
LL | foo::<B>();
93-
| ~
94-
help: consider importing this struct
88+
| ^ help: an enum with a similar name exists: `B`
9589
|
96-
LL | use bar::C;
90+
note: struct `bar::C` exists but is inaccessible
91+
--> $DIR/glob-resolve1.rs:18:5
9792
|
93+
LL | struct C;
94+
| ^^^^^^^^^ not accessible
9895

9996
error[E0412]: cannot find type `D` in this scope
10097
--> $DIR/glob-resolve1.rs:34:11
@@ -103,16 +100,13 @@ LL | pub enum B {
103100
| ---------- similarly named enum `B` defined here
104101
...
105102
LL | foo::<D>();
106-
| ^
107-
|
108-
help: an enum with a similar name exists
109-
|
110-
LL | foo::<B>();
111-
| ~
112-
help: consider importing this type alias
103+
| ^ help: an enum with a similar name exists: `B`
113104
|
114-
LL | use bar::D;
105+
note: type alias `bar::D` exists but is inaccessible
106+
--> $DIR/glob-resolve1.rs:20:5
115107
|
108+
LL | type D = isize;
109+
| ^^^^^^^^^^^^^^^ not accessible
116110

117111
error: aborting due to 8 previous errors
118112

src/test/ui/imports/issue-4366-2.stderr

+4-3
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ error[E0412]: cannot find type `Bar` in this scope
44
LL | fn sub() -> Bar { 1 }
55
| ^^^ not found in this scope
66
|
7-
help: consider importing this type alias
8-
|
9-
LL | use a::b::Bar;
7+
note: type alias `a::b::Bar` exists but is inaccessible
8+
--> $DIR/issue-4366-2.rs:11:9
109
|
10+
LL | type Bar = isize;
11+
| ^^^^^^^^^^^^^^^^^ not accessible
1112

1213
error[E0423]: expected function, found module `foo`
1314
--> $DIR/issue-4366-2.rs:25:5

src/test/ui/resolve/issue-42944.stderr

+4-3
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ error[E0425]: cannot find function, tuple struct or tuple variant `Bx` in this s
1616
LL | Bx(());
1717
| ^^ not found in this scope
1818
|
19-
help: consider importing this tuple struct
20-
|
21-
LL | use foo::Bx;
19+
note: tuple struct `foo::Bx` exists but is inaccessible
20+
--> $DIR/issue-42944.rs:2:5
2221
|
22+
LL | pub struct Bx(());
23+
| ^^^^^^^^^^^^^^^^^^ not accessible
2324

2425
error: aborting due to 2 previous errors
2526

src/test/ui/resolve/issue-88472.rs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Regression test for #88472, where a suggestion was issued to
2+
// import an inaccessible struct.
3+
4+
#![warn(unused_imports)]
5+
//~^ NOTE: the lint level is defined here
6+
7+
mod a {
8+
struct Foo;
9+
//~^ NOTE: struct `a::Foo` exists but is inaccessible
10+
//~| NOTE: not accessible
11+
}
12+
13+
mod b {
14+
use crate::a::*;
15+
//~^ WARNING: unused import
16+
type Bar = Foo;
17+
//~^ ERROR: cannot find type `Foo` in this scope [E0412]
18+
//~| NOTE: not found in this scope
19+
}
20+
21+
mod c {
22+
enum Eee {}
23+
//~^ NOTE: these enums exist but are inaccessible
24+
//~| NOTE: `c::Eee`: not accessible
25+
26+
mod d {
27+
enum Eee {}
28+
//~^ NOTE: `c::d::Eee`: not accessible
29+
}
30+
}
31+
32+
mod e {
33+
type Baz = Eee;
34+
//~^ ERROR: cannot find type `Eee` in this scope [E0412]
35+
//~| NOTE: not found in this scope
36+
}
37+
38+
fn main() {}

0 commit comments

Comments
 (0)