#[feature(uniform_paths)]: allow `use x::y;` to resolve through `self::x`, not just `::x`.

This commit is contained in:
Taylor Cramer 2018-07-13 12:38:49 -07:00 committed by Eduard-Mihai Burtescu
parent f9b1176eef
commit 39ce9ef00e
7 changed files with 367 additions and 7 deletions

View File

@ -142,8 +142,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 +156,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,7 +170,7 @@ 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,
@ -179,6 +180,11 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
// 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 @@ 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,
};

View File

@ -144,6 +144,50 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> {
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() &&
(

View File

@ -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();
}

View File

@ -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(());
}

View File

@ -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();
}

View File

@ -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(());
}

View File

@ -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");
}