5481c1bd6d
rustc_ast currently has a few dependencies on rustc_lexer. Ideally, an AST would not have any dependency its lexer, for minimizing unnecessarily design-time dependencies. Breaking this dependency would also have practical benefits, since modifying rustc_lexer would not trigger a rebuild of rustc_ast. This commit does not remove the rustc_ast --> rustc_lexer dependency, but it does remove one of the sources of this dependency, which is the code that handles fuzzy matching between symbol names for making suggestions in diagnostics. Since that code depends only on Symbol, it is easy to move it to rustc_span. It might even be best to move it to a separate crate, since other tools such as Cargo use the same algorithm, and have simply contain a duplicate of the code. This changes the signature of find_best_match_for_name so that it is no longer generic over its input. I checked the optimized binaries, and this function was duplicated at nearly every call site, because most call sites used short-lived iterator chains, generic over Map and such. But there's no good reason for a function like this to be generic, since all it does is immediately convert the generic input (the Iterator impl) to a concrete Vec<Symbol>. This has all of the costs of generics (duplicated method bodies) with no benefit. Changing find_best_match_for_name to be non-generic removed about 10KB of code from the optimized binary. I know it's a drop in the bucket, but we have to start reducing binary size, and beginning to tame over-use of generics is part of that.
1500 lines
63 KiB
Rust
1500 lines
63 KiB
Rust
//! A bunch of methods and structures more or less related to resolving imports.
|
|
|
|
use crate::diagnostics::Suggestion;
|
|
use crate::Determinacy::{self, *};
|
|
use crate::Namespace::{self, MacroNS, TypeNS};
|
|
use crate::{module_to_string, names_to_string};
|
|
use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind};
|
|
use crate::{BindingKey, ModuleKind, ResolutionError, Resolver, Segment};
|
|
use crate::{CrateLint, Module, ModuleOrUniformRoot, ParentScope, PerNS, ScopeSet, Weak};
|
|
use crate::{NameBinding, NameBindingKind, PathResult, PrivacyError, ToNameBinding};
|
|
|
|
use rustc_ast::unwrap_or;
|
|
use rustc_ast::NodeId;
|
|
use rustc_ast_lowering::ResolverAstLowering;
|
|
use rustc_data_structures::fx::FxHashSet;
|
|
use rustc_data_structures::ptr_key::PtrKey;
|
|
use rustc_errors::{pluralize, struct_span_err, Applicability};
|
|
use rustc_hir::def::{self, PartialRes};
|
|
use rustc_hir::def_id::DefId;
|
|
use rustc_middle::hir::exports::Export;
|
|
use rustc_middle::ty;
|
|
use rustc_middle::{bug, span_bug};
|
|
use rustc_session::lint::builtin::{PUB_USE_OF_PRIVATE_EXTERN_CRATE, UNUSED_IMPORTS};
|
|
use rustc_session::lint::BuiltinLintDiagnostics;
|
|
use rustc_session::DiagnosticMessageId;
|
|
use rustc_span::hygiene::ExpnId;
|
|
use rustc_span::lev_distance::find_best_match_for_name;
|
|
use rustc_span::symbol::{kw, Ident, Symbol};
|
|
use rustc_span::{MultiSpan, Span};
|
|
|
|
use tracing::*;
|
|
|
|
use std::cell::Cell;
|
|
use std::{mem, ptr};
|
|
|
|
type Res = def::Res<NodeId>;
|
|
|
|
/// Contains data for specific kinds of imports.
|
|
#[derive(Clone, Debug)]
|
|
pub enum ImportKind<'a> {
|
|
Single {
|
|
/// `source` in `use prefix::source as target`.
|
|
source: Ident,
|
|
/// `target` in `use prefix::source as target`.
|
|
target: Ident,
|
|
/// Bindings to which `source` refers to.
|
|
source_bindings: PerNS<Cell<Result<&'a NameBinding<'a>, Determinacy>>>,
|
|
/// Bindings introduced by `target`.
|
|
target_bindings: PerNS<Cell<Option<&'a NameBinding<'a>>>>,
|
|
/// `true` for `...::{self [as target]}` imports, `false` otherwise.
|
|
type_ns_only: bool,
|
|
/// Did this import result from a nested import? ie. `use foo::{bar, baz};`
|
|
nested: bool,
|
|
},
|
|
Glob {
|
|
is_prelude: bool,
|
|
max_vis: Cell<ty::Visibility>, // The visibility of the greatest re-export.
|
|
// n.b. `max_vis` is only used in `finalize_import` to check for re-export errors.
|
|
},
|
|
ExternCrate {
|
|
source: Option<Symbol>,
|
|
target: Ident,
|
|
},
|
|
MacroUse,
|
|
}
|
|
|
|
/// One import.
|
|
#[derive(Debug, Clone)]
|
|
crate struct Import<'a> {
|
|
pub kind: ImportKind<'a>,
|
|
|
|
/// The ID of the `extern crate`, `UseTree` etc that imported this `Import`.
|
|
///
|
|
/// In the case where the `Import` was expanded from a "nested" use tree,
|
|
/// this id is the ID of the leaf tree. For example:
|
|
///
|
|
/// ```ignore (pacify the mercilous tidy)
|
|
/// use foo::bar::{a, b}
|
|
/// ```
|
|
///
|
|
/// If this is the import for `foo::bar::a`, we would have the ID of the `UseTree`
|
|
/// for `a` in this field.
|
|
pub id: NodeId,
|
|
|
|
/// The `id` of the "root" use-kind -- this is always the same as
|
|
/// `id` except in the case of "nested" use trees, in which case
|
|
/// it will be the `id` of the root use tree. e.g., in the example
|
|
/// from `id`, this would be the ID of the `use foo::bar`
|
|
/// `UseTree` node.
|
|
pub root_id: NodeId,
|
|
|
|
/// Span of the entire use statement.
|
|
pub use_span: Span,
|
|
|
|
/// Span of the entire use statement with attributes.
|
|
pub use_span_with_attributes: Span,
|
|
|
|
/// Did the use statement have any attributes?
|
|
pub has_attributes: bool,
|
|
|
|
/// Span of this use tree.
|
|
pub span: Span,
|
|
|
|
/// Span of the *root* use tree (see `root_id`).
|
|
pub root_span: Span,
|
|
|
|
pub parent_scope: ParentScope<'a>,
|
|
pub module_path: Vec<Segment>,
|
|
/// The resolution of `module_path`.
|
|
pub imported_module: Cell<Option<ModuleOrUniformRoot<'a>>>,
|
|
pub vis: Cell<ty::Visibility>,
|
|
pub used: Cell<bool>,
|
|
}
|
|
|
|
impl<'a> Import<'a> {
|
|
pub fn is_glob(&self) -> bool {
|
|
matches!(self.kind, ImportKind::Glob { .. })
|
|
}
|
|
|
|
pub fn is_nested(&self) -> bool {
|
|
match self.kind {
|
|
ImportKind::Single { nested, .. } => nested,
|
|
_ => false,
|
|
}
|
|
}
|
|
|
|
crate fn crate_lint(&self) -> CrateLint {
|
|
CrateLint::UsePath { root_id: self.root_id, root_span: self.root_span }
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Default, Debug)]
|
|
/// Records information about the resolution of a name in a namespace of a module.
|
|
pub struct NameResolution<'a> {
|
|
/// Single imports that may define the name in the namespace.
|
|
/// Imports are arena-allocated, so it's ok to use pointers as keys.
|
|
single_imports: FxHashSet<PtrKey<'a, Import<'a>>>,
|
|
/// The least shadowable known binding for this name, or None if there are no known bindings.
|
|
pub binding: Option<&'a NameBinding<'a>>,
|
|
shadowed_glob: Option<&'a NameBinding<'a>>,
|
|
}
|
|
|
|
impl<'a> NameResolution<'a> {
|
|
// Returns the binding for the name if it is known or None if it not known.
|
|
pub(crate) fn binding(&self) -> Option<&'a NameBinding<'a>> {
|
|
self.binding.and_then(|binding| {
|
|
if !binding.is_glob_import() || self.single_imports.is_empty() {
|
|
Some(binding)
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
}
|
|
|
|
crate fn add_single_import(&mut self, import: &'a Import<'a>) {
|
|
self.single_imports.insert(PtrKey(import));
|
|
}
|
|
}
|
|
|
|
impl<'a> Resolver<'a> {
|
|
crate fn resolve_ident_in_module_unadjusted(
|
|
&mut self,
|
|
module: ModuleOrUniformRoot<'a>,
|
|
ident: Ident,
|
|
ns: Namespace,
|
|
parent_scope: &ParentScope<'a>,
|
|
record_used: bool,
|
|
path_span: Span,
|
|
) -> Result<&'a NameBinding<'a>, Determinacy> {
|
|
self.resolve_ident_in_module_unadjusted_ext(
|
|
module,
|
|
ident,
|
|
ns,
|
|
parent_scope,
|
|
false,
|
|
record_used,
|
|
path_span,
|
|
)
|
|
.map_err(|(determinacy, _)| determinacy)
|
|
}
|
|
|
|
/// Attempts to resolve `ident` in namespaces `ns` of `module`.
|
|
/// Invariant: if `record_used` is `Some`, expansion and import resolution must be complete.
|
|
crate fn resolve_ident_in_module_unadjusted_ext(
|
|
&mut self,
|
|
module: ModuleOrUniformRoot<'a>,
|
|
ident: Ident,
|
|
ns: Namespace,
|
|
parent_scope: &ParentScope<'a>,
|
|
restricted_shadowing: bool,
|
|
record_used: bool,
|
|
path_span: Span,
|
|
) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
|
|
let module = match module {
|
|
ModuleOrUniformRoot::Module(module) => module,
|
|
ModuleOrUniformRoot::CrateRootAndExternPrelude => {
|
|
assert!(!restricted_shadowing);
|
|
let binding = self.early_resolve_ident_in_lexical_scope(
|
|
ident,
|
|
ScopeSet::AbsolutePath(ns),
|
|
parent_scope,
|
|
record_used,
|
|
record_used,
|
|
path_span,
|
|
);
|
|
return binding.map_err(|determinacy| (determinacy, Weak::No));
|
|
}
|
|
ModuleOrUniformRoot::ExternPrelude => {
|
|
assert!(!restricted_shadowing);
|
|
return if ns != TypeNS {
|
|
Err((Determined, Weak::No))
|
|
} else if let Some(binding) = self.extern_prelude_get(ident, !record_used) {
|
|
Ok(binding)
|
|
} else if !self.graph_root.unexpanded_invocations.borrow().is_empty() {
|
|
// Macro-expanded `extern crate` items can add names to extern prelude.
|
|
Err((Undetermined, Weak::No))
|
|
} else {
|
|
Err((Determined, Weak::No))
|
|
};
|
|
}
|
|
ModuleOrUniformRoot::CurrentScope => {
|
|
assert!(!restricted_shadowing);
|
|
if ns == TypeNS {
|
|
if ident.name == kw::Crate || ident.name == kw::DollarCrate {
|
|
let module = self.resolve_crate_root(ident);
|
|
let binding = (module, ty::Visibility::Public, module.span, ExpnId::root())
|
|
.to_name_binding(self.arenas);
|
|
return Ok(binding);
|
|
} else if ident.name == kw::Super || ident.name == kw::SelfLower {
|
|
// FIXME: Implement these with renaming requirements so that e.g.
|
|
// `use super;` doesn't work, but `use super as name;` does.
|
|
// Fall through here to get an error from `early_resolve_...`.
|
|
}
|
|
}
|
|
|
|
let scopes = ScopeSet::All(ns, true);
|
|
let binding = self.early_resolve_ident_in_lexical_scope(
|
|
ident,
|
|
scopes,
|
|
parent_scope,
|
|
record_used,
|
|
record_used,
|
|
path_span,
|
|
);
|
|
return binding.map_err(|determinacy| (determinacy, Weak::No));
|
|
}
|
|
};
|
|
|
|
let key = self.new_key(ident, ns);
|
|
let resolution =
|
|
self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports.
|
|
|
|
if let Some(binding) = resolution.binding {
|
|
if !restricted_shadowing && binding.expansion != ExpnId::root() {
|
|
if let NameBindingKind::Res(_, true) = binding.kind {
|
|
self.macro_expanded_macro_export_errors.insert((path_span, binding.span));
|
|
}
|
|
}
|
|
}
|
|
|
|
let check_usable = |this: &mut Self, binding: &'a NameBinding<'a>| {
|
|
if let Some(unusable_binding) = this.unusable_binding {
|
|
if ptr::eq(binding, unusable_binding) {
|
|
return Err((Determined, Weak::No));
|
|
}
|
|
}
|
|
// `extern crate` are always usable for backwards compatibility, see issue #37020,
|
|
// remove this together with `PUB_USE_OF_PRIVATE_EXTERN_CRATE`.
|
|
let usable = this.is_accessible_from(binding.vis, parent_scope.module)
|
|
|| binding.is_extern_crate();
|
|
if usable { Ok(binding) } else { Err((Determined, Weak::No)) }
|
|
};
|
|
|
|
if record_used {
|
|
return resolution
|
|
.binding
|
|
.and_then(|binding| {
|
|
// If the primary binding is unusable, search further and return the shadowed glob
|
|
// binding if it exists. What we really want here is having two separate scopes in
|
|
// a module - one for non-globs and one for globs, but until that's done use this
|
|
// hack to avoid inconsistent resolution ICEs during import validation.
|
|
if let Some(unusable_binding) = self.unusable_binding {
|
|
if ptr::eq(binding, unusable_binding) {
|
|
return resolution.shadowed_glob;
|
|
}
|
|
}
|
|
Some(binding)
|
|
})
|
|
.ok_or((Determined, Weak::No))
|
|
.and_then(|binding| {
|
|
if self.last_import_segment && check_usable(self, binding).is_err() {
|
|
Err((Determined, Weak::No))
|
|
} else {
|
|
self.record_use(ident, ns, binding, restricted_shadowing);
|
|
|
|
if let Some(shadowed_glob) = resolution.shadowed_glob {
|
|
// Forbid expanded shadowing to avoid time travel.
|
|
if restricted_shadowing
|
|
&& binding.expansion != ExpnId::root()
|
|
&& binding.res() != shadowed_glob.res()
|
|
{
|
|
self.ambiguity_errors.push(AmbiguityError {
|
|
kind: AmbiguityKind::GlobVsExpanded,
|
|
ident,
|
|
b1: binding,
|
|
b2: shadowed_glob,
|
|
misc1: AmbiguityErrorMisc::None,
|
|
misc2: AmbiguityErrorMisc::None,
|
|
});
|
|
}
|
|
}
|
|
|
|
if !(self.is_accessible_from(binding.vis, parent_scope.module) ||
|
|
// Remove this together with `PUB_USE_OF_PRIVATE_EXTERN_CRATE`
|
|
(self.last_import_segment && binding.is_extern_crate()))
|
|
{
|
|
self.privacy_errors.push(PrivacyError {
|
|
ident,
|
|
binding,
|
|
dedup_span: path_span,
|
|
});
|
|
}
|
|
|
|
Ok(binding)
|
|
}
|
|
});
|
|
}
|
|
|
|
// Items and single imports are not shadowable, if we have one, then it's determined.
|
|
if let Some(binding) = resolution.binding {
|
|
if !binding.is_glob_import() {
|
|
return check_usable(self, binding);
|
|
}
|
|
}
|
|
|
|
// --- From now on we either have a glob resolution or no resolution. ---
|
|
|
|
// Check if one of single imports can still define the name,
|
|
// if it can then our result is not determined and can be invalidated.
|
|
for single_import in &resolution.single_imports {
|
|
if !self.is_accessible_from(single_import.vis.get(), parent_scope.module) {
|
|
continue;
|
|
}
|
|
let module = unwrap_or!(
|
|
single_import.imported_module.get(),
|
|
return Err((Undetermined, Weak::No))
|
|
);
|
|
let ident = match single_import.kind {
|
|
ImportKind::Single { source, .. } => source,
|
|
_ => unreachable!(),
|
|
};
|
|
match self.resolve_ident_in_module(
|
|
module,
|
|
ident,
|
|
ns,
|
|
&single_import.parent_scope,
|
|
false,
|
|
path_span,
|
|
) {
|
|
Err(Determined) => continue,
|
|
Ok(binding)
|
|
if !self.is_accessible_from(binding.vis, single_import.parent_scope.module) =>
|
|
{
|
|
continue;
|
|
}
|
|
Ok(_) | Err(Undetermined) => return Err((Undetermined, Weak::No)),
|
|
}
|
|
}
|
|
|
|
// So we have a resolution that's from a glob import. This resolution is determined
|
|
// if it cannot be shadowed by some new item/import expanded from a macro.
|
|
// This happens either if there are no unexpanded macros, or expanded names cannot
|
|
// shadow globs (that happens in macro namespace or with restricted shadowing).
|
|
//
|
|
// Additionally, any macro in any module can plant names in the root module if it creates
|
|
// `macro_export` macros, so the root module effectively has unresolved invocations if any
|
|
// module has unresolved invocations.
|
|
// However, it causes resolution/expansion to stuck too often (#53144), so, to make
|
|
// progress, we have to ignore those potential unresolved invocations from other modules
|
|
// and prohibit access to macro-expanded `macro_export` macros instead (unless restricted
|
|
// shadowing is enabled, see `macro_expanded_macro_export_errors`).
|
|
let unexpanded_macros = !module.unexpanded_invocations.borrow().is_empty();
|
|
if let Some(binding) = resolution.binding {
|
|
if !unexpanded_macros || ns == MacroNS || restricted_shadowing {
|
|
return check_usable(self, binding);
|
|
} else {
|
|
return Err((Undetermined, Weak::No));
|
|
}
|
|
}
|
|
|
|
// --- From now on we have no resolution. ---
|
|
|
|
// Now we are in situation when new item/import can appear only from a glob or a macro
|
|
// expansion. With restricted shadowing names from globs and macro expansions cannot
|
|
// shadow names from outer scopes, so we can freely fallback from module search to search
|
|
// in outer scopes. For `early_resolve_ident_in_lexical_scope` to continue search in outer
|
|
// scopes we return `Undetermined` with `Weak::Yes`.
|
|
|
|
// Check if one of unexpanded macros can still define the name,
|
|
// if it can then our "no resolution" result is not determined and can be invalidated.
|
|
if unexpanded_macros {
|
|
return Err((Undetermined, Weak::Yes));
|
|
}
|
|
|
|
// Check if one of glob imports can still define the name,
|
|
// if it can then our "no resolution" result is not determined and can be invalidated.
|
|
for glob_import in module.globs.borrow().iter() {
|
|
if !self.is_accessible_from(glob_import.vis.get(), parent_scope.module) {
|
|
continue;
|
|
}
|
|
let module = match glob_import.imported_module.get() {
|
|
Some(ModuleOrUniformRoot::Module(module)) => module,
|
|
Some(_) => continue,
|
|
None => return Err((Undetermined, Weak::Yes)),
|
|
};
|
|
let tmp_parent_scope;
|
|
let (mut adjusted_parent_scope, mut ident) =
|
|
(parent_scope, ident.normalize_to_macros_2_0());
|
|
match ident.span.glob_adjust(module.expansion, glob_import.span) {
|
|
Some(Some(def)) => {
|
|
tmp_parent_scope =
|
|
ParentScope { module: self.macro_def_scope(def), ..*parent_scope };
|
|
adjusted_parent_scope = &tmp_parent_scope;
|
|
}
|
|
Some(None) => {}
|
|
None => continue,
|
|
};
|
|
let result = self.resolve_ident_in_module_unadjusted(
|
|
ModuleOrUniformRoot::Module(module),
|
|
ident,
|
|
ns,
|
|
adjusted_parent_scope,
|
|
false,
|
|
path_span,
|
|
);
|
|
|
|
match result {
|
|
Err(Determined) => continue,
|
|
Ok(binding)
|
|
if !self.is_accessible_from(binding.vis, glob_import.parent_scope.module) =>
|
|
{
|
|
continue;
|
|
}
|
|
Ok(_) | Err(Undetermined) => return Err((Undetermined, Weak::Yes)),
|
|
}
|
|
}
|
|
|
|
// No resolution and no one else can define the name - determinate error.
|
|
Err((Determined, Weak::No))
|
|
}
|
|
|
|
// Given a binding and an import that resolves to it,
|
|
// return the corresponding binding defined by the import.
|
|
crate fn import(
|
|
&self,
|
|
binding: &'a NameBinding<'a>,
|
|
import: &'a Import<'a>,
|
|
) -> &'a NameBinding<'a> {
|
|
let vis = if binding.pseudo_vis().is_at_least(import.vis.get(), self) ||
|
|
// cf. `PUB_USE_OF_PRIVATE_EXTERN_CRATE`
|
|
!import.is_glob() && binding.is_extern_crate()
|
|
{
|
|
import.vis.get()
|
|
} else {
|
|
binding.pseudo_vis()
|
|
};
|
|
|
|
if let ImportKind::Glob { ref max_vis, .. } = import.kind {
|
|
if vis == import.vis.get() || vis.is_at_least(max_vis.get(), self) {
|
|
max_vis.set(vis)
|
|
}
|
|
}
|
|
|
|
self.arenas.alloc_name_binding(NameBinding {
|
|
kind: NameBindingKind::Import { binding, import, used: Cell::new(false) },
|
|
ambiguity: None,
|
|
span: import.span,
|
|
vis,
|
|
expansion: import.parent_scope.expansion,
|
|
})
|
|
}
|
|
|
|
// Define the name or return the existing binding if there is a collision.
|
|
crate fn try_define(
|
|
&mut self,
|
|
module: Module<'a>,
|
|
key: BindingKey,
|
|
binding: &'a NameBinding<'a>,
|
|
) -> Result<(), &'a NameBinding<'a>> {
|
|
let res = binding.res();
|
|
self.check_reserved_macro_name(key.ident, res);
|
|
self.set_binding_parent_module(binding, module);
|
|
self.update_resolution(module, key, |this, resolution| {
|
|
if let Some(old_binding) = resolution.binding {
|
|
if res == Res::Err {
|
|
// Do not override real bindings with `Res::Err`s from error recovery.
|
|
return Ok(());
|
|
}
|
|
match (old_binding.is_glob_import(), binding.is_glob_import()) {
|
|
(true, true) => {
|
|
if res != old_binding.res() {
|
|
resolution.binding = Some(this.ambiguity(
|
|
AmbiguityKind::GlobVsGlob,
|
|
old_binding,
|
|
binding,
|
|
));
|
|
} else if !old_binding.vis.is_at_least(binding.vis, &*this) {
|
|
// We are glob-importing the same item but with greater visibility.
|
|
resolution.binding = Some(binding);
|
|
}
|
|
}
|
|
(old_glob @ true, false) | (old_glob @ false, true) => {
|
|
let (glob_binding, nonglob_binding) =
|
|
if old_glob { (old_binding, binding) } else { (binding, old_binding) };
|
|
if glob_binding.res() != nonglob_binding.res()
|
|
&& key.ns == MacroNS
|
|
&& nonglob_binding.expansion != ExpnId::root()
|
|
{
|
|
resolution.binding = Some(this.ambiguity(
|
|
AmbiguityKind::GlobVsExpanded,
|
|
nonglob_binding,
|
|
glob_binding,
|
|
));
|
|
} else {
|
|
resolution.binding = Some(nonglob_binding);
|
|
}
|
|
resolution.shadowed_glob = Some(glob_binding);
|
|
}
|
|
(false, false) => {
|
|
return Err(old_binding);
|
|
}
|
|
}
|
|
} else {
|
|
resolution.binding = Some(binding);
|
|
}
|
|
|
|
Ok(())
|
|
})
|
|
}
|
|
|
|
fn ambiguity(
|
|
&self,
|
|
kind: AmbiguityKind,
|
|
primary_binding: &'a NameBinding<'a>,
|
|
secondary_binding: &'a NameBinding<'a>,
|
|
) -> &'a NameBinding<'a> {
|
|
self.arenas.alloc_name_binding(NameBinding {
|
|
ambiguity: Some((secondary_binding, kind)),
|
|
..primary_binding.clone()
|
|
})
|
|
}
|
|
|
|
// Use `f` to mutate the resolution of the name in the module.
|
|
// If the resolution becomes a success, define it in the module's glob importers.
|
|
fn update_resolution<T, F>(&mut self, module: Module<'a>, key: BindingKey, f: F) -> T
|
|
where
|
|
F: FnOnce(&mut Resolver<'a>, &mut NameResolution<'a>) -> T,
|
|
{
|
|
// Ensure that `resolution` isn't borrowed when defining in the module's glob importers,
|
|
// during which the resolution might end up getting re-defined via a glob cycle.
|
|
let (binding, t) = {
|
|
let resolution = &mut *self.resolution(module, key).borrow_mut();
|
|
let old_binding = resolution.binding();
|
|
|
|
let t = f(self, resolution);
|
|
|
|
match resolution.binding() {
|
|
_ if old_binding.is_some() => return t,
|
|
None => return t,
|
|
Some(binding) => match old_binding {
|
|
Some(old_binding) if ptr::eq(old_binding, binding) => return t,
|
|
_ => (binding, t),
|
|
},
|
|
}
|
|
};
|
|
|
|
// Define `binding` in `module`s glob importers.
|
|
for import in module.glob_importers.borrow_mut().iter() {
|
|
let mut ident = key.ident;
|
|
let scope = match ident.span.reverse_glob_adjust(module.expansion, import.span) {
|
|
Some(Some(def)) => self.macro_def_scope(def),
|
|
Some(None) => import.parent_scope.module,
|
|
None => continue,
|
|
};
|
|
if self.is_accessible_from(binding.vis, scope) {
|
|
let imported_binding = self.import(binding, import);
|
|
let key = BindingKey { ident, ..key };
|
|
let _ = self.try_define(import.parent_scope.module, key, imported_binding);
|
|
}
|
|
}
|
|
|
|
t
|
|
}
|
|
|
|
// Define a "dummy" resolution containing a Res::Err as a placeholder for a
|
|
// failed resolution
|
|
fn import_dummy_binding(&mut self, import: &'a Import<'a>) {
|
|
if let ImportKind::Single { target, .. } = import.kind {
|
|
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.
|
|
this.record_use(target, ns, dummy_binding, false);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An error that may be transformed into a diagnostic later. Used to combine multiple unresolved
|
|
/// import errors within the same use tree into a single diagnostic.
|
|
#[derive(Debug, Clone)]
|
|
struct UnresolvedImportError {
|
|
span: Span,
|
|
label: Option<String>,
|
|
note: Vec<String>,
|
|
suggestion: Option<Suggestion>,
|
|
}
|
|
|
|
pub struct ImportResolver<'a, 'b> {
|
|
pub r: &'a mut Resolver<'b>,
|
|
}
|
|
|
|
impl<'a, 'b> ty::DefIdTree for &'a ImportResolver<'a, 'b> {
|
|
fn parent(self, id: DefId) -> Option<DefId> {
|
|
self.r.parent(id)
|
|
}
|
|
}
|
|
|
|
impl<'a, 'b> ImportResolver<'a, 'b> {
|
|
// Import resolution
|
|
//
|
|
// This is a fixed-point algorithm. We resolve imports until our efforts
|
|
// are stymied by an unresolved import; then we bail out of the current
|
|
// module and continue. We terminate successfully once no more imports
|
|
// remain or unsuccessfully when no forward progress in resolving imports
|
|
// is made.
|
|
|
|
/// Resolves all imports for the crate. This method performs the fixed-
|
|
/// point iteration.
|
|
pub fn resolve_imports(&mut self) {
|
|
let mut prev_num_indeterminates = self.r.indeterminate_imports.len() + 1;
|
|
while self.r.indeterminate_imports.len() < prev_num_indeterminates {
|
|
prev_num_indeterminates = self.r.indeterminate_imports.len();
|
|
for import in mem::take(&mut self.r.indeterminate_imports) {
|
|
match self.resolve_import(&import) {
|
|
true => self.r.determined_imports.push(import),
|
|
false => self.r.indeterminate_imports.push(import),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn finalize_imports(&mut self) {
|
|
for module in self.r.arenas.local_modules().iter() {
|
|
self.finalize_resolutions_in(module);
|
|
}
|
|
|
|
let mut seen_spans = FxHashSet::default();
|
|
let mut errors = vec![];
|
|
let mut prev_root_id: NodeId = NodeId::from_u32(0);
|
|
let determined_imports = mem::take(&mut self.r.determined_imports);
|
|
let indeterminate_imports = mem::take(&mut self.r.indeterminate_imports);
|
|
|
|
for (is_indeterminate, import) in determined_imports
|
|
.into_iter()
|
|
.map(|i| (false, i))
|
|
.chain(indeterminate_imports.into_iter().map(|i| (true, i)))
|
|
{
|
|
if let Some(err) = self.finalize_import(import) {
|
|
if let ImportKind::Single { source, ref source_bindings, .. } = import.kind {
|
|
if source.name == kw::SelfLower {
|
|
// Silence `unresolved import` error if E0429 is already emitted
|
|
if let Err(Determined) = source_bindings.value_ns.get() {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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()
|
|
{
|
|
// In the case of a new import line, throw a diagnostic message
|
|
// for the previous line.
|
|
self.throw_unresolved_import_error(errors, None);
|
|
errors = vec![];
|
|
}
|
|
if seen_spans.insert(err.span) {
|
|
let path = import_path_to_string(
|
|
&import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(),
|
|
&import.kind,
|
|
err.span,
|
|
);
|
|
errors.push((path, err));
|
|
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, TypeNS));
|
|
let path = import_path_to_string(
|
|
&import.module_path.iter().map(|seg| seg.ident).collect::<Vec<_>>(),
|
|
&import.kind,
|
|
import.span,
|
|
);
|
|
let err = UnresolvedImportError {
|
|
span: import.span,
|
|
label: None,
|
|
note: Vec::new(),
|
|
suggestion: None,
|
|
};
|
|
errors.push((path, err));
|
|
}
|
|
}
|
|
|
|
if !errors.is_empty() {
|
|
self.throw_unresolved_import_error(errors, None);
|
|
}
|
|
}
|
|
|
|
fn throw_unresolved_import_error(
|
|
&self,
|
|
errors: Vec<(String, UnresolvedImportError)>,
|
|
span: Option<MultiSpan>,
|
|
) {
|
|
/// Upper limit on the number of `span_label` messages.
|
|
const MAX_LABEL_COUNT: usize = 10;
|
|
|
|
let (span, msg) = if errors.is_empty() {
|
|
(span.unwrap(), "unresolved import".to_string())
|
|
} else {
|
|
let span = MultiSpan::from_spans(errors.iter().map(|(_, err)| err.span).collect());
|
|
|
|
let paths = errors.iter().map(|(path, _)| format!("`{}`", path)).collect::<Vec<_>>();
|
|
|
|
let msg = format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),);
|
|
|
|
(span, msg)
|
|
};
|
|
|
|
let mut diag = struct_span_err!(self.r.session, span, E0432, "{}", &msg);
|
|
|
|
if let Some((_, UnresolvedImportError { note, .. })) = errors.iter().last() {
|
|
for message in note {
|
|
diag.note(&message);
|
|
}
|
|
}
|
|
|
|
for (_, err) in errors.into_iter().take(MAX_LABEL_COUNT) {
|
|
if let Some(label) = err.label {
|
|
diag.span_label(err.span, label);
|
|
}
|
|
|
|
if let Some((suggestions, msg, applicability)) = err.suggestion {
|
|
diag.multipart_suggestion(&msg, suggestions, applicability);
|
|
}
|
|
}
|
|
|
|
diag.emit();
|
|
}
|
|
|
|
/// Attempts to resolve the given import, returning true if its resolution is determined.
|
|
/// If successful, the resolved bindings are written into the module.
|
|
fn resolve_import(&mut self, import: &'b Import<'b>) -> bool {
|
|
debug!(
|
|
"(resolving import for module) resolving import `{}::...` in `{}`",
|
|
Segment::names_to_string(&import.module_path),
|
|
module_to_string(import.parent_scope.module).unwrap_or_else(|| "???".to_string()),
|
|
);
|
|
|
|
let module = if let Some(module) = import.imported_module.get() {
|
|
module
|
|
} else {
|
|
// For better failure detection, pretend that the import will
|
|
// not define any names while resolving its module path.
|
|
let orig_vis = import.vis.replace(ty::Visibility::Invisible);
|
|
let path_res = self.r.resolve_path(
|
|
&import.module_path,
|
|
None,
|
|
&import.parent_scope,
|
|
false,
|
|
import.span,
|
|
import.crate_lint(),
|
|
);
|
|
import.vis.set(orig_vis);
|
|
|
|
match path_res {
|
|
PathResult::Module(module) => module,
|
|
PathResult::Indeterminate => return false,
|
|
PathResult::NonModule(..) | PathResult::Failed { .. } => return true,
|
|
}
|
|
};
|
|
|
|
import.imported_module.set(Some(module));
|
|
let (source, target, source_bindings, target_bindings, type_ns_only) = match import.kind {
|
|
ImportKind::Single {
|
|
source,
|
|
target,
|
|
ref source_bindings,
|
|
ref target_bindings,
|
|
type_ns_only,
|
|
..
|
|
} => (source, target, source_bindings, target_bindings, type_ns_only),
|
|
ImportKind::Glob { .. } => {
|
|
self.resolve_glob_import(import);
|
|
return true;
|
|
}
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
let mut indeterminate = false;
|
|
self.r.per_ns(|this, ns| {
|
|
if !type_ns_only || ns == TypeNS {
|
|
if let Err(Undetermined) = source_bindings[ns].get() {
|
|
// For better failure detection, pretend that the import will
|
|
// not define any names while resolving its module path.
|
|
let orig_vis = import.vis.replace(ty::Visibility::Invisible);
|
|
let binding = this.resolve_ident_in_module(
|
|
module,
|
|
source,
|
|
ns,
|
|
&import.parent_scope,
|
|
false,
|
|
import.span,
|
|
);
|
|
import.vis.set(orig_vis);
|
|
|
|
source_bindings[ns].set(binding);
|
|
} else {
|
|
return;
|
|
};
|
|
|
|
let parent = import.parent_scope.module;
|
|
match source_bindings[ns].get() {
|
|
Err(Undetermined) => indeterminate = true,
|
|
// Don't update the resolution, because it was never added.
|
|
Err(Determined) if target.name == kw::Underscore => {}
|
|
Err(Determined) => {
|
|
let key = this.new_key(target, ns);
|
|
this.update_resolution(parent, key, |_, resolution| {
|
|
resolution.single_imports.remove(&PtrKey(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);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
!indeterminate
|
|
}
|
|
|
|
/// Performs final import resolution, consistency checks and error reporting.
|
|
///
|
|
/// Optionally returns an unresolved import error. This error is buffered and used to
|
|
/// consolidate multiple unresolved import errors into a single diagnostic.
|
|
fn finalize_import(&mut self, import: &'b Import<'b>) -> Option<UnresolvedImportError> {
|
|
let orig_vis = import.vis.replace(ty::Visibility::Invisible);
|
|
let orig_unusable_binding = match &import.kind {
|
|
ImportKind::Single { target_bindings, .. } => {
|
|
Some(mem::replace(&mut self.r.unusable_binding, target_bindings[TypeNS].get()))
|
|
}
|
|
_ => None,
|
|
};
|
|
let prev_ambiguity_errors_len = self.r.ambiguity_errors.len();
|
|
let path_res = self.r.resolve_path(
|
|
&import.module_path,
|
|
None,
|
|
&import.parent_scope,
|
|
true,
|
|
import.span,
|
|
import.crate_lint(),
|
|
);
|
|
let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len;
|
|
if let Some(orig_unusable_binding) = orig_unusable_binding {
|
|
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, TypeNS));
|
|
}
|
|
let module = match path_res {
|
|
PathResult::Module(module) => {
|
|
// Consistency checks, analogous to `finalize_macro_resolutions`.
|
|
if let Some(initial_module) = import.imported_module.get() {
|
|
if !ModuleOrUniformRoot::same_def(module, initial_module) && no_ambiguity {
|
|
span_bug!(import.span, "inconsistent resolution for an import");
|
|
}
|
|
} else if self.r.privacy_errors.is_empty() {
|
|
let msg = "cannot determine resolution for the import";
|
|
let msg_note = "import resolution is stuck, try simplifying other imports";
|
|
self.r.session.struct_span_err(import.span, msg).note(msg_note).emit();
|
|
}
|
|
|
|
module
|
|
}
|
|
PathResult::Failed { is_error_from_last_segment: false, span, label, suggestion } => {
|
|
if no_ambiguity {
|
|
assert!(import.imported_module.get().is_none());
|
|
self.r
|
|
.report_error(span, ResolutionError::FailedToResolve { label, suggestion });
|
|
}
|
|
return None;
|
|
}
|
|
PathResult::Failed { is_error_from_last_segment: true, span, label, suggestion } => {
|
|
if no_ambiguity {
|
|
assert!(import.imported_module.get().is_none());
|
|
let err = match self.make_path_suggestion(
|
|
span,
|
|
import.module_path.clone(),
|
|
&import.parent_scope,
|
|
) {
|
|
Some((suggestion, note)) => UnresolvedImportError {
|
|
span,
|
|
label: None,
|
|
note,
|
|
suggestion: Some((
|
|
vec![(span, Segment::names_to_string(&suggestion))],
|
|
String::from("a similar path exists"),
|
|
Applicability::MaybeIncorrect,
|
|
)),
|
|
},
|
|
None => UnresolvedImportError {
|
|
span,
|
|
label: Some(label),
|
|
note: Vec::new(),
|
|
suggestion,
|
|
},
|
|
};
|
|
return Some(err);
|
|
}
|
|
return None;
|
|
}
|
|
PathResult::NonModule(path_res) if path_res.base_res() == Res::Err => {
|
|
if no_ambiguity {
|
|
assert!(import.imported_module.get().is_none());
|
|
}
|
|
// The error was already reported earlier.
|
|
return None;
|
|
}
|
|
PathResult::Indeterminate | PathResult::NonModule(..) => unreachable!(),
|
|
};
|
|
|
|
let (ident, target, source_bindings, target_bindings, type_ns_only) = match import.kind {
|
|
ImportKind::Single {
|
|
source,
|
|
target,
|
|
ref source_bindings,
|
|
ref target_bindings,
|
|
type_ns_only,
|
|
..
|
|
} => (source, target, source_bindings, target_bindings, type_ns_only),
|
|
ImportKind::Glob { is_prelude, ref max_vis } => {
|
|
if import.module_path.len() <= 1 {
|
|
// HACK(eddyb) `lint_if_path_starts_with_module` needs at least
|
|
// 2 segments, so the `resolve_path` above won't trigger it.
|
|
let mut full_path = import.module_path.clone();
|
|
full_path.push(Segment::from_ident(Ident::invalid()));
|
|
self.r.lint_if_path_starts_with_module(
|
|
import.crate_lint(),
|
|
&full_path,
|
|
import.span,
|
|
None,
|
|
);
|
|
}
|
|
|
|
if let ModuleOrUniformRoot::Module(module) = module {
|
|
if module.def_id() == import.parent_scope.module.def_id() {
|
|
// Importing a module into itself is not allowed.
|
|
return Some(UnresolvedImportError {
|
|
span: import.span,
|
|
label: Some(String::from("cannot glob-import a module into itself")),
|
|
note: Vec::new(),
|
|
suggestion: None,
|
|
});
|
|
}
|
|
}
|
|
if !is_prelude &&
|
|
max_vis.get() != ty::Visibility::Invisible && // Allow empty globs.
|
|
!max_vis.get().is_at_least(import.vis.get(), &*self)
|
|
{
|
|
let msg = "glob import doesn't reexport anything because no candidate is public enough";
|
|
self.r.lint_buffer.buffer_lint(UNUSED_IMPORTS, import.id, import.span, msg);
|
|
}
|
|
return None;
|
|
}
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
let mut all_ns_err = true;
|
|
self.r.per_ns(|this, ns| {
|
|
if !type_ns_only || ns == TypeNS {
|
|
let orig_vis = import.vis.replace(ty::Visibility::Invisible);
|
|
let orig_unusable_binding =
|
|
mem::replace(&mut this.unusable_binding, target_bindings[ns].get());
|
|
let orig_last_import_segment = mem::replace(&mut this.last_import_segment, true);
|
|
let binding = this.resolve_ident_in_module(
|
|
module,
|
|
ident,
|
|
ns,
|
|
&import.parent_scope,
|
|
true,
|
|
import.span,
|
|
);
|
|
this.last_import_segment = orig_last_import_segment;
|
|
this.unusable_binding = orig_unusable_binding;
|
|
import.vis.set(orig_vis);
|
|
|
|
match binding {
|
|
Ok(binding) => {
|
|
// Consistency checks, analogous to `finalize_macro_resolutions`.
|
|
let initial_res = source_bindings[ns].get().map(|initial_binding| {
|
|
all_ns_err = false;
|
|
if let Some(target_binding) = target_bindings[ns].get() {
|
|
if target.name == kw::Underscore
|
|
&& initial_binding.is_extern_crate()
|
|
&& !initial_binding.is_import()
|
|
{
|
|
this.record_use(
|
|
ident,
|
|
ns,
|
|
target_binding,
|
|
import.module_path.is_empty(),
|
|
);
|
|
}
|
|
}
|
|
initial_binding.res()
|
|
});
|
|
let res = binding.res();
|
|
if let Ok(initial_res) = initial_res {
|
|
if res != initial_res && this.ambiguity_errors.is_empty() {
|
|
span_bug!(import.span, "inconsistent resolution for an import");
|
|
}
|
|
} else if res != Res::Err
|
|
&& this.ambiguity_errors.is_empty()
|
|
&& this.privacy_errors.is_empty()
|
|
{
|
|
let msg = "cannot determine resolution for the import";
|
|
let msg_note =
|
|
"import resolution is stuck, try simplifying other imports";
|
|
this.session.struct_span_err(import.span, msg).note(msg_note).emit();
|
|
}
|
|
}
|
|
Err(..) => {
|
|
// FIXME: This assert may fire if public glob is later shadowed by a private
|
|
// single import (see test `issue-55884-2.rs`). In theory single imports should
|
|
// always block globs, even if they are not yet resolved, so that this kind of
|
|
// self-inconsistent resolution never happens.
|
|
// Re-enable the assert when the issue is fixed.
|
|
// assert!(result[ns].get().is_err());
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
if all_ns_err {
|
|
let mut all_ns_failed = true;
|
|
self.r.per_ns(|this, ns| {
|
|
if !type_ns_only || ns == TypeNS {
|
|
let binding = this.resolve_ident_in_module(
|
|
module,
|
|
ident,
|
|
ns,
|
|
&import.parent_scope,
|
|
true,
|
|
import.span,
|
|
);
|
|
if binding.is_ok() {
|
|
all_ns_failed = false;
|
|
}
|
|
}
|
|
});
|
|
|
|
return if all_ns_failed {
|
|
let resolutions = match module {
|
|
ModuleOrUniformRoot::Module(module) => {
|
|
Some(self.r.resolutions(module).borrow())
|
|
}
|
|
_ => None,
|
|
};
|
|
let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter());
|
|
let names = resolutions
|
|
.filter_map(|(BindingKey { ident: i, .. }, resolution)| {
|
|
if *i == ident {
|
|
return None;
|
|
} // Never suggest the same name
|
|
match *resolution.borrow() {
|
|
NameResolution { binding: Some(name_binding), .. } => {
|
|
match name_binding.kind {
|
|
NameBindingKind::Import { binding, .. } => {
|
|
match binding.kind {
|
|
// Never suggest the name that has binding error
|
|
// i.e., the name that cannot be previously resolved
|
|
NameBindingKind::Res(Res::Err, _) => None,
|
|
_ => Some(i.name),
|
|
}
|
|
}
|
|
_ => Some(i.name),
|
|
}
|
|
}
|
|
NameResolution { ref single_imports, .. }
|
|
if single_imports.is_empty() =>
|
|
{
|
|
None
|
|
}
|
|
_ => Some(i.name),
|
|
}
|
|
})
|
|
.collect::<Vec<Symbol>>();
|
|
|
|
let lev_suggestion =
|
|
find_best_match_for_name(&names, ident.name, None).map(|suggestion| {
|
|
(
|
|
vec![(ident.span, suggestion.to_string())],
|
|
String::from("a similar name exists in the module"),
|
|
Applicability::MaybeIncorrect,
|
|
)
|
|
});
|
|
|
|
let (suggestion, note) =
|
|
match self.check_for_module_export_macro(import, module, ident) {
|
|
Some((suggestion, note)) => (suggestion.or(lev_suggestion), note),
|
|
_ => (lev_suggestion, Vec::new()),
|
|
};
|
|
|
|
let label = match module {
|
|
ModuleOrUniformRoot::Module(module) => {
|
|
let module_str = module_to_string(module);
|
|
if let Some(module_str) = module_str {
|
|
format!("no `{}` in `{}`", ident, module_str)
|
|
} else {
|
|
format!("no `{}` in the root", ident)
|
|
}
|
|
}
|
|
_ => {
|
|
if !ident.is_path_segment_keyword() {
|
|
format!("no external crate `{}`", ident)
|
|
} else {
|
|
// HACK(eddyb) this shows up for `self` & `super`, which
|
|
// should work instead - for now keep the same error message.
|
|
format!("no `{}` in the root", ident)
|
|
}
|
|
}
|
|
};
|
|
|
|
Some(UnresolvedImportError {
|
|
span: import.span,
|
|
label: Some(label),
|
|
note,
|
|
suggestion,
|
|
})
|
|
} else {
|
|
// `resolve_ident_in_module` reported a privacy error.
|
|
self.r.import_dummy_binding(import);
|
|
None
|
|
};
|
|
}
|
|
|
|
let mut reexport_error = None;
|
|
let mut any_successful_reexport = false;
|
|
self.r.per_ns(|this, ns| {
|
|
if let Ok(binding) = source_bindings[ns].get() {
|
|
let vis = import.vis.get();
|
|
if !binding.pseudo_vis().is_at_least(vis, &*this) {
|
|
reexport_error = Some((ns, binding));
|
|
} else {
|
|
any_successful_reexport = true;
|
|
}
|
|
}
|
|
});
|
|
|
|
// All namespaces must be re-exported with extra visibility for an error to occur.
|
|
if !any_successful_reexport {
|
|
let (ns, binding) = reexport_error.unwrap();
|
|
if ns == TypeNS && binding.is_extern_crate() {
|
|
let msg = format!(
|
|
"extern crate `{}` is private, and cannot be \
|
|
re-exported (error E0365), consider declaring with \
|
|
`pub`",
|
|
ident
|
|
);
|
|
self.r.lint_buffer.buffer_lint(
|
|
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
|
|
import.id,
|
|
import.span,
|
|
&msg,
|
|
);
|
|
} else if ns == TypeNS {
|
|
struct_span_err!(
|
|
self.r.session,
|
|
import.span,
|
|
E0365,
|
|
"`{}` is private, and cannot be re-exported",
|
|
ident
|
|
)
|
|
.span_label(import.span, format!("re-export of private `{}`", ident))
|
|
.note(&format!("consider declaring type or module `{}` with `pub`", ident))
|
|
.emit();
|
|
} else {
|
|
let msg = format!("`{}` is private, and cannot be re-exported", ident);
|
|
let note_msg =
|
|
format!("consider marking `{}` as `pub` in the imported module", ident,);
|
|
struct_span_err!(self.r.session, import.span, E0364, "{}", &msg)
|
|
.span_note(import.span, ¬e_msg)
|
|
.emit();
|
|
}
|
|
}
|
|
|
|
if import.module_path.len() <= 1 {
|
|
// HACK(eddyb) `lint_if_path_starts_with_module` needs at least
|
|
// 2 segments, so the `resolve_path` above won't trigger it.
|
|
let mut full_path = import.module_path.clone();
|
|
full_path.push(Segment::from_ident(ident));
|
|
self.r.per_ns(|this, ns| {
|
|
if let Ok(binding) = source_bindings[ns].get() {
|
|
this.lint_if_path_starts_with_module(
|
|
import.crate_lint(),
|
|
&full_path,
|
|
import.span,
|
|
Some(binding),
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Record what this import resolves to for later uses in documentation,
|
|
// this may resolve to either a value or a type, but for documentation
|
|
// purposes it's good enough to just favor one over the other.
|
|
self.r.per_ns(|this, ns| {
|
|
if let Ok(binding) = source_bindings[ns].get() {
|
|
this.import_res_map.entry(import.id).or_default()[ns] = Some(binding.res());
|
|
}
|
|
});
|
|
|
|
self.check_for_redundant_imports(ident, import, source_bindings, target_bindings, target);
|
|
|
|
debug!("(resolving single import) successfully resolved import");
|
|
None
|
|
}
|
|
|
|
fn check_for_redundant_imports(
|
|
&mut self,
|
|
ident: Ident,
|
|
import: &'b Import<'b>,
|
|
source_bindings: &PerNS<Cell<Result<&'b NameBinding<'b>, Determinacy>>>,
|
|
target_bindings: &PerNS<Cell<Option<&'b NameBinding<'b>>>>,
|
|
target: Ident,
|
|
) {
|
|
// Skip if the import was produced by a macro.
|
|
if import.parent_scope.expansion != ExpnId::root() {
|
|
return;
|
|
}
|
|
|
|
// Skip if we are inside a named module (in contrast to an anonymous
|
|
// module defined by a block).
|
|
if let ModuleKind::Def(..) = import.parent_scope.module.kind {
|
|
return;
|
|
}
|
|
|
|
let mut is_redundant = PerNS { value_ns: None, type_ns: None, macro_ns: None };
|
|
|
|
let mut redundant_span = PerNS { value_ns: None, type_ns: None, macro_ns: None };
|
|
|
|
self.r.per_ns(|this, ns| {
|
|
if let Ok(binding) = source_bindings[ns].get() {
|
|
if binding.res() == Res::Err {
|
|
return;
|
|
}
|
|
|
|
let orig_unusable_binding =
|
|
mem::replace(&mut this.unusable_binding, target_bindings[ns].get());
|
|
|
|
match this.early_resolve_ident_in_lexical_scope(
|
|
target,
|
|
ScopeSet::All(ns, false),
|
|
&import.parent_scope,
|
|
false,
|
|
false,
|
|
import.span,
|
|
) {
|
|
Ok(other_binding) => {
|
|
is_redundant[ns] = Some(
|
|
binding.res() == other_binding.res() && !other_binding.is_ambiguity(),
|
|
);
|
|
redundant_span[ns] = Some((other_binding.span, other_binding.is_import()));
|
|
}
|
|
Err(_) => is_redundant[ns] = Some(false),
|
|
}
|
|
|
|
this.unusable_binding = orig_unusable_binding;
|
|
}
|
|
});
|
|
|
|
if !is_redundant.is_empty() && is_redundant.present_items().all(|is_redundant| is_redundant)
|
|
{
|
|
let mut redundant_spans: Vec<_> = redundant_span.present_items().collect();
|
|
redundant_spans.sort();
|
|
redundant_spans.dedup();
|
|
self.r.lint_buffer.buffer_lint_with_diagnostic(
|
|
UNUSED_IMPORTS,
|
|
import.id,
|
|
import.span,
|
|
&format!("the item `{}` is imported redundantly", ident),
|
|
BuiltinLintDiagnostics::RedundantImport(redundant_spans, ident),
|
|
);
|
|
}
|
|
}
|
|
|
|
fn resolve_glob_import(&mut self, import: &'b Import<'b>) {
|
|
let module = match import.imported_module.get().unwrap() {
|
|
ModuleOrUniformRoot::Module(module) => module,
|
|
_ => {
|
|
self.r.session.span_err(import.span, "cannot glob-import all possible crates");
|
|
return;
|
|
}
|
|
};
|
|
|
|
if module.is_trait() {
|
|
self.r.session.span_err(import.span, "items in traits are not importable.");
|
|
return;
|
|
} else if module.def_id() == import.parent_scope.module.def_id() {
|
|
return;
|
|
} else if let ImportKind::Glob { is_prelude: true, .. } = import.kind {
|
|
self.r.prelude = Some(module);
|
|
return;
|
|
}
|
|
|
|
// Add to module's glob_importers
|
|
module.glob_importers.borrow_mut().push(import);
|
|
|
|
// Ensure that `resolutions` isn't borrowed during `try_define`,
|
|
// since it might get updated via a glob cycle.
|
|
let bindings = self
|
|
.r
|
|
.resolutions(module)
|
|
.borrow()
|
|
.iter()
|
|
.filter_map(|(key, resolution)| {
|
|
resolution.borrow().binding().map(|binding| (*key, binding))
|
|
})
|
|
.collect::<Vec<_>>();
|
|
for (mut key, binding) in bindings {
|
|
let scope = match key.ident.span.reverse_glob_adjust(module.expansion, import.span) {
|
|
Some(Some(def)) => self.r.macro_def_scope(def),
|
|
Some(None) => import.parent_scope.module,
|
|
None => continue,
|
|
};
|
|
if self.r.is_accessible_from(binding.pseudo_vis(), scope) {
|
|
let imported_binding = self.r.import(binding, import);
|
|
let _ = self.r.try_define(import.parent_scope.module, key, imported_binding);
|
|
}
|
|
}
|
|
|
|
// Record the destination of this import
|
|
self.r.record_partial_res(import.id, PartialRes::new(module.res().unwrap()));
|
|
}
|
|
|
|
// Miscellaneous post-processing, including recording re-exports,
|
|
// reporting conflicts, and reporting unresolved imports.
|
|
fn finalize_resolutions_in(&mut self, module: Module<'b>) {
|
|
// Since import resolution is finished, globs will not define any more names.
|
|
*module.globs.borrow_mut() = Vec::new();
|
|
|
|
let mut reexports = Vec::new();
|
|
|
|
module.for_each_child(self.r, |this, ident, ns, binding| {
|
|
// Filter away ambiguous imports and anything that has def-site
|
|
// hygiene.
|
|
// FIXME: Implement actual cross-crate hygiene.
|
|
let is_good_import =
|
|
binding.is_import() && !binding.is_ambiguity() && !ident.span.from_expansion();
|
|
if is_good_import || binding.is_macro_def() {
|
|
let res = binding.res().map_id(|id| this.local_def_id(id));
|
|
if res != def::Res::Err {
|
|
reexports.push(Export { ident, res, span: binding.span, vis: binding.vis });
|
|
}
|
|
}
|
|
|
|
if let NameBindingKind::Import { binding: orig_binding, import, .. } = binding.kind {
|
|
if ns == TypeNS
|
|
&& orig_binding.is_variant()
|
|
&& !orig_binding.vis.is_at_least(binding.vis, &*this)
|
|
{
|
|
let msg = match import.kind {
|
|
ImportKind::Single { .. } => {
|
|
format!("variant `{}` is private and cannot be re-exported", ident)
|
|
}
|
|
ImportKind::Glob { .. } => {
|
|
let msg = "enum is private and its variants \
|
|
cannot be re-exported"
|
|
.to_owned();
|
|
let error_id = (
|
|
DiagnosticMessageId::ErrorId(0), // no code?!
|
|
Some(binding.span),
|
|
msg.clone(),
|
|
);
|
|
let fresh =
|
|
this.session.one_time_diagnostics.borrow_mut().insert(error_id);
|
|
if !fresh {
|
|
return;
|
|
}
|
|
msg
|
|
}
|
|
ref s => bug!("unexpected import kind {:?}", s),
|
|
};
|
|
let mut err = this.session.struct_span_err(binding.span, &msg);
|
|
|
|
let imported_module = match import.imported_module.get() {
|
|
Some(ModuleOrUniformRoot::Module(module)) => module,
|
|
_ => bug!("module should exist"),
|
|
};
|
|
let parent_module = imported_module.parent.expect("parent should exist");
|
|
let resolutions = this.resolutions(parent_module).borrow();
|
|
let enum_path_segment_index = import.module_path.len() - 1;
|
|
let enum_ident = import.module_path[enum_path_segment_index].ident;
|
|
|
|
let key = this.new_key(enum_ident, TypeNS);
|
|
let enum_resolution = resolutions.get(&key).expect("resolution should exist");
|
|
let enum_span =
|
|
enum_resolution.borrow().binding.expect("binding should exist").span;
|
|
let enum_def_span = this.session.source_map().guess_head_span(enum_span);
|
|
let enum_def_snippet = this
|
|
.session
|
|
.source_map()
|
|
.span_to_snippet(enum_def_span)
|
|
.expect("snippet should exist");
|
|
// potentially need to strip extant `crate`/`pub(path)` for suggestion
|
|
let after_vis_index = enum_def_snippet
|
|
.find("enum")
|
|
.expect("`enum` keyword should exist in snippet");
|
|
let suggestion = format!("pub {}", &enum_def_snippet[after_vis_index..]);
|
|
|
|
this.session.diag_span_suggestion_once(
|
|
&mut err,
|
|
DiagnosticMessageId::ErrorId(0),
|
|
enum_def_span,
|
|
"consider making the enum public",
|
|
suggestion,
|
|
);
|
|
err.emit();
|
|
}
|
|
}
|
|
});
|
|
|
|
if !reexports.is_empty() {
|
|
if let Some(def_id) = module.def_id() {
|
|
// Call to `expect_local` should be fine because current
|
|
// code is only called for local modules.
|
|
self.r.export_map.insert(def_id.expect_local(), reexports);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn import_path_to_string(names: &[Ident], import_kind: &ImportKind<'_>, span: Span) -> String {
|
|
let pos = names.iter().position(|p| span == p.span && p.name != kw::PathRoot);
|
|
let global = !names.is_empty() && names[0].name == kw::PathRoot;
|
|
if let Some(pos) = pos {
|
|
let names = if global { &names[1..pos + 1] } else { &names[..pos + 1] };
|
|
names_to_string(&names.iter().map(|ident| ident.name).collect::<Vec<_>>())
|
|
} else {
|
|
let names = if global { &names[1..] } else { names };
|
|
if names.is_empty() {
|
|
import_kind_to_string(import_kind)
|
|
} else {
|
|
format!(
|
|
"{}::{}",
|
|
names_to_string(&names.iter().map(|ident| ident.name).collect::<Vec<_>>()),
|
|
import_kind_to_string(import_kind),
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn import_kind_to_string(import_kind: &ImportKind<'_>) -> String {
|
|
match import_kind {
|
|
ImportKind::Single { source, .. } => source.to_string(),
|
|
ImportKind::Glob { .. } => "*".to_string(),
|
|
ImportKind::ExternCrate { .. } => "<extern crate>".to_string(),
|
|
ImportKind::MacroUse => "#[macro_use]".to_string(),
|
|
}
|
|
}
|