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
7 changes: 6 additions & 1 deletion compiler/rustc_resolve/src/build_reduced_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1013,7 +1013,12 @@ impl<'a, 'ra, 'tcx> DefCollector<'a, 'ra, 'tcx> {
);
crate_id.map(|crate_id| {
self.r.extern_crate_map.insert(local_def_id, crate_id);
self.r.expect_module(crate_id.as_def_id())
let module = self.r.expect_module(crate_id.as_def_id());
self.r.try_add_def_id_for_namespaced_crate(
crate_id.as_def_id(),
IdentKey::new(orig_ident),
);
module
})
}
.map(|module| {
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_resolve/src/error_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1499,6 +1499,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
Scope::ModuleGlobs(..) => {
// Already handled in `ModuleNonGlobs`.
}
Scope::NamespacedCrates(..) => {
// Do not suggest namespaced crates as ordinary in-module typo candidates.
}
Scope::MacroUsePrelude => {
suggestions.extend(this.macro_use_prelude.iter().filter_map(
|(name, binding)| {
Expand Down
39 changes: 36 additions & 3 deletions compiler/rustc_resolve/src/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let module_only = matches!(scope_set, ScopeSet::Module(..));
let module_and_extern_prelude = matches!(scope_set, ScopeSet::ModuleAndExternPrelude(..));
let extern_prelude = matches!(scope_set, ScopeSet::ExternPrelude);
let visit_namespaced_crates = ns == TypeNS && !self.namespaced_crate_names.is_empty();
let mut scope = match ns {
_ if module_only || module_and_extern_prelude => Scope::ModuleNonGlobs(module, None),
_ if extern_prelude => Scope::ExternPreludeItems,
Expand Down Expand Up @@ -151,6 +152,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
true
}
Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..) => true,
Scope::NamespacedCrates(..) => visit_namespaced_crates,
Scope::MacroUsePrelude => use_prelude || orig_ident_span.is_rust_2015(),
Scope::BuiltinAttrs => true,
Scope::ExternPreludeItems | Scope::ExternPreludeFlags => {
Expand Down Expand Up @@ -194,15 +196,35 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
MacroRulesScope::Empty => Scope::ModuleNonGlobs(module, None),
},
Scope::ModuleNonGlobs(module, lint_id) => Scope::ModuleGlobs(module, lint_id),
Scope::ModuleGlobs(..) if module_only => break,
Scope::ModuleGlobs(..) if module_and_extern_prelude => match ns {
Scope::ModuleGlobs(..) if module_only && !visit_namespaced_crates => break,
Scope::ModuleGlobs(module, lint_id) if module_only => {
Scope::NamespacedCrates(module, lint_id)
}
Scope::NamespacedCrates(..) if module_only => break,
Scope::ModuleGlobs(..) if module_and_extern_prelude && !visit_namespaced_crates => {
match ns {
TypeNS => {
ctxt.update_unchecked(|ctxt| ctxt.adjust(ExpnId::root()));
Scope::ExternPreludeItems
}
ValueNS | MacroNS => break,
}
}
Scope::ModuleGlobs(module, lint_id) if module_and_extern_prelude => {
Scope::NamespacedCrates(module, lint_id)
}
Scope::NamespacedCrates(..) if module_and_extern_prelude => match ns {
TypeNS => {
ctxt.update_unchecked(|ctxt| ctxt.adjust(ExpnId::root()));
Scope::ExternPreludeItems
}
ValueNS | MacroNS => break,
},
Scope::ModuleGlobs(module, prev_lint_id) => {
Scope::ModuleGlobs(module, prev_lint_id) if visit_namespaced_crates => {
Scope::NamespacedCrates(module, prev_lint_id)
}
Scope::ModuleGlobs(module, prev_lint_id)
| Scope::NamespacedCrates(module, prev_lint_id) => {
use_prelude = !module.no_implicit_prelude;
match self.hygienic_lexical_parent(module, &mut ctxt, derive_fallback_lint_id) {
Some((parent_module, lint_id)) => {
Expand Down Expand Up @@ -709,6 +731,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
Err(ControlFlow::Break(..)) => return binding,
}
}
Scope::NamespacedCrates(module, _) => {
match self.resolve_namespaced_crate_in_module(
module,
ident,
orig_ident_span,
finalize.is_some(),
) {
Some(decl) => Ok(decl),
None => Err(Determinacy::Determined),
}
}
Scope::MacroUsePrelude => match self.macro_use_prelude.get(&ident.name).cloned() {
Some(decl) => Ok(decl),
None => Err(Determinacy::determined(!self.graph_root.has_unexpanded_invocations())),
Expand Down
58 changes: 58 additions & 0 deletions compiler/rustc_resolve/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ mod macros;
pub mod rustdoc;

type Res = def::Res<NodeId>;
type NamespacedCrateNames = FxHashMap<Symbol, FxHashMap<Symbol, Symbol>>;

#[derive(Copy, Clone, PartialEq, Debug)]
enum Determinacy {
Expand Down Expand Up @@ -125,6 +126,9 @@ enum Scope<'ra> {
/// The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK`
/// lint if it should be reported.
ModuleGlobs(Module<'ra>, Option<NodeId>),
/// Namespaced extern crates available under the given module.
/// These have lower precedence than the module's regular non-glob and glob names.
NamespacedCrates(Module<'ra>, Option<NodeId>),
/// Names introduced by `#[macro_use]` attributes on `extern crate` items.
MacroUsePrelude,
/// Built-in attributes.
Expand Down Expand Up @@ -1526,6 +1530,10 @@ pub struct Resolver<'ra, 'tcx> {
/// Needed because glob delegations exclude explicitly defined names.
impl_binding_keys: FxHashMap<LocalDefId, FxHashSet<BindingKey>> = default::fx_hash_map(),

/// Extern crates with names of the form `foo::bar`, keyed by root and child.
namespaced_crate_names: NamespacedCrateNames,
namespaced_crate_root_by_def_id: CacheRefCell<FxHashMap<DefId, Symbol>>,

/// This is the `Span` where an `extern crate foo;` suggestion would be inserted, if `foo`
/// could be a crate that wasn't imported. For diagnostics use only.
current_crate_outer_attr_insert_span: Span,
Expand Down Expand Up @@ -1797,6 +1805,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
invocation_parents.insert(LocalExpnId::ROOT, InvocationParent::ROOT);

let extern_prelude = build_extern_prelude(tcx, attrs);
let namespaced_crate_names = build_namespaced_crate_names(tcx);
let registered_tools = tcx.registered_tools(());
let edition = tcx.sess.edition();

Expand Down Expand Up @@ -1864,6 +1873,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
macro_reachable_adts: Default::default(),
doc_link_resolutions: Default::default(),
doc_link_traits_in_scope: Default::default(),
namespaced_crate_names,
namespaced_crate_root_by_def_id: Default::default(),
current_crate_outer_attr_insert_span,
disambiguators: Default::default(),
delegation_infos: Default::default(),
Expand Down Expand Up @@ -2117,6 +2128,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
Scope::ModuleGlobs(..) => {
// Already handled in `ModuleNonGlobs` (but see #144993).
}
Scope::NamespacedCrates(..) => {
// This scope only resolves child crate roots, it does not introduce traits.
}
Scope::StdLibPrelude => {
if let Some(module) = this.prelude {
this.get_mut().traits_in_module(module, assoc_item, &mut found_traits);
Expand Down Expand Up @@ -2476,6 +2490,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
crate_id.map(|crate_id| {
let def_id = crate_id.as_def_id();
let res = Res::Def(DefKind::Mod, def_id);
self.try_add_def_id_for_namespaced_crate(def_id, ident);
self.arenas.new_pub_def_decl(res, DUMMY_SP, LocalExpnId::ROOT)
})
}
Expand All @@ -2486,6 +2501,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
})
}

fn try_add_def_id_for_namespaced_crate(&self, def_id: DefId, ident: IdentKey) {
if self.namespaced_crate_names.contains_key(&ident.name) {
self.namespaced_crate_root_by_def_id.borrow_mut().insert(def_id, ident.name);
}
}

fn resolve_namespaced_crate_in_module(
&self,
module: Module<'ra>,
ident: IdentKey,
orig_ident_span: Span,
finalize: bool,
) -> Option<Decl<'ra>> {
let def_id = module.opt_def_id()?;
let root_name = {
let names = self.namespaced_crate_root_by_def_id.borrow();
names.get(&def_id).copied()
}?;
let full_name = self.namespaced_crate_names.get(&root_name)?.get(&ident.name).copied()?;
self.extern_prelude_get_flag(IdentKey::with_root_ctxt(full_name), orig_ident_span, finalize)
}

/// Rustdoc uses this to resolve doc link paths in a recoverable way. `PathResult<'a>`
/// isn't something that can be returned because it can't be made to live that long,
/// and also it's a private type. Fortunately rustdoc doesn't need to know the error,
Expand Down Expand Up @@ -2724,6 +2761,27 @@ fn build_extern_prelude<'tcx, 'ra>(
extern_prelude
}

fn build_namespaced_crate_names(tcx: TyCtxt<'_>) -> NamespacedCrateNames {
let mut names: NamespacedCrateNames = FxHashMap::default();
for (name, entry) in tcx.sess.opts.externs.iter() {
if !entry.add_prelude {
continue;
}
let Some((root, child)) = name.split_once("::") else {
continue;
};
if tcx.sess.opts.externs.get(root).is_none() {
continue;
}

names
.entry(Symbol::intern(root))
.or_default()
.insert(Symbol::intern(child), Symbol::intern(name));
}
names
}

fn names_to_string(names: impl Iterator<Item = Symbol>) -> String {
let mut result = String::new();
for (i, name) in names.enumerate().filter(|(_, name)| *name != kw::PathRoot) {
Expand Down
4 changes: 1 addition & 3 deletions tests/ui/resolve/open-ns-1.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//@ check-pass
//@ aux-crate:my_api=open-ns-my_api.rs
//@ aux-crate:my_api::utils=open-ns-my_api_utils.rs
//@ aux-crate:my_api::core=open-ns-my_api_core.rs
Expand All @@ -6,14 +7,11 @@

use my_api::root_function;
use my_api::utils::util;
//~^ ERROR unresolved import `my_api::utils`

fn main() {
let _ = root_function();
let _ = my_api::root_function();
let _ = my_api::utils::utils_helper();
//~^ ERROR cannot find `utils` in `my_api` [E0433]
let _ = util::util_mod_helper();
let _ = my_api::core::core_fn();
//~^ ERROR cannot find `core` in `my_api` [E0433]
}
22 changes: 0 additions & 22 deletions tests/ui/resolve/open-ns-1.stderr

This file was deleted.

14 changes: 14 additions & 0 deletions tests/ui/resolve/open-ns-12.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//@ check-pass
//@ aux-crate: my_api=open-ns-my_api.rs
//@ aux-crate: my_api::utils=open-ns-my_api_utils.rs
//@ compile-flags: -Z namespaced-crates
//@ edition: 2024

extern crate my_api;

use my_api::utils::utils_helper;

fn main() {
let _ = utils_helper();
let _ = my_api::utils::utils_helper();
}
3 changes: 1 addition & 2 deletions tests/ui/resolve/open-ns-2.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
//@ check-pass
//@ aux-crate: my_api=open-ns-my_api.rs
//@ aux-crate: my_api::utils=open-ns-my_api_utils.rs
//@ aux-crate: my_api::core=open-ns-my_api_core.rs
//@ compile-flags: -Z namespaced-crates
//@ edition: 2024

use my_api::core::{core_fn, core_fn2};
//~^ ERROR unresolved import `my_api::core` [E0432]
use my_api::utils::*;
//~^ ERROR unresolved import `my_api::utils` [E0432]
use my_api::*;

fn main() {
Expand Down
15 changes: 0 additions & 15 deletions tests/ui/resolve/open-ns-2.stderr

This file was deleted.

4 changes: 2 additions & 2 deletions tests/ui/resolve/open-ns-4.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// This tests that namespaced crates are shadowed.
// Tests fully qualified paths through an existing root crate.

//@ aux-crate: my_api=open-ns-my_api.rs
//@ aux-crate: my_api::utils=open-ns-my_api_utils.rs
//@ compile-flags: -Z namespaced-crates
//@ edition: 2024
//@ check-pass

fn main() {
let _ = my_api::root_function();
let _ = my_api::utils::utils_helper();
//~^ ERROR cannot find `utils` in `my_api` [E0433]
}
9 changes: 0 additions & 9 deletions tests/ui/resolve/open-ns-4.stderr

This file was deleted.

5 changes: 2 additions & 3 deletions tests/ui/resolve/open-ns-7.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
// Tests that namespaced crates cannot be resolved if shadowed.
// Tests that a namespaced child crate resolves through an existing root crate.

//@ aux-crate: my_api=open-ns-my_api.rs
//@ aux-crate: my_api::utils=open-ns-my_api_utils.rs
//@ compile-flags: -Z namespaced-crates
//@ edition: 2024
//@ check-pass

use my_api::utils::utils_helper;
//~^ ERROR unresolved import `my_api::utils` [E0432]

fn main() {
let _ = my_api::utils::utils_helper();
//~^ ERROR cannot find `utils` in `my_api` [E0433]
}
16 changes: 0 additions & 16 deletions tests/ui/resolve/open-ns-7.stderr

This file was deleted.

Loading