]> git.lizzy.rs Git - rust.git/commitdiff
#[feature(uniform_paths)]: allow `use x::y;` to resolve through `self::x`, not just...
authorTaylor Cramer <cramertj@google.com>
Fri, 13 Jul 2018 19:38:49 +0000 (12:38 -0700)
committerEduard-Mihai Burtescu <edy.burt@gmail.com>
Tue, 14 Aug 2018 04:06:50 +0000 (07:06 +0300)
src/librustc_resolve/build_reduced_graph.rs
src/librustc_resolve/resolve_imports.rs
src/test/run-pass/uniform-paths/basic-nested.rs [new file with mode: 0644]
src/test/run-pass/uniform-paths/basic.rs [new file with mode: 0644]
src/test/run-pass/uniform-paths/macros-nested.rs [new file with mode: 0644]
src/test/run-pass/uniform-paths/macros.rs [new file with mode: 0644]
src/test/run-pass/uniform-paths/same-crate.rs [new file with mode: 0644]

index 06bc41548b959d4e59ef1f2ab4af1d28382b2d43..a4144d705b29e32b5ec81d5d35064ac03ed0353c 100644 (file)
@@ -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,
                 };
 
index 58f7532adaf500484cc77765e23eddd62cef270b..7209291aebdb5ce86bf588f5e3a3d7668ca7e472 100644 (file)
@@ -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 (file)
index 0000000..086fd58
--- /dev/null
@@ -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 (file)
index 0000000..59a0404
--- /dev/null
@@ -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 (file)
index 0000000..9cf05cd
--- /dev/null
@@ -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 (file)
index 0000000..ad74f5d
--- /dev/null
@@ -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 (file)
index 0000000..a3eefa7
--- /dev/null
@@ -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");
+}