diff options
| author | bors <bors@rust-lang.org> | 2018-08-14 04:22:14 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2018-08-14 04:22:14 +0000 |
| commit | 3e05f80a3790bb96247e9325fe54e484086f77f5 (patch) | |
| tree | 1e7fdb011323c7e175d76fa1a690386e06ffb274 | |
| parent | d5a448b3f47b22c9cb010198bdcc49d4392b090b (diff) | |
| parent | 13bc0b5a48aff52e828b6b645b166ccfa56e7681 (diff) | |
| download | rust-3e05f80a3790bb96247e9325fe54e484086f77f5.tar.gz rust-3e05f80a3790bb96247e9325fe54e484086f77f5.zip | |
Auto merge of #52923 - eddyb:relative-imports, r=petrochenkov
#[feature(uniform_paths)]: allow `use x::y;` to resolve through `self::x`, not just `::x`.
_Branch originally by @cramertj, based on @petrochenkov's [description on the internals forum](https://internals.rust-lang.org/t/relative-paths-in-rust-2018/7883/30?u=petrochenkov)._
_(note, however, that the approach has significantly changed since)_
Implements `#[feature(uniform_paths)]` from #53130, by treating unqualified `use` paths as maybe-relative. That is, `use x::y;`, where `x` is a plain identifier (not a keyword), is no longer the same as `use ::x::y;`, and before picking an external crate named `x`, it first looks for an item named `x` in the same module (i.e. `self::x`) and prefers that local item instead.
Such a "maybe-relative" `x` can only resolve to an external crate if it's listed in "`extern_prelude`" (i.e. `core` / `std` and all the crates passed to `--extern`; the latter includes Cargo dependencies) - this is the same condition as being able to refer to the external crate from an unqualified, non-`use` path.
All other crates must be explicitly imported with an absolute path, e.g. `use ::x::y;`
To detect an ambiguity between the external crate and the local item with the same name, a "canary" import (e.g. `use self::x as _;`), tagged with the `is_uniform_paths_canary` flag, is injected. As the initial implementation is not sophisticated enough to handle all possible ways in which `self::x` could appear (e.g. from macro expansion), this also guards against accidentally picking the external crate, when it might actually get "shadowed" later.
Also, more canaries are injected for each block scope around the `use`, as `self::x` cannot resolve to any items named `x` in those scopes, but non-`use` paths can, and that could be confusing or even backwards-incompatible.
Errors are emitted only if the main "canary" import succeeds while an external crate exists (or if any of the block-scoped ones succeed at all), and ambiguities have custom error reporting, e.g.:
```rust
#![feature(uniform_paths)]
pub mod foo {
use std::io;
pub mod std { pub mod io {} }
}
```
```rust
error: import from `std` is ambiguous
--> test.rs:3:9
|
3 | use std::io;
| ^^^ could refer to external crate `::std`
4 | pub mod std { pub mod io {} }
| ----------------------------- could also refer to `self::std`
|
= help: write `::std` or `self::std` explicitly instead
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`
```
Another example, this time with a block-scoped item shadowing a module-scoped one:
```rust
#![feature(uniform_paths)]
enum Foo { A, B }
fn main() {
enum Foo {}
use Foo::*;
}
```
```rust
error: import from `Foo` is ambiguous
--> test.rs:5:9
|
4 | enum Foo {}
| ----------- shadowed by block-scoped `Foo`
5 | use Foo::*;
| ^^^
|
= help: write `::Foo` or `self::Foo` explicitly instead
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`
```
Additionally, this PR, because replacing "the `finalize_import` hack" was a blocker:
* fixes #52140
* fixes #52141
* fixes #52705
cc @aturon @joshtriplett
39 files changed, 1295 insertions, 271 deletions
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index b6e03b66510..ee98cc6cf92 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -865,7 +865,7 @@ where krate = time(sess, "crate injection", || { let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| &**s); - syntax::std_inject::maybe_inject_crates_ref(krate, alt_std_name) + syntax::std_inject::maybe_inject_crates_ref(krate, alt_std_name, sess.edition()) }); let mut addl_plugins = Some(addl_plugins); diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index d3b70933e2c..a10bb3e25df 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -1139,32 +1139,4 @@ impl<'a> CrateLoader<'a> { cnum } - - pub fn process_use_extern( - &mut self, - name: Symbol, - span: Span, - id: ast::NodeId, - definitions: &Definitions, - ) -> CrateNum { - let cnum = self.resolve_crate( - &None, name, name, None, None, span, PathKind::Crate, DepKind::Explicit - ).0; - - let def_id = definitions.opt_local_def_id(id).unwrap(); - let path_len = definitions.def_path(def_id.index).data.len(); - - self.update_extern_crate( - cnum, - ExternCrate { - src: ExternCrateSource::Use, - span, - path_len, - direct: true, - }, - &mut FxHashSet(), - ); - - cnum - } } diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index bf87b00c149..fa2af891f10 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -17,7 +17,7 @@ use macros::{InvocationData, LegacyScope}; use resolve_imports::ImportDirective; use resolve_imports::ImportDirectiveSubclass::{self, GlobImport, SingleImport}; use {Module, ModuleData, ModuleKind, NameBinding, NameBindingKind, ToNameBinding}; -use {PerNS, Resolver, ResolverArenas}; +use {ModuleOrUniformRoot, PerNS, Resolver, ResolverArenas}; use Namespace::{self, TypeNS, ValueNS, MacroNS}; use {resolve_error, resolve_struct_error, ResolutionError}; @@ -113,16 +113,24 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } } - fn build_reduced_graph_for_use_tree(&mut self, - root_use_tree: &ast::UseTree, - root_id: NodeId, - use_tree: &ast::UseTree, - id: NodeId, - vis: ty::Visibility, - prefix: &ast::Path, - nested: bool, - item: &Item, - expansion: Mark) { + fn build_reduced_graph_for_use_tree( + &mut self, + root_use_tree: &ast::UseTree, + root_id: NodeId, + use_tree: &ast::UseTree, + id: NodeId, + vis: ty::Visibility, + prefix: &ast::Path, + mut uniform_paths_canary_emitted: bool, + nested: bool, + item: &Item, + expansion: Mark, + ) { + debug!("build_reduced_graph_for_use_tree(prefix={:?}, \ + uniform_paths_canary_emitted={}, \ + use_tree={:?}, nested={})", + prefix, uniform_paths_canary_emitted, use_tree, nested); + let is_prelude = attr::contains_name(&item.attrs, "prelude_import"); let path = &use_tree.prefix; @@ -131,6 +139,103 @@ impl<'a, 'cl> Resolver<'a, 'cl> { .map(|seg| seg.ident) .collect(); + debug!("build_reduced_graph_for_use_tree: module_path={:?}", module_path); + + // `#[feature(uniform_paths)]` allows an unqualified import path, + // e.g. `use x::...;` to resolve not just globally (`use ::x::...;`) + // but also relatively (`use self::x::...;`). To catch ambiguities + // that might arise from both of these being available and resolution + // silently picking one of them, an artificial `use self::x as _;` + // import is injected as a "canary", and an error is emitted if it + // successfully resolves while an `x` external crate exists. + // + // For each block scope around the `use` item, one special canary + // import of the form `use x as _;` is also injected, having its + // parent set to that scope; `resolve_imports` will only resolve + // it within its appropriate scope; if any of them successfully + // resolve, an ambiguity error is emitted, since the original + // import can't see the item in the block scope (`self::x` only + // looks in the enclosing module), but a non-`use` path could. + // + // Additionally, the canary might be able to catch limitations of the + // current implementation, where `::x` may be chosen due to `self::x` + // not existing, but `self::x` could appear later, from macro expansion. + // + // NB. The canary currently only errors if the `x::...` path *could* + // resolve as a relative path through the extern crate, i.e. `x` is + // in `extern_prelude`, *even though* `::x` might still forcefully + // load a non-`extern_prelude` crate. + // While always producing an ambiguity errors if `self::x` exists and + // a crate *could* be loaded, would be more conservative, imports for + // local modules named `test` (or less commonly, `syntax` or `log`), + // would need to be qualified (e.g. `self::test`), which is considered + // ergonomically unacceptable. + let emit_uniform_paths_canary = + !uniform_paths_canary_emitted && + module_path.get(0).map_or(false, |ident| { + !ident.is_path_segment_keyword() + }); + if emit_uniform_paths_canary { + // Relative paths should only get here if the feature-gate is on. + assert!(self.session.rust_2018() && + self.session.features_untracked().uniform_paths); + + let source = module_path[0]; + // Helper closure to emit a canary with the given base path. + let emit = |this: &mut Self, base: Option<Ident>| { + let subclass = SingleImport { + target: Ident { + name: keywords::Underscore.name().gensymed(), + span: source.span, + }, + source, + result: PerNS { + type_ns: Cell::new(Err(Undetermined)), + value_ns: Cell::new(Err(Undetermined)), + macro_ns: Cell::new(Err(Undetermined)), + }, + type_ns_only: false, + }; + this.add_import_directive( + base.into_iter().collect(), + subclass.clone(), + source.span, + id, + root_use_tree.span, + root_id, + ty::Visibility::Invisible, + expansion, + true, // is_uniform_paths_canary + ); + }; + + // A single simple `self::x` canary. + emit(self, Some(Ident { + name: keywords::SelfValue.name(), + span: source.span, + })); + + // One special unprefixed canary per block scope around + // the import, to detect items unreachable by `self::x`. + let orig_current_module = self.current_module; + let mut span = source.span.modern(); + loop { + match self.current_module.kind { + ModuleKind::Block(..) => emit(self, None), + ModuleKind::Def(..) => break, + } + match self.hygienic_lexical_parent(self.current_module, &mut span) { + Some(module) => { + self.current_module = module; + } + None => break, + } + } + self.current_module = orig_current_module; + + uniform_paths_canary_emitted = true; + } + match use_tree.kind { ast::UseTreeKind::Simple(rename, ..) => { let mut ident = use_tree.ident(); @@ -142,8 +247,10 @@ impl<'a, 'cl> Resolver<'a, 'cl> { if source.name == keywords::SelfValue.name() { type_ns_only = true; - let last_segment = *module_path.last().unwrap(); - if last_segment.name == keywords::CrateRoot.name() { + let empty_prefix = module_path.last().map_or(true, |ident| { + ident.name == keywords::CrateRoot.name() + }); + if empty_prefix { resolve_error( self, use_tree.span, @@ -154,10 +261,9 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } // Replace `use foo::self;` with `use foo;` - let _ = module_path.pop(); - source = last_segment; + source = module_path.pop().unwrap(); if rename.is_none() { - ident = last_segment; + ident = source; } } } else { @@ -169,13 +275,23 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } // Disallow `use $crate;` - if source.name == keywords::DollarCrate.name() && path.segments.len() == 1 { + if source.name == keywords::DollarCrate.name() && module_path.is_empty() { let crate_root = self.resolve_crate_root(source); let crate_name = match crate_root.kind { ModuleKind::Def(_, name) => name, ModuleKind::Block(..) => unreachable!(), }; - source.name = crate_name; + // HACK(eddyb) unclear how good this is, but keeping `$crate` + // in `source` breaks `src/test/compile-fail/import-crate-var.rs`, + // while the current crate doesn't have a valid `crate_name`. + if crate_name != keywords::Invalid.name() { + // `crate_name` should not be interpreted as relative. + module_path.push(Ident { + name: keywords::CrateRoot.name(), + span: source.span, + }); + source.name = crate_name; + } if rename.is_none() { ident.name = crate_name; } @@ -187,6 +303,12 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } } + if ident.name == keywords::Crate.name() { + self.session.span_err(ident.span, + "crate root imports need to be explicitly named: \ + `use crate as name;`"); + } + let subclass = SingleImport { target: ident, source, @@ -206,6 +328,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { root_id, vis, expansion, + false, // is_uniform_paths_canary ); } ast::UseTreeKind::Glob => { @@ -222,6 +345,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { root_id, vis, expansion, + false, // is_uniform_paths_canary ); } ast::UseTreeKind::Nested(ref items) => { @@ -256,7 +380,16 @@ impl<'a, 'cl> Resolver<'a, 'cl> { for &(ref tree, id) in items { self.build_reduced_graph_for_use_tree( - root_use_tree, root_id, tree, id, vis, &prefix, true, item, expansion + root_use_tree, + root_id, + tree, + id, + vis, + &prefix, + uniform_paths_canary_emitted, + true, + item, + expansion, ); } } @@ -272,14 +405,32 @@ impl<'a, 'cl> Resolver<'a, 'cl> { match item.node { ItemKind::Use(ref use_tree) => { + let uniform_paths = + self.session.rust_2018() && + self.session.features_untracked().uniform_paths; // Imports are resolved as global by default, add starting root segment. + let root = if !uniform_paths { + use_tree.prefix.make_root() + } else { + // Except when `#![feature(uniform_paths)]` is on. + None + }; let prefix = ast::Path { - segments: use_tree.prefix.make_root().into_iter().collect(), + segments: root.into_iter().collect(), span: use_tree.span, }; self.build_reduced_graph_for_use_tree( - use_tree, item.id, use_tree, item.id, vis, &prefix, false, item, expansion, + use_tree, + item.id, + use_tree, + item.id, + vis, + &prefix, + false, // uniform_paths_canary_emitted + false, + item, + expansion, ); } @@ -299,7 +450,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { root_id: item.id, id: item.id, parent, - imported_module: Cell::new(Some(module)), + imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))), subclass: ImportDirectiveSubclass::ExternCrate(orig_name), root_span: item.span, span: item.span, @@ -307,6 +458,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { vis: Cell::new(vis), expansion, used: Cell::new(used), + is_uniform_paths_canary: false, }); self.potentially_unused_imports.push(directive); let imported_binding = self.import(binding, directive); @@ -701,7 +853,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { root_id: item.id, id: item.id, parent: graph_root, - imported_module: Cell::new(Some(module)), + imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))), subclass: ImportDirectiveSubclass::MacroUse, root_span: span, span, @@ -709,6 +861,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { vis: Cell::new(ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX))), expansion, used: Cell::new(false), + is_uniform_paths_canary: false, }); if let Some(span) = legacy_imports.import_all { @@ -721,7 +874,13 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } else { for (name, span) in legacy_imports.imports { let ident = Ident::with_empty_ctxt(name); - let result = self.resolve_ident_in_module(module, ident, MacroNS, false, span); + let result = self.resolve_ident_in_module( + ModuleOrUniformRoot::Module(module), + ident, + MacroNS, + false, + span, + ); if let Ok(binding) = result { let directive = macro_use_directive(span); self.potentially_unused_imports.push(directive); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index e31e2cc1dff..992ea12ffa2 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -953,9 +953,20 @@ impl<'a> LexicalScopeBinding<'a> { } } +#[derive(Copy, Clone, Debug)] +pub enum ModuleOrUniformRoot<'a> { + /// Regular module. + Module(Module<'a>), + + /// The `{{root}}` (`CrateRoot` aka "global") / `extern` initial segment + /// in which external crates resolve, and also `crate` (only in `{{root}}`, + /// but *not* `extern`), in the Rust 2018 edition. + UniformRoot(Name), +} + #[derive(Clone, Debug)] enum PathResult<'a> { - Module(Module<'a>), + Module(ModuleOrUniformRoot<'a>), NonModule(PathResolution), Indeterminate, Failed(Span, String, bool /* is the error from the last segment? */), @@ -1583,11 +1594,13 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { let hir::Path { ref segments, span, ref mut def } = *path; let path: Vec<_> = segments.iter().map(|seg| seg.ident).collect(); // FIXME (Manishearth): Intra doc links won't get warned of epoch changes - match self.resolve_path(&path, Some(namespace), true, span, CrateLint::No) { - PathResult::Module(module) => *def = module.def().unwrap(), + match self.resolve_path(None, &path, Some(namespace), true, span, CrateLint::No) { + PathResult::Module(ModuleOrUniformRoot::Module(module)) => + *def = module.def().unwrap(), PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => *def = path_res.base_def(), PathResult::NonModule(..) => match self.resolve_path( + None, &path, None, true, @@ -1599,6 +1612,7 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { } _ => {} }, + PathResult::Module(ModuleOrUniformRoot::UniformRoot(_)) | PathResult::Indeterminate => unreachable!(), PathResult::Failed(span, msg, _) => { error_callback(self, span, ResolutionError::FailedToResolve(&msg)); @@ -1881,7 +1895,12 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { }; let item = self.resolve_ident_in_module_unadjusted( - module, ident, ns, false, record_used, path_span, + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + record_used, + path_span, ); if let Ok(binding) = item { // The ident resolves to an item. @@ -1906,7 +1925,12 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { let orig_current_module = self.current_module; self.current_module = module; // Lexical resolutions can never be a privacy error. let result = self.resolve_ident_in_module_unadjusted( - module, ident, ns, false, record_used, path_span, + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + record_used, + path_span, ); self.current_module = orig_current_module; @@ -1954,8 +1978,14 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { return Some(LexicalScopeBinding::Item(binding)); } if let Some(prelude) = self.prelude { - if let Ok(binding) = self.resolve_ident_in_module_unadjusted(prelude, ident, ns, - false, false, path_span) { + if let Ok(binding) = self.resolve_ident_in_module_unadjusted( + ModuleOrUniformRoot::Module(prelude), + ident, + ns, + false, + false, + path_span, + ) { return Some(LexicalScopeBinding::Item(binding)); } } @@ -2013,7 +2043,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { } fn resolve_ident_in_module(&mut self, - module: Module<'a>, + module: ModuleOrUniformRoot<'a>, mut ident: Ident, ns: Namespace, record_used: bool, @@ -2021,8 +2051,10 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { -> Result<&'a NameBinding<'a>, Determinacy> { ident.span = ident.span.modern(); let orig_current_module = self.current_module; - if let Some(def) = ident.span.adjust(module.expansion) { - self.current_module = self.macro_def_scope(def); + if let ModuleOrUniformRoot::Module(module) = module { + if let Some(def) = ident.span.adjust(module.expansion) { + self.current_module = self.macro_def_scope(def); + } } let result = self.resolve_ident_in_module_unadjusted( module, ident, ns, false, record_used, span, @@ -2410,13 +2442,16 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { if def != Def::Err { new_id = Some(def.def_id()); let span = trait_ref.path.span; - if let PathResult::Module(module) = self.resolve_path( - &path, - None, - false, - span, - CrateLint::SimplePath(trait_ref.ref_id), - ) { + if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = + self.resolve_path( + None, + &path, + None, + false, + span, + CrateLint::SimplePath(trait_ref.ref_id), + ) + { new_val = Some((module, trait_ref.clone())); } } @@ -2533,7 +2568,13 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { // If there is a TraitRef in scope for an impl, then the method must be in the // trait. if let Some((module, _)) = self.current_trait_ref { - if self.resolve_ident_in_module(module, ident, ns, false, span).is_err() { + if self.resolve_ident_in_module( + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + span, + ).is_err() { let path = &self.current_trait_ref.as_ref().unwrap().1.path; resolve_error(self, span, err(ident.name, &path_names_to_string(path))); } @@ -2908,9 +2949,10 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { (String::new(), "the crate root".to_string()) } else { let mod_path = &path[..path.len() - 1]; - let mod_prefix = match this.resolve_path(mod_path, Some(TypeNS), + let mod_prefix = match this.resolve_path(None, mod_path, Some(TypeNS), false, span, CrateLint::No) { - PathResult::Module(module) => module.def(), + PathResult::Module(ModuleOrUniformRoot::Module(module)) => + module.def(), _ => None, }.map_or(String::new(), |def| format!("{} ", def.kind_name())); (mod_prefix, format!("`{}`", names_to_string(mod_path))) @@ -3319,6 +3361,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { } let result = match self.resolve_path( + None, &path, Some(ns), true, @@ -3326,7 +3369,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { crate_lint, ) { PathResult::NonModule(path_res) => path_res, - PathResult::Module(module) if !module.is_normal() => { + PathResult::Module(ModuleOrUniformRoot::Module(module)) if !module.is_normal() => { PathResolution::new(module.def().unwrap()) } // In `a(::assoc_item)*` `a` cannot be a module. If `a` does resolve to a module we @@ -3341,18 +3384,21 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { // // Such behavior is required for backward compatibility. // The same fallback is used when `a` resolves to nothing. - PathResult::Module(..) | PathResult::Failed(..) + PathResult::Module(ModuleOrUniformRoot::Module(_)) | + PathResult::Failed(..) if (ns == TypeNS || path.len() > 1) && self.primitive_type_table.primitive_types .contains_key(&path[0].name) => { let prim = self.primitive_type_table.primitive_types[&path[0].name]; PathResolution::with_unresolved_segments(Def::PrimTy(prim), path.len() - 1) } - PathResult::Module(module) => PathResolution::new(module.def().unwrap()), + PathResult::Module(ModuleOrUniformRoot::Module(module)) => + PathResolution::new(module.def().unwrap()), PathResult::Failed(span, msg, false) => { resolve_error(self, span, ResolutionError::FailedToResolve(&msg)); err_path_resolution() } + PathResult::Module(ModuleOrUniformRoot::UniformRoot(_)) | PathResult::Failed(..) => return None, PathResult::Indeterminate => bug!("indetermined path result in resolve_qpath"), }; @@ -3362,6 +3408,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { path[0].name != keywords::DollarCrate.name() { let unqualified_result = { match self.resolve_path( + None, &[*path.last().unwrap()], Some(ns), false, @@ -3369,7 +3416,8 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { CrateLint::No, ) { PathResult::NonModule(path_res) => path_res.base_def(), - PathResult::Module(module) => module.def().unwrap(), + PathResult::Module(ModuleOrUniformRoot::Module(module)) => + module.def().unwrap(), _ => return Some(result), } }; @@ -3384,13 +3432,14 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { fn resolve_path( &mut self, + base_module: Option<ModuleOrUniformRoot<'a>>, path: &[Ident], opt_ns: Option<Namespace>, // `None` indicates a module path record_used: bool, path_span: Span, crate_lint: CrateLint, ) -> PathResult<'a> { - let mut module = None; + let mut module = base_module; let mut allow_super = true; let mut second_binding = None; @@ -3412,49 +3461,48 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { if i == 0 && ns == TypeNS && name == keywords::SelfValue.name() { let mut ctxt = ident.span.ctxt().modern(); - module = Some(self.resolve_self(&mut ctxt, self.current_module)); + module = Some(ModuleOrUniformRoot::Module( + self.resolve_self(&mut ctxt, self.current_module))); continue } else if allow_super && ns == TypeNS && name == keywords::Super.name() { let mut ctxt = ident.span.ctxt().modern(); - let self_module = match i { - 0 => self.resolve_self(&mut ctxt, self.current_module), - _ => module.unwrap(), + let self_module_parent = match i { + 0 => self.resolve_self(&mut ctxt, self.current_module).parent, + _ => match module { + Some(ModuleOrUniformRoot::Module(module)) => module.parent, + _ => None, + }, }; - if let Some(parent) = self_module.parent { - module = Some(self.resolve_self(&mut ctxt, parent)); + if let Some(parent) = self_module_parent { + module = Some(ModuleOrUniformRoot::Module( + self.resolve_self(&mut ctxt, parent))); continue } else { let msg = "There are too many initial `super`s.".to_string(); return PathResult::Failed(ident.span, msg, false); } - } else if i == 0 && ns == TypeNS && name == keywords::Extern.name() { - continue; } allow_super = false; if ns == TypeNS { + if i == 0 { + if name == keywords::Extern.name() || + name == keywords::CrateRoot.name() && + self.session.features_untracked().extern_absolute_paths && + self.session.rust_2018() { + module = Some(ModuleOrUniformRoot::UniformRoot(name)); + continue; + } + } if (i == 0 && name == keywords::CrateRoot.name()) || (i == 0 && name == keywords::Crate.name()) || (i == 0 && name == keywords::DollarCrate.name()) || (i == 1 && name == keywords::Crate.name() && path[0].name == keywords::CrateRoot.name()) { // `::a::b`, `crate::a::b`, `::crate::a::b` or `$crate::a::b` - module = Some(self.resolve_crate_root(ident)); + module = Some(ModuleOrUniformRoot::Module( + self.resolve_crate_root(ident))); continue - } else if i == 1 && !ident.is_path_segment_keyword() { - let prev_name = path[0].name; - if prev_name == keywords::Extern.name() || - prev_name == keywords::CrateRoot.name() && - self.session.features_untracked().extern_absolute_paths && - self.session.rust_2018() { - // `::extern_crate::a::b` - let crate_id = self.crate_loader.process_path_extern(name, ident.span); - let crate_root = - self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX }); - self.populate_module_if_necessary(crate_root); - module = Some(crate_root); - continue - } } } @@ -3513,7 +3561,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { let def = binding.def(); let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(def); if let Some(next_module) = binding.module() { - module = Some(next_module); + module = Some(ModuleOrUniformRoot::Module(next_module)); } else if def == Def::ToolMod && i + 1 != path.len() { let def = Def::NonMacroAttr(NonMacroAttrKind::Tool); return PathResult::NonModule(PathResolution::new(def)); @@ -3537,14 +3585,18 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { } Err(Undetermined) => return PathResult::Indeterminate, Err(Determined) => { - if let Some(module) = module { + if let Some(ModuleOrUniformRoot::Module(module)) = module { if opt_ns.is_some() && !module.is_normal() { return PathResult::NonModule(PathResolution::with_unresolved_segments( module.def().unwrap(), path.len() - i )); } } - let msg = if module.and_then(ModuleData::def) == self.graph_root.def() { + let module_def = match module { + Some(ModuleOrUniformRoot::Module(module)) => module.def(), + _ => None, + }; + let msg = if module_def == self.graph_root.def() { let is_mod = |def| match def { Def::Mod(..) => true, _ => false }; let mut candidates = self.lookup_import_candidates(name, TypeNS, is_mod); @@ -3568,7 +3620,10 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { self.lint_if_path_starts_with_module(crate_lint, path, path_span, second_binding); - PathResult::Module(module.unwrap_or(self.graph_root)) + PathResult::Module(module.unwrap_or_else(|| { + span_bug!(path_span, "resolve_path: empty(?) path {:?} has no module", path); + })) + } fn lint_if_path_starts_with_module( @@ -3578,6 +3633,17 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { path_span: Span, second_binding: Option<&NameBinding>, ) { + // In the 2018 edition this lint is a hard error, so nothing to do + if self.session.rust_2018() { + return + } + + // In the 2015 edition there's no use in emitting lints unless the + // crate's already enabled the feature that we're going to suggest + if !self.session.features_untracked().crate_in_paths { + return + } + let (diag_id, diag_span) = match crate_lint { CrateLint::No => return, CrateLint::SimplePath(id) => (id, path_span), @@ -3620,24 +3686,11 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { } } - self.lint_path_starts_with_module(diag_id, diag_span); - } - - fn lint_path_starts_with_module(&self, id: NodeId, span: Span) { - // In the 2018 edition this lint is a hard error, so nothing to do - if self.session.rust_2018() { - return - } - // In the 2015 edition there's no use in emitting lints unless the - // crate's already enabled the feature that we're going to suggest - if !self.session.features_untracked().crate_in_paths { - return - } let diag = lint::builtin::BuiltinLintDiagnostics - ::AbsPathWithModule(span); + ::AbsPathWithModule(diag_span); self.session.buffer_lint_with_diagnostic( lint::builtin::ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, - id, span, + diag_id, diag_span, "absolute paths must start with `self`, `super`, \ `crate`, or an external crate name in the 2018 edition", diag); @@ -3782,8 +3835,13 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { // Look for associated items in the current trait. if let Some((module, _)) = self.current_trait_ref { - if let Ok(binding) = - self.resolve_ident_in_module(module, ident, ns, false, module.span) { + if let Ok(binding) = self.resolve_ident_in_module( + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + module.span, + ) { let def = binding.def(); if filter_fn(def) { return Some(if self.has_self.contains(&def.def_id()) { @@ -3855,9 +3913,11 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { } else { // Search in module. let mod_path = &path[..path.len() - 1]; - if let PathResult::Module(module) = self.resolve_path(mod_path, Some(TypeNS), + if let PathResult::Module(module) = self.resolve_path(None, mod_path, Some(TypeNS), false, span, CrateLint::No) { - add_module_candidates(module, &mut names); + if let ModuleOrUniformRoot::Module(module) = module { + add_module_candidates(module, &mut names); + } } } @@ -4096,7 +4156,13 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { let mut found_traits = Vec::new(); // Look for the current trait. if let Some((module, _)) = self.current_trait_ref { - if self.resolve_ident_in_module(module, ident, ns, false, module.span).is_ok() { + if self.resolve_ident_in_module( + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + module.span, + ).is_ok() { let def_id = module.def_id().unwrap(); found_traits.push(TraitCandidate { def_id: def_id, import_id: None }); } @@ -4144,8 +4210,14 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { if ident.span.glob_adjust(module.expansion, binding.span.ctxt().modern()).is_none() { continue } - if self.resolve_ident_in_module_unadjusted(module, ident, ns, false, false, module.span) - .is_ok() { + if self.resolve_ident_in_module_unadjusted( + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + false, + module.span, + ).is_ok() { let import_id = match binding.kind { NameBindingKind::Import { directive, .. } => { self.maybe_unused_trait_imports.insert(directive.id); diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index fe9d3c7eb99..44d0c888c5d 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -10,6 +10,7 @@ use {AmbiguityError, CrateLint, Resolver, ResolutionError, is_known_tool, resolve_error}; use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult, ToNameBinding}; +use ModuleOrUniformRoot; use Namespace::{self, TypeNS, MacroNS}; use build_reduced_graph::{BuildReducedGraphVisitor, IsMacroExport}; use resolve_imports::ImportResolver; @@ -538,7 +539,8 @@ impl<'a, 'cl> Resolver<'a, 'cl> { return Err(Determinacy::Determined); } - let def = match self.resolve_path(&path, Some(MacroNS), false, span, CrateLint::No) { + let res = self.resolve_path(None, &path, Some(MacroNS), false, span, CrateLint::No); + let def = match res { PathResult::NonModule(path_res) => match path_res.base_def() { Def::Err => Err(Determinacy::Determined), def @ _ => { @@ -655,7 +657,12 @@ impl<'a, 'cl> Resolver<'a, 'cl> { WhereToResolve::Module(module) => { let orig_current_module = mem::replace(&mut self.current_module, module); let binding = self.resolve_ident_in_module_unadjusted( - module, ident, ns, true, record_used, path_span, + ModuleOrUniformRoot::Module(module), + ident, + ns, + true, + record_used, + path_span, ); self.current_module = orig_current_module; binding.map(MacroBinding::Modern) @@ -715,9 +722,14 @@ impl<'a, 'cl> Resolver<'a, 'cl> { let mut result = Err(Determinacy::Determined); if use_prelude { if let Some(prelude) = self.prelude { - if let Ok(binding) = - self.resolve_ident_in_module_unadjusted(prelude, ident, ns, - false, false, path_span) { + if let Ok(binding) = self.resolve_ident_in_module_unadjusted( + ModuleOrUniformRoot::Module(prelude), + ident, + ns, + false, + false, + path_span, + ) { result = Ok(MacroBinding::Global(binding)); } } @@ -893,7 +905,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { pub fn finalize_current_module_macro_resolutions(&mut self) { let module = self.current_module; for &(ref path, span) in module.macro_resolutions.borrow().iter() { - match self.resolve_path(&path, Some(MacroNS), true, span, CrateLint::No) { + match self.resolve_path(None, &path, Some(MacroNS), true, span, CrateLint::No) { PathResult::NonModule(_) => {}, PathResult::Failed(span, msg, _) => { resolve_error(self, span, ResolutionError::FailedToResolve(&msg)); diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 715292bc116..1d8cc609f95 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -10,8 +10,8 @@ use self::ImportDirectiveSubclass::*; -use {AmbiguityError, CrateLint, Module, PerNS}; -use Namespace::{self, TypeNS, MacroNS}; +use {AmbiguityError, CrateLint, Module, ModuleOrUniformRoot, PerNS}; +use Namespace::{self, TypeNS, MacroNS, ValueNS}; use {NameBinding, NameBindingKind, ToNameBinding, PathResult, PrivacyError}; use Resolver; use {names_to_string, module_to_string}; @@ -85,11 +85,19 @@ pub struct ImportDirective<'a> { pub parent: Module<'a>, pub module_path: Vec<Ident>, - pub imported_module: Cell<Option<Module<'a>>>, // the resolution of `module_path` + /// The resolution of `module_path`. + pub imported_module: Cell<Option<ModuleOrUniformRoot<'a>>>, pub subclass: ImportDirectiveSubclass<'a>, pub vis: Cell<ty::Visibility>, pub expansion: Mark, pub used: Cell<bool>, + + /// Whether this import is a "canary" for the `uniform_paths` feature, + /// i.e. `use x::...;` results in an `use self::x as _;` canary. + /// This flag affects diagnostics: an error is reported if and only if + /// the import resolves successfully and an external crate with the same + /// name (`x` above) also exists; any resolution failures are ignored. + pub is_uniform_paths_canary: bool, } impl<'a> ImportDirective<'a> { @@ -133,13 +141,87 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { /// Attempts to resolve `ident` in namespaces `ns` of `module`. /// Invariant: if `record_used` is `Some`, expansion and import resolution must be complete. pub fn resolve_ident_in_module_unadjusted(&mut self, - module: Module<'a>, + module: ModuleOrUniformRoot<'a>, ident: Ident, ns: Namespace, restricted_shadowing: bool, record_used: bool, path_span: Span) -> Result<&'a NameBinding<'a>, Determinacy> { + let module = match module { + ModuleOrUniformRoot::Module(module) => module, + ModuleOrUniformRoot::UniformRoot(root) => { + // HACK(eddyb): `resolve_path` uses `keywords::Invalid` to indicate + // paths of length 0, and currently these are relative `use` paths. + let can_be_relative = !ident.is_path_segment_keyword() && + root == keywords::Invalid.name(); + if can_be_relative { + // Relative paths should only get here if the feature-gate is on. + assert!(self.session.rust_2018() && + self.session.features_untracked().uniform_paths); + + // Try first to resolve relatively. + let mut ctxt = ident.span.ctxt().modern(); + let self_module = self.resolve_self(&mut ctxt, self.current_module); + + let binding = self.resolve_ident_in_module_unadjusted( + ModuleOrUniformRoot::Module(self_module), + ident, + ns, + restricted_shadowing, + record_used, + path_span, + ); + + // FIXME(eddyb) This may give false negatives, specifically + // if a crate with the same name is found in `extern_prelude`, + // preventing the check below this one from returning `binding` + // in all cases. + // + // That is, if there's no crate with the same name, `binding` + // is always returned, which is the result of doing the exact + // same lookup of `ident`, in the `self` module. + // But when a crate does exist, it will get chosen even when + // macro expansion could result in a success from the lookup + // in the `self` module, later on. + // + // NB. This is currently alleviated by the "ambiguity canaries" + // (see `is_uniform_paths_canary`) that get introduced for the + // maybe-relative imports handled here: if the false negative + // case were to arise, it would *also* cause an ambiguity error. + if binding.is_ok() { + return binding; + } + + // Fall back to resolving to an external crate. + if !self.extern_prelude.contains(&ident.name) { + // ... unless the crate name is not in the `extern_prelude`. + return binding; + } + } + + let crate_root = if + root != keywords::Extern.name() && + ( + ident.name == keywords::Crate.name() || + ident.name == keywords::DollarCrate.name() + ) + { + self.resolve_crate_root(ident) + } else if !ident.is_path_segment_keyword() { + let crate_id = + self.crate_loader.process_path_extern(ident.name, ident.span); + self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX }) + } else { + return Err(Determined); + }; + self.populate_module_if_necessary(crate_root); + let binding = (crate_root, ty::Visibility::Public, + ident.span, Mark::root()).to_name_binding(self.arenas); + return Ok(binding); + } + }; + self.populate_module_if_necessary(module); let resolution = self.resolution(module, ident, ns) @@ -260,7 +342,11 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { if !self.is_accessible(glob_import.vis.get()) { continue } - let module = unwrap_or!(glob_import.imported_module.get(), return Err(Undetermined)); + let module = match glob_import.imported_module.get() { + Some(ModuleOrUniformRoot::Module(module)) => module, + Some(ModuleOrUniformRoot::UniformRoot(_)) => continue, + None => return Err(Undetermined), + }; let (orig_current_module, mut ident) = (self.current_module, ident.modern()); match ident.span.glob_adjust(module.expansion, glob_import.span.ctxt().modern()) { Some(Some(def)) => self.current_module = self.macro_def_scope(def), @@ -268,7 +354,12 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { None => continue, }; let result = self.resolve_ident_in_module_unadjusted( - module, ident, ns, false, false, path_span, + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + false, + path_span, ); self.current_module = orig_current_module; match result { @@ -290,7 +381,8 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { root_span: Span, root_id: NodeId, vis: ty::Visibility, - expansion: Mark) { + expansion: Mark, + is_uniform_paths_canary: bool) { let current_module = self.current_module; let directive = self.arenas.alloc_import_directive(ImportDirective { parent: current_module, @@ -304,8 +396,11 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { vis: Cell::new(vis), expansion, used: Cell::new(false), + is_uniform_paths_canary, }); + debug!("add_import_directive({:?})", directive); + self.indeterminate_imports.push(directive); match directive.subclass { SingleImport { target, type_ns_only, .. } => { @@ -523,7 +618,55 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { let mut seen_spans = FxHashSet(); for i in 0 .. self.determined_imports.len() { let import = self.determined_imports[i]; - if let Some((span, err)) = self.finalize_import(import) { + let error = self.finalize_import(import); + + // For a `#![feature(uniform_paths)]` `use self::x as _` canary, + // failure is ignored, while success may cause an ambiguity error. + if import.is_uniform_paths_canary { + let (name, result) = match import.subclass { + SingleImport { source, ref result, .. } => { + let type_ns = result[TypeNS].get().ok(); + let value_ns = result[ValueNS].get().ok(); + (source.name, type_ns.or(value_ns)) + } + _ => bug!(), + }; + + if error.is_some() { + continue; + } + + let is_explicit_self = + import.module_path.len() > 0 && + import.module_path[0].name == keywords::SelfValue.name(); + let extern_crate_exists = self.extern_prelude.contains(&name); + + // A successful `self::x` is ambiguous with an `x` external crate. + if is_explicit_self && !extern_crate_exists { + continue; + } + + errors = true; + + let msg = format!("import from `{}` is ambiguous", name); + let mut err = self.session.struct_span_err(import.span, &msg); + if extern_crate_exists { + err.span_label(import.span, + format!("could refer to external crate `::{}`", name)); + } + if let Some(result) = result { + if is_explicit_self { + err.span_label(result.span, + format!("could also refer to `self::{}`", name)); + } else { + err.span_label(result.span, + format!("shadowed by block-scoped `{}`", name)); + } + } + err.help(&format!("write `::{0}` or `self::{0}` explicitly instead", name)); + err.note("relative `use` paths enabled by `#![feature(uniform_paths)]`"); + err.emit(); + } else if let Some((span, err)) = error { errors = true; if let SingleImport { source, ref result, .. } = import.subclass { @@ -553,9 +696,14 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { // Report unresolved imports only if no hard error was already reported // to avoid generating multiple errors on the same import. if !errors { - if let Some(import) = self.indeterminate_imports.iter().next() { + for import in &self.indeterminate_imports { + if import.is_uniform_paths_canary { + continue; + } + let error = ResolutionError::UnresolvedImport(None); resolve_error(self.resolver, import.span, error); + break; } } } @@ -576,8 +724,18 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { // For better failure detection, pretend that the import will not define any names // while resolving its module path. directive.vis.set(ty::Visibility::Invisible); - let result = self.resolve_path(&directive.module_path[..], None, false, - directive.span, directive.crate_lint()); + let result = self.resolve_path( + Some(if directive.is_uniform_paths_canary { + ModuleOrUniformRoot::Module(directive.parent) + } else { + ModuleOrUniformRoot::UniformRoot(keywords::Invalid.name()) + }), + &directive.module_path[..], + None, + false, + directive.span, + directive.crate_lint(), + ); directive.vis.set(vis); match result { @@ -644,77 +802,13 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { fn finalize_import(&mut self, directive: &'b ImportDirective<'b>) -> Option<(Span, String)> { self.current_module = directive.parent; let ImportDirective { ref module_path, span, .. } = *directive; - let mut warn_if_binding_comes_from_local_crate = false; - - // FIXME: Last path segment is treated specially in import resolution, so extern crate - // mode for absolute paths needs some special support for single-segment imports. - if module_path.len() == 1 && (module_path[0].name == keywords::CrateRoot.name() || - module_path[0].name == keywords::Extern.name()) { - let is_extern = module_path[0].name == keywords::Extern.name() || - (self.session.features_untracked().extern_absolute_paths && - self.session.rust_2018()); - match directive.subclass { - GlobImport { .. } if is_extern => { - return Some((directive.span, - "cannot glob-import all possible crates".to_string())); - } - GlobImport { .. } if self.session.features_untracked().extern_absolute_paths => { - self.lint_path_starts_with_module( - directive.root_id, - directive.root_span, - ); - } - SingleImport { source, target, .. } => { - let crate_root = if source.name == keywords::Crate.name() && - module_path[0].name != keywords::Extern.name() { - if target.name == keywords::Crate.name() { - return Some((directive.span, - "crate root imports need to be explicitly named: \ - `use crate as name;`".to_string())); - } else { - Some(self.resolve_crate_root(source)) - } - } else if is_extern && !source.is_path_segment_keyword() { - let crate_id = - self.resolver.crate_loader.process_use_extern( - source.name, - directive.span, - directive.id, - &self.resolver.definitions, - ); - let crate_root = - self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX }); - self.populate_module_if_necessary(crate_root); - Some(crate_root) - } else { - warn_if_binding_comes_from_local_crate = true; - None - }; - - if let Some(crate_root) = crate_root { - let binding = (crate_root, ty::Visibility::Public, directive.span, - directive.expansion).to_name_binding(self.arenas); - let binding = self.arenas.alloc_name_binding(NameBinding { - kind: NameBindingKind::Import { - binding, - directive, - used: Cell::new(false), - }, - vis: directive.vis.get(), - span: directive.span, - expansion: directive.expansion, - }); - let _ = self.try_define(directive.parent, target, TypeNS, binding); - let import = self.import_map.entry(directive.id).or_default(); - import[TypeNS] = Some(PathResolution::new(binding.def())); - return None; - } - } - _ => {} - } - } let module_result = self.resolve_path( + Some(if directive.is_uniform_paths_canary { + ModuleOrUniformRoot::Module(directive.parent) + } else { + ModuleOrUniformRoot::UniformRoot(keywords::Invalid.name()) + }), &module_path, None, true, @@ -734,7 +828,7 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { if !self_path.is_empty() && !is_special(self_path[0]) && !(self_path.len() > 1 && is_special(self_path[1])) { self_path[0].name = keywords::SelfValue.name(); - self_result = Some(self.resolve_path(&self_path, None, false, + self_result = Some(self.resolve_path(None, &self_path, None, false, span, CrateLint::No)); } return if let Some(PathResult::Module(..)) = self_result { @@ -748,12 +842,27 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { let (ident, result, type_ns_only) = match directive.subclass { SingleImport { source, ref result, type_ns_only, .. } => (source, result, type_ns_only), - GlobImport { .. } if module.def_id() == directive.parent.def_id() => { - // Importing a module into itself is not allowed. - return Some((directive.span, - "Cannot glob-import a module into itself.".to_string())); - } GlobImport { is_prelude, ref max_vis } => { + if 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 = module_path.clone(); + full_path.push(keywords::Invalid.ident()); + self.lint_if_path_starts_with_module( + directive.crate_lint(), + &full_path, + directive.span, + None, + ); + } + + if let ModuleOrUniformRoot::Module(module) = module { + if module.def_id() == directive.parent.def_id() { + // Importing a module into itself is not allowed. + return Some((directive.span, + "Cannot glob-import a module into itself.".to_string())); + } + } if !is_prelude && max_vis.get() != ty::Visibility::Invisible && // Allow empty globs. !max_vis.get().is_at_least(directive.vis.get(), &*self) { @@ -770,8 +879,10 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { if let Ok(binding) = result[ns].get() { all_ns_err = false; if this.record_use(ident, ns, binding, directive.span) { - this.resolution(module, ident, ns).borrow_mut().binding = - Some(this.dummy_binding); + if let ModuleOrUniformRoot::Module(module) = module { + this.resolution(module, ident, ns).borrow_mut().binding = + Some(this.dummy_binding); + } } } }); @@ -786,8 +897,13 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { }); return if all_ns_failed { - let resolutions = module.resolutions.borrow(); - let names = resolutions.iter().filter_map(|(&(ref i, _), resolution)| { + let resolutions = match module { + ModuleOrUniformRoot::Module(module) => + Some(module.resolutions.borrow()), + ModuleOrUniformRoot::UniformRoot(_) => None, + }; + let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter()); + let names = resolutions.filter_map(|(&(ref i, _), resolution)| { if *i == ident { return None; } // Never suggest the same name match *resolution.borrow() { NameResolution { binding: Some(name_binding), .. } => { @@ -813,11 +929,24 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { Some(name) => format!(". Did you mean to use `{}`?", name), None => "".to_owned(), }; - let module_str = module_to_string(module); - let msg = if let Some(module_str) = module_str { - format!("no `{}` in `{}`{}", ident, module_str, lev_suggestion) - } else { - format!("no `{}` in the root{}", ident, lev_suggestion) + let msg = 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, lev_suggestion) + } else { + format!("no `{}` in the root{}", ident, lev_suggestion) + } + } + ModuleOrUniformRoot::UniformRoot(_) => { + if !ident.is_path_segment_keyword() { + format!("no `{}` external crate{}", ident, lev_suggestion) + } 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, lev_suggestion) + } + } }; Some((span, msg)) } else { @@ -868,26 +997,20 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { } } - if warn_if_binding_comes_from_local_crate { - let mut warned = false; + if 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 = module_path.clone(); + full_path.push(ident); self.per_ns(|this, ns| { - let binding = match result[ns].get().ok() { - Some(b) => b, - None => return - }; - if let NameBindingKind::Import { directive: d, .. } = binding.kind { - if let ImportDirectiveSubclass::ExternCrate(..) = d.subclass { - return - } - } - if warned { - return + if let Ok(binding) = result[ns].get() { + this.lint_if_path_starts_with_module( + directive.crate_lint(), + &full_path, + directive.span, + Some(binding), + ); } - warned = true; - this.lint_path_starts_with_module( - directive.root_id, - directive.root_span, - ); }); } @@ -904,7 +1027,15 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { } fn resolve_glob_import(&mut self, directive: &'b ImportDirective<'b>) { - let module = directive.imported_module.get().unwrap(); + let module = match directive.imported_module.get().unwrap() { + ModuleOrUniformRoot::Module(module) => module, + ModuleOrUniformRoot::UniformRoot(_) => { + self.session.span_err(directive.span, + "cannot glob-import all possible crates"); + return; + } + }; + self.populate_module_if_necessary(module); if let Some(Def::Trait(_)) = module.def() { @@ -1026,8 +1157,10 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { }; let mut err = self.session.struct_span_err(binding.span, &msg); - let imported_module = directive.imported_module.get() - .expect("module should exist"); + let imported_module = match directive.imported_module.get() { + Some(ModuleOrUniformRoot::Module(module)) => module, + _ => bug!("module should exist"), + }; let resolutions = imported_module.parent.expect("parent should exist") .resolutions.borrow(); let enum_path_segment_index = directive.module_path.len() - 1; diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 6925ed2afb8..b66946affad 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -101,7 +101,7 @@ impl Path { // or starts with something like `self`/`super`/`$crate`/etc. pub fn make_root(&self) -> Option<PathSegment> { if let Some(ident) = self.segments.get(0).map(|seg| seg.ident) { - if ident.is_path_segment_keyword() && ident.name != keywords::Crate.name() { + if ident.is_path_segment_keyword() { return None; } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index b779b2eb689..56e69b9df9e 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -507,6 +507,9 @@ declare_features! ( // Support for arbitrary delimited token streams in non-macro attributes. (active, unrestricted_attribute_tokens, "1.30.0", Some(44690), None), + + // Allows `use x::y;` to resolve through `self::x`, not just `::x`. + (active, uniform_paths, "1.30.0", Some(53130), None), ); declare_features! ( diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index 68121d42b69..626a610017d 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -11,6 +11,8 @@ use ast; use attr; use std::cell::Cell; +use std::iter; +use edition::Edition; use ext::hygiene::{Mark, SyntaxContext}; use symbol::{Symbol, keywords}; use syntax_pos::{DUMMY_SP, Span}; @@ -43,7 +45,13 @@ thread_local! { static INJECTED_CRATE_NAME: Cell<Option<&'static str>> = Cell::new(None); } -pub fn maybe_inject_crates_ref(mut krate: ast::Crate, alt_std_name: Option<&str>) -> ast::Crate { +pub fn maybe_inject_crates_ref( + mut krate: ast::Crate, + alt_std_name: Option<&str>, + edition: Edition, +) -> ast::Crate { + let rust_2018 = edition >= Edition::Edition2018; + // the first name in this list is the crate name of the crate with the prelude let names: &[&str] = if attr::contains_name(&krate.attrs, "no_core") { return krate; @@ -58,14 +66,27 @@ pub fn maybe_inject_crates_ref(mut krate: ast::Crate, alt_std_name: Option<&str> }; // .rev() to preserve ordering above in combination with insert(0, ...) - for name in names.iter().rev() { + let alt_std_name = alt_std_name.map(Symbol::intern); + for orig_name in names.iter().rev() { + let orig_name = Symbol::intern(orig_name); + let mut rename = orig_name; + // HACK(eddyb) gensym the injected crates on the Rust 2018 edition, + // so they don't accidentally interfere with the new import paths. + if rust_2018 { + rename = orig_name.gensymed(); + } + let orig_name = if rename != orig_name { + Some(orig_name) + } else { + None + }; krate.module.items.insert(0, P(ast::Item { attrs: vec![attr::mk_attr_outer(DUMMY_SP, attr::mk_attr_id(), attr::mk_word_item(ast::Ident::from_str("macro_use")))], vis: dummy_spanned(ast::VisibilityKind::Inherited), - node: ast::ItemKind::ExternCrate(alt_std_name.map(Symbol::intern)), - ident: ast::Ident::from_str(name), + node: ast::ItemKind::ExternCrate(alt_std_name.or(orig_name)), + ident: ast::Ident::with_empty_ctxt(rename), id: ast::DUMMY_NODE_ID, span: DUMMY_SP, tokens: None, @@ -91,9 +112,11 @@ pub fn maybe_inject_crates_ref(mut krate: ast::Crate, alt_std_name: Option<&str> vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited), node: ast::ItemKind::Use(P(ast::UseTree { prefix: ast::Path { - segments: [name, "prelude", "v1"].into_iter().map(|name| { - ast::PathSegment::from_ident(ast::Ident::from_str(name)) - }).collect(), + segments: iter::once(keywords::CrateRoot.ident()) + .chain( + [name, "prelude", "v1"].iter().cloned() + .map(ast::Ident::from_str) + ).map(ast::PathSegment::from_ident).collect(), span, }, kind: ast::UseTreeKind::Glob, diff --git a/src/test/compile-fail/dollar-crate-is-keyword.rs b/src/test/compile-fail/dollar-crate-is-keyword.rs index 70597a230a8..4a667f020ad 100644 --- a/src/test/compile-fail/dollar-crate-is-keyword.rs +++ b/src/test/compile-fail/dollar-crate-is-keyword.rs @@ -10,7 +10,11 @@ macro_rules! m { () => { - struct $crate {} //~ ERROR expected identifier, found reserved identifier `$crate` + // Avoid having more than one `$crate`-named item in the same module, + // as even though they error, they still parse as `$crate` and conflict. + mod foo { + struct $crate {} //~ ERROR expected identifier, found reserved identifier `$crate` + } use $crate; // OK //~^ WARN `$crate` may not be imported diff --git a/src/test/compile-fail/import-crate-var.rs b/src/test/compile-fail/import-crate-var.rs index e58ba2c8891..b09883d9ade 100644 --- a/src/test/compile-fail/import-crate-var.rs +++ b/src/test/compile-fail/import-crate-var.rs @@ -9,15 +9,14 @@ // except according to those terms. // aux-build:import_crate_var.rs -// error-pattern: `$crate` may not be imported -// error-pattern: `use $crate;` was erroneously allowed and will become a hard error -// error-pattern: compilation successful #![feature(rustc_attrs)] #[macro_use] extern crate import_crate_var; #[rustc_error] -fn main() { +fn main() { //~ ERROR compilation successful m!(); + //~^ WARN `$crate` may not be imported + //~| NOTE `use $crate;` was erroneously allowed and will become a hard error } diff --git a/src/test/compile-fail/keyword-extern-as-identifier.rs b/src/test/compile-fail/keyword-extern-as-identifier.rs index e5927d09b41..3e445853957 100644 --- a/src/test/compile-fail/keyword-extern-as-identifier.rs +++ b/src/test/compile-fail/keyword-extern-as-identifier.rs @@ -11,5 +11,5 @@ #![feature(extern_in_paths)] fn main() { - let extern = 0; //~ ERROR expected unit struct/variant or constant, found module `extern` + let extern = 0; //~ ERROR cannot find unit struct/variant or constant `extern` in this scope } diff --git a/src/test/compile-fail/rfc-2126-extern-absolute-paths/single-segment.rs b/src/test/compile-fail/rfc-2126-extern-absolute-paths/single-segment.rs index e9de0f01b30..69fc4b4f7f8 100644 --- a/src/test/compile-fail/rfc-2126-extern-absolute-paths/single-segment.rs +++ b/src/test/compile-fail/rfc-2126-extern-absolute-paths/single-segment.rs @@ -11,10 +11,8 @@ // aux-build:xcrate.rs // edition:2018 -use crate; //~ ERROR unresolved import `crate` - //~^ NOTE crate root imports need to be explicitly named: `use crate as name;` -use *; //~ ERROR unresolved import `*` - //~^ NOTE cannot glob-import all possible crates +use crate; //~ ERROR crate root imports need to be explicitly named: `use crate as name;` +use *; //~ ERROR cannot glob-import all possible crates fn main() { let s = ::xcrate; //~ ERROR expected value, found module `xcrate` diff --git a/src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs b/src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs index ebc42aa9d44..017844a0252 100644 --- a/src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs +++ b/src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs @@ -14,8 +14,7 @@ use extern; //~ ERROR unresolved import `extern` //~^ NOTE no `extern` in the root -use extern::*; //~ ERROR unresolved import `extern::*` - //~^ NOTE cannot glob-import all possible crates +use extern::*; //~ ERROR cannot glob-import all possible crates fn main() { let s = extern::xcrate; //~ ERROR expected value, found module `extern::xcrate` diff --git a/src/test/pretty/cast-lt.pp b/src/test/pretty/cast-lt.pp index f1b4b4f5a0c..b8d920754ad 100644 --- a/src/test/pretty/cast-lt.pp +++ b/src/test/pretty/cast-lt.pp @@ -1,7 +1,7 @@ #![feature(prelude_import)] #![no_std] #[prelude_import] -use std::prelude::v1::*; +use ::std::prelude::v1::*; #[macro_use] extern crate std; // Copyright 2017 The Rust Project Developers. See the COPYRIGHT diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp index a4380d9212f..5f42b86c82a 100644 --- a/src/test/pretty/issue-4264.pp +++ b/src/test/pretty/issue-4264.pp @@ -1,5 +1,5 @@ #[prelude_import] -use std::prelude::v1::*; +use ::std::prelude::v1::*; #[macro_use] extern crate std; // Copyright 2014 The Rust Project Developers. See the COPYRIGHT diff --git a/src/test/run-pass/issue-52140/auxiliary/some_crate.rs b/src/test/run-pass/issue-52140/auxiliary/some_crate.rs new file mode 100644 index 00000000000..bf8dee0863a --- /dev/null +++ b/src/test/run-pass/issue-52140/auxiliary/some_crate.rs @@ -0,0 +1,15 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +pub fn hello() { + println!("Hello, world!"); +} diff --git a/src/test/run-pass/issue-52140/main.rs b/src/test/run-pass/issue-52140/main.rs new file mode 100644 index 00000000000..6fae6adc442 --- /dev/null +++ b/src/test/run-pass/issue-52140/main.rs @@ -0,0 +1,21 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:some_crate.rs +// edition:2018 + +mod foo { + pub use some_crate; +} + +fn main() { + ::some_crate::hello(); + foo::some_crate::hello(); +} diff --git a/src/test/run-pass/issue-52141/auxiliary/some_crate.rs b/src/test/run-pass/issue-52141/auxiliary/some_crate.rs new file mode 100644 index 00000000000..bf8dee0863a --- /dev/null +++ b/src/test/run-pass/issue-52141/auxiliary/some_crate.rs @@ -0,0 +1,15 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +pub fn hello() { + println!("Hello, world!"); +} diff --git a/src/test/run-pass/issue-52141/main.rs b/src/test/run-pass/issue-52141/main.rs new file mode 100644 index 00000000000..8f3fc9f9698 --- /dev/null +++ b/src/test/run-pass/issue-52141/main.rs @@ -0,0 +1,24 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:some_crate.rs +// edition:2018 + +use some_crate as some_name; + +mod foo { + pub use crate::some_name::*; +} + +fn main() { + ::some_crate::hello(); + some_name::hello(); + foo::hello(); +} diff --git a/src/test/run-pass/issue-52705/auxiliary/png.rs b/src/test/run-pass/issue-52705/auxiliary/png.rs new file mode 100644 index 00000000000..48d53a2cbac --- /dev/null +++ b/src/test/run-pass/issue-52705/auxiliary/png.rs @@ -0,0 +1,13 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +pub struct DecodingError; diff --git a/src/test/run-pass/issue-52705/main.rs b/src/test/run-pass/issue-52705/main.rs new file mode 100644 index 00000000000..101f67e3e7b --- /dev/null +++ b/src/test/run-pass/issue-52705/main.rs @@ -0,0 +1,22 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:png.rs +// edition:2018 + +mod png { + use png as png_ext; + + fn foo() -> png_ext::DecodingError { unimplemented!() } +} + +fn main() { + println!("Hello, world!"); +} diff --git a/src/test/run-pass/uniform-paths/basic-nested.rs b/src/test/run-pass/uniform-paths/basic-nested.rs new file mode 100644 index 00000000000..086fd58cffa --- /dev/null +++ b/src/test/run-pass/uniform-paths/basic-nested.rs @@ -0,0 +1,52 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +// This test is similar to `basic.rs`, but nested in modules. + +mod foo { + // Test that ambiguity errors are not emitted between `self::test` and + // `::test`, assuming the latter (crate) is not in `extern_prelude`. + mod test { + pub struct Foo(pub ()); + } + pub use test::Foo; + + // Test that qualified paths can refer to both the external crate and local item. + mod std { + pub struct io(pub ()); + } + pub use ::std::io as std_io; + pub use self::std::io as local_io; +} + +// Test that we can refer to the external crate unqualified +// (when there isn't a local item with the same name). +use std::io; + +mod bar { + // Also test the unqualified external crate import in a nested module, + // to show that the above import doesn't resolve through a local `std` + // item, e.g. the automatically injected `extern crate std;`, which in + // the Rust 2018 should no longer be visible through `crate::std`. + pub use std::io; +} + + +fn main() { + foo::Foo(()); + foo::std_io::stdout(); + foo::local_io(()); + io::stdout(); + bar::io::stdout(); +} diff --git a/src/test/run-pass/uniform-paths/basic.rs b/src/test/run-pass/uniform-paths/basic.rs new file mode 100644 index 00000000000..59a0404e4c3 --- /dev/null +++ b/src/test/run-pass/uniform-paths/basic.rs @@ -0,0 +1,33 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +// Test that ambiguity errors are not emitted between `self::test` and +// `::test`, assuming the latter (crate) is not in `extern_prelude`. +mod test { + pub struct Foo(pub ()); +} +use test::Foo; + +// Test that qualified paths can refer to both the external crate and local item. +mod std { + pub struct io(pub ()); +} +use ::std::io as std_io; +use self::std::io as local_io; + +fn main() { + Foo(()); + std_io::stdout(); + local_io(()); +} diff --git a/src/test/run-pass/uniform-paths/macros-nested.rs b/src/test/run-pass/uniform-paths/macros-nested.rs new file mode 100644 index 00000000000..9cf05cd9560 --- /dev/null +++ b/src/test/run-pass/uniform-paths/macros-nested.rs @@ -0,0 +1,62 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +// This test is similar to `macros.rs`, but nested in modules. + +mod foo { + // Test that ambiguity errors are not emitted between `self::test` and + // `::test`, assuming the latter (crate) is not in `extern_prelude`. + macro_rules! m1 { + () => { + mod test { + pub struct Foo(pub ()); + } + } + } + pub use test::Foo; + m1!(); + + // Test that qualified paths can refer to both the external crate and local item. + macro_rules! m2 { + () => { + mod std { + pub struct io(pub ()); + } + } + } + pub use ::std::io as std_io; + pub use self::std::io as local_io; + m2!(); +} + +// Test that we can refer to the external crate unqualified +// (when there isn't a local item with the same name). +use std::io; + +mod bar { + // Also test the unqualified external crate import in a nested module, + // to show that the above import doesn't resolve through a local `std` + // item, e.g. the automatically injected `extern crate std;`, which in + // the Rust 2018 should no longer be visible through `crate::std`. + pub use std::io; +} + + +fn main() { + foo::Foo(()); + foo::std_io::stdout(); + foo::local_io(()); + io::stdout(); + bar::io::stdout(); +} diff --git a/src/test/run-pass/uniform-paths/macros.rs b/src/test/run-pass/uniform-paths/macros.rs new file mode 100644 index 00000000000..ad74f5d0876 --- /dev/null +++ b/src/test/run-pass/uniform-paths/macros.rs @@ -0,0 +1,45 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +// This test is similar to `basic.rs`, but with macros defining local items. + +// Test that ambiguity errors are not emitted between `self::test` and +// `::test`, assuming the latter (crate) is not in `extern_prelude`. +macro_rules! m1 { + () => { + mod test { + pub struct Foo(pub ()); + } + } +} +use test::Foo; +m1!(); + +// Test that qualified paths can refer to both the external crate and local item. +macro_rules! m2 { + () => { + mod std { + pub struct io(pub ()); + } + } +} +use ::std::io as std_io; +use self::std::io as local_io; +m2!(); + +fn main() { + Foo(()); + std_io::stdout(); + local_io(()); +} diff --git a/src/test/run-pass/uniform-paths/same-crate.rs b/src/test/run-pass/uniform-paths/same-crate.rs new file mode 100644 index 00000000000..a3eefa7134c --- /dev/null +++ b/src/test/run-pass/uniform-paths/same-crate.rs @@ -0,0 +1,109 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +pub const A: usize = 0; + +pub mod foo { + pub const B: usize = 1; + + pub mod bar { + pub const C: usize = 2; + + pub enum E { + V1(usize), + V2(String), + } + + pub fn test() -> String { + format!("{} {} {}", crate::A, crate::foo::B, C) + } + + pub fn test_use() -> String { + use crate::A; + use crate::foo::B; + + format!("{} {} {}", A, B, C) + } + + pub fn test_enum() -> String { + use E::*; + match E::V1(10) { + V1(i) => { format!("V1: {}", i) } + V2(s) => { format!("V2: {}", s) } + } + } + } + + pub fn test() -> String { + format!("{} {} {}", crate::A, B, bar::C) + } + + pub fn test_use() -> String { + use crate::A; + use bar::C; + + format!("{} {} {}", A, B, C) + } + + pub fn test_enum() -> String { + use bar::E::*; + match bar::E::V1(10) { + V1(i) => { format!("V1: {}", i) } + V2(s) => { format!("V2: {}", s) } + } + } +} + +pub fn test() -> String { + format!("{} {} {}", A, foo::B, foo::bar::C) +} + +pub fn test_use() -> String { + use foo::B; + use foo::bar::C; + + format!("{} {} {}", A, B, C) +} + +pub fn test_enum() -> String { + use foo::bar::E::*; + match foo::bar::E::V1(10) { + V1(i) => { format!("V1: {}", i) } + V2(s) => { format!("V2: {}", s) } + } +} + +fn main() { + let output = [ + test(), + foo::test(), + foo::bar::test(), + test_use(), + foo::test_use(), + foo::bar::test_use(), + test_enum(), + foo::test_enum(), + foo::bar::test_enum(), + ].join("\n"); + assert_eq!(output, "\ +0 1 2 +0 1 2 +0 1 2 +0 1 2 +0 1 2 +0 1 2 +V1: 10 +V1: 10 +V1: 10"); +} diff --git a/src/test/ui/feature-gate-uniform-paths.rs b/src/test/ui/feature-gate-uniform-paths.rs new file mode 100644 index 00000000000..140655d52bd --- /dev/null +++ b/src/test/ui/feature-gate-uniform-paths.rs @@ -0,0 +1,22 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub mod foo { + pub use bar::Bar; + //~^ ERROR unresolved import `bar` + + pub mod bar { + pub struct Bar; + } +} + +fn main() { + let _ = foo::Bar; +} diff --git a/src/test/ui/feature-gate-uniform-paths.stderr b/src/test/ui/feature-gate-uniform-paths.stderr new file mode 100644 index 00000000000..68faacfcbe7 --- /dev/null +++ b/src/test/ui/feature-gate-uniform-paths.stderr @@ -0,0 +1,9 @@ +error[E0432]: unresolved import `bar` + --> $DIR/feature-gate-uniform-paths.rs:12:13 + | +LL | pub use bar::Bar; + | ^^^ Did you mean `self::bar`? + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0432`. diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.rs b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.rs new file mode 100644 index 00000000000..5f29e7bc99e --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.rs @@ -0,0 +1,31 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +// This test is similar to `ambiguity-macros.rs`, but nested in a module. + +mod foo { + pub use std::io; + //~^ ERROR import from `std` is ambiguous + + macro_rules! m { + () => { + mod std { + pub struct io; + } + } + } + m!(); +} + +fn main() {} diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.stderr b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.stderr new file mode 100644 index 00000000000..d400987dfee --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.stderr @@ -0,0 +1,16 @@ +error: import from `std` is ambiguous + --> $DIR/ambiguity-macros-nested.rs:18:13 + | +LL | pub use std::io; + | ^^^ could refer to external crate `::std` +... +LL | / mod std { +LL | | pub struct io; +LL | | } + | |_____________- could also refer to `self::std` + | + = help: write `::std` or `self::std` explicitly instead + = note: relative `use` paths enabled by `#![feature(uniform_paths)]` + +error: aborting due to previous error + diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.rs b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.rs new file mode 100644 index 00000000000..547b2508b96 --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.rs @@ -0,0 +1,29 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +// This test is similar to `ambiguity.rs`, but with macros defining local items. + +use std::io; +//~^ ERROR import from `std` is ambiguous + +macro_rules! m { + () => { + mod std { + pub struct io; + } + } +} +m!(); + +fn main() {} diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.stderr b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.stderr new file mode 100644 index 00000000000..24a2061a3cb --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.stderr @@ -0,0 +1,16 @@ +error: import from `std` is ambiguous + --> $DIR/ambiguity-macros.rs:17:5 + | +LL | use std::io; + | ^^^ could refer to external crate `::std` +... +LL | / mod std { +LL | | pub struct io; +LL | | } + | |_________- could also refer to `self::std` + | + = help: write `::std` or `self::std` explicitly instead + = note: relative `use` paths enabled by `#![feature(uniform_paths)]` + +error: aborting due to previous error + diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.rs b/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.rs new file mode 100644 index 00000000000..fe00fd94ee9 --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.rs @@ -0,0 +1,26 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +// This test is similar to `ambiguity.rs`, but nested in a module. + +mod foo { + pub use std::io; + //~^ ERROR import from `std` is ambiguous + + mod std { + pub struct io; + } +} + +fn main() {} diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.stderr b/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.stderr new file mode 100644 index 00000000000..5104aba8b44 --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.stderr @@ -0,0 +1,16 @@ +error: import from `std` is ambiguous + --> $DIR/ambiguity-nested.rs:18:13 + | +LL | pub use std::io; + | ^^^ could refer to external crate `::std` +... +LL | / mod std { +LL | | pub struct io; +LL | | } + | |_____- could also refer to `self::std` + | + = help: write `::std` or `self::std` explicitly instead + = note: relative `use` paths enabled by `#![feature(uniform_paths)]` + +error: aborting due to previous error + diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity.rs b/src/test/ui/rust-2018/uniform-paths/ambiguity.rs new file mode 100644 index 00000000000..49ab2f0c191 --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity.rs @@ -0,0 +1,22 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +use std::io; +//~^ ERROR import from `std` is ambiguous + +mod std { + pub struct io; +} + +fn main() {} diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity.stderr b/src/test/ui/rust-2018/uniform-paths/ambiguity.stderr new file mode 100644 index 00000000000..2e227dce96c --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity.stderr @@ -0,0 +1,16 @@ +error: import from `std` is ambiguous + --> $DIR/ambiguity.rs:15:5 + | +LL | use std::io; + | ^^^ could refer to external crate `::std` +... +LL | / mod std { +LL | | pub struct io; +LL | | } + | |_- could also refer to `self::std` + | + = help: write `::std` or `self::std` explicitly instead + = note: relative `use` paths enabled by `#![feature(uniform_paths)]` + +error: aborting due to previous error + diff --git a/src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.rs b/src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.rs new file mode 100644 index 00000000000..b8489c61dad --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.rs @@ -0,0 +1,23 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +enum Foo { A, B } + +fn main() { + enum Foo {} + use Foo::*; + //~^ ERROR import from `Foo` is ambiguous + + let _ = (A, B); +} diff --git a/src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.stderr b/src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.stderr new file mode 100644 index 00000000000..656af91a8c7 --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.stderr @@ -0,0 +1,13 @@ +error: import from `Foo` is ambiguous + --> $DIR/block-scoped-shadow.rs:19:9 + | +LL | enum Foo {} + | ----------- shadowed by block-scoped `Foo` +LL | use Foo::*; + | ^^^ + | + = help: write `::Foo` or `self::Foo` explicitly instead + = note: relative `use` paths enabled by `#![feature(uniform_paths)]` + +error: aborting due to previous error + |
