Skip to content

Commit

Permalink
rustc_resolve: also inject canaries to detect block scopes shadowing …
Browse files Browse the repository at this point in the history
…`uniform_paths` imports.
  • Loading branch information
eddyb committed Aug 14, 2018
1 parent 2ad865d commit 13bc0b5
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 34 deletions.
84 changes: 58 additions & 26 deletions src/librustc_resolve/build_reduced_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
// import is injected as a "canary", and an error is emitted if it
// successfully resolves while an `x` external crate exists.
//
// For each block scope around the `use` item, one special canary
// import of the form `use x as _;` is also injected, having its
// parent set to that scope; `resolve_imports` will only resolve
// it within its appropriate scope; if any of them successfully
// resolve, an ambiguity error is emitted, since the original
// import can't see the item in the block scope (`self::x` only
// looks in the enclosing module), but a non-`use` path could.
//
// Additionally, the canary might be able to catch limitations of the
// current implementation, where `::x` may be chosen due to `self::x`
// not existing, but `self::x` could appear later, from macro expansion.
Expand All @@ -173,33 +181,57 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
self.session.features_untracked().uniform_paths);

let source = module_path[0];
let subclass = SingleImport {
target: Ident {
name: keywords::Underscore.name().gensymed(),
span: source.span,
},
source,
result: PerNS {
type_ns: Cell::new(Err(Undetermined)),
value_ns: Cell::new(Err(Undetermined)),
macro_ns: Cell::new(Err(Undetermined)),
},
type_ns_only: false,
// Helper closure to emit a canary with the given base path.
let emit = |this: &mut Self, base: Option<Ident>| {
let subclass = SingleImport {
target: Ident {
name: keywords::Underscore.name().gensymed(),
span: source.span,
},
source,
result: PerNS {
type_ns: Cell::new(Err(Undetermined)),
value_ns: Cell::new(Err(Undetermined)),
macro_ns: Cell::new(Err(Undetermined)),
},
type_ns_only: false,
};
this.add_import_directive(
base.into_iter().collect(),
subclass.clone(),
source.span,
id,
root_use_tree.span,
root_id,
ty::Visibility::Invisible,
expansion,
true, // is_uniform_paths_canary
);
};
self.add_import_directive(
vec![Ident {
name: keywords::SelfValue.name(),
span: source.span,
}],
subclass,
source.span,
id,
root_use_tree.span,
root_id,
ty::Visibility::Invisible,
expansion,
true, // is_uniform_paths_canary
);

// A single simple `self::x` canary.
emit(self, Some(Ident {
name: keywords::SelfValue.name(),
span: source.span,
}));

// One special unprefixed canary per block scope around
// the import, to detect items unreachable by `self::x`.
let orig_current_module = self.current_module;
let mut span = source.span.modern();
loop {
match self.current_module.kind {
ModuleKind::Block(..) => emit(self, None),
ModuleKind::Def(..) => break,
}
match self.hygienic_lexical_parent(self.current_module, &mut span) {
Some(module) => {
self.current_module = module;
}
None => break,
}
}
self.current_module = orig_current_module;

uniform_paths_canary_emitted = true;
}
Expand Down
32 changes: 24 additions & 8 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};
use Namespace::{self, TypeNS, MacroNS, ValueNS};
use {NameBinding, NameBindingKind, ToNameBinding, PathResult, PrivacyError};
use Resolver;
use {names_to_string, module_to_string};
Expand Down Expand Up @@ -636,24 +636,32 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
continue;
}

let is_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 !extern_crate_exists {
if is_explicit_self && !extern_crate_exists {
continue;
}

errors = true;

let msg = format!("import from `{}` is ambiguous", name);
let mut err = self.session.struct_span_err(import.span, &msg);
err.span_label(import.span,
format!("could refer to external crate `::{}`", name));
if extern_crate_exists {
err.span_label(import.span,
format!("could refer to external crate `::{}`", name));
}
if let Some(result) = result {
err.span_label(result.span,
format!("could also refer to `self::{}`", name));
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));
}
}
err.help(&format!("write `::{0}` or `self::{0}` explicitly instead", name));
err.note("relative `use` paths enabled by `#![feature(uniform_paths)]`");
Expand Down Expand Up @@ -717,7 +725,11 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
// while resolving its module path.
directive.vis.set(ty::Visibility::Invisible);
let result = self.resolve_path(
Some(ModuleOrUniformRoot::UniformRoot(keywords::Invalid.name())),
Some(if directive.is_uniform_paths_canary {
ModuleOrUniformRoot::Module(directive.parent)
} else {
ModuleOrUniformRoot::UniformRoot(keywords::Invalid.name())
}),
&directive.module_path[..],
None,
false,
Expand Down Expand Up @@ -792,7 +804,11 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
let ImportDirective { ref module_path, span, .. } = *directive;

let module_result = self.resolve_path(
Some(ModuleOrUniformRoot::UniformRoot(keywords::Invalid.name())),
Some(if directive.is_uniform_paths_canary {
ModuleOrUniformRoot::Module(directive.parent)
} else {
ModuleOrUniformRoot::UniformRoot(keywords::Invalid.name())
}),
&module_path,
None,
true,
Expand Down
23 changes: 23 additions & 0 deletions src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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)]

enum Foo { A, B }

fn main() {
enum Foo {}
use Foo::*;
//~^ ERROR import from `Foo` is ambiguous

let _ = (A, B);
}
13 changes: 13 additions & 0 deletions src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
error: import from `Foo` is ambiguous
--> $DIR/block-scoped-shadow.rs:19:9
|
LL | enum Foo {}
| ----------- shadowed by block-scoped `Foo`
LL | use Foo::*;
| ^^^
|
= help: write `::Foo` or `self::Foo` explicitly instead
= note: relative `use` paths enabled by `#![feature(uniform_paths)]`

error: aborting due to previous error

0 comments on commit 13bc0b5

Please sign in to comment.