Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resolve: Create dummy bindings for all unresolved imports #95887

Merged
merged 1 commit into from
Apr 16, 2022
Merged
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
58 changes: 28 additions & 30 deletions compiler/rustc_resolve/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,18 +583,23 @@ impl<'a> Resolver<'a> {
t
}

// Define a "dummy" resolution containing a Res::Err as a placeholder for a
// failed resolution
// Define a dummy resolution containing a `Res::Err` as a placeholder for a failed resolution,
// also mark such failed imports as used to avoid duplicate diagnostics.
fn import_dummy_binding(&mut self, import: &'a Import<'a>) {
if let ImportKind::Single { target, .. } = import.kind {
if let ImportKind::Single { target, ref target_bindings, .. } = import.kind {
if target_bindings.iter().any(|binding| binding.get().is_some()) {
return; // Has resolution, do not create the dummy binding
}
let dummy_binding = self.dummy_binding;
let dummy_binding = self.import(dummy_binding, import);
self.per_ns(|this, ns| {
let key = this.new_key(target, ns);
let _ = this.try_define(import.parent_scope.module, key, dummy_binding);
});
// Consider erroneous imports used to avoid duplicate diagnostics.
self.record_use(target, dummy_binding, false);
} else if import.imported_module.get().is_none() {
import.used.set(true);
self.used_imports.insert(import.id);
}
}
}
Expand Down Expand Up @@ -659,7 +664,13 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
.map(|i| (false, i))
.chain(indeterminate_imports.into_iter().map(|i| (true, i)))
{
if let Some(err) = self.finalize_import(import) {
let unresolved_import_error = self.finalize_import(import);

// If this import is unresolved then create a dummy import
// resolution for it so that later resolve stages won't complain.
self.r.import_dummy_binding(import);

if let Some(err) = unresolved_import_error {
if let ImportKind::Single { source, ref source_bindings, .. } = import.kind {
if source.name == kw::SelfLower {
// Silence `unresolved import` error if E0429 is already emitted
Expand All @@ -669,9 +680,6 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
}
}

// If the error is a single failed import then create a "fake" import
// resolution for it so that later resolve stages won't complain.
self.r.import_dummy_binding(import);
if prev_root_id.as_u32() != 0
&& prev_root_id.as_u32() != import.root_id.as_u32()
&& !errors.is_empty()
Expand All @@ -691,8 +699,6 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
prev_root_id = import.root_id;
}
} else if is_indeterminate {
// Consider erroneous imports used to avoid duplicate diagnostics.
self.r.used_imports.insert(import.id);
let path = import_path_to_string(
&import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(),
&import.kind,
Expand Down Expand Up @@ -824,26 +830,23 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
Err(Undetermined) => indeterminate = true,
// Don't update the resolution, because it was never added.
Err(Determined) if target.name == kw::Underscore => {}
Err(Determined) => {
Ok(binding) if binding.is_importable() => {
let imported_binding = this.import(binding, import);
target_bindings[ns].set(Some(imported_binding));
this.define(parent, target, ns, imported_binding);
}
source_binding @ (Ok(..) | Err(Determined)) => {
if source_binding.is_ok() {
let msg = format!("`{}` is not directly importable", target);
struct_span_err!(this.session, import.span, E0253, "{}", &msg)
.span_label(import.span, "cannot be imported directly")
.emit();
}
let key = this.new_key(target, ns);
this.update_resolution(parent, key, |_, resolution| {
resolution.single_imports.remove(&Interned::new_unchecked(import));
});
}
Ok(binding) if !binding.is_importable() => {
let msg = format!("`{}` is not directly importable", target);
struct_span_err!(this.session, import.span, E0253, "{}", &msg)
.span_label(import.span, "cannot be imported directly")
.emit();
// Do not import this illegal binding. Import a dummy binding and pretend
// everything is fine
this.import_dummy_binding(import);
}
Ok(binding) => {
let imported_binding = this.import(binding, import);
target_bindings[ns].set(Some(imported_binding));
this.define(parent, target, ns, imported_binding);
}
}
}
});
Expand Down Expand Up @@ -876,10 +879,6 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
self.r.unusable_binding = orig_unusable_binding;
}
import.vis.set(orig_vis);
if let PathResult::Failed { .. } | PathResult::NonModule(..) = path_res {
// Consider erroneous imports used to avoid duplicate diagnostics.
self.r.used_imports.insert(import.id);
}
let module = match path_res {
PathResult::Module(module) => {
// Consistency checks, analogous to `finalize_macro_resolutions`.
Expand Down Expand Up @@ -1144,7 +1143,6 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
})
} else {
// `resolve_ident_in_module` reported a privacy error.
self.r.import_dummy_binding(import);
None
};
}
Expand Down
6 changes: 6 additions & 0 deletions src/test/rustdoc-ui/intra-doc/unresolved-import-recovery.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Regression test for issue #95879.

use unresolved_crate::module::Name; //~ ERROR failed to resolve

/// [Name]
pub struct S;
11 changes: 11 additions & 0 deletions src/test/rustdoc-ui/intra-doc/unresolved-import-recovery.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error[E0433]: failed to resolve: maybe a missing crate `unresolved_crate`?
--> $DIR/unresolved-import-recovery.rs:3:5
|
LL | use unresolved_crate::module::Name;
| ^^^^^^^^^^^^^^^^ maybe a missing crate `unresolved_crate`?

error: Compilation failed, aborting rustdoc

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0433`.
2 changes: 1 addition & 1 deletion src/test/ui/use/use-super-global-path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mod foo {

pub fn g() {
use ::super::main; //~ ERROR global paths cannot start with `super`
main(); //~ ERROR cannot find function `main` in this scope
main();
}
}

Expand Down
16 changes: 2 additions & 14 deletions src/test/ui/use/use-super-global-path.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,6 @@ error[E0433]: failed to resolve: global paths cannot start with `super`
LL | use ::super::main;
| ^^^^^ global paths cannot start with `super`

error[E0425]: cannot find function `main` in this scope
--> $DIR/use-super-global-path.rs:12:9
|
LL | main();
| ^^^^ not found in this scope
|
help: consider importing this function
|
LL | use main;
|

error: aborting due to 4 previous errors
error: aborting due to 3 previous errors

Some errors have detailed explanations: E0425, E0433.
For more information about an error, try `rustc --explain E0425`.
For more information about this error, try `rustc --explain E0433`.