Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustc_resolve: overhaul #![feature(uniform_paths)] error reporting. #53427

Merged
merged 1 commit into from
Aug 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 95 additions & 34 deletions src/librustc_resolve/resolve_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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.
Expand Down Expand Up @@ -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() {
Expand All @@ -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;

Expand Down Expand Up @@ -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 {
Expand Down
9 changes: 8 additions & 1 deletion src/test/run-pass/uniform-paths/basic-nested.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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() {}
}


Expand All @@ -49,4 +54,6 @@ fn main() {
foo::local_io(());
io::stdout();
bar::io::stdout();
bar::std();
bar::std!();
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

mod foo {
pub use std::io;
//~^ ERROR import from `std` is ambiguous
//~^ ERROR `std` import is ambiguous

macro_rules! m {
() => {
Expand Down
Original file line number Diff line number Diff line change
@@ -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)]`
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/rust-2018/uniform-paths/ambiguity-macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
() => {
Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/rust-2018/uniform-paths/ambiguity-macros.stderr
Original file line number Diff line number Diff line change
@@ -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)]`
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/rust-2018/uniform-paths/ambiguity-nested.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/rust-2018/uniform-paths/ambiguity-nested.stderr
Original file line number Diff line number Diff line change
@@ -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)]`
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/rust-2018/uniform-paths/ambiguity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/rust-2018/uniform-paths/ambiguity.stderr
Original file line number Diff line number Diff line change
@@ -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)]`
Expand Down
10 changes: 9 additions & 1 deletion src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
40 changes: 36 additions & 4 deletions src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.stderr
Original file line number Diff line number Diff line change
@@ -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

17 changes: 17 additions & 0 deletions src/test/ui/rust-2018/uniform-paths/redundant.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Loading