Skip to content

Commit

Permalink
#[feature(uniform_paths)]: allow use x::y; to resolve through `self…
Browse files Browse the repository at this point in the history
…::x`, not just `::x`.
  • Loading branch information
cramertj authored and eddyb committed Aug 14, 2018
1 parent f9b1176 commit 39ce9ef
Show file tree
Hide file tree
Showing 7 changed files with 367 additions and 7 deletions.
29 changes: 22 additions & 7 deletions src/librustc_resolve/build_reduced_graph.rs
Expand Up @@ -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,
Expand All @@ -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 {
Expand All @@ -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,
Expand All @@ -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() {
Expand Down Expand Up @@ -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,
};

Expand Down
44 changes: 44 additions & 0 deletions src/librustc_resolve/resolve_imports.rs
Expand Up @@ -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() &&
(
Expand Down
52 changes: 52 additions & 0 deletions 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();
}
33 changes: 33 additions & 0 deletions 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(());
}
62 changes: 62 additions & 0 deletions 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();
}
45 changes: 45 additions & 0 deletions 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(());
}

0 comments on commit 39ce9ef

Please sign in to comment.