suggest candidates for unresolved import
This commit is contained in:
parent
8b0c05d9ad
commit
0571b0af65
10 changed files with 145 additions and 8 deletions
|
@ -70,6 +70,7 @@ impl TypoSuggestion {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A free importable items suggested in case of resolution failure.
|
/// A free importable items suggested in case of resolution failure.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct ImportSuggestion {
|
pub(crate) struct ImportSuggestion {
|
||||||
pub did: Option<DefId>,
|
pub did: Option<DefId>,
|
||||||
pub descr: &'static str,
|
pub descr: &'static str,
|
||||||
|
@ -139,6 +140,7 @@ impl<'a> Resolver<'a> {
|
||||||
if instead { Instead::Yes } else { Instead::No },
|
if instead { Instead::Yes } else { Instead::No },
|
||||||
found_use,
|
found_use,
|
||||||
IsPattern::No,
|
IsPattern::No,
|
||||||
|
IsImport::No,
|
||||||
path,
|
path,
|
||||||
);
|
);
|
||||||
err.emit();
|
err.emit();
|
||||||
|
@ -698,6 +700,7 @@ impl<'a> Resolver<'a> {
|
||||||
Instead::No,
|
Instead::No,
|
||||||
FoundUse::Yes,
|
FoundUse::Yes,
|
||||||
IsPattern::Yes,
|
IsPattern::Yes,
|
||||||
|
IsImport::No,
|
||||||
vec![],
|
vec![],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1481,6 +1484,7 @@ impl<'a> Resolver<'a> {
|
||||||
Instead::No,
|
Instead::No,
|
||||||
FoundUse::Yes,
|
FoundUse::Yes,
|
||||||
IsPattern::No,
|
IsPattern::No,
|
||||||
|
IsImport::No,
|
||||||
vec![],
|
vec![],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2449,6 +2453,34 @@ enum IsPattern {
|
||||||
No,
|
No,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether a binding is part of a use statement. Used for diagnostics.
|
||||||
|
enum IsImport {
|
||||||
|
Yes,
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn import_candidates(
|
||||||
|
session: &Session,
|
||||||
|
source_span: &IndexVec<LocalDefId, Span>,
|
||||||
|
err: &mut Diagnostic,
|
||||||
|
// This is `None` if all placement locations are inside expansions
|
||||||
|
use_placement_span: Option<Span>,
|
||||||
|
candidates: &[ImportSuggestion],
|
||||||
|
) {
|
||||||
|
show_candidates(
|
||||||
|
session,
|
||||||
|
source_span,
|
||||||
|
err,
|
||||||
|
use_placement_span,
|
||||||
|
candidates,
|
||||||
|
Instead::Yes,
|
||||||
|
FoundUse::Yes,
|
||||||
|
IsPattern::No,
|
||||||
|
IsImport::Yes,
|
||||||
|
vec![],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// When an entity with a given name is not available in scope, we search for
|
/// When an entity with a given name is not available in scope, we search for
|
||||||
/// entities with that name in all crates. This method allows outputting the
|
/// entities with that name in all crates. This method allows outputting the
|
||||||
/// results of this search in a programmer-friendly way
|
/// results of this search in a programmer-friendly way
|
||||||
|
@ -2462,6 +2494,7 @@ fn show_candidates(
|
||||||
instead: Instead,
|
instead: Instead,
|
||||||
found_use: FoundUse,
|
found_use: FoundUse,
|
||||||
is_pattern: IsPattern,
|
is_pattern: IsPattern,
|
||||||
|
is_import: IsImport,
|
||||||
path: Vec<Segment>,
|
path: Vec<Segment>,
|
||||||
) {
|
) {
|
||||||
if candidates.is_empty() {
|
if candidates.is_empty() {
|
||||||
|
@ -2521,7 +2554,8 @@ fn show_candidates(
|
||||||
// produce an additional newline to separate the new use statement
|
// produce an additional newline to separate the new use statement
|
||||||
// from the directly following item.
|
// from the directly following item.
|
||||||
let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" };
|
let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" };
|
||||||
candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline);
|
let add_use = if let IsImport::Yes = is_import { "" } else { "use " };
|
||||||
|
candidate.0 = format!("{}{};\n{}", add_use, &candidate.0, additional_newline);
|
||||||
}
|
}
|
||||||
|
|
||||||
err.span_suggestions(
|
err.span_suggestions(
|
||||||
|
@ -2551,7 +2585,7 @@ fn show_candidates(
|
||||||
|
|
||||||
err.note(&msg);
|
err.note(&msg);
|
||||||
}
|
}
|
||||||
} else {
|
} else if matches!(is_import, IsImport::No) {
|
||||||
assert!(!inaccessible_path_strings.is_empty());
|
assert!(!inaccessible_path_strings.is_empty());
|
||||||
|
|
||||||
let prefix =
|
let prefix =
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
//! A bunch of methods and structures more or less related to resolving imports.
|
//! A bunch of methods and structures more or less related to resolving imports.
|
||||||
|
|
||||||
use crate::diagnostics::Suggestion;
|
use crate::diagnostics::{import_candidates, Suggestion};
|
||||||
use crate::Determinacy::{self, *};
|
use crate::Determinacy::{self, *};
|
||||||
use crate::Namespace::{self, *};
|
use crate::Namespace::{self, *};
|
||||||
use crate::{module_to_string, names_to_string};
|
use crate::{module_to_string, names_to_string, ImportSuggestion};
|
||||||
use crate::{AmbiguityKind, BindingKey, ModuleKind, ResolutionError, Resolver, Segment};
|
use crate::{AmbiguityKind, BindingKey, ModuleKind, ResolutionError, Resolver, Segment};
|
||||||
use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet};
|
use crate::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet};
|
||||||
use crate::{NameBinding, NameBindingKind, PathResult};
|
use crate::{NameBinding, NameBindingKind, PathResult};
|
||||||
|
@ -406,6 +406,7 @@ struct UnresolvedImportError {
|
||||||
label: Option<String>,
|
label: Option<String>,
|
||||||
note: Option<String>,
|
note: Option<String>,
|
||||||
suggestion: Option<Suggestion>,
|
suggestion: Option<Suggestion>,
|
||||||
|
candidate: Option<Vec<ImportSuggestion>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ImportResolver<'a, 'b> {
|
pub struct ImportResolver<'a, 'b> {
|
||||||
|
@ -497,6 +498,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
||||||
label: None,
|
label: None,
|
||||||
note: None,
|
note: None,
|
||||||
suggestion: None,
|
suggestion: None,
|
||||||
|
candidate: None,
|
||||||
};
|
};
|
||||||
if path.contains("::") {
|
if path.contains("::") {
|
||||||
errors.push((path, err))
|
errors.push((path, err))
|
||||||
|
@ -547,6 +549,16 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
||||||
}
|
}
|
||||||
diag.multipart_suggestion(&msg, suggestions, applicability);
|
diag.multipart_suggestion(&msg, suggestions, applicability);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(candidate) = &err.candidate {
|
||||||
|
import_candidates(
|
||||||
|
self.r.session,
|
||||||
|
&self.r.source_span,
|
||||||
|
&mut diag,
|
||||||
|
Some(err.span),
|
||||||
|
&candidate,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diag.emit();
|
diag.emit();
|
||||||
|
@ -664,6 +676,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
||||||
Some(finalize),
|
Some(finalize),
|
||||||
ignore_binding,
|
ignore_binding,
|
||||||
);
|
);
|
||||||
|
|
||||||
let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len;
|
let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len;
|
||||||
import.vis.set(orig_vis);
|
import.vis.set(orig_vis);
|
||||||
let module = match path_res {
|
let module = match path_res {
|
||||||
|
@ -706,12 +719,14 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
||||||
String::from("a similar path exists"),
|
String::from("a similar path exists"),
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
)),
|
)),
|
||||||
|
candidate: None,
|
||||||
},
|
},
|
||||||
None => UnresolvedImportError {
|
None => UnresolvedImportError {
|
||||||
span,
|
span,
|
||||||
label: Some(label),
|
label: Some(label),
|
||||||
note: None,
|
note: None,
|
||||||
suggestion,
|
suggestion,
|
||||||
|
candidate: None,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return Some(err);
|
return Some(err);
|
||||||
|
@ -754,6 +769,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
||||||
label: Some(String::from("cannot glob-import a module into itself")),
|
label: Some(String::from("cannot glob-import a module into itself")),
|
||||||
note: None,
|
note: None,
|
||||||
suggestion: None,
|
suggestion: None,
|
||||||
|
candidate: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -919,11 +935,19 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let parent_suggestion =
|
||||||
|
self.r.lookup_import_candidates(ident, TypeNS, &import.parent_scope, |_| true);
|
||||||
|
|
||||||
Some(UnresolvedImportError {
|
Some(UnresolvedImportError {
|
||||||
span: import.span,
|
span: import.span,
|
||||||
label: Some(label),
|
label: Some(label),
|
||||||
note,
|
note,
|
||||||
suggestion,
|
suggestion,
|
||||||
|
candidate: if !parent_suggestion.is_empty() {
|
||||||
|
Some(parent_suggestion)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// `resolve_ident_in_module` reported a privacy error.
|
// `resolve_ident_in_module` reported a privacy error.
|
||||||
|
|
|
@ -26,6 +26,11 @@ error[E0432]: unresolved import `env`
|
||||||
|
|
|
|
||||||
LL | use env;
|
LL | use env;
|
||||||
| ^^^ no `env` in the root
|
| ^^^ no `env` in the root
|
||||||
|
|
|
||||||
|
help: consider importing this module instead
|
||||||
|
|
|
||||||
|
LL | use std::env;
|
||||||
|
| ~~~~~~~~~
|
||||||
|
|
||||||
error: cannot determine resolution for the macro `env`
|
error: cannot determine resolution for the macro `env`
|
||||||
--> $DIR/issue-55897.rs:6:22
|
--> $DIR/issue-55897.rs:6:22
|
||||||
|
|
|
@ -3,6 +3,18 @@ error[E0432]: unresolved import `empty::issue_56125`
|
||||||
|
|
|
|
||||||
LL | use empty::issue_56125;
|
LL | use empty::issue_56125;
|
||||||
| ^^^^^^^^^^^^^^^^^^ no `issue_56125` in `m3::empty`
|
| ^^^^^^^^^^^^^^^^^^ no `issue_56125` in `m3::empty`
|
||||||
|
|
|
||||||
|
help: consider importing one of these items instead
|
||||||
|
|
|
||||||
|
LL | use crate::m3::last_segment::issue_56125;
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LL | use crate::m3::non_last_segment::non_last_segment::issue_56125;
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LL | use issue_56125::issue_56125;
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LL | use issue_56125::last_segment::issue_56125;
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
and 1 other candidate
|
||||||
|
|
||||||
error[E0659]: `issue_56125` is ambiguous
|
error[E0659]: `issue_56125` is ambiguous
|
||||||
--> $DIR/issue-56125.rs:6:9
|
--> $DIR/issue-56125.rs:6:9
|
||||||
|
|
|
@ -3,6 +3,11 @@ error[E0432]: unresolved import `single_err::something`
|
||||||
|
|
|
|
||||||
LL | use single_err::something;
|
LL | use single_err::something;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^ no `something` in `single_err`
|
| ^^^^^^^^^^^^^^^^^^^^^ no `something` in `single_err`
|
||||||
|
|
|
||||||
|
help: consider importing this module instead
|
||||||
|
|
|
||||||
|
LL | use glob_ok::something;
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,13 @@ error[E0432]: unresolved import `alloc`
|
||||||
|
|
|
|
||||||
LL | use alloc;
|
LL | use alloc;
|
||||||
| ^^^^^ no external crate `alloc`
|
| ^^^^^ no external crate `alloc`
|
||||||
|
|
|
||||||
|
help: consider importing one of these items instead
|
||||||
|
|
|
||||||
|
LL | use core::alloc;
|
||||||
|
| ~~~~~~~~~~~~
|
||||||
|
LL | use std::alloc;
|
||||||
|
| ~~~~~~~~~~~
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,11 @@ error[E0432]: unresolved import `std::simd::intrinsics`
|
||||||
|
|
|
|
||||||
LL | use std::simd::intrinsics;
|
LL | use std::simd::intrinsics;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^ no `intrinsics` in `simd`
|
| ^^^^^^^^^^^^^^^^^^^^^ no `intrinsics` in `simd`
|
||||||
|
|
|
||||||
|
help: consider importing this module instead
|
||||||
|
|
|
||||||
|
LL | use std::intrinsics;
|
||||||
|
| ~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,16 @@ error[E0432]: unresolved import `test`
|
||||||
--> $DIR/inaccessible-test-modules.rs:6:5
|
--> $DIR/inaccessible-test-modules.rs:6:5
|
||||||
|
|
|
|
||||||
LL | use test as y;
|
LL | use test as y;
|
||||||
| ----^^^^^
|
| ^^^^^^^^^ no `test` in the root
|
||||||
| |
|
|
|
||||||
| no `test` in the root
|
help: a similar name exists in the module
|
||||||
| help: a similar name exists in the module: `test`
|
|
|
||||||
|
LL | use test as y;
|
||||||
|
| ~~~~
|
||||||
|
help: consider importing this module instead
|
||||||
|
|
|
||||||
|
LL | use test::test;
|
||||||
|
| ~~~~~~~~~~~
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
|
13
src/test/ui/unresolved/unresolved-candidates.rs
Normal file
13
src/test/ui/unresolved/unresolved-candidates.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
mod a {
|
||||||
|
pub trait Trait {}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod b {
|
||||||
|
use Trait; //~ ERROR unresolved import `Trait`
|
||||||
|
}
|
||||||
|
|
||||||
|
mod c {
|
||||||
|
impl Trait for () {} //~ ERROR cannot find trait `Trait` in this scope
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
26
src/test/ui/unresolved/unresolved-candidates.stderr
Normal file
26
src/test/ui/unresolved/unresolved-candidates.stderr
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
error[E0432]: unresolved import `Trait`
|
||||||
|
--> $DIR/unresolved-candidates.rs:6:9
|
||||||
|
|
|
||||||
|
LL | use Trait;
|
||||||
|
| ^^^^^ no `Trait` in the root
|
||||||
|
|
|
||||||
|
help: consider importing this trait instead
|
||||||
|
|
|
||||||
|
LL | use a::Trait;
|
||||||
|
| ~~~~~~~~~
|
||||||
|
|
||||||
|
error[E0405]: cannot find trait `Trait` in this scope
|
||||||
|
--> $DIR/unresolved-candidates.rs:10:10
|
||||||
|
|
|
||||||
|
LL | impl Trait for () {}
|
||||||
|
| ^^^^^ not found in this scope
|
||||||
|
|
|
||||||
|
help: consider importing this trait
|
||||||
|
|
|
||||||
|
LL | use a::Trait;
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0405, E0432.
|
||||||
|
For more information about an error, try `rustc --explain E0405`.
|
Loading…
Add table
Add a link
Reference in a new issue