Skip to content

Commit 0571b0a

Browse files
committed
suggest candidates for unresolved import
1 parent 8b0c05d commit 0571b0a

File tree

10 files changed

+145
-8
lines changed

10 files changed

+145
-8
lines changed

compiler/rustc_resolve/src/diagnostics.rs

+36-2
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ impl TypoSuggestion {
7070
}
7171

7272
/// A free importable items suggested in case of resolution failure.
73+
#[derive(Debug, Clone)]
7374
pub(crate) struct ImportSuggestion {
7475
pub did: Option<DefId>,
7576
pub descr: &'static str,
@@ -139,6 +140,7 @@ impl<'a> Resolver<'a> {
139140
if instead { Instead::Yes } else { Instead::No },
140141
found_use,
141142
IsPattern::No,
143+
IsImport::No,
142144
path,
143145
);
144146
err.emit();
@@ -698,6 +700,7 @@ impl<'a> Resolver<'a> {
698700
Instead::No,
699701
FoundUse::Yes,
700702
IsPattern::Yes,
703+
IsImport::No,
701704
vec![],
702705
);
703706
}
@@ -1481,6 +1484,7 @@ impl<'a> Resolver<'a> {
14811484
Instead::No,
14821485
FoundUse::Yes,
14831486
IsPattern::No,
1487+
IsImport::No,
14841488
vec![],
14851489
);
14861490

@@ -2449,6 +2453,34 @@ enum IsPattern {
24492453
No,
24502454
}
24512455

2456+
/// Whether a binding is part of a use statement. Used for diagnostics.
2457+
enum IsImport {
2458+
Yes,
2459+
No,
2460+
}
2461+
2462+
pub(crate) fn import_candidates(
2463+
session: &Session,
2464+
source_span: &IndexVec<LocalDefId, Span>,
2465+
err: &mut Diagnostic,
2466+
// This is `None` if all placement locations are inside expansions
2467+
use_placement_span: Option<Span>,
2468+
candidates: &[ImportSuggestion],
2469+
) {
2470+
show_candidates(
2471+
session,
2472+
source_span,
2473+
err,
2474+
use_placement_span,
2475+
candidates,
2476+
Instead::Yes,
2477+
FoundUse::Yes,
2478+
IsPattern::No,
2479+
IsImport::Yes,
2480+
vec![],
2481+
);
2482+
}
2483+
24522484
/// When an entity with a given name is not available in scope, we search for
24532485
/// entities with that name in all crates. This method allows outputting the
24542486
/// results of this search in a programmer-friendly way
@@ -2462,6 +2494,7 @@ fn show_candidates(
24622494
instead: Instead,
24632495
found_use: FoundUse,
24642496
is_pattern: IsPattern,
2497+
is_import: IsImport,
24652498
path: Vec<Segment>,
24662499
) {
24672500
if candidates.is_empty() {
@@ -2521,7 +2554,8 @@ fn show_candidates(
25212554
// produce an additional newline to separate the new use statement
25222555
// from the directly following item.
25232556
let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" };
2524-
candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline);
2557+
let add_use = if let IsImport::Yes = is_import { "" } else { "use " };
2558+
candidate.0 = format!("{}{};\n{}", add_use, &candidate.0, additional_newline);
25252559
}
25262560

25272561
err.span_suggestions(
@@ -2551,7 +2585,7 @@ fn show_candidates(
25512585

25522586
err.note(&msg);
25532587
}
2554-
} else {
2588+
} else if matches!(is_import, IsImport::No) {
25552589
assert!(!inaccessible_path_strings.is_empty());
25562590

25572591
let prefix =

compiler/rustc_resolve/src/imports.rs

+26-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//! A bunch of methods and structures more or less related to resolving imports.
22
3-
use crate::diagnostics::Suggestion;
3+
use crate::diagnostics::{import_candidates, Suggestion};
44
use crate::Determinacy::{self, *};
55
use crate::Namespace::{self, *};
6-
use crate::{module_to_string, names_to_string};
6+
use crate::{module_to_string, names_to_string, ImportSuggestion};
77
use crate::{AmbiguityKind, BindingKey, ModuleKind, ResolutionError, Resolver, Segment};
88
use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet};
99
use crate::{NameBinding, NameBindingKind, PathResult};
@@ -406,6 +406,7 @@ struct UnresolvedImportError {
406406
label: Option<String>,
407407
note: Option<String>,
408408
suggestion: Option<Suggestion>,
409+
candidate: Option<Vec<ImportSuggestion>>,
409410
}
410411

411412
pub struct ImportResolver<'a, 'b> {
@@ -497,6 +498,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
497498
label: None,
498499
note: None,
499500
suggestion: None,
501+
candidate: None,
500502
};
501503
if path.contains("::") {
502504
errors.push((path, err))
@@ -547,6 +549,16 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
547549
}
548550
diag.multipart_suggestion(&msg, suggestions, applicability);
549551
}
552+
553+
if let Some(candidate) = &err.candidate {
554+
import_candidates(
555+
self.r.session,
556+
&self.r.source_span,
557+
&mut diag,
558+
Some(err.span),
559+
&candidate,
560+
)
561+
}
550562
}
551563

552564
diag.emit();
@@ -664,6 +676,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
664676
Some(finalize),
665677
ignore_binding,
666678
);
679+
667680
let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len;
668681
import.vis.set(orig_vis);
669682
let module = match path_res {
@@ -706,12 +719,14 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
706719
String::from("a similar path exists"),
707720
Applicability::MaybeIncorrect,
708721
)),
722+
candidate: None,
709723
},
710724
None => UnresolvedImportError {
711725
span,
712726
label: Some(label),
713727
note: None,
714728
suggestion,
729+
candidate: None,
715730
},
716731
};
717732
return Some(err);
@@ -754,6 +769,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
754769
label: Some(String::from("cannot glob-import a module into itself")),
755770
note: None,
756771
suggestion: None,
772+
candidate: None,
757773
});
758774
}
759775
}
@@ -919,11 +935,19 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
919935
}
920936
};
921937

938+
let parent_suggestion =
939+
self.r.lookup_import_candidates(ident, TypeNS, &import.parent_scope, |_| true);
940+
922941
Some(UnresolvedImportError {
923942
span: import.span,
924943
label: Some(label),
925944
note,
926945
suggestion,
946+
candidate: if !parent_suggestion.is_empty() {
947+
Some(parent_suggestion)
948+
} else {
949+
None
950+
},
927951
})
928952
} else {
929953
// `resolve_ident_in_module` reported a privacy error.

src/test/ui/extenv/issue-55897.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ error[E0432]: unresolved import `env`
2626
|
2727
LL | use env;
2828
| ^^^ no `env` in the root
29+
|
30+
help: consider importing this module instead
31+
|
32+
LL | use std::env;
33+
| ~~~~~~~~~
2934

3035
error: cannot determine resolution for the macro `env`
3136
--> $DIR/issue-55897.rs:6:22

src/test/ui/imports/issue-56125.stderr

+12
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,18 @@ error[E0432]: unresolved import `empty::issue_56125`
33
|
44
LL | use empty::issue_56125;
55
| ^^^^^^^^^^^^^^^^^^ no `issue_56125` in `m3::empty`
6+
|
7+
help: consider importing one of these items instead
8+
|
9+
LL | use crate::m3::last_segment::issue_56125;
10+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11+
LL | use crate::m3::non_last_segment::non_last_segment::issue_56125;
12+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13+
LL | use issue_56125::issue_56125;
14+
| ~~~~~~~~~~~~~~~~~~~~~~~~~
15+
LL | use issue_56125::last_segment::issue_56125;
16+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17+
and 1 other candidate
618

719
error[E0659]: `issue_56125` is ambiguous
820
--> $DIR/issue-56125.rs:6:9

src/test/ui/imports/issue-57015.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ error[E0432]: unresolved import `single_err::something`
33
|
44
LL | use single_err::something;
55
| ^^^^^^^^^^^^^^^^^^^^^ no `something` in `single_err`
6+
|
7+
help: consider importing this module instead
8+
|
9+
LL | use glob_ok::something;
10+
| ~~~~~~~~~~~~~~~~~~~
611

712
error: aborting due to previous error
813

src/test/ui/rfc-2126-extern-absolute-paths/not-allowed.stderr

+7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ error[E0432]: unresolved import `alloc`
33
|
44
LL | use alloc;
55
| ^^^^^ no external crate `alloc`
6+
|
7+
help: consider importing one of these items instead
8+
|
9+
LL | use core::alloc;
10+
| ~~~~~~~~~~~~
11+
LL | use std::alloc;
12+
| ~~~~~~~~~~~
613

714
error: aborting due to previous error
815

src/test/ui/simd/portable-intrinsics-arent-exposed.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ error[E0432]: unresolved import `std::simd::intrinsics`
1111
|
1212
LL | use std::simd::intrinsics;
1313
| ^^^^^^^^^^^^^^^^^^^^^ no `intrinsics` in `simd`
14+
|
15+
help: consider importing this module instead
16+
|
17+
LL | use std::intrinsics;
18+
| ~~~~~~~~~~~~~~~~
1419

1520
error: aborting due to 2 previous errors
1621

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

+10-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@ error[E0432]: unresolved import `test`
1111
--> $DIR/inaccessible-test-modules.rs:6:5
1212
|
1313
LL | use test as y;
14-
| ----^^^^^
15-
| |
16-
| no `test` in the root
17-
| help: a similar name exists in the module: `test`
14+
| ^^^^^^^^^ no `test` in the root
15+
|
16+
help: a similar name exists in the module
17+
|
18+
LL | use test as y;
19+
| ~~~~
20+
help: consider importing this module instead
21+
|
22+
LL | use test::test;
23+
| ~~~~~~~~~~~
1824

1925
error: aborting due to 2 previous errors
2026

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
mod a {
2+
pub trait Trait {}
3+
}
4+
5+
mod b {
6+
use Trait; //~ ERROR unresolved import `Trait`
7+
}
8+
9+
mod c {
10+
impl Trait for () {} //~ ERROR cannot find trait `Trait` in this scope
11+
}
12+
13+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error[E0432]: unresolved import `Trait`
2+
--> $DIR/unresolved-candidates.rs:6:9
3+
|
4+
LL | use Trait;
5+
| ^^^^^ no `Trait` in the root
6+
|
7+
help: consider importing this trait instead
8+
|
9+
LL | use a::Trait;
10+
| ~~~~~~~~~
11+
12+
error[E0405]: cannot find trait `Trait` in this scope
13+
--> $DIR/unresolved-candidates.rs:10:10
14+
|
15+
LL | impl Trait for () {}
16+
| ^^^^^ not found in this scope
17+
|
18+
help: consider importing this trait
19+
|
20+
LL | use a::Trait;
21+
|
22+
23+
error: aborting due to 2 previous errors
24+
25+
Some errors have detailed explanations: E0405, E0432.
26+
For more information about an error, try `rustc --explain E0405`.

0 commit comments

Comments
 (0)