rustc_resolve: reduce rightwards drift with let..else 👉💨
This commit is contained in:
@@ -1038,20 +1038,21 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
||||
if filter_fn(res) {
|
||||
for derive in parent_scope.derives {
|
||||
let parent_scope = &ParentScope { derives: &[], ..*parent_scope };
|
||||
if let Ok((Some(ext), _)) = this.resolve_macro_path(
|
||||
let Ok((Some(ext), _)) = this.resolve_macro_path(
|
||||
derive,
|
||||
Some(MacroKind::Derive),
|
||||
parent_scope,
|
||||
false,
|
||||
false,
|
||||
None,
|
||||
) {
|
||||
suggestions.extend(
|
||||
ext.helper_attrs
|
||||
.iter()
|
||||
.map(|name| TypoSuggestion::typo_from_name(*name, res)),
|
||||
);
|
||||
}
|
||||
) else {
|
||||
continue;
|
||||
};
|
||||
suggestions.extend(
|
||||
ext.helper_attrs
|
||||
.iter()
|
||||
.map(|name| TypoSuggestion::typo_from_name(*name, res)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1362,48 +1363,50 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
||||
// otherwise cause duplicate suggestions.
|
||||
continue;
|
||||
}
|
||||
let crate_id = self.crate_loader(|c| c.maybe_process_path_extern(ident.name));
|
||||
if let Some(crate_id) = crate_id {
|
||||
let crate_def_id = crate_id.as_def_id();
|
||||
let crate_root = self.expect_module(crate_def_id);
|
||||
let Some(crate_id) = self.crate_loader(|c| c.maybe_process_path_extern(ident.name))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// Check if there's already an item in scope with the same name as the crate.
|
||||
// If so, we have to disambiguate the potential import suggestions by making
|
||||
// the paths *global* (i.e., by prefixing them with `::`).
|
||||
let needs_disambiguation =
|
||||
self.resolutions(parent_scope.module).borrow().iter().any(
|
||||
|(key, name_resolution)| {
|
||||
if key.ns == TypeNS
|
||||
&& key.ident == ident
|
||||
&& let Some(binding) = name_resolution.borrow().binding
|
||||
{
|
||||
match binding.res() {
|
||||
// No disambiguation needed if the identically named item we
|
||||
// found in scope actually refers to the crate in question.
|
||||
Res::Def(_, def_id) => def_id != crate_def_id,
|
||||
Res::PrimTy(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
let crate_def_id = crate_id.as_def_id();
|
||||
let crate_root = self.expect_module(crate_def_id);
|
||||
|
||||
// Check if there's already an item in scope with the same name as the crate.
|
||||
// If so, we have to disambiguate the potential import suggestions by making
|
||||
// the paths *global* (i.e., by prefixing them with `::`).
|
||||
let needs_disambiguation =
|
||||
self.resolutions(parent_scope.module).borrow().iter().any(
|
||||
|(key, name_resolution)| {
|
||||
if key.ns == TypeNS
|
||||
&& key.ident == ident
|
||||
&& let Some(binding) = name_resolution.borrow().binding
|
||||
{
|
||||
match binding.res() {
|
||||
// No disambiguation needed if the identically named item we
|
||||
// found in scope actually refers to the crate in question.
|
||||
Res::Def(_, def_id) => def_id != crate_def_id,
|
||||
Res::PrimTy(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
},
|
||||
);
|
||||
let mut crate_path = ThinVec::new();
|
||||
if needs_disambiguation {
|
||||
crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP));
|
||||
}
|
||||
crate_path.push(ast::PathSegment::from_ident(ident));
|
||||
|
||||
suggestions.extend(self.lookup_import_candidates_from_module(
|
||||
lookup_ident,
|
||||
namespace,
|
||||
parent_scope,
|
||||
crate_root,
|
||||
crate_path,
|
||||
&filter_fn,
|
||||
));
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
);
|
||||
let mut crate_path = ThinVec::new();
|
||||
if needs_disambiguation {
|
||||
crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP));
|
||||
}
|
||||
crate_path.push(ast::PathSegment::from_ident(ident));
|
||||
|
||||
suggestions.extend(self.lookup_import_candidates_from_module(
|
||||
lookup_ident,
|
||||
namespace,
|
||||
parent_scope,
|
||||
crate_root,
|
||||
crate_path,
|
||||
&filter_fn,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1500,7 +1503,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
||||
});
|
||||
}
|
||||
for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] {
|
||||
if let Ok(binding) = self.early_resolve_ident_in_lexical_scope(
|
||||
let Ok(binding) = self.early_resolve_ident_in_lexical_scope(
|
||||
ident,
|
||||
ScopeSet::All(ns),
|
||||
parent_scope,
|
||||
@@ -1508,53 +1511,53 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
||||
false,
|
||||
None,
|
||||
None,
|
||||
) {
|
||||
let desc = match binding.res() {
|
||||
Res::Def(DefKind::Macro(MacroKind::Bang), _) => {
|
||||
"a function-like macro".to_string()
|
||||
}
|
||||
Res::Def(DefKind::Macro(MacroKind::Attr), _) | Res::NonMacroAttr(..) => {
|
||||
format!("an attribute: `#[{ident}]`")
|
||||
}
|
||||
Res::Def(DefKind::Macro(MacroKind::Derive), _) => {
|
||||
format!("a derive macro: `#[derive({ident})]`")
|
||||
}
|
||||
Res::ToolMod => {
|
||||
// Don't confuse the user with tool modules.
|
||||
continue;
|
||||
}
|
||||
Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => {
|
||||
"only a trait, without a derive macro".to_string()
|
||||
}
|
||||
res => format!(
|
||||
"{} {}, not {} {}",
|
||||
res.article(),
|
||||
res.descr(),
|
||||
macro_kind.article(),
|
||||
macro_kind.descr_expected(),
|
||||
),
|
||||
};
|
||||
if let crate::NameBindingKind::Import { import, .. } = binding.kind
|
||||
&& !import.span.is_dummy()
|
||||
{
|
||||
let note = errors::IdentImporterHereButItIsDesc {
|
||||
span: import.span,
|
||||
imported_ident: ident,
|
||||
imported_ident_desc: &desc,
|
||||
};
|
||||
err.subdiagnostic(note);
|
||||
// Silence the 'unused import' warning we might get,
|
||||
// since this diagnostic already covers that import.
|
||||
self.record_use(ident, binding, Used::Other);
|
||||
return;
|
||||
) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let desc = match binding.res() {
|
||||
Res::Def(DefKind::Macro(MacroKind::Bang), _) => "a function-like macro".to_string(),
|
||||
Res::Def(DefKind::Macro(MacroKind::Attr), _) | Res::NonMacroAttr(..) => {
|
||||
format!("an attribute: `#[{ident}]`")
|
||||
}
|
||||
let note = errors::IdentInScopeButItIsDesc {
|
||||
Res::Def(DefKind::Macro(MacroKind::Derive), _) => {
|
||||
format!("a derive macro: `#[derive({ident})]`")
|
||||
}
|
||||
Res::ToolMod => {
|
||||
// Don't confuse the user with tool modules.
|
||||
continue;
|
||||
}
|
||||
Res::Def(DefKind::Trait, _) if macro_kind == MacroKind::Derive => {
|
||||
"only a trait, without a derive macro".to_string()
|
||||
}
|
||||
res => format!(
|
||||
"{} {}, not {} {}",
|
||||
res.article(),
|
||||
res.descr(),
|
||||
macro_kind.article(),
|
||||
macro_kind.descr_expected(),
|
||||
),
|
||||
};
|
||||
if let crate::NameBindingKind::Import { import, .. } = binding.kind
|
||||
&& !import.span.is_dummy()
|
||||
{
|
||||
let note = errors::IdentImporterHereButItIsDesc {
|
||||
span: import.span,
|
||||
imported_ident: ident,
|
||||
imported_ident_desc: &desc,
|
||||
};
|
||||
err.subdiagnostic(note);
|
||||
// Silence the 'unused import' warning we might get,
|
||||
// since this diagnostic already covers that import.
|
||||
self.record_use(ident, binding, Used::Other);
|
||||
return;
|
||||
}
|
||||
let note = errors::IdentInScopeButItIsDesc {
|
||||
imported_ident: ident,
|
||||
imported_ident_desc: &desc,
|
||||
};
|
||||
err.subdiagnostic(note);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1738,15 +1741,16 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
||||
/// If the binding refers to a tuple struct constructor with fields,
|
||||
/// returns the span of its fields.
|
||||
fn ctor_fields_span(&self, binding: NameBinding<'_>) -> Option<Span> {
|
||||
if let NameBindingKind::Res(Res::Def(
|
||||
let NameBindingKind::Res(Res::Def(
|
||||
DefKind::Ctor(CtorOf::Struct, CtorKind::Fn),
|
||||
ctor_def_id,
|
||||
)) = binding.kind
|
||||
{
|
||||
let def_id = self.tcx.parent(ctor_def_id);
|
||||
return self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to); // None for `struct Foo()`
|
||||
}
|
||||
None
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let def_id = self.tcx.parent(ctor_def_id);
|
||||
self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to) // None for `struct Foo()`
|
||||
}
|
||||
|
||||
fn report_privacy_error(&mut self, privacy_error: &PrivacyError<'ra>) {
|
||||
@@ -2399,115 +2403,115 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
||||
let binding_key = BindingKey::new(ident, MacroNS);
|
||||
let resolution = resolutions.get(&binding_key)?;
|
||||
let binding = resolution.borrow().binding()?;
|
||||
if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() {
|
||||
let module_name = crate_module.kind.name().unwrap();
|
||||
let import_snippet = match import.kind {
|
||||
ImportKind::Single { source, target, .. } if source != target => {
|
||||
format!("{source} as {target}")
|
||||
}
|
||||
_ => format!("{ident}"),
|
||||
};
|
||||
|
||||
let mut corrections: Vec<(Span, String)> = Vec::new();
|
||||
if !import.is_nested() {
|
||||
// Assume this is the easy case of `use issue_59764::foo::makro;` and just remove
|
||||
// intermediate segments.
|
||||
corrections.push((import.span, format!("{module_name}::{import_snippet}")));
|
||||
} else {
|
||||
// Find the binding span (and any trailing commas and spaces).
|
||||
// ie. `use a::b::{c, d, e};`
|
||||
// ^^^
|
||||
let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding(
|
||||
self.tcx.sess,
|
||||
import.span,
|
||||
import.use_span,
|
||||
);
|
||||
debug!(found_closing_brace, ?binding_span);
|
||||
|
||||
let mut removal_span = binding_span;
|
||||
|
||||
// If the binding span ended with a closing brace, as in the below example:
|
||||
// ie. `use a::b::{c, d};`
|
||||
// ^
|
||||
// Then expand the span of characters to remove to include the previous
|
||||
// binding's trailing comma.
|
||||
// ie. `use a::b::{c, d};`
|
||||
// ^^^
|
||||
if found_closing_brace
|
||||
&& let Some(previous_span) =
|
||||
extend_span_to_previous_binding(self.tcx.sess, binding_span)
|
||||
{
|
||||
debug!(?previous_span);
|
||||
removal_span = removal_span.with_lo(previous_span.lo());
|
||||
}
|
||||
debug!(?removal_span);
|
||||
|
||||
// Remove the `removal_span`.
|
||||
corrections.push((removal_span, "".to_string()));
|
||||
|
||||
// Find the span after the crate name and if it has nested imports immediately
|
||||
// after the crate name already.
|
||||
// ie. `use a::b::{c, d};`
|
||||
// ^^^^^^^^^
|
||||
// or `use a::{b, c, d}};`
|
||||
// ^^^^^^^^^^^
|
||||
let (has_nested, after_crate_name) = find_span_immediately_after_crate_name(
|
||||
self.tcx.sess,
|
||||
module_name,
|
||||
import.use_span,
|
||||
);
|
||||
debug!(has_nested, ?after_crate_name);
|
||||
|
||||
let source_map = self.tcx.sess.source_map();
|
||||
|
||||
// Make sure this is actually crate-relative.
|
||||
let is_definitely_crate = import
|
||||
.module_path
|
||||
.first()
|
||||
.is_some_and(|f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super);
|
||||
|
||||
// Add the import to the start, with a `{` if required.
|
||||
let start_point = source_map.start_point(after_crate_name);
|
||||
if is_definitely_crate
|
||||
&& let Ok(start_snippet) = source_map.span_to_snippet(start_point)
|
||||
{
|
||||
corrections.push((
|
||||
start_point,
|
||||
if has_nested {
|
||||
// In this case, `start_snippet` must equal '{'.
|
||||
format!("{start_snippet}{import_snippet}, ")
|
||||
} else {
|
||||
// In this case, add a `{`, then the moved import, then whatever
|
||||
// was there before.
|
||||
format!("{{{import_snippet}, {start_snippet}")
|
||||
},
|
||||
));
|
||||
|
||||
// Add a `};` to the end if nested, matching the `{` added at the start.
|
||||
if !has_nested {
|
||||
corrections
|
||||
.push((source_map.end_point(after_crate_name), "};".to_string()));
|
||||
}
|
||||
} else {
|
||||
// If the root import is module-relative, add the import separately
|
||||
corrections.push((
|
||||
import.use_span.shrink_to_lo(),
|
||||
format!("use {module_name}::{import_snippet};\n"),
|
||||
));
|
||||
}
|
||||
let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() else {
|
||||
return None;
|
||||
};
|
||||
let module_name = crate_module.kind.name().unwrap();
|
||||
let import_snippet = match import.kind {
|
||||
ImportKind::Single { source, target, .. } if source != target => {
|
||||
format!("{source} as {target}")
|
||||
}
|
||||
_ => format!("{ident}"),
|
||||
};
|
||||
|
||||
let suggestion = Some((
|
||||
corrections,
|
||||
String::from("a macro with this name exists at the root of the crate"),
|
||||
Applicability::MaybeIncorrect,
|
||||
));
|
||||
Some((suggestion, Some("this could be because a macro annotated with `#[macro_export]` will be exported \
|
||||
at the root of the crate instead of the module where it is defined"
|
||||
.to_string())))
|
||||
let mut corrections: Vec<(Span, String)> = Vec::new();
|
||||
if !import.is_nested() {
|
||||
// Assume this is the easy case of `use issue_59764::foo::makro;` and just remove
|
||||
// intermediate segments.
|
||||
corrections.push((import.span, format!("{module_name}::{import_snippet}")));
|
||||
} else {
|
||||
None
|
||||
// Find the binding span (and any trailing commas and spaces).
|
||||
// ie. `use a::b::{c, d, e};`
|
||||
// ^^^
|
||||
let (found_closing_brace, binding_span) = find_span_of_binding_until_next_binding(
|
||||
self.tcx.sess,
|
||||
import.span,
|
||||
import.use_span,
|
||||
);
|
||||
debug!(found_closing_brace, ?binding_span);
|
||||
|
||||
let mut removal_span = binding_span;
|
||||
|
||||
// If the binding span ended with a closing brace, as in the below example:
|
||||
// ie. `use a::b::{c, d};`
|
||||
// ^
|
||||
// Then expand the span of characters to remove to include the previous
|
||||
// binding's trailing comma.
|
||||
// ie. `use a::b::{c, d};`
|
||||
// ^^^
|
||||
if found_closing_brace
|
||||
&& let Some(previous_span) =
|
||||
extend_span_to_previous_binding(self.tcx.sess, binding_span)
|
||||
{
|
||||
debug!(?previous_span);
|
||||
removal_span = removal_span.with_lo(previous_span.lo());
|
||||
}
|
||||
debug!(?removal_span);
|
||||
|
||||
// Remove the `removal_span`.
|
||||
corrections.push((removal_span, "".to_string()));
|
||||
|
||||
// Find the span after the crate name and if it has nested imports immediately
|
||||
// after the crate name already.
|
||||
// ie. `use a::b::{c, d};`
|
||||
// ^^^^^^^^^
|
||||
// or `use a::{b, c, d}};`
|
||||
// ^^^^^^^^^^^
|
||||
let (has_nested, after_crate_name) =
|
||||
find_span_immediately_after_crate_name(self.tcx.sess, module_name, import.use_span);
|
||||
debug!(has_nested, ?after_crate_name);
|
||||
|
||||
let source_map = self.tcx.sess.source_map();
|
||||
|
||||
// Make sure this is actually crate-relative.
|
||||
let is_definitely_crate = import
|
||||
.module_path
|
||||
.first()
|
||||
.is_some_and(|f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super);
|
||||
|
||||
// Add the import to the start, with a `{` if required.
|
||||
let start_point = source_map.start_point(after_crate_name);
|
||||
if is_definitely_crate
|
||||
&& let Ok(start_snippet) = source_map.span_to_snippet(start_point)
|
||||
{
|
||||
corrections.push((
|
||||
start_point,
|
||||
if has_nested {
|
||||
// In this case, `start_snippet` must equal '{'.
|
||||
format!("{start_snippet}{import_snippet}, ")
|
||||
} else {
|
||||
// In this case, add a `{`, then the moved import, then whatever
|
||||
// was there before.
|
||||
format!("{{{import_snippet}, {start_snippet}")
|
||||
},
|
||||
));
|
||||
|
||||
// Add a `};` to the end if nested, matching the `{` added at the start.
|
||||
if !has_nested {
|
||||
corrections.push((source_map.end_point(after_crate_name), "};".to_string()));
|
||||
}
|
||||
} else {
|
||||
// If the root import is module-relative, add the import separately
|
||||
corrections.push((
|
||||
import.use_span.shrink_to_lo(),
|
||||
format!("use {module_name}::{import_snippet};\n"),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let suggestion = Some((
|
||||
corrections,
|
||||
String::from("a macro with this name exists at the root of the crate"),
|
||||
Applicability::MaybeIncorrect,
|
||||
));
|
||||
Some((
|
||||
suggestion,
|
||||
Some(
|
||||
"this could be because a macro annotated with `#[macro_export]` will be exported \
|
||||
at the root of the crate instead of the module where it is defined"
|
||||
.to_string(),
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
/// Finds a cfg-ed out item inside `module` with the matching name.
|
||||
|
||||
Reference in New Issue
Block a user