From 39ce9ef00e6f418a7db972e07063c1445e72e0c3 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Fri, 13 Jul 2018 12:38:49 -0700 Subject: [PATCH] #[feature(uniform_paths)]: allow `use x::y;` to resolve through `self::x`, not just `::x`. --- src/librustc_resolve/build_reduced_graph.rs | 29 +++-- src/librustc_resolve/resolve_imports.rs | 44 +++++++ .../run-pass/uniform-paths/basic-nested.rs | 52 +++++++++ src/test/run-pass/uniform-paths/basic.rs | 33 ++++++ .../run-pass/uniform-paths/macros-nested.rs | 62 ++++++++++ src/test/run-pass/uniform-paths/macros.rs | 45 ++++++++ src/test/run-pass/uniform-paths/same-crate.rs | 109 ++++++++++++++++++ 7 files changed, 367 insertions(+), 7 deletions(-) create mode 100644 src/test/run-pass/uniform-paths/basic-nested.rs create mode 100644 src/test/run-pass/uniform-paths/basic.rs create mode 100644 src/test/run-pass/uniform-paths/macros-nested.rs create mode 100644 src/test/run-pass/uniform-paths/macros.rs create mode 100644 src/test/run-pass/uniform-paths/same-crate.rs diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 06bc41548b9..a4144d705b2 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -142,8 +142,10 @@ fn build_reduced_graph_for_use_tree(&mut self, 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 +156,9 @@ fn build_reduced_graph_for_use_tree(&mut self, } // 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,7 +170,7 @@ fn build_reduced_graph_for_use_tree(&mut self, } // 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, @@ -179,6 +180,11 @@ fn build_reduced_graph_for_use_tree(&mut self, // 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() { @@ -283,9 +289,18 @@ fn build_reduced_graph_for_item(&mut self, item: &Item, expansion: Mark) { 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, }; diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 58f7532adaf..7209291aebd 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -144,6 +144,50 @@ pub fn resolve_ident_in_module_unadjusted(&mut self, 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. + 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() && ( 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 or the MIT license +// , 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 or the MIT license +// , 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 or the MIT license +// , 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 or the MIT license +// , 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 or the MIT license +// , 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"); +} -- 2.44.0