Auto merge of #53427 - eddyb:uniform-paths-diagnostics, r=varkor
rustc_resolve: overhaul `#![feature(uniform_paths)]` error reporting. Fixes #53408 by only considering external crates to conflict within their (type/module) namespace, *not* with the value or macro namespaces, and also by adding a special-cased error for redundant `use crate_name;` imports (without actually allowing them). Also, all canaries for a given import are grouped into one diagnostic per namespace, in order to make block-scoped ambiguities clearer. See changed/added tests for more details. r? @petrochenkov cc @aturon @joshtriplett
This commit is contained in:
commit
f34933ba0a
14 changed files with 195 additions and 56 deletions
|
@ -11,7 +11,7 @@
|
||||||
use self::ImportDirectiveSubclass::*;
|
use self::ImportDirectiveSubclass::*;
|
||||||
|
|
||||||
use {AmbiguityError, CrateLint, Module, ModuleOrUniformRoot, PerNS};
|
use {AmbiguityError, CrateLint, Module, ModuleOrUniformRoot, PerNS};
|
||||||
use Namespace::{self, TypeNS, MacroNS, ValueNS};
|
use Namespace::{self, TypeNS, MacroNS};
|
||||||
use {NameBinding, NameBindingKind, ToNameBinding, PathResult, PrivacyError};
|
use {NameBinding, NameBindingKind, ToNameBinding, PathResult, PrivacyError};
|
||||||
use Resolver;
|
use Resolver;
|
||||||
use {names_to_string, module_to_string};
|
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 syntax_pos::Span;
|
||||||
|
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::fmt::Write;
|
||||||
use std::{mem, ptr};
|
use std::{mem, ptr};
|
||||||
|
|
||||||
/// Contains data for specific types of import directives.
|
/// 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);
|
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 errors = false;
|
||||||
let mut seen_spans = FxHashSet();
|
let mut seen_spans = FxHashSet();
|
||||||
for i in 0 .. self.determined_imports.len() {
|
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,
|
// For a `#![feature(uniform_paths)]` `use self::x as _` canary,
|
||||||
// failure is ignored, while success may cause an ambiguity error.
|
// failure is ignored, while success may cause an ambiguity error.
|
||||||
if import.is_uniform_paths_canary {
|
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() {
|
if error.is_some() {
|
||||||
continue;
|
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.len() > 0 &&
|
||||||
import.module_path[0].name == keywords::SelfValue.name();
|
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.
|
let (prev_name, canary_results) =
|
||||||
if is_explicit_self && !extern_crate_exists {
|
uniform_paths_canaries.entry((import.span, import.id))
|
||||||
continue;
|
.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);
|
self.per_ns(|_, ns| {
|
||||||
let mut err = self.session.struct_span_err(import.span, &msg);
|
if let Some(result) = result[ns].get().ok() {
|
||||||
if extern_crate_exists {
|
if has_explicit_self {
|
||||||
err.span_label(import.span,
|
// There should only be one `self::x` (module-scoped) canary.
|
||||||
format!("could refer to external crate `::{}`", name));
|
assert_eq!(canary_results[ns].module_scope, None);
|
||||||
}
|
canary_results[ns].module_scope = Some(result.span);
|
||||||
if let Some(result) = result {
|
} else {
|
||||||
if is_explicit_self {
|
canary_results[ns].block_scopes.push(result.span);
|
||||||
err.span_label(result.span,
|
}
|
||||||
format!("could also refer to `self::{}`", name));
|
|
||||||
} else {
|
|
||||||
err.span_label(result.span,
|
|
||||||
format!("shadowed by block-scoped `{}`", name));
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
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 {
|
} else if let Some((span, err)) = error {
|
||||||
errors = true;
|
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
|
// Report unresolved imports only if no hard error was already reported
|
||||||
// to avoid generating multiple errors on the same import.
|
// to avoid generating multiple errors on the same import.
|
||||||
if !errors {
|
if !errors {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
// edition:2018
|
// edition:2018
|
||||||
|
|
||||||
#![feature(uniform_paths)]
|
#![feature(decl_macro, uniform_paths)]
|
||||||
|
|
||||||
// This test is similar to `basic.rs`, but nested in modules.
|
// 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
|
// item, e.g. the automatically injected `extern crate std;`, which in
|
||||||
// the Rust 2018 should no longer be visible through `crate::std`.
|
// the Rust 2018 should no longer be visible through `crate::std`.
|
||||||
pub use std::io;
|
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(());
|
foo::local_io(());
|
||||||
io::stdout();
|
io::stdout();
|
||||||
bar::io::stdout();
|
bar::io::stdout();
|
||||||
|
bar::std();
|
||||||
|
bar::std!();
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
mod foo {
|
mod foo {
|
||||||
pub use std::io;
|
pub use std::io;
|
||||||
//~^ ERROR import from `std` is ambiguous
|
//~^ ERROR `std` import is ambiguous
|
||||||
|
|
||||||
macro_rules! m {
|
macro_rules! m {
|
||||||
() => {
|
() => {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
error: import from `std` is ambiguous
|
error: `std` import is ambiguous
|
||||||
--> $DIR/ambiguity-macros-nested.rs:18:13
|
--> $DIR/ambiguity-macros-nested.rs:18:13
|
||||||
|
|
|
|
||||||
LL | pub use std::io;
|
LL | pub use std::io;
|
||||||
| ^^^ could refer to external crate `::std`
|
| ^^^ can refer to external crate `::std`
|
||||||
...
|
...
|
||||||
LL | / mod std {
|
LL | / mod std {
|
||||||
LL | | pub struct io;
|
LL | | pub struct io;
|
||||||
LL | | }
|
LL | | }
|
||||||
| |_____________- could also refer to `self::std`
|
| |_____________- can refer to `self::std`
|
||||||
|
|
|
|
||||||
= help: write `::std` or `self::std` explicitly instead
|
= help: write `::std` or `self::std` explicitly instead
|
||||||
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`
|
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
// This test is similar to `ambiguity.rs`, but with macros defining local items.
|
// This test is similar to `ambiguity.rs`, but with macros defining local items.
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
//~^ ERROR import from `std` is ambiguous
|
//~^ ERROR `std` import is ambiguous
|
||||||
|
|
||||||
macro_rules! m {
|
macro_rules! m {
|
||||||
() => {
|
() => {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
error: import from `std` is ambiguous
|
error: `std` import is ambiguous
|
||||||
--> $DIR/ambiguity-macros.rs:17:5
|
--> $DIR/ambiguity-macros.rs:17:5
|
||||||
|
|
|
|
||||||
LL | use std::io;
|
LL | use std::io;
|
||||||
| ^^^ could refer to external crate `::std`
|
| ^^^ can refer to external crate `::std`
|
||||||
...
|
...
|
||||||
LL | / mod std {
|
LL | / mod std {
|
||||||
LL | | pub struct io;
|
LL | | pub struct io;
|
||||||
LL | | }
|
LL | | }
|
||||||
| |_________- could also refer to `self::std`
|
| |_________- can refer to `self::std`
|
||||||
|
|
|
|
||||||
= help: write `::std` or `self::std` explicitly instead
|
= help: write `::std` or `self::std` explicitly instead
|
||||||
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`
|
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
mod foo {
|
mod foo {
|
||||||
pub use std::io;
|
pub use std::io;
|
||||||
//~^ ERROR import from `std` is ambiguous
|
//~^ ERROR `std` import is ambiguous
|
||||||
|
|
||||||
mod std {
|
mod std {
|
||||||
pub struct io;
|
pub struct io;
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
error: import from `std` is ambiguous
|
error: `std` import is ambiguous
|
||||||
--> $DIR/ambiguity-nested.rs:18:13
|
--> $DIR/ambiguity-nested.rs:18:13
|
||||||
|
|
|
|
||||||
LL | pub use std::io;
|
LL | pub use std::io;
|
||||||
| ^^^ could refer to external crate `::std`
|
| ^^^ can refer to external crate `::std`
|
||||||
...
|
...
|
||||||
LL | / mod std {
|
LL | / mod std {
|
||||||
LL | | pub struct io;
|
LL | | pub struct io;
|
||||||
LL | | }
|
LL | | }
|
||||||
| |_____- could also refer to `self::std`
|
| |_____- can refer to `self::std`
|
||||||
|
|
|
|
||||||
= help: write `::std` or `self::std` explicitly instead
|
= help: write `::std` or `self::std` explicitly instead
|
||||||
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`
|
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#![feature(uniform_paths)]
|
#![feature(uniform_paths)]
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
//~^ ERROR import from `std` is ambiguous
|
//~^ ERROR `std` import is ambiguous
|
||||||
|
|
||||||
mod std {
|
mod std {
|
||||||
pub struct io;
|
pub struct io;
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
error: import from `std` is ambiguous
|
error: `std` import is ambiguous
|
||||||
--> $DIR/ambiguity.rs:15:5
|
--> $DIR/ambiguity.rs:15:5
|
||||||
|
|
|
|
||||||
LL | use std::io;
|
LL | use std::io;
|
||||||
| ^^^ could refer to external crate `::std`
|
| ^^^ can refer to external crate `::std`
|
||||||
...
|
...
|
||||||
LL | / mod std {
|
LL | / mod std {
|
||||||
LL | | pub struct io;
|
LL | | pub struct io;
|
||||||
LL | | }
|
LL | | }
|
||||||
| |_- could also refer to `self::std`
|
| |_- can refer to `self::std`
|
||||||
|
|
|
|
||||||
= help: write `::std` or `self::std` explicitly instead
|
= help: write `::std` or `self::std` explicitly instead
|
||||||
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`
|
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`
|
||||||
|
|
|
@ -14,10 +14,18 @@
|
||||||
|
|
||||||
enum Foo { A, B }
|
enum Foo { A, B }
|
||||||
|
|
||||||
|
struct std;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
enum Foo {}
|
enum Foo {}
|
||||||
use Foo::*;
|
use Foo::*;
|
||||||
//~^ ERROR import from `Foo` is ambiguous
|
//~^ ERROR `Foo` import is ambiguous
|
||||||
|
|
||||||
let _ = (A, B);
|
let _ = (A, B);
|
||||||
|
|
||||||
|
fn std() {}
|
||||||
|
enum std {}
|
||||||
|
use std as foo;
|
||||||
|
//~^ ERROR `std` import is ambiguous
|
||||||
|
//~| ERROR `std` import is ambiguous
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,45 @@
|
||||||
error: import from `Foo` is ambiguous
|
error: `Foo` import is ambiguous
|
||||||
--> $DIR/block-scoped-shadow.rs:19:9
|
--> $DIR/block-scoped-shadow.rs:21:9
|
||||||
|
|
|
|
||||||
|
LL | enum Foo { A, B }
|
||||||
|
| ----------------- can refer to `self::Foo`
|
||||||
|
...
|
||||||
LL | enum Foo {}
|
LL | enum Foo {}
|
||||||
| ----------- shadowed by block-scoped `Foo`
|
| ----------- shadowed by block-scoped `Foo`
|
||||||
LL | use 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)]`
|
= 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
src/test/ui/rust-2018/uniform-paths/redundant.rs
Normal file
17
src/test/ui/rust-2018/uniform-paths/redundant.rs
Normal file
|
@ -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() {}
|
14
src/test/ui/rust-2018/uniform-paths/redundant.stderr
Normal file
14
src/test/ui/rust-2018/uniform-paths/redundant.stderr
Normal file
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue