Skip to content

Commit 7a87e30

Browse files
committed
rustc_resolve: overhaul #![feature(uniform_paths)] error reporting.
1 parent fc323ba commit 7a87e30

14 files changed

+195
-56
lines changed

src/librustc_resolve/resolve_imports.rs

+95-34
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use self::ImportDirectiveSubclass::*;
1212

1313
use {AmbiguityError, CrateLint, Module, ModuleOrUniformRoot, PerNS};
14-
use Namespace::{self, TypeNS, MacroNS, ValueNS};
14+
use Namespace::{self, TypeNS, MacroNS};
1515
use {NameBinding, NameBindingKind, ToNameBinding, PathResult, PrivacyError};
1616
use Resolver;
1717
use {names_to_string, module_to_string};
@@ -34,6 +34,8 @@ use syntax::util::lev_distance::find_best_match_for_name;
3434
use syntax_pos::Span;
3535

3636
use std::cell::{Cell, RefCell};
37+
use std::collections::BTreeMap;
38+
use std::fmt::Write;
3739
use std::{mem, ptr};
3840

3941
/// Contains data for specific types of import directives.
@@ -615,6 +617,17 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
615617
self.finalize_resolutions_in(module);
616618
}
617619

620+
#[derive(Default)]
621+
struct UniformPathsCanaryResult {
622+
module_scope: Option<Span>,
623+
block_scopes: Vec<Span>,
624+
}
625+
// Collect all tripped `uniform_paths` canaries separately.
626+
let mut uniform_paths_canaries: BTreeMap<
627+
(Span, NodeId),
628+
(Name, PerNS<UniformPathsCanaryResult>),
629+
> = BTreeMap::new();
630+
618631
let mut errors = false;
619632
let mut seen_spans = FxHashSet();
620633
for i in 0 .. self.determined_imports.len() {
@@ -624,49 +637,37 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
624637
// For a `#![feature(uniform_paths)]` `use self::x as _` canary,
625638
// failure is ignored, while success may cause an ambiguity error.
626639
if import.is_uniform_paths_canary {
627-
let (name, result) = match import.subclass {
628-
SingleImport { source, ref result, .. } => {
629-
let type_ns = result[TypeNS].get().ok();
630-
let value_ns = result[ValueNS].get().ok();
631-
(source.name, type_ns.or(value_ns))
632-
}
633-
_ => bug!(),
634-
};
635-
636640
if error.is_some() {
637641
continue;
638642
}
639643

640-
let is_explicit_self =
644+
let (name, result) = match import.subclass {
645+
SingleImport { source, ref result, .. } => (source.name, result),
646+
_ => bug!(),
647+
};
648+
649+
let has_explicit_self =
641650
import.module_path.len() > 0 &&
642651
import.module_path[0].name == keywords::SelfValue.name();
643-
let extern_crate_exists = self.extern_prelude.contains(&name);
644652

645-
// A successful `self::x` is ambiguous with an `x` external crate.
646-
if is_explicit_self && !extern_crate_exists {
647-
continue;
648-
}
653+
let (prev_name, canary_results) =
654+
uniform_paths_canaries.entry((import.span, import.id))
655+
.or_insert((name, PerNS::default()));
649656

650-
errors = true;
657+
// All the canaries with the same `id` should have the same `name`.
658+
assert_eq!(*prev_name, name);
651659

652-
let msg = format!("import from `{}` is ambiguous", name);
653-
let mut err = self.session.struct_span_err(import.span, &msg);
654-
if extern_crate_exists {
655-
err.span_label(import.span,
656-
format!("could refer to external crate `::{}`", name));
657-
}
658-
if let Some(result) = result {
659-
if is_explicit_self {
660-
err.span_label(result.span,
661-
format!("could also refer to `self::{}`", name));
662-
} else {
663-
err.span_label(result.span,
664-
format!("shadowed by block-scoped `{}`", name));
660+
self.per_ns(|_, ns| {
661+
if let Some(result) = result[ns].get().ok() {
662+
if has_explicit_self {
663+
// There should only be one `self::x` (module-scoped) canary.
664+
assert_eq!(canary_results[ns].module_scope, None);
665+
canary_results[ns].module_scope = Some(result.span);
666+
} else {
667+
canary_results[ns].block_scopes.push(result.span);
668+
}
665669
}
666-
}
667-
err.help(&format!("write `::{0}` or `self::{0}` explicitly instead", name));
668-
err.note("relative `use` paths enabled by `#![feature(uniform_paths)]`");
669-
err.emit();
670+
});
670671
} else if let Some((span, err)) = error {
671672
errors = true;
672673

@@ -694,6 +695,66 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
694695
}
695696
}
696697

698+
for ((span, _), (name, results)) in uniform_paths_canaries {
699+
self.per_ns(|this, ns| {
700+
let results = &results[ns];
701+
702+
let has_external_crate =
703+
ns == TypeNS && this.extern_prelude.contains(&name);
704+
705+
// An ambiguity requires more than one possible resolution.
706+
let possible_resultions =
707+
(has_external_crate as usize) +
708+
(results.module_scope.is_some() as usize) +
709+
(!results.block_scopes.is_empty() as usize);
710+
if possible_resultions <= 1 {
711+
return;
712+
}
713+
714+
errors = true;
715+
716+
// Special-case the error when `self::x` finds its own `use x;`.
717+
if has_external_crate &&
718+
results.module_scope == Some(span) &&
719+
results.block_scopes.is_empty() {
720+
let msg = format!("`{}` import is redundant", name);
721+
this.session.struct_span_err(span, &msg)
722+
.span_label(span,
723+
format!("refers to external crate `::{}`", name))
724+
.span_label(span,
725+
format!("defines `self::{}`, shadowing itself", name))
726+
.help(&format!("remove or write `::{}` explicitly instead", name))
727+
.note("relative `use` paths enabled by `#![feature(uniform_paths)]`")
728+
.emit();
729+
return;
730+
}
731+
732+
let msg = format!("`{}` import is ambiguous", name);
733+
let mut err = this.session.struct_span_err(span, &msg);
734+
let mut suggestion_choices = String::new();
735+
if has_external_crate {
736+
write!(suggestion_choices, "`::{}`", name);
737+
err.span_label(span,
738+
format!("can refer to external crate `::{}`", name));
739+
}
740+
if let Some(span) = results.module_scope {
741+
if !suggestion_choices.is_empty() {
742+
suggestion_choices.push_str(" or ");
743+
}
744+
write!(suggestion_choices, "`self::{}`", name);
745+
err.span_label(span,
746+
format!("can refer to `self::{}`", name));
747+
}
748+
for &span in &results.block_scopes {
749+
err.span_label(span,
750+
format!("shadowed by block-scoped `{}`", name));
751+
}
752+
err.help(&format!("write {} explicitly instead", suggestion_choices));
753+
err.note("relative `use` paths enabled by `#![feature(uniform_paths)]`");
754+
err.emit();
755+
});
756+
}
757+
697758
// Report unresolved imports only if no hard error was already reported
698759
// to avoid generating multiple errors on the same import.
699760
if !errors {

src/test/run-pass/uniform-paths/basic-nested.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
// edition:2018
1212

13-
#![feature(uniform_paths)]
13+
#![feature(decl_macro, uniform_paths)]
1414

1515
// This test is similar to `basic.rs`, but nested in modules.
1616

@@ -40,6 +40,11 @@ mod bar {
4040
// item, e.g. the automatically injected `extern crate std;`, which in
4141
// the Rust 2018 should no longer be visible through `crate::std`.
4242
pub use std::io;
43+
44+
// Also test that items named `std` in other namespaces don't
45+
// cause ambiguity errors for the import from `std` above.
46+
pub fn std() {}
47+
pub macro std() {}
4348
}
4449

4550

@@ -49,4 +54,6 @@ fn main() {
4954
foo::local_io(());
5055
io::stdout();
5156
bar::io::stdout();
57+
bar::std();
58+
bar::std!();
5259
}

src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
mod foo {
1818
pub use std::io;
19-
//~^ ERROR import from `std` is ambiguous
19+
//~^ ERROR `std` import is ambiguous
2020

2121
macro_rules! m {
2222
() => {

src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
error: import from `std` is ambiguous
1+
error: `std` import is ambiguous
22
--> $DIR/ambiguity-macros-nested.rs:18:13
33
|
44
LL | pub use std::io;
5-
| ^^^ could refer to external crate `::std`
5+
| ^^^ can refer to external crate `::std`
66
...
77
LL | / mod std {
88
LL | | pub struct io;
99
LL | | }
10-
| |_____________- could also refer to `self::std`
10+
| |_____________- can refer to `self::std`
1111
|
1212
= help: write `::std` or `self::std` explicitly instead
1313
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`

src/test/ui/rust-2018/uniform-paths/ambiguity-macros.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// This test is similar to `ambiguity.rs`, but with macros defining local items.
1616

1717
use std::io;
18-
//~^ ERROR import from `std` is ambiguous
18+
//~^ ERROR `std` import is ambiguous
1919

2020
macro_rules! m {
2121
() => {

src/test/ui/rust-2018/uniform-paths/ambiguity-macros.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
error: import from `std` is ambiguous
1+
error: `std` import is ambiguous
22
--> $DIR/ambiguity-macros.rs:17:5
33
|
44
LL | use std::io;
5-
| ^^^ could refer to external crate `::std`
5+
| ^^^ can refer to external crate `::std`
66
...
77
LL | / mod std {
88
LL | | pub struct io;
99
LL | | }
10-
| |_________- could also refer to `self::std`
10+
| |_________- can refer to `self::std`
1111
|
1212
= help: write `::std` or `self::std` explicitly instead
1313
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`

src/test/ui/rust-2018/uniform-paths/ambiguity-nested.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
mod foo {
1818
pub use std::io;
19-
//~^ ERROR import from `std` is ambiguous
19+
//~^ ERROR `std` import is ambiguous
2020

2121
mod std {
2222
pub struct io;

src/test/ui/rust-2018/uniform-paths/ambiguity-nested.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
error: import from `std` is ambiguous
1+
error: `std` import is ambiguous
22
--> $DIR/ambiguity-nested.rs:18:13
33
|
44
LL | pub use std::io;
5-
| ^^^ could refer to external crate `::std`
5+
| ^^^ can refer to external crate `::std`
66
...
77
LL | / mod std {
88
LL | | pub struct io;
99
LL | | }
10-
| |_____- could also refer to `self::std`
10+
| |_____- can refer to `self::std`
1111
|
1212
= help: write `::std` or `self::std` explicitly instead
1313
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`

src/test/ui/rust-2018/uniform-paths/ambiguity.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
#![feature(uniform_paths)]
1414

1515
use std::io;
16-
//~^ ERROR import from `std` is ambiguous
16+
//~^ ERROR `std` import is ambiguous
1717

1818
mod std {
1919
pub struct io;

src/test/ui/rust-2018/uniform-paths/ambiguity.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
error: import from `std` is ambiguous
1+
error: `std` import is ambiguous
22
--> $DIR/ambiguity.rs:15:5
33
|
44
LL | use std::io;
5-
| ^^^ could refer to external crate `::std`
5+
| ^^^ can refer to external crate `::std`
66
...
77
LL | / mod std {
88
LL | | pub struct io;
99
LL | | }
10-
| |_- could also refer to `self::std`
10+
| |_- can refer to `self::std`
1111
|
1212
= help: write `::std` or `self::std` explicitly instead
1313
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`

src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,18 @@
1414

1515
enum Foo { A, B }
1616

17+
struct std;
18+
1719
fn main() {
1820
enum Foo {}
1921
use Foo::*;
20-
//~^ ERROR import from `Foo` is ambiguous
22+
//~^ ERROR `Foo` import is ambiguous
2123

2224
let _ = (A, B);
25+
26+
fn std() {}
27+
enum std {}
28+
use std as foo;
29+
//~^ ERROR `std` import is ambiguous
30+
//~| ERROR `std` import is ambiguous
2331
}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,45 @@
1-
error: import from `Foo` is ambiguous
2-
--> $DIR/block-scoped-shadow.rs:19:9
1+
error: `Foo` import is ambiguous
2+
--> $DIR/block-scoped-shadow.rs:21:9
33
|
4+
LL | enum Foo { A, B }
5+
| ----------------- can refer to `self::Foo`
6+
...
47
LL | enum Foo {}
58
| ----------- shadowed by block-scoped `Foo`
69
LL | use Foo::*;
710
| ^^^
811
|
9-
= help: write `::Foo` or `self::Foo` explicitly instead
12+
= help: write `self::Foo` explicitly instead
1013
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`
1114

12-
error: aborting due to previous error
15+
error: `std` import is ambiguous
16+
--> $DIR/block-scoped-shadow.rs:28:9
17+
|
18+
LL | struct std;
19+
| ----------- can refer to `self::std`
20+
...
21+
LL | enum std {}
22+
| ----------- shadowed by block-scoped `std`
23+
LL | use std as foo;
24+
| ^^^ can refer to external crate `::std`
25+
|
26+
= help: write `::std` or `self::std` explicitly instead
27+
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`
28+
29+
error: `std` import is ambiguous
30+
--> $DIR/block-scoped-shadow.rs:28:9
31+
|
32+
LL | struct std;
33+
| ----------- can refer to `self::std`
34+
...
35+
LL | fn std() {}
36+
| ----------- shadowed by block-scoped `std`
37+
LL | enum std {}
38+
LL | use std as foo;
39+
| ^^^
40+
|
41+
= help: write `self::std` explicitly instead
42+
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`
43+
44+
error: aborting due to 3 previous errors
1345

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// edition:2018
12+
13+
#![feature(uniform_paths)]
14+
15+
use std;
16+
17+
fn main() {}

0 commit comments

Comments
 (0)