1
Fork 0

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:
Matthias Krüger 2022-10-10 20:47:34 +02:00 committed by GitHub
commit 0bd1cba98b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 145 additions and 8 deletions

View file

@ -70,6 +70,7 @@ impl TypoSuggestion {
}
/// A free importable items suggested in case of resolution failure.
#[derive(Debug, Clone)]
pub(crate) struct ImportSuggestion {
pub did: Option<DefId>,
pub descr: &'static str,
@ -139,6 +140,7 @@ impl<'a> Resolver<'a> {
if instead { Instead::Yes } else { Instead::No },
found_use,
IsPattern::No,
IsImport::No,
path,
);
err.emit();
@ -698,6 +700,7 @@ impl<'a> Resolver<'a> {
Instead::No,
FoundUse::Yes,
IsPattern::Yes,
IsImport::No,
vec![],
);
}
@ -1481,6 +1484,7 @@ impl<'a> Resolver<'a> {
Instead::No,
FoundUse::Yes,
IsPattern::No,
IsImport::No,
vec![],
);
@ -2449,6 +2453,34 @@ enum IsPattern {
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
/// entities with that name in all crates. This method allows outputting the
/// results of this search in a programmer-friendly way
@ -2462,6 +2494,7 @@ fn show_candidates(
instead: Instead,
found_use: FoundUse,
is_pattern: IsPattern,
is_import: IsImport,
path: Vec<Segment>,
) {
if candidates.is_empty() {
@ -2521,7 +2554,8 @@ fn show_candidates(
// produce an additional newline to separate the new use statement
// from the directly following item.
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(
@ -2551,7 +2585,7 @@ fn show_candidates(
err.note(&msg);
}
} else {
} else if matches!(is_import, IsImport::No) {
assert!(!inaccessible_path_strings.is_empty());
let prefix =

View file

@ -1,9 +1,9 @@
//! 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::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::{Finalize, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet};
use crate::{NameBinding, NameBindingKind, PathResult};
@ -406,6 +406,7 @@ struct UnresolvedImportError {
label: Option<String>,
note: Option<String>,
suggestion: Option<Suggestion>,
candidate: Option<Vec<ImportSuggestion>>,
}
pub struct ImportResolver<'a, 'b> {
@ -497,6 +498,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
label: None,
note: None,
suggestion: None,
candidate: None,
};
if path.contains("::") {
errors.push((path, err))
@ -547,6 +549,16 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
}
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();
@ -664,6 +676,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
Some(finalize),
ignore_binding,
);
let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len;
import.vis.set(orig_vis);
let module = match path_res {
@ -706,12 +719,14 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
String::from("a similar path exists"),
Applicability::MaybeIncorrect,
)),
candidate: None,
},
None => UnresolvedImportError {
span,
label: Some(label),
note: None,
suggestion,
candidate: None,
},
};
return Some(err);
@ -754,6 +769,7 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
label: Some(String::from("cannot glob-import a module into itself")),
note: 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 {
span: import.span,
label: Some(label),
note,
suggestion,
candidate: if !parent_suggestion.is_empty() {
Some(parent_suggestion)
} else {
None
},
})
} else {
// `resolve_ident_in_module` reported a privacy error.