Skip to content

Commit 684a371

Browse files
author
Yuki Okushi
authored
Rollup merge of #106175 - compiler-errors:bad-import-sugg, r=oli-obk
Fix bad import suggestion with nested `use` tree Fixes #105566 Fixes #105373 Ideally, we'd find some way to turn these into structured suggestions -- perhaps on a separate line as a different `use` statement, but I have no idea how to access the span for the whole `use` from this point in the import resolution code.
2 parents 5773e8b + 1d66a67 commit 684a371

8 files changed

+167
-43
lines changed

compiler/rustc_resolve/src/diagnostics.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ impl<'a> Resolver<'a> {
161161
found_use,
162162
DiagnosticMode::Normal,
163163
path,
164+
"",
164165
);
165166
err.emit();
166167
} else if let Some((span, msg, sugg, appl)) = suggestion {
@@ -690,6 +691,7 @@ impl<'a> Resolver<'a> {
690691
FoundUse::Yes,
691692
DiagnosticMode::Pattern,
692693
vec![],
694+
"",
693695
);
694696
}
695697
err
@@ -1344,6 +1346,7 @@ impl<'a> Resolver<'a> {
13441346
FoundUse::Yes,
13451347
DiagnosticMode::Normal,
13461348
vec![],
1349+
"",
13471350
);
13481351

13491352
if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
@@ -2309,7 +2312,7 @@ enum FoundUse {
23092312
}
23102313

23112314
/// Whether a binding is part of a pattern or a use statement. Used for diagnostics.
2312-
enum DiagnosticMode {
2315+
pub(crate) enum DiagnosticMode {
23132316
Normal,
23142317
/// The binding is part of a pattern
23152318
Pattern,
@@ -2324,6 +2327,8 @@ pub(crate) fn import_candidates(
23242327
// This is `None` if all placement locations are inside expansions
23252328
use_placement_span: Option<Span>,
23262329
candidates: &[ImportSuggestion],
2330+
mode: DiagnosticMode,
2331+
append: &str,
23272332
) {
23282333
show_candidates(
23292334
session,
@@ -2333,8 +2338,9 @@ pub(crate) fn import_candidates(
23332338
candidates,
23342339
Instead::Yes,
23352340
FoundUse::Yes,
2336-
DiagnosticMode::Import,
2341+
mode,
23372342
vec![],
2343+
append,
23382344
);
23392345
}
23402346

@@ -2352,6 +2358,7 @@ fn show_candidates(
23522358
found_use: FoundUse,
23532359
mode: DiagnosticMode,
23542360
path: Vec<Segment>,
2361+
append: &str,
23552362
) {
23562363
if candidates.is_empty() {
23572364
return;
@@ -2416,7 +2423,7 @@ fn show_candidates(
24162423
// produce an additional newline to separate the new use statement
24172424
// from the directly following item.
24182425
let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" };
2419-
candidate.0 = format!("{}{};\n{}", add_use, &candidate.0, additional_newline);
2426+
candidate.0 = format!("{add_use}{}{append};\n{additional_newline}", &candidate.0);
24202427
}
24212428

24222429
err.span_suggestions(

compiler/rustc_resolve/src/imports.rs

+55-26
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! A bunch of methods and structures more or less related to resolving imports.
22
3-
use crate::diagnostics::{import_candidates, Suggestion};
3+
use crate::diagnostics::{import_candidates, DiagnosticMode, Suggestion};
44
use crate::Determinacy::{self, *};
55
use crate::Namespace::*;
66
use crate::{module_to_string, names_to_string, ImportSuggestion};
@@ -402,7 +402,7 @@ struct UnresolvedImportError {
402402
label: Option<String>,
403403
note: Option<String>,
404404
suggestion: Option<Suggestion>,
405-
candidate: Option<Vec<ImportSuggestion>>,
405+
candidates: Option<Vec<ImportSuggestion>>,
406406
}
407407

408408
pub struct ImportResolver<'a, 'b> {
@@ -475,12 +475,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
475475
errors = vec![];
476476
}
477477
if seen_spans.insert(err.span) {
478-
let path = import_path_to_string(
479-
&import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(),
480-
&import.kind,
481-
err.span,
482-
);
483-
errors.push((path, err));
478+
errors.push((import, err));
484479
prev_root_id = import.root_id;
485480
}
486481
} else if is_indeterminate {
@@ -494,10 +489,12 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
494489
label: None,
495490
note: None,
496491
suggestion: None,
497-
candidate: None,
492+
candidates: None,
498493
};
494+
// FIXME: there should be a better way of doing this than
495+
// formatting this as a string then checking for `::`
499496
if path.contains("::") {
500-
errors.push((path, err))
497+
errors.push((import, err))
501498
}
502499
}
503500
}
@@ -507,7 +504,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
507504
}
508505
}
509506

510-
fn throw_unresolved_import_error(&self, errors: Vec<(String, UnresolvedImportError)>) {
507+
fn throw_unresolved_import_error(&self, errors: Vec<(&Import<'_>, UnresolvedImportError)>) {
511508
if errors.is_empty() {
512509
return;
513510
}
@@ -516,7 +513,17 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
516513
const MAX_LABEL_COUNT: usize = 10;
517514

518515
let span = MultiSpan::from_spans(errors.iter().map(|(_, err)| err.span).collect());
519-
let paths = errors.iter().map(|(path, _)| format!("`{}`", path)).collect::<Vec<_>>();
516+
let paths = errors
517+
.iter()
518+
.map(|(import, err)| {
519+
let path = import_path_to_string(
520+
&import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(),
521+
&import.kind,
522+
err.span,
523+
);
524+
format!("`{path}`")
525+
})
526+
.collect::<Vec<_>>();
520527
let msg = format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),);
521528

522529
let mut diag = struct_span_err!(self.r.session, span, E0432, "{}", &msg);
@@ -525,7 +532,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
525532
diag.note(note);
526533
}
527534

528-
for (_, err) in errors.into_iter().take(MAX_LABEL_COUNT) {
535+
for (import, err) in errors.into_iter().take(MAX_LABEL_COUNT) {
529536
if let Some(label) = err.label {
530537
diag.span_label(err.span, label);
531538
}
@@ -538,14 +545,36 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
538545
diag.multipart_suggestion(&msg, suggestions, applicability);
539546
}
540547

541-
if let Some(candidate) = &err.candidate {
542-
import_candidates(
543-
self.r.session,
544-
&self.r.untracked.source_span,
545-
&mut diag,
546-
Some(err.span),
547-
&candidate,
548-
)
548+
if let Some(candidates) = &err.candidates {
549+
match &import.kind {
550+
ImportKind::Single { nested: false, source, target, .. } => import_candidates(
551+
self.r.session,
552+
&self.r.untracked.source_span,
553+
&mut diag,
554+
Some(err.span),
555+
&candidates,
556+
DiagnosticMode::Import,
557+
(source != target)
558+
.then(|| format!(" as {target}"))
559+
.as_deref()
560+
.unwrap_or(""),
561+
),
562+
ImportKind::Single { nested: true, source, target, .. } => {
563+
import_candidates(
564+
self.r.session,
565+
&self.r.untracked.source_span,
566+
&mut diag,
567+
None,
568+
&candidates,
569+
DiagnosticMode::Normal,
570+
(source != target)
571+
.then(|| format!(" as {target}"))
572+
.as_deref()
573+
.unwrap_or(""),
574+
);
575+
}
576+
_ => {}
577+
}
549578
}
550579
}
551580

@@ -707,14 +736,14 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
707736
String::from("a similar path exists"),
708737
Applicability::MaybeIncorrect,
709738
)),
710-
candidate: None,
739+
candidates: None,
711740
},
712741
None => UnresolvedImportError {
713742
span,
714743
label: Some(label),
715744
note: None,
716745
suggestion,
717-
candidate: None,
746+
candidates: None,
718747
},
719748
};
720749
return Some(err);
@@ -761,7 +790,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
761790
)),
762791
note: None,
763792
suggestion: None,
764-
candidate: None,
793+
candidates: None,
765794
});
766795
}
767796
}
@@ -873,7 +902,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
873902
let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter());
874903
let names = resolutions
875904
.filter_map(|(BindingKey { ident: i, .. }, resolution)| {
876-
if *i == ident {
905+
if i.name == ident.name {
877906
return None;
878907
} // Never suggest the same name
879908
match *resolution.borrow() {
@@ -943,7 +972,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
943972
label: Some(label),
944973
note,
945974
suggestion,
946-
candidate: if !parent_suggestion.is_empty() {
975+
candidates: if !parent_suggestion.is_empty() {
947976
Some(parent_suggestion)
948977
} else {
949978
None

src/test/ui/hygiene/extern-prelude-from-opaque-fail.stderr

+1-4
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ error[E0432]: unresolved import `my_core`
22
--> $DIR/extern-prelude-from-opaque-fail.rs:20:9
33
|
44
LL | use my_core;
5-
| ^^^^^^^
6-
| |
7-
| no `my_core` in the root
8-
| help: a similar name exists in the module: `my_core`
5+
| ^^^^^^^ no `my_core` in the root
96

107
error[E0432]: unresolved import `my_core`
118
--> $DIR/extern-prelude-from-opaque-fail.rs:7:13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// edition: 2021
2+
3+
#![allow(unused)]
4+
5+
mod A {
6+
pub(crate) type AA = ();
7+
pub(crate) type BB = ();
8+
9+
mod A2 {
10+
use super::{super::C::D::AA, AA as _};
11+
//~^ ERROR unresolved import
12+
}
13+
}
14+
15+
mod C {
16+
pub mod D {}
17+
}
18+
19+
mod B {
20+
use crate::C::{self, AA};
21+
//~^ ERROR unresolved import
22+
23+
use crate::{A, C::BB};
24+
//~^ ERROR unresolved import
25+
}
26+
27+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
error[E0432]: unresolved import `super::super::C::D::AA`
2+
--> $DIR/bad-import-in-nested.rs:10:21
3+
|
4+
LL | use super::{super::C::D::AA, AA as _};
5+
| ^^^^^^^^^^^^^^^ no `AA` in `C::D`
6+
|
7+
= note: consider importing this type alias instead:
8+
crate::A::AA
9+
10+
error[E0432]: unresolved import `crate::C::AA`
11+
--> $DIR/bad-import-in-nested.rs:20:26
12+
|
13+
LL | use crate::C::{self, AA};
14+
| ^^ no `AA` in `C`
15+
|
16+
= note: consider importing this type alias instead:
17+
crate::A::AA
18+
19+
error[E0432]: unresolved import `crate::C::BB`
20+
--> $DIR/bad-import-in-nested.rs:23:20
21+
|
22+
LL | use crate::{A, C::BB};
23+
| ^^^^^ no `BB` in `C`
24+
|
25+
= note: consider importing this type alias instead:
26+
crate::A::BB
27+
28+
error: aborting due to 3 previous errors
29+
30+
For more information about this error, try `rustc --explain E0432`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
mod A {
2+
pub type B = ();
3+
pub type B2 = ();
4+
}
5+
6+
mod C {
7+
use crate::D::B as _;
8+
//~^ ERROR unresolved import `crate::D::B`
9+
10+
use crate::D::B2;
11+
//~^ ERROR unresolved import `crate::D::B2`
12+
}
13+
14+
mod D {}
15+
16+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0432]: unresolved import `crate::D::B`
2+
--> $DIR/bad-import-with-rename.rs:7:9
3+
|
4+
LL | use crate::D::B as _;
5+
| ^^^^^^^^^^^^^^^^ no `B` in `D`
6+
|
7+
help: consider importing this type alias instead
8+
|
9+
LL | use A::B as _;
10+
| ~~~~~~~~~~
11+
12+
error[E0432]: unresolved import `crate::D::B2`
13+
--> $DIR/bad-import-with-rename.rs:10:9
14+
|
15+
LL | use crate::D::B2;
16+
| ^^^^^^^^^^^^ no `B2` in `D`
17+
|
18+
help: consider importing this type alias instead
19+
|
20+
LL | use A::B2;
21+
| ~~~~~~
22+
23+
error: aborting due to 2 previous errors
24+
25+
For more information about this error, try `rustc --explain E0432`.

src/test/ui/test-attrs/inaccessible-test-modules.stderr

+3-10
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,18 @@ error[E0432]: unresolved import `main`
22
--> $DIR/inaccessible-test-modules.rs:5:5
33
|
44
LL | use main as x;
5-
| ----^^^^^
6-
| |
7-
| no `main` in the root
8-
| help: a similar name exists in the module: `main`
5+
| ^^^^^^^^^ no `main` in the root
96

107
error[E0432]: unresolved import `test`
118
--> $DIR/inaccessible-test-modules.rs:6:5
129
|
1310
LL | use test as y;
1411
| ^^^^^^^^^ no `test` in the root
1512
|
16-
help: a similar name exists in the module
17-
|
18-
LL | use test as y;
19-
| ~~~~
2013
help: consider importing this module instead
2114
|
22-
LL | use test::test;
23-
| ~~~~~~~~~~~
15+
LL | use test::test as y;
16+
| ~~~~~~~~~~~~~~~~
2417

2518
error: aborting due to 2 previous errors
2619

0 commit comments

Comments
 (0)