Skip to content

Commit

Permalink
Rollup merge of #95887 - petrochenkov:doclink5, r=cjgillot
Browse files Browse the repository at this point in the history
resolve: Create dummy bindings for all unresolved imports

Apparently such bindings weren't previously created for all unresolved imports, causing issues like #95879.
In this PR I'm trying to create such dummy bindings in a more centralized way by calling `import_dummy_binding` once for all imports in `finalize_imports`.

Fixes #95879.
  • Loading branch information
Dylan-DPC committed Apr 16, 2022
2 parents c842240 + 09195d3 commit 0a8acac
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 45 deletions.
58 changes: 28 additions & 30 deletions compiler/rustc_resolve/src/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,18 +310,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 @@ -386,7 +391,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 @@ -396,9 +407,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 @@ -418,8 +426,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 @@ -553,26 +559,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 @@ -605,10 +608,6 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
);
let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len;
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 @@ -872,7 +871,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`.

0 comments on commit 0a8acac

Please sign in to comment.