Rollup merge of #102876 - SparrowLii:import-candidate, r=fee1-dead
suggest candidates for unresolved import Currently we prompt suggestion of candidates(help notes of `use xxx::yyy`) for names which cannot be resolved, but we don't do that for import statements themselves that couldn't be resolved. It seems reasonable to add candidate help information for these statements as well. Fixes #102711
This commit is contained in:
commit
0bd1cba98b
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