Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 64 additions & 13 deletions compiler/rustc_resolve/src/diagnostics/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ pub(crate) struct ImportSuggestion {
/// An extra note that should be issued if this item is suggested
pub note: Option<String>,
pub is_stable: bool,
pub is_exact_match: bool,
}

/// Adjust the impl span so that just the `impl` keyword is taken by removing
Expand Down Expand Up @@ -1590,16 +1591,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
}

fn lookup_import_candidates_from_module<FilterFn>(
fn lookup_import_candidates_from_module<IdentFilterFn, FilterFn>(
&self,
lookup_ident: Ident,
namespace: Namespace,
parent_scope: &ParentScope<'ra>,
start_module: Module<'ra>,
crate_path: ThinVec<ast::PathSegment>,
ident_filter_fn: IdentFilterFn,
filter_fn: FilterFn,
) -> Vec<ImportSuggestion>
where
IdentFilterFn: Fn(Ident, Ident) -> bool,
FilterFn: Fn(Res) -> bool,
{
let mut candidates = Vec::new();
Expand Down Expand Up @@ -1671,7 +1674,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// collect results based on the filter function
// avoid suggesting anything from the same module in which we are resolving
// avoid suggesting anything with a hygienic name
if ident.name == lookup_ident.name
if ident_filter_fn(ident.orig(orig_ident_span), lookup_ident)
&& ns == namespace
&& in_module != parent_scope.module
&& ident.ctxt.is_root()
Expand Down Expand Up @@ -1751,6 +1754,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
note,
via_import,
is_stable,
is_exact_match: ident.name == lookup_ident.name,
});
}
}
Expand Down Expand Up @@ -1827,6 +1831,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
///
/// N.B., the method does not look into imports, but this is not a problem,
/// since we report the definitions (thus, the de-aliased imports).
///
/// The method is implemented in `lookup_import_candidates_impl`. The `_impl` method allows applying a different filter function on the ident than the exact match function used by default here.
pub(crate) fn lookup_import_candidates<FilterFn>(
&self,
lookup_ident: Ident,
Expand All @@ -1836,6 +1842,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
) -> Vec<ImportSuggestion>
where
FilterFn: Fn(Res) -> bool,
{
self.lookup_import_candidates_impl(
lookup_ident,
namespace,
parent_scope,
|ident: Ident, lookup_ident: Ident| ident.name == lookup_ident.name,
filter_fn,
)
}

/// The actual impl of the `lookup_import_candidates function`.
pub(crate) fn lookup_import_candidates_impl<IdentFilterFn, FilterFn>(
&self,
lookup_ident: Ident,
namespace: Namespace,
parent_scope: &ParentScope<'ra>,
ident_filter_fn: IdentFilterFn,
filter_fn: FilterFn,
) -> Vec<ImportSuggestion>
where
IdentFilterFn: Fn(Ident, Ident) -> bool,
FilterFn: Fn(Res) -> bool,
{
let crate_path = thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(kw::Crate))];
let mut suggestions = self.lookup_import_candidates_from_module(
Expand All @@ -1844,6 +1872,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
parent_scope,
self.graph_root.to_module(),
crate_path,
&ident_filter_fn,
&filter_fn,
);

Expand Down Expand Up @@ -1898,6 +1927,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
parent_scope,
crate_root,
crate_path,
&ident_filter_fn,
&filter_fn,
));
}
Expand Down Expand Up @@ -3899,7 +3929,7 @@ pub(crate) fn import_candidates(
);
}

type PathString<'a> = (String, &'a str, Option<Span>, &'a Option<String>, bool);
type PathString<'a> = (String, &'a str, Option<Span>, &'a Option<String>, bool, bool);

/// 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
Expand Down Expand Up @@ -3935,6 +3965,7 @@ fn show_candidates(
c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))),
&c.note,
c.via_import,
c.is_exact_match,
))
}
} else {
Expand All @@ -3944,6 +3975,7 @@ fn show_candidates(
c.did.and_then(|did| Some(tcx.source_span(did.as_local()?))),
&c.note,
c.via_import,
c.is_exact_match,
))
}
});
Expand Down Expand Up @@ -3975,9 +4007,10 @@ fn show_candidates(

if !accessible_path_strings.is_empty() {
let (determiner, kind, s, name, through) =
if let [(name, descr, _, _, via_import)] = &accessible_path_strings[..] {
if let [(name, descr, _, _, via_import, is_exact_match)] = &accessible_path_strings[..]
{
(
"this",
if *is_exact_match { "this" } else { "this similarly named" },
*descr,
"",
format!(" `{name}`"),
Expand All @@ -3988,12 +4021,24 @@ fn show_candidates(
// instead of the more generic "items".
let kinds = accessible_path_strings
.iter()
.map(|(_, descr, _, _, _)| *descr)
.map(|(_, descr, _, _, _, _)| *descr)
.collect::<UnordSet<&str>>();
let kind = if let Some(kind) = kinds.get_only() { kind } else { "item" };
let s = if kind.ends_with('s') { "es" } else { "s" };
// we should only suggest case insensitive suggestion if no case sensitive match was found,
// so all the suggestion should have the same is_exact_match value.

("one of these", kind, s, String::new(), "")
(
if accessible_path_strings[0].5 {
"one of these"
} else {
"one of these similarly named"
},
kind,
s,
String::new(),
"",
)
};

let instead = if let Instead::Yes = instead { " instead" } else { "" };
Expand Down Expand Up @@ -4087,9 +4132,12 @@ fn show_candidates(
{
let prefix =
if let DiagMode::Pattern = mode { "you might have meant to match on " } else { "" };
if let [(name, descr, source_span, note, _)] = &inaccessible_path_strings[..] {
if let [(name, descr, source_span, note, _, is_exact_match)] =
&inaccessible_path_strings[..]
{
let msg = format!(
"{prefix}{descr} `{name}`{} exists but is inaccessible",
"{prefix}{}{descr} `{name}`{} exists but is inaccessible",
if *is_exact_match { "" } else { "similarly named " },
if let DiagMode::Pattern = mode { ", which" } else { "" }
);

Expand All @@ -4107,17 +4155,20 @@ fn show_candidates(
} else {
let descr = inaccessible_path_strings
.iter()
.map(|&(_, descr, _, _, _)| descr)
.map(|&(_, descr, _, _, _, _)| descr)
.all_equal_value()
.unwrap_or("item");
let plural_descr =
if descr.ends_with('s') { format!("{descr}es") } else { format!("{descr}s") };

let mut msg = format!("{prefix}these {plural_descr} exist but are inaccessible");
let are_exact_matches = inaccessible_path_strings[0].5;
let mut msg = format!(
"{prefix}these {}{plural_descr} exist but are inaccessible",
if are_exact_matches { "" } else { "similarly named " },
);
let mut has_colon = false;

let mut spans = Vec::new();
for (name, _, source_span, _, _) in &inaccessible_path_strings {
for (name, _, source_span, _, _, _) in &inaccessible_path_strings {
if let Some(source_span) = source_span {
let span = tcx.sess.source_map().guess_head_span(*source_span);
spans.push((name, span));
Expand Down
98 changes: 91 additions & 7 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::unord::UnordItems;
use rustc_errors::codes::*;
use rustc_errors::{
Applicability, Diag, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, pluralize,
struct_span_code_err,
Applicability, Diag, Diagnostic, ErrorGuaranteed, MultiSpan, SuggestionStyle, Suggestions,
pluralize, struct_span_code_err,
};
use rustc_hir as hir;
use rustc_hir::attrs::diagnostic::{CustomDiagnostic, FormatArgs};
Expand Down Expand Up @@ -760,6 +760,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
};

let (found, suggested_candidates, mut candidates) = self.try_lookup_name_relaxed(
true,
&mut err,
source,
path,
Expand Down Expand Up @@ -788,11 +789,35 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
suggested_candidates,
);

self.err_code_special_cases(&mut err, source, path, span);

let no_suggestion = match &err.suggestions {
Suggestions::Enabled(suggestions) => suggestions.is_empty(),
Suggestions::Sealed(suggestions) => suggestions.is_empty(),
Suggestions::Disabled => false,
};
if let Some(E0425) = err.code
&& candidates.is_empty()
&& no_suggestion
{
candidates = self
.try_lookup_name_relaxed(
false,
&mut err,
source,
path,
following_seg,
span,
res,
&base_error,
)
.2;
}

if fallback {
// Fallback label.
err.span_label(base_error.span, base_error.fallback_label);
}
self.err_code_special_cases(&mut err, source, path, span);

let module = base_error.module.unwrap_or_else(|| CRATE_DEF_ID.to_def_id());
self.r.find_cfg_stripped(&mut err, &path.last().unwrap().ident.name, module);
Expand Down Expand Up @@ -916,6 +941,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {

fn try_lookup_name_relaxed(
&mut self,
case_sensitive: bool, // a subset of the tests are run when false
err: &mut Diag<'_>,
source: PathSource<'_, '_, '_>,
path: &[Segment],
Expand All @@ -935,16 +961,70 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
let mut suggested_candidates = FxHashSet::default();
// Try to lookup name in more relaxed fashion for better error reporting.
let ident = path.last().unwrap().ident;
let is_expected = &|res| source.is_expected(res);
// we do not suggest alternative capitalizations if only one letter, too many constants can match and it become noisy.
if !case_sensitive && ident.as_str().len() < 2 {
return (false, suggested_candidates, Vec::new());
}

let ident_filter = &|ident: Ident, ident_lookup: Ident| {
if case_sensitive {
ident.name == ident_lookup.name
} else {
ident.name.as_str().to_lowercase() == ident_lookup.name.as_str().to_lowercase()
}
};
let is_expected = &|res| {
if case_sensitive {
source.is_expected(res)
} else {
if following_seg.is_none() {
source.is_expected(res)
} else {
matches!(res, Res::Def(DefKind::Mod, _)) //fixme(GTimothy):check that this is
// necessary/correct
}
}
};
let ns = source.namespace();
let is_enum_variant = &|res| matches!(res, Res::Def(DefKind::Variant, _));
let path_str = Segment::names_to_string(path);
let ident_span = path.last().map_or(span, |ident| ident.ident.span);
let mut candidates = self
.r
.lookup_import_candidates(ident, ns, &self.parent_scope, is_expected)
.lookup_import_candidates_impl(ident, ns, &self.parent_scope, ident_filter, is_expected)
.into_iter()
.filter(|ImportSuggestion { did, .. }| {
if !case_sensitive {
// If there's a following segment, only keep modules that contain it
if let Some(following) = following_seg {
let Some(did) = did else { return false };
let Some(module) = self.r.get_module(*did) else { return false };
let mut found = false;
module.for_each_child(self.r, |_, ident, _, _, _| {
if ident.name == following.ident.name {
found = true;
}
});
if !found {
return false;
}
}

// Filter out items that are in the prelude
if let Some(prelude) = self.r.prelude {
if let Some(suggestion_did) = did {
let mut is_in_prelude = false;
prelude.for_each_child(self.r, |_, _, _, _, decl| {
if decl.res().opt_def_id() == Some(*suggestion_did) {
is_in_prelude = true;
}
});
if is_in_prelude {
return false;
}
}
}
}
match (did, res.and_then(|res| res.opt_def_id())) {
(Some(suggestion_did), Some(actual_did)) => *suggestion_did != actual_did,
_ => true,
Expand All @@ -963,6 +1043,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
// Put them back if we have no more candidates to suggest...
candidates = intrinsic_candidates;
}
if !case_sensitive {
return (false, suggested_candidates, candidates);
}
let crate_def_id = CRATE_DEF_ID.to_def_id();
if candidates.is_empty() && is_expected(Res::Def(DefKind::Enum, crate_def_id)) {
let mut enum_candidates: Vec<_> = self
Expand Down Expand Up @@ -1153,7 +1236,6 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
}
}
}

if candidates.is_empty() {
candidates = self.smart_resolve_partial_mod_path_errors(path, following_seg);
}
Expand Down Expand Up @@ -3088,7 +3170,8 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
path_segments.push(ast::PathSegment::from_ident(ident.orig(orig_ident_span)));
let doc_visible = doc_visible
&& (module_def_id.is_local() || !r.tcx.is_doc_hidden(module_def_id));
if module_def_id == def_id {
let is_exact_match = module_def_id == def_id;
if is_exact_match {
let path = Path { span: name_binding.span, segments: path_segments };
result = Some((
r.expect_module(module_def_id),
Expand All @@ -3101,6 +3184,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
note: None,
via_import: false,
is_stable: true,
is_exact_match,
},
));
} else {
Expand Down
7 changes: 7 additions & 0 deletions tests/ui/cast/cast-errors-issue-43825.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ error[E0425]: cannot find value `error` in this scope
|
LL | let error = error;
| ^^^^^ not found in this scope
|
help: consider importing one of these similarly named items
|
LL + use std::fmt::Error;
|
LL + use std::fs::TryLockError::Error;
|
Comment thread
mu001999 marked this conversation as resolved.

error: aborting due to 1 previous error

Expand Down
Loading
Loading