about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_resolve/resolve_imports.rs129
-rw-r--r--src/test/run-pass/uniform-paths/basic-nested.rs9
-rw-r--r--src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.rs2
-rw-r--r--src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.stderr6
-rw-r--r--src/test/ui/rust-2018/uniform-paths/ambiguity-macros.rs2
-rw-r--r--src/test/ui/rust-2018/uniform-paths/ambiguity-macros.stderr6
-rw-r--r--src/test/ui/rust-2018/uniform-paths/ambiguity-nested.rs2
-rw-r--r--src/test/ui/rust-2018/uniform-paths/ambiguity-nested.stderr6
-rw-r--r--src/test/ui/rust-2018/uniform-paths/ambiguity.rs2
-rw-r--r--src/test/ui/rust-2018/uniform-paths/ambiguity.stderr6
-rw-r--r--src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.rs10
-rw-r--r--src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.stderr40
-rw-r--r--src/test/ui/rust-2018/uniform-paths/redundant.rs17
-rw-r--r--src/test/ui/rust-2018/uniform-paths/redundant.stderr14
14 files changed, 195 insertions, 56 deletions
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index 35e58bc9fee..c70f52e91db 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -11,7 +11,7 @@
 use self::ImportDirectiveSubclass::*;
 
 use {AmbiguityError, CrateLint, Module, ModuleOrUniformRoot, PerNS};
-use Namespace::{self, TypeNS, MacroNS, ValueNS};
+use Namespace::{self, TypeNS, MacroNS};
 use {NameBinding, NameBindingKind, ToNameBinding, PathResult, PrivacyError};
 use Resolver;
 use {names_to_string, module_to_string};
@@ -34,6 +34,8 @@ use syntax::util::lev_distance::find_best_match_for_name;
 use syntax_pos::Span;
 
 use std::cell::{Cell, RefCell};
+use std::collections::BTreeMap;
+use std::fmt::Write;
 use std::{mem, ptr};
 
 /// Contains data for specific types of import directives.
@@ -615,6 +617,17 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
             self.finalize_resolutions_in(module);
         }
 
+        #[derive(Default)]
+        struct UniformPathsCanaryResult {
+            module_scope: Option<Span>,
+            block_scopes: Vec<Span>,
+        }
+        // Collect all tripped `uniform_paths` canaries separately.
+        let mut uniform_paths_canaries: BTreeMap<
+            (Span, NodeId),
+            (Name, PerNS<UniformPathsCanaryResult>),
+        > = BTreeMap::new();
+
         let mut errors = false;
         let mut seen_spans = FxHashSet();
         for i in 0 .. self.determined_imports.len() {
@@ -624,49 +637,37 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
             // 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 =
+                let (name, result) = match import.subclass {
+                    SingleImport { source, ref result, .. } => (source.name, result),
+                    _ => bug!(),
+                };
+
+                let has_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;
-                }
+                let (prev_name, canary_results) =
+                    uniform_paths_canaries.entry((import.span, import.id))
+                        .or_insert((name, PerNS::default()));
 
-                errors = true;
+                // All the canaries with the same `id` should have the same `name`.
+                assert_eq!(*prev_name, name);
 
-                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));
+                self.per_ns(|_, ns| {
+                    if let Some(result) = result[ns].get().ok() {
+                        if has_explicit_self {
+                            // There should only be one `self::x` (module-scoped) canary.
+                            assert_eq!(canary_results[ns].module_scope, None);
+                            canary_results[ns].module_scope = Some(result.span);
+                        } else {
+                            canary_results[ns].block_scopes.push(result.span);
+                        }
                     }
-                }
-                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;
 
@@ -694,6 +695,66 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
             }
         }
 
+        for ((span, _), (name, results)) in uniform_paths_canaries {
+            self.per_ns(|this, ns| {
+                let results = &results[ns];
+
+                let has_external_crate =
+                    ns == TypeNS && this.extern_prelude.contains(&name);
+
+                // An ambiguity requires more than one possible resolution.
+                let possible_resultions =
+                    (has_external_crate as usize) +
+                    (results.module_scope.is_some() as usize) +
+                    (!results.block_scopes.is_empty() as usize);
+                if possible_resultions <= 1 {
+                    return;
+                }
+
+                errors = true;
+
+                // Special-case the error when `self::x` finds its own `use x;`.
+                if has_external_crate &&
+                   results.module_scope == Some(span) &&
+                   results.block_scopes.is_empty() {
+                    let msg = format!("`{}` import is redundant", name);
+                    this.session.struct_span_err(span, &msg)
+                        .span_label(span,
+                            format!("refers to external crate `::{}`", name))
+                        .span_label(span,
+                            format!("defines `self::{}`, shadowing itself", name))
+                        .help(&format!("remove or write `::{}` explicitly instead", name))
+                        .note("relative `use` paths enabled by `#![feature(uniform_paths)]`")
+                        .emit();
+                    return;
+                }
+
+                let msg = format!("`{}` import is ambiguous", name);
+                let mut err = this.session.struct_span_err(span, &msg);
+                let mut suggestion_choices = String::new();
+                if has_external_crate {
+                    write!(suggestion_choices, "`::{}`", name);
+                    err.span_label(span,
+                        format!("can refer to external crate `::{}`", name));
+                }
+                if let Some(span) = results.module_scope {
+                    if !suggestion_choices.is_empty() {
+                        suggestion_choices.push_str(" or ");
+                    }
+                    write!(suggestion_choices, "`self::{}`", name);
+                    err.span_label(span,
+                        format!("can refer to `self::{}`", name));
+                }
+                for &span in &results.block_scopes {
+                    err.span_label(span,
+                        format!("shadowed by block-scoped `{}`", name));
+                }
+                err.help(&format!("write {} explicitly instead", suggestion_choices));
+                err.note("relative `use` paths enabled by `#![feature(uniform_paths)]`");
+                err.emit();
+            });
+        }
+
         // Report unresolved imports only if no hard error was already reported
         // to avoid generating multiple errors on the same import.
         if !errors {
diff --git a/src/test/run-pass/uniform-paths/basic-nested.rs b/src/test/run-pass/uniform-paths/basic-nested.rs
index 086fd58cffa..954736dfd90 100644
--- a/src/test/run-pass/uniform-paths/basic-nested.rs
+++ b/src/test/run-pass/uniform-paths/basic-nested.rs
@@ -10,7 +10,7 @@
 
 // edition:2018
 
-#![feature(uniform_paths)]
+#![feature(decl_macro, uniform_paths)]
 
 // This test is similar to `basic.rs`, but nested in modules.
 
@@ -40,6 +40,11 @@ mod bar {
     // 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;
+
+    // Also test that items named `std` in other namespaces don't
+    // cause ambiguity errors for the import from `std` above.
+    pub fn std() {}
+    pub macro std() {}
 }
 
 
@@ -49,4 +54,6 @@ fn main() {
     foo::local_io(());
     io::stdout();
     bar::io::stdout();
+    bar::std();
+    bar::std!();
 }
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
index 5f29e7bc99e..e0992c90666 100644
--- a/src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.rs
+++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.rs
@@ -16,7 +16,7 @@
 
 mod foo {
     pub use std::io;
-    //~^ ERROR import from `std` is ambiguous
+    //~^ ERROR `std` import is ambiguous
 
     macro_rules! m {
         () => {
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
index d400987dfee..154ee412e72 100644
--- a/src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.stderr
+++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.stderr
@@ -1,13 +1,13 @@
-error: import from `std` is ambiguous
+error: `std` import is ambiguous
   --> $DIR/ambiguity-macros-nested.rs:18:13
    |
 LL |       pub use std::io;
-   |               ^^^ could refer to external crate `::std`
+   |               ^^^ can refer to external crate `::std`
 ...
 LL | /             mod std {
 LL | |                 pub struct io;
 LL | |             }
-   | |_____________- could also refer to `self::std`
+   | |_____________- can refer to `self::std`
    |
    = help: write `::std` or `self::std` explicitly instead
    = note: relative `use` paths enabled by `#![feature(uniform_paths)]`
diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.rs b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.rs
index 547b2508b96..9439d92aa71 100644
--- a/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.rs
+++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.rs
@@ -15,7 +15,7 @@
 // This test is similar to `ambiguity.rs`, but with macros defining local items.
 
 use std::io;
-//~^ ERROR import from `std` is ambiguous
+//~^ ERROR `std` import is ambiguous
 
 macro_rules! m {
     () => {
diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.stderr b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.stderr
index 24a2061a3cb..3c0d5601f9c 100644
--- a/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.stderr
+++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.stderr
@@ -1,13 +1,13 @@
-error: import from `std` is ambiguous
+error: `std` import is ambiguous
   --> $DIR/ambiguity-macros.rs:17:5
    |
 LL |   use std::io;
-   |       ^^^ could refer to external crate `::std`
+   |       ^^^ can refer to external crate `::std`
 ...
 LL | /         mod std {
 LL | |             pub struct io;
 LL | |         }
-   | |_________- could also refer to `self::std`
+   | |_________- can refer to `self::std`
    |
    = help: write `::std` or `self::std` explicitly instead
    = note: relative `use` paths enabled by `#![feature(uniform_paths)]`
diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.rs b/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.rs
index fe00fd94ee9..1756acc6057 100644
--- a/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.rs
+++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.rs
@@ -16,7 +16,7 @@
 
 mod foo {
     pub use std::io;
-    //~^ ERROR import from `std` is ambiguous
+    //~^ ERROR `std` import is ambiguous
 
     mod std {
         pub struct io;
diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.stderr b/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.stderr
index 5104aba8b44..a607eeb0b43 100644
--- a/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.stderr
+++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.stderr
@@ -1,13 +1,13 @@
-error: import from `std` is ambiguous
+error: `std` import is ambiguous
   --> $DIR/ambiguity-nested.rs:18:13
    |
 LL |       pub use std::io;
-   |               ^^^ could refer to external crate `::std`
+   |               ^^^ can refer to external crate `::std`
 ...
 LL | /     mod std {
 LL | |         pub struct io;
 LL | |     }
-   | |_____- could also refer to `self::std`
+   | |_____- can refer to `self::std`
    |
    = help: write `::std` or `self::std` explicitly instead
    = note: relative `use` paths enabled by `#![feature(uniform_paths)]`
diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity.rs b/src/test/ui/rust-2018/uniform-paths/ambiguity.rs
index 49ab2f0c191..9ae3d79c22c 100644
--- a/src/test/ui/rust-2018/uniform-paths/ambiguity.rs
+++ b/src/test/ui/rust-2018/uniform-paths/ambiguity.rs
@@ -13,7 +13,7 @@
 #![feature(uniform_paths)]
 
 use std::io;
-//~^ ERROR import from `std` is ambiguous
+//~^ ERROR `std` import is ambiguous
 
 mod std {
     pub struct io;
diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity.stderr b/src/test/ui/rust-2018/uniform-paths/ambiguity.stderr
index 2e227dce96c..c65db3072f4 100644
--- a/src/test/ui/rust-2018/uniform-paths/ambiguity.stderr
+++ b/src/test/ui/rust-2018/uniform-paths/ambiguity.stderr
@@ -1,13 +1,13 @@
-error: import from `std` is ambiguous
+error: `std` import is ambiguous
   --> $DIR/ambiguity.rs:15:5
    |
 LL |   use std::io;
-   |       ^^^ could refer to external crate `::std`
+   |       ^^^ can refer to external crate `::std`
 ...
 LL | / mod std {
 LL | |     pub struct io;
 LL | | }
-   | |_- could also refer to `self::std`
+   | |_- can refer to `self::std`
    |
    = help: write `::std` or `self::std` explicitly instead
    = note: relative `use` paths enabled by `#![feature(uniform_paths)]`
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
index b8489c61dad..ee141d444b2 100644
--- a/src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.rs
+++ b/src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.rs
@@ -14,10 +14,18 @@
 
 enum Foo { A, B }
 
+struct std;
+
 fn main() {
     enum Foo {}
     use Foo::*;
-    //~^ ERROR import from `Foo` is ambiguous
+    //~^ ERROR `Foo` import is ambiguous
 
     let _ = (A, B);
+
+    fn std() {}
+    enum std {}
+    use std as foo;
+    //~^ ERROR `std` import is ambiguous
+    //~| ERROR `std` import is ambiguous
 }
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
index 656af91a8c7..86d95f2ac45 100644
--- a/src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.stderr
+++ b/src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.stderr
@@ -1,13 +1,45 @@
-error: import from `Foo` is ambiguous
-  --> $DIR/block-scoped-shadow.rs:19:9
+error: `Foo` import is ambiguous
+  --> $DIR/block-scoped-shadow.rs:21:9
    |
+LL | enum Foo { A, B }
+   | ----------------- can refer to `self::Foo`
+...
 LL |     enum Foo {}
    |     ----------- shadowed by block-scoped `Foo`
 LL |     use Foo::*;
    |         ^^^
    |
-   = help: write `::Foo` or `self::Foo` explicitly instead
+   = help: write `self::Foo` explicitly instead
    = note: relative `use` paths enabled by `#![feature(uniform_paths)]`
 
-error: aborting due to previous error
+error: `std` import is ambiguous
+  --> $DIR/block-scoped-shadow.rs:28:9
+   |
+LL | struct std;
+   | ----------- can refer to `self::std`
+...
+LL |     enum std {}
+   |     ----------- shadowed by block-scoped `std`
+LL |     use std as foo;
+   |         ^^^ can refer to external crate `::std`
+   |
+   = help: write `::std` or `self::std` explicitly instead
+   = note: relative `use` paths enabled by `#![feature(uniform_paths)]`
+
+error: `std` import is ambiguous
+  --> $DIR/block-scoped-shadow.rs:28:9
+   |
+LL | struct std;
+   | ----------- can refer to `self::std`
+...
+LL |     fn std() {}
+   |     ----------- shadowed by block-scoped `std`
+LL |     enum std {}
+LL |     use std as foo;
+   |         ^^^
+   |
+   = help: write `self::std` explicitly instead
+   = note: relative `use` paths enabled by `#![feature(uniform_paths)]`
+
+error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/rust-2018/uniform-paths/redundant.rs b/src/test/ui/rust-2018/uniform-paths/redundant.rs
new file mode 100644
index 00000000000..8f384b7e7af
--- /dev/null
+++ b/src/test/ui/rust-2018/uniform-paths/redundant.rs
@@ -0,0 +1,17 @@
+// 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;
+
+fn main() {}
diff --git a/src/test/ui/rust-2018/uniform-paths/redundant.stderr b/src/test/ui/rust-2018/uniform-paths/redundant.stderr
new file mode 100644
index 00000000000..dd38407e164
--- /dev/null
+++ b/src/test/ui/rust-2018/uniform-paths/redundant.stderr
@@ -0,0 +1,14 @@
+error: `std` import is redundant
+  --> $DIR/redundant.rs:15:5
+   |
+LL | use std;
+   |     ^^^
+   |     |
+   |     refers to external crate `::std`
+   |     defines `self::std`, shadowing itself
+   |
+   = help: remove or write `::std` explicitly instead
+   = note: relative `use` paths enabled by `#![feature(uniform_paths)]`
+
+error: aborting due to previous error
+