diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index b6e03b66510e5..ee98cc6cf927e 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -865,7 +865,7 @@ where krate = time(sess, "crate injection", || { let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| &**s); - syntax::std_inject::maybe_inject_crates_ref(krate, alt_std_name) + syntax::std_inject::maybe_inject_crates_ref(krate, alt_std_name, sess.edition()) }); let mut addl_plugins = Some(addl_plugins); diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index d3b70933e2cd4..a10bb3e25df65 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -1139,32 +1139,4 @@ impl<'a> CrateLoader<'a> { cnum } - - pub fn process_use_extern( - &mut self, - name: Symbol, - span: Span, - id: ast::NodeId, - definitions: &Definitions, - ) -> CrateNum { - let cnum = self.resolve_crate( - &None, name, name, None, None, span, PathKind::Crate, DepKind::Explicit - ).0; - - let def_id = definitions.opt_local_def_id(id).unwrap(); - let path_len = definitions.def_path(def_id.index).data.len(); - - self.update_extern_crate( - cnum, - ExternCrate { - src: ExternCrateSource::Use, - span, - path_len, - direct: true, - }, - &mut FxHashSet(), - ); - - cnum - } } diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index bf87b00c14969..fa2af891f109b 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -17,7 +17,7 @@ use macros::{InvocationData, LegacyScope}; use resolve_imports::ImportDirective; use resolve_imports::ImportDirectiveSubclass::{self, GlobImport, SingleImport}; use {Module, ModuleData, ModuleKind, NameBinding, NameBindingKind, ToNameBinding}; -use {PerNS, Resolver, ResolverArenas}; +use {ModuleOrUniformRoot, PerNS, Resolver, ResolverArenas}; use Namespace::{self, TypeNS, ValueNS, MacroNS}; use {resolve_error, resolve_struct_error, ResolutionError}; @@ -113,16 +113,24 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } } - fn build_reduced_graph_for_use_tree(&mut self, - root_use_tree: &ast::UseTree, - root_id: NodeId, - use_tree: &ast::UseTree, - id: NodeId, - vis: ty::Visibility, - prefix: &ast::Path, - nested: bool, - item: &Item, - expansion: Mark) { + fn build_reduced_graph_for_use_tree( + &mut self, + root_use_tree: &ast::UseTree, + root_id: NodeId, + use_tree: &ast::UseTree, + id: NodeId, + vis: ty::Visibility, + prefix: &ast::Path, + mut uniform_paths_canary_emitted: bool, + nested: bool, + item: &Item, + expansion: Mark, + ) { + debug!("build_reduced_graph_for_use_tree(prefix={:?}, \ + uniform_paths_canary_emitted={}, \ + use_tree={:?}, nested={})", + prefix, uniform_paths_canary_emitted, use_tree, nested); + let is_prelude = attr::contains_name(&item.attrs, "prelude_import"); let path = &use_tree.prefix; @@ -131,6 +139,103 @@ impl<'a, 'cl> Resolver<'a, 'cl> { .map(|seg| seg.ident) .collect(); + debug!("build_reduced_graph_for_use_tree: module_path={:?}", module_path); + + // `#[feature(uniform_paths)]` allows an unqualified import path, + // e.g. `use x::...;` to resolve not just globally (`use ::x::...;`) + // but also relatively (`use self::x::...;`). To catch ambiguities + // that might arise from both of these being available and resolution + // silently picking one of them, an artificial `use self::x as _;` + // 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. + // + // NB. The canary currently only errors if the `x::...` path *could* + // resolve as a relative path through the extern crate, i.e. `x` is + // in `extern_prelude`, *even though* `::x` might still forcefully + // load a non-`extern_prelude` crate. + // While always producing an ambiguity errors if `self::x` exists and + // a crate *could* be loaded, would be more conservative, imports for + // local modules named `test` (or less commonly, `syntax` or `log`), + // would need to be qualified (e.g. `self::test`), which is considered + // ergonomically unacceptable. + let emit_uniform_paths_canary = + !uniform_paths_canary_emitted && + module_path.get(0).map_or(false, |ident| { + !ident.is_path_segment_keyword() + }); + if emit_uniform_paths_canary { + // Relative paths should only get here if the feature-gate is on. + assert!(self.session.rust_2018() && + self.session.features_untracked().uniform_paths); + + let source = module_path[0]; + // Helper closure to emit a canary with the given base path. + let emit = |this: &mut Self, base: Option| { + 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 + ); + }; + + // 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; + } + match use_tree.kind { ast::UseTreeKind::Simple(rename, ..) => { let mut ident = use_tree.ident(); @@ -142,8 +247,10 @@ impl<'a, 'cl> Resolver<'a, 'cl> { if source.name == keywords::SelfValue.name() { type_ns_only = true; - let last_segment = *module_path.last().unwrap(); - if last_segment.name == keywords::CrateRoot.name() { + let empty_prefix = module_path.last().map_or(true, |ident| { + ident.name == keywords::CrateRoot.name() + }); + if empty_prefix { resolve_error( self, use_tree.span, @@ -154,10 +261,9 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } // Replace `use foo::self;` with `use foo;` - let _ = module_path.pop(); - source = last_segment; + source = module_path.pop().unwrap(); if rename.is_none() { - ident = last_segment; + ident = source; } } } else { @@ -169,13 +275,23 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } // Disallow `use $crate;` - if source.name == keywords::DollarCrate.name() && path.segments.len() == 1 { + if source.name == keywords::DollarCrate.name() && module_path.is_empty() { let crate_root = self.resolve_crate_root(source); let crate_name = match crate_root.kind { ModuleKind::Def(_, name) => name, ModuleKind::Block(..) => unreachable!(), }; - source.name = crate_name; + // HACK(eddyb) unclear how good this is, but keeping `$crate` + // in `source` breaks `src/test/compile-fail/import-crate-var.rs`, + // while the current crate doesn't have a valid `crate_name`. + if crate_name != keywords::Invalid.name() { + // `crate_name` should not be interpreted as relative. + module_path.push(Ident { + name: keywords::CrateRoot.name(), + span: source.span, + }); + source.name = crate_name; + } if rename.is_none() { ident.name = crate_name; } @@ -187,6 +303,12 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } } + if ident.name == keywords::Crate.name() { + self.session.span_err(ident.span, + "crate root imports need to be explicitly named: \ + `use crate as name;`"); + } + let subclass = SingleImport { target: ident, source, @@ -206,6 +328,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { root_id, vis, expansion, + false, // is_uniform_paths_canary ); } ast::UseTreeKind::Glob => { @@ -222,6 +345,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { root_id, vis, expansion, + false, // is_uniform_paths_canary ); } ast::UseTreeKind::Nested(ref items) => { @@ -256,7 +380,16 @@ impl<'a, 'cl> Resolver<'a, 'cl> { for &(ref tree, id) in items { self.build_reduced_graph_for_use_tree( - root_use_tree, root_id, tree, id, vis, &prefix, true, item, expansion + root_use_tree, + root_id, + tree, + id, + vis, + &prefix, + uniform_paths_canary_emitted, + true, + item, + expansion, ); } } @@ -272,14 +405,32 @@ impl<'a, 'cl> Resolver<'a, 'cl> { match item.node { ItemKind::Use(ref use_tree) => { + let uniform_paths = + self.session.rust_2018() && + self.session.features_untracked().uniform_paths; // Imports are resolved as global by default, add starting root segment. + let root = if !uniform_paths { + use_tree.prefix.make_root() + } else { + // Except when `#![feature(uniform_paths)]` is on. + None + }; let prefix = ast::Path { - segments: use_tree.prefix.make_root().into_iter().collect(), + segments: root.into_iter().collect(), span: use_tree.span, }; self.build_reduced_graph_for_use_tree( - use_tree, item.id, use_tree, item.id, vis, &prefix, false, item, expansion, + use_tree, + item.id, + use_tree, + item.id, + vis, + &prefix, + false, // uniform_paths_canary_emitted + false, + item, + expansion, ); } @@ -299,7 +450,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { root_id: item.id, id: item.id, parent, - imported_module: Cell::new(Some(module)), + imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))), subclass: ImportDirectiveSubclass::ExternCrate(orig_name), root_span: item.span, span: item.span, @@ -307,6 +458,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { vis: Cell::new(vis), expansion, used: Cell::new(used), + is_uniform_paths_canary: false, }); self.potentially_unused_imports.push(directive); let imported_binding = self.import(binding, directive); @@ -701,7 +853,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { root_id: item.id, id: item.id, parent: graph_root, - imported_module: Cell::new(Some(module)), + imported_module: Cell::new(Some(ModuleOrUniformRoot::Module(module))), subclass: ImportDirectiveSubclass::MacroUse, root_span: span, span, @@ -709,6 +861,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { vis: Cell::new(ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX))), expansion, used: Cell::new(false), + is_uniform_paths_canary: false, }); if let Some(span) = legacy_imports.import_all { @@ -721,7 +874,13 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } else { for (name, span) in legacy_imports.imports { let ident = Ident::with_empty_ctxt(name); - let result = self.resolve_ident_in_module(module, ident, MacroNS, false, span); + let result = self.resolve_ident_in_module( + ModuleOrUniformRoot::Module(module), + ident, + MacroNS, + false, + span, + ); if let Ok(binding) = result { let directive = macro_use_directive(span); self.potentially_unused_imports.push(directive); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index e31e2cc1dff59..992ea12ffa2b1 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -953,9 +953,20 @@ impl<'a> LexicalScopeBinding<'a> { } } +#[derive(Copy, Clone, Debug)] +pub enum ModuleOrUniformRoot<'a> { + /// Regular module. + Module(Module<'a>), + + /// The `{{root}}` (`CrateRoot` aka "global") / `extern` initial segment + /// in which external crates resolve, and also `crate` (only in `{{root}}`, + /// but *not* `extern`), in the Rust 2018 edition. + UniformRoot(Name), +} + #[derive(Clone, Debug)] enum PathResult<'a> { - Module(Module<'a>), + Module(ModuleOrUniformRoot<'a>), NonModule(PathResolution), Indeterminate, Failed(Span, String, bool /* is the error from the last segment? */), @@ -1583,11 +1594,13 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { let hir::Path { ref segments, span, ref mut def } = *path; let path: Vec<_> = segments.iter().map(|seg| seg.ident).collect(); // FIXME (Manishearth): Intra doc links won't get warned of epoch changes - match self.resolve_path(&path, Some(namespace), true, span, CrateLint::No) { - PathResult::Module(module) => *def = module.def().unwrap(), + match self.resolve_path(None, &path, Some(namespace), true, span, CrateLint::No) { + PathResult::Module(ModuleOrUniformRoot::Module(module)) => + *def = module.def().unwrap(), PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => *def = path_res.base_def(), PathResult::NonModule(..) => match self.resolve_path( + None, &path, None, true, @@ -1599,6 +1612,7 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { } _ => {} }, + PathResult::Module(ModuleOrUniformRoot::UniformRoot(_)) | PathResult::Indeterminate => unreachable!(), PathResult::Failed(span, msg, _) => { error_callback(self, span, ResolutionError::FailedToResolve(&msg)); @@ -1881,7 +1895,12 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { }; let item = self.resolve_ident_in_module_unadjusted( - module, ident, ns, false, record_used, path_span, + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + record_used, + path_span, ); if let Ok(binding) = item { // The ident resolves to an item. @@ -1906,7 +1925,12 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { let orig_current_module = self.current_module; self.current_module = module; // Lexical resolutions can never be a privacy error. let result = self.resolve_ident_in_module_unadjusted( - module, ident, ns, false, record_used, path_span, + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + record_used, + path_span, ); self.current_module = orig_current_module; @@ -1954,8 +1978,14 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { return Some(LexicalScopeBinding::Item(binding)); } if let Some(prelude) = self.prelude { - if let Ok(binding) = self.resolve_ident_in_module_unadjusted(prelude, ident, ns, - false, false, path_span) { + if let Ok(binding) = self.resolve_ident_in_module_unadjusted( + ModuleOrUniformRoot::Module(prelude), + ident, + ns, + false, + false, + path_span, + ) { return Some(LexicalScopeBinding::Item(binding)); } } @@ -2013,7 +2043,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { } fn resolve_ident_in_module(&mut self, - module: Module<'a>, + module: ModuleOrUniformRoot<'a>, mut ident: Ident, ns: Namespace, record_used: bool, @@ -2021,8 +2051,10 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { -> Result<&'a NameBinding<'a>, Determinacy> { ident.span = ident.span.modern(); let orig_current_module = self.current_module; - if let Some(def) = ident.span.adjust(module.expansion) { - self.current_module = self.macro_def_scope(def); + if let ModuleOrUniformRoot::Module(module) = module { + if let Some(def) = ident.span.adjust(module.expansion) { + self.current_module = self.macro_def_scope(def); + } } let result = self.resolve_ident_in_module_unadjusted( module, ident, ns, false, record_used, span, @@ -2410,13 +2442,16 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { if def != Def::Err { new_id = Some(def.def_id()); let span = trait_ref.path.span; - if let PathResult::Module(module) = self.resolve_path( - &path, - None, - false, - span, - CrateLint::SimplePath(trait_ref.ref_id), - ) { + if let PathResult::Module(ModuleOrUniformRoot::Module(module)) = + self.resolve_path( + None, + &path, + None, + false, + span, + CrateLint::SimplePath(trait_ref.ref_id), + ) + { new_val = Some((module, trait_ref.clone())); } } @@ -2533,7 +2568,13 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { // If there is a TraitRef in scope for an impl, then the method must be in the // trait. if let Some((module, _)) = self.current_trait_ref { - if self.resolve_ident_in_module(module, ident, ns, false, span).is_err() { + if self.resolve_ident_in_module( + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + span, + ).is_err() { let path = &self.current_trait_ref.as_ref().unwrap().1.path; resolve_error(self, span, err(ident.name, &path_names_to_string(path))); } @@ -2908,9 +2949,10 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { (String::new(), "the crate root".to_string()) } else { let mod_path = &path[..path.len() - 1]; - let mod_prefix = match this.resolve_path(mod_path, Some(TypeNS), + let mod_prefix = match this.resolve_path(None, mod_path, Some(TypeNS), false, span, CrateLint::No) { - PathResult::Module(module) => module.def(), + PathResult::Module(ModuleOrUniformRoot::Module(module)) => + module.def(), _ => None, }.map_or(String::new(), |def| format!("{} ", def.kind_name())); (mod_prefix, format!("`{}`", names_to_string(mod_path))) @@ -3319,6 +3361,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { } let result = match self.resolve_path( + None, &path, Some(ns), true, @@ -3326,7 +3369,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { crate_lint, ) { PathResult::NonModule(path_res) => path_res, - PathResult::Module(module) if !module.is_normal() => { + PathResult::Module(ModuleOrUniformRoot::Module(module)) if !module.is_normal() => { PathResolution::new(module.def().unwrap()) } // In `a(::assoc_item)*` `a` cannot be a module. If `a` does resolve to a module we @@ -3341,18 +3384,21 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { // // Such behavior is required for backward compatibility. // The same fallback is used when `a` resolves to nothing. - PathResult::Module(..) | PathResult::Failed(..) + PathResult::Module(ModuleOrUniformRoot::Module(_)) | + PathResult::Failed(..) if (ns == TypeNS || path.len() > 1) && self.primitive_type_table.primitive_types .contains_key(&path[0].name) => { let prim = self.primitive_type_table.primitive_types[&path[0].name]; PathResolution::with_unresolved_segments(Def::PrimTy(prim), path.len() - 1) } - PathResult::Module(module) => PathResolution::new(module.def().unwrap()), + PathResult::Module(ModuleOrUniformRoot::Module(module)) => + PathResolution::new(module.def().unwrap()), PathResult::Failed(span, msg, false) => { resolve_error(self, span, ResolutionError::FailedToResolve(&msg)); err_path_resolution() } + PathResult::Module(ModuleOrUniformRoot::UniformRoot(_)) | PathResult::Failed(..) => return None, PathResult::Indeterminate => bug!("indetermined path result in resolve_qpath"), }; @@ -3362,6 +3408,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { path[0].name != keywords::DollarCrate.name() { let unqualified_result = { match self.resolve_path( + None, &[*path.last().unwrap()], Some(ns), false, @@ -3369,7 +3416,8 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { CrateLint::No, ) { PathResult::NonModule(path_res) => path_res.base_def(), - PathResult::Module(module) => module.def().unwrap(), + PathResult::Module(ModuleOrUniformRoot::Module(module)) => + module.def().unwrap(), _ => return Some(result), } }; @@ -3384,13 +3432,14 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { fn resolve_path( &mut self, + base_module: Option>, path: &[Ident], opt_ns: Option, // `None` indicates a module path record_used: bool, path_span: Span, crate_lint: CrateLint, ) -> PathResult<'a> { - let mut module = None; + let mut module = base_module; let mut allow_super = true; let mut second_binding = None; @@ -3412,49 +3461,48 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { if i == 0 && ns == TypeNS && name == keywords::SelfValue.name() { let mut ctxt = ident.span.ctxt().modern(); - module = Some(self.resolve_self(&mut ctxt, self.current_module)); + module = Some(ModuleOrUniformRoot::Module( + self.resolve_self(&mut ctxt, self.current_module))); continue } else if allow_super && ns == TypeNS && name == keywords::Super.name() { let mut ctxt = ident.span.ctxt().modern(); - let self_module = match i { - 0 => self.resolve_self(&mut ctxt, self.current_module), - _ => module.unwrap(), + let self_module_parent = match i { + 0 => self.resolve_self(&mut ctxt, self.current_module).parent, + _ => match module { + Some(ModuleOrUniformRoot::Module(module)) => module.parent, + _ => None, + }, }; - if let Some(parent) = self_module.parent { - module = Some(self.resolve_self(&mut ctxt, parent)); + if let Some(parent) = self_module_parent { + module = Some(ModuleOrUniformRoot::Module( + self.resolve_self(&mut ctxt, parent))); continue } else { let msg = "There are too many initial `super`s.".to_string(); return PathResult::Failed(ident.span, msg, false); } - } else if i == 0 && ns == TypeNS && name == keywords::Extern.name() { - continue; } allow_super = false; if ns == TypeNS { + if i == 0 { + if name == keywords::Extern.name() || + name == keywords::CrateRoot.name() && + self.session.features_untracked().extern_absolute_paths && + self.session.rust_2018() { + module = Some(ModuleOrUniformRoot::UniformRoot(name)); + continue; + } + } if (i == 0 && name == keywords::CrateRoot.name()) || (i == 0 && name == keywords::Crate.name()) || (i == 0 && name == keywords::DollarCrate.name()) || (i == 1 && name == keywords::Crate.name() && path[0].name == keywords::CrateRoot.name()) { // `::a::b`, `crate::a::b`, `::crate::a::b` or `$crate::a::b` - module = Some(self.resolve_crate_root(ident)); + module = Some(ModuleOrUniformRoot::Module( + self.resolve_crate_root(ident))); continue - } else if i == 1 && !ident.is_path_segment_keyword() { - let prev_name = path[0].name; - if prev_name == keywords::Extern.name() || - prev_name == keywords::CrateRoot.name() && - self.session.features_untracked().extern_absolute_paths && - self.session.rust_2018() { - // `::extern_crate::a::b` - let crate_id = self.crate_loader.process_path_extern(name, ident.span); - let crate_root = - self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX }); - self.populate_module_if_necessary(crate_root); - module = Some(crate_root); - continue - } } } @@ -3513,7 +3561,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { let def = binding.def(); let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(def); if let Some(next_module) = binding.module() { - module = Some(next_module); + module = Some(ModuleOrUniformRoot::Module(next_module)); } else if def == Def::ToolMod && i + 1 != path.len() { let def = Def::NonMacroAttr(NonMacroAttrKind::Tool); return PathResult::NonModule(PathResolution::new(def)); @@ -3537,14 +3585,18 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { } Err(Undetermined) => return PathResult::Indeterminate, Err(Determined) => { - if let Some(module) = module { + if let Some(ModuleOrUniformRoot::Module(module)) = module { if opt_ns.is_some() && !module.is_normal() { return PathResult::NonModule(PathResolution::with_unresolved_segments( module.def().unwrap(), path.len() - i )); } } - let msg = if module.and_then(ModuleData::def) == self.graph_root.def() { + let module_def = match module { + Some(ModuleOrUniformRoot::Module(module)) => module.def(), + _ => None, + }; + let msg = if module_def == self.graph_root.def() { let is_mod = |def| match def { Def::Mod(..) => true, _ => false }; let mut candidates = self.lookup_import_candidates(name, TypeNS, is_mod); @@ -3568,7 +3620,10 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { self.lint_if_path_starts_with_module(crate_lint, path, path_span, second_binding); - PathResult::Module(module.unwrap_or(self.graph_root)) + PathResult::Module(module.unwrap_or_else(|| { + span_bug!(path_span, "resolve_path: empty(?) path {:?} has no module", path); + })) + } fn lint_if_path_starts_with_module( @@ -3578,6 +3633,17 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { path_span: Span, second_binding: Option<&NameBinding>, ) { + // In the 2018 edition this lint is a hard error, so nothing to do + if self.session.rust_2018() { + return + } + + // In the 2015 edition there's no use in emitting lints unless the + // crate's already enabled the feature that we're going to suggest + if !self.session.features_untracked().crate_in_paths { + return + } + let (diag_id, diag_span) = match crate_lint { CrateLint::No => return, CrateLint::SimplePath(id) => (id, path_span), @@ -3620,24 +3686,11 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { } } - self.lint_path_starts_with_module(diag_id, diag_span); - } - - fn lint_path_starts_with_module(&self, id: NodeId, span: Span) { - // In the 2018 edition this lint is a hard error, so nothing to do - if self.session.rust_2018() { - return - } - // In the 2015 edition there's no use in emitting lints unless the - // crate's already enabled the feature that we're going to suggest - if !self.session.features_untracked().crate_in_paths { - return - } let diag = lint::builtin::BuiltinLintDiagnostics - ::AbsPathWithModule(span); + ::AbsPathWithModule(diag_span); self.session.buffer_lint_with_diagnostic( lint::builtin::ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, - id, span, + diag_id, diag_span, "absolute paths must start with `self`, `super`, \ `crate`, or an external crate name in the 2018 edition", diag); @@ -3782,8 +3835,13 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { // Look for associated items in the current trait. if let Some((module, _)) = self.current_trait_ref { - if let Ok(binding) = - self.resolve_ident_in_module(module, ident, ns, false, module.span) { + if let Ok(binding) = self.resolve_ident_in_module( + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + module.span, + ) { let def = binding.def(); if filter_fn(def) { return Some(if self.has_self.contains(&def.def_id()) { @@ -3855,9 +3913,11 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { } else { // Search in module. let mod_path = &path[..path.len() - 1]; - if let PathResult::Module(module) = self.resolve_path(mod_path, Some(TypeNS), + if let PathResult::Module(module) = self.resolve_path(None, mod_path, Some(TypeNS), false, span, CrateLint::No) { - add_module_candidates(module, &mut names); + if let ModuleOrUniformRoot::Module(module) = module { + add_module_candidates(module, &mut names); + } } } @@ -4096,7 +4156,13 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { let mut found_traits = Vec::new(); // Look for the current trait. if let Some((module, _)) = self.current_trait_ref { - if self.resolve_ident_in_module(module, ident, ns, false, module.span).is_ok() { + if self.resolve_ident_in_module( + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + module.span, + ).is_ok() { let def_id = module.def_id().unwrap(); found_traits.push(TraitCandidate { def_id: def_id, import_id: None }); } @@ -4144,8 +4210,14 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { if ident.span.glob_adjust(module.expansion, binding.span.ctxt().modern()).is_none() { continue } - if self.resolve_ident_in_module_unadjusted(module, ident, ns, false, false, module.span) - .is_ok() { + if self.resolve_ident_in_module_unadjusted( + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + false, + module.span, + ).is_ok() { let import_id = match binding.kind { NameBindingKind::Import { directive, .. } => { self.maybe_unused_trait_imports.insert(directive.id); diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index fe9d3c7eb9982..44d0c888c5dd2 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -10,6 +10,7 @@ use {AmbiguityError, CrateLint, Resolver, ResolutionError, is_known_tool, resolve_error}; use {Module, ModuleKind, NameBinding, NameBindingKind, PathResult, ToNameBinding}; +use ModuleOrUniformRoot; use Namespace::{self, TypeNS, MacroNS}; use build_reduced_graph::{BuildReducedGraphVisitor, IsMacroExport}; use resolve_imports::ImportResolver; @@ -538,7 +539,8 @@ impl<'a, 'cl> Resolver<'a, 'cl> { return Err(Determinacy::Determined); } - let def = match self.resolve_path(&path, Some(MacroNS), false, span, CrateLint::No) { + let res = self.resolve_path(None, &path, Some(MacroNS), false, span, CrateLint::No); + let def = match res { PathResult::NonModule(path_res) => match path_res.base_def() { Def::Err => Err(Determinacy::Determined), def @ _ => { @@ -655,7 +657,12 @@ impl<'a, 'cl> Resolver<'a, 'cl> { WhereToResolve::Module(module) => { let orig_current_module = mem::replace(&mut self.current_module, module); let binding = self.resolve_ident_in_module_unadjusted( - module, ident, ns, true, record_used, path_span, + ModuleOrUniformRoot::Module(module), + ident, + ns, + true, + record_used, + path_span, ); self.current_module = orig_current_module; binding.map(MacroBinding::Modern) @@ -715,9 +722,14 @@ impl<'a, 'cl> Resolver<'a, 'cl> { let mut result = Err(Determinacy::Determined); if use_prelude { if let Some(prelude) = self.prelude { - if let Ok(binding) = - self.resolve_ident_in_module_unadjusted(prelude, ident, ns, - false, false, path_span) { + if let Ok(binding) = self.resolve_ident_in_module_unadjusted( + ModuleOrUniformRoot::Module(prelude), + ident, + ns, + false, + false, + path_span, + ) { result = Ok(MacroBinding::Global(binding)); } } @@ -893,7 +905,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { pub fn finalize_current_module_macro_resolutions(&mut self) { let module = self.current_module; for &(ref path, span) in module.macro_resolutions.borrow().iter() { - match self.resolve_path(&path, Some(MacroNS), true, span, CrateLint::No) { + match self.resolve_path(None, &path, Some(MacroNS), true, span, CrateLint::No) { PathResult::NonModule(_) => {}, PathResult::Failed(span, msg, _) => { resolve_error(self, span, ResolutionError::FailedToResolve(&msg)); diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 715292bc11622..1d8cc609f95ac 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -10,8 +10,8 @@ use self::ImportDirectiveSubclass::*; -use {AmbiguityError, CrateLint, Module, PerNS}; -use Namespace::{self, TypeNS, MacroNS}; +use {AmbiguityError, CrateLint, Module, ModuleOrUniformRoot, PerNS}; +use Namespace::{self, TypeNS, MacroNS, ValueNS}; use {NameBinding, NameBindingKind, ToNameBinding, PathResult, PrivacyError}; use Resolver; use {names_to_string, module_to_string}; @@ -85,11 +85,19 @@ pub struct ImportDirective<'a> { pub parent: Module<'a>, pub module_path: Vec, - pub imported_module: Cell>>, // the resolution of `module_path` + /// The resolution of `module_path`. + pub imported_module: Cell>>, pub subclass: ImportDirectiveSubclass<'a>, pub vis: Cell, pub expansion: Mark, pub used: Cell, + + /// Whether this import is a "canary" for the `uniform_paths` feature, + /// i.e. `use x::...;` results in an `use self::x as _;` canary. + /// This flag affects diagnostics: an error is reported if and only if + /// the import resolves successfully and an external crate with the same + /// name (`x` above) also exists; any resolution failures are ignored. + pub is_uniform_paths_canary: bool, } impl<'a> ImportDirective<'a> { @@ -133,13 +141,87 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { /// Attempts to resolve `ident` in namespaces `ns` of `module`. /// Invariant: if `record_used` is `Some`, expansion and import resolution must be complete. pub fn resolve_ident_in_module_unadjusted(&mut self, - module: Module<'a>, + module: ModuleOrUniformRoot<'a>, ident: Ident, ns: Namespace, restricted_shadowing: bool, record_used: bool, path_span: Span) -> Result<&'a NameBinding<'a>, Determinacy> { + let module = match module { + ModuleOrUniformRoot::Module(module) => module, + ModuleOrUniformRoot::UniformRoot(root) => { + // HACK(eddyb): `resolve_path` uses `keywords::Invalid` to indicate + // paths of length 0, and currently these are relative `use` paths. + let can_be_relative = !ident.is_path_segment_keyword() && + root == keywords::Invalid.name(); + if can_be_relative { + // Relative paths should only get here if the feature-gate is on. + assert!(self.session.rust_2018() && + self.session.features_untracked().uniform_paths); + + // Try first to resolve relatively. + let mut ctxt = ident.span.ctxt().modern(); + let self_module = self.resolve_self(&mut ctxt, self.current_module); + + let binding = self.resolve_ident_in_module_unadjusted( + ModuleOrUniformRoot::Module(self_module), + ident, + ns, + restricted_shadowing, + record_used, + path_span, + ); + + // FIXME(eddyb) This may give false negatives, specifically + // if a crate with the same name is found in `extern_prelude`, + // preventing the check below this one from returning `binding` + // in all cases. + // + // That is, if there's no crate with the same name, `binding` + // is always returned, which is the result of doing the exact + // same lookup of `ident`, in the `self` module. + // But when a crate does exist, it will get chosen even when + // macro expansion could result in a success from the lookup + // in the `self` module, later on. + // + // NB. This is currently alleviated by the "ambiguity canaries" + // (see `is_uniform_paths_canary`) that get introduced for the + // maybe-relative imports handled here: if the false negative + // case were to arise, it would *also* cause an ambiguity error. + if binding.is_ok() { + return binding; + } + + // Fall back to resolving to an external crate. + if !self.extern_prelude.contains(&ident.name) { + // ... unless the crate name is not in the `extern_prelude`. + return binding; + } + } + + let crate_root = if + root != keywords::Extern.name() && + ( + ident.name == keywords::Crate.name() || + ident.name == keywords::DollarCrate.name() + ) + { + self.resolve_crate_root(ident) + } else if !ident.is_path_segment_keyword() { + let crate_id = + self.crate_loader.process_path_extern(ident.name, ident.span); + self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX }) + } else { + return Err(Determined); + }; + self.populate_module_if_necessary(crate_root); + let binding = (crate_root, ty::Visibility::Public, + ident.span, Mark::root()).to_name_binding(self.arenas); + return Ok(binding); + } + }; + self.populate_module_if_necessary(module); let resolution = self.resolution(module, ident, ns) @@ -260,7 +342,11 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { if !self.is_accessible(glob_import.vis.get()) { continue } - let module = unwrap_or!(glob_import.imported_module.get(), return Err(Undetermined)); + let module = match glob_import.imported_module.get() { + Some(ModuleOrUniformRoot::Module(module)) => module, + Some(ModuleOrUniformRoot::UniformRoot(_)) => continue, + None => return Err(Undetermined), + }; let (orig_current_module, mut ident) = (self.current_module, ident.modern()); match ident.span.glob_adjust(module.expansion, glob_import.span.ctxt().modern()) { Some(Some(def)) => self.current_module = self.macro_def_scope(def), @@ -268,7 +354,12 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { None => continue, }; let result = self.resolve_ident_in_module_unadjusted( - module, ident, ns, false, false, path_span, + ModuleOrUniformRoot::Module(module), + ident, + ns, + false, + false, + path_span, ); self.current_module = orig_current_module; match result { @@ -290,7 +381,8 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { root_span: Span, root_id: NodeId, vis: ty::Visibility, - expansion: Mark) { + expansion: Mark, + is_uniform_paths_canary: bool) { let current_module = self.current_module; let directive = self.arenas.alloc_import_directive(ImportDirective { parent: current_module, @@ -304,8 +396,11 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { vis: Cell::new(vis), expansion, used: Cell::new(false), + is_uniform_paths_canary, }); + debug!("add_import_directive({:?})", directive); + self.indeterminate_imports.push(directive); match directive.subclass { SingleImport { target, type_ns_only, .. } => { @@ -523,7 +618,55 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { let mut seen_spans = FxHashSet(); for i in 0 .. self.determined_imports.len() { let import = self.determined_imports[i]; - if let Some((span, err)) = self.finalize_import(import) { + let error = self.finalize_import(import); + + // 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 = + 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; + } + + errors = true; + + 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)); + } + } + 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; if let SingleImport { source, ref result, .. } = import.subclass { @@ -553,9 +696,14 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { // Report unresolved imports only if no hard error was already reported // to avoid generating multiple errors on the same import. if !errors { - if let Some(import) = self.indeterminate_imports.iter().next() { + for import in &self.indeterminate_imports { + if import.is_uniform_paths_canary { + continue; + } + let error = ResolutionError::UnresolvedImport(None); resolve_error(self.resolver, import.span, error); + break; } } } @@ -576,8 +724,18 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { // For better failure detection, pretend that the import will not define any names // while resolving its module path. directive.vis.set(ty::Visibility::Invisible); - let result = self.resolve_path(&directive.module_path[..], None, false, - directive.span, directive.crate_lint()); + let result = self.resolve_path( + Some(if directive.is_uniform_paths_canary { + ModuleOrUniformRoot::Module(directive.parent) + } else { + ModuleOrUniformRoot::UniformRoot(keywords::Invalid.name()) + }), + &directive.module_path[..], + None, + false, + directive.span, + directive.crate_lint(), + ); directive.vis.set(vis); match result { @@ -644,77 +802,13 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { fn finalize_import(&mut self, directive: &'b ImportDirective<'b>) -> Option<(Span, String)> { self.current_module = directive.parent; let ImportDirective { ref module_path, span, .. } = *directive; - let mut warn_if_binding_comes_from_local_crate = false; - - // FIXME: Last path segment is treated specially in import resolution, so extern crate - // mode for absolute paths needs some special support for single-segment imports. - if module_path.len() == 1 && (module_path[0].name == keywords::CrateRoot.name() || - module_path[0].name == keywords::Extern.name()) { - let is_extern = module_path[0].name == keywords::Extern.name() || - (self.session.features_untracked().extern_absolute_paths && - self.session.rust_2018()); - match directive.subclass { - GlobImport { .. } if is_extern => { - return Some((directive.span, - "cannot glob-import all possible crates".to_string())); - } - GlobImport { .. } if self.session.features_untracked().extern_absolute_paths => { - self.lint_path_starts_with_module( - directive.root_id, - directive.root_span, - ); - } - SingleImport { source, target, .. } => { - let crate_root = if source.name == keywords::Crate.name() && - module_path[0].name != keywords::Extern.name() { - if target.name == keywords::Crate.name() { - return Some((directive.span, - "crate root imports need to be explicitly named: \ - `use crate as name;`".to_string())); - } else { - Some(self.resolve_crate_root(source)) - } - } else if is_extern && !source.is_path_segment_keyword() { - let crate_id = - self.resolver.crate_loader.process_use_extern( - source.name, - directive.span, - directive.id, - &self.resolver.definitions, - ); - let crate_root = - self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX }); - self.populate_module_if_necessary(crate_root); - Some(crate_root) - } else { - warn_if_binding_comes_from_local_crate = true; - None - }; - - if let Some(crate_root) = crate_root { - let binding = (crate_root, ty::Visibility::Public, directive.span, - directive.expansion).to_name_binding(self.arenas); - let binding = self.arenas.alloc_name_binding(NameBinding { - kind: NameBindingKind::Import { - binding, - directive, - used: Cell::new(false), - }, - vis: directive.vis.get(), - span: directive.span, - expansion: directive.expansion, - }); - let _ = self.try_define(directive.parent, target, TypeNS, binding); - let import = self.import_map.entry(directive.id).or_default(); - import[TypeNS] = Some(PathResolution::new(binding.def())); - return None; - } - } - _ => {} - } - } let module_result = self.resolve_path( + Some(if directive.is_uniform_paths_canary { + ModuleOrUniformRoot::Module(directive.parent) + } else { + ModuleOrUniformRoot::UniformRoot(keywords::Invalid.name()) + }), &module_path, None, true, @@ -734,7 +828,7 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { if !self_path.is_empty() && !is_special(self_path[0]) && !(self_path.len() > 1 && is_special(self_path[1])) { self_path[0].name = keywords::SelfValue.name(); - self_result = Some(self.resolve_path(&self_path, None, false, + self_result = Some(self.resolve_path(None, &self_path, None, false, span, CrateLint::No)); } return if let Some(PathResult::Module(..)) = self_result { @@ -748,12 +842,27 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { let (ident, result, type_ns_only) = match directive.subclass { SingleImport { source, ref result, type_ns_only, .. } => (source, result, type_ns_only), - GlobImport { .. } if module.def_id() == directive.parent.def_id() => { - // Importing a module into itself is not allowed. - return Some((directive.span, - "Cannot glob-import a module into itself.".to_string())); - } GlobImport { is_prelude, ref max_vis } => { + if module_path.len() <= 1 { + // HACK(eddyb) `lint_if_path_starts_with_module` needs at least + // 2 segments, so the `resolve_path` above won't trigger it. + let mut full_path = module_path.clone(); + full_path.push(keywords::Invalid.ident()); + self.lint_if_path_starts_with_module( + directive.crate_lint(), + &full_path, + directive.span, + None, + ); + } + + if let ModuleOrUniformRoot::Module(module) = module { + if module.def_id() == directive.parent.def_id() { + // Importing a module into itself is not allowed. + return Some((directive.span, + "Cannot glob-import a module into itself.".to_string())); + } + } if !is_prelude && max_vis.get() != ty::Visibility::Invisible && // Allow empty globs. !max_vis.get().is_at_least(directive.vis.get(), &*self) { @@ -770,8 +879,10 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { if let Ok(binding) = result[ns].get() { all_ns_err = false; if this.record_use(ident, ns, binding, directive.span) { - this.resolution(module, ident, ns).borrow_mut().binding = - Some(this.dummy_binding); + if let ModuleOrUniformRoot::Module(module) = module { + this.resolution(module, ident, ns).borrow_mut().binding = + Some(this.dummy_binding); + } } } }); @@ -786,8 +897,13 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { }); return if all_ns_failed { - let resolutions = module.resolutions.borrow(); - let names = resolutions.iter().filter_map(|(&(ref i, _), resolution)| { + let resolutions = match module { + ModuleOrUniformRoot::Module(module) => + Some(module.resolutions.borrow()), + ModuleOrUniformRoot::UniformRoot(_) => None, + }; + let resolutions = resolutions.as_ref().into_iter().flat_map(|r| r.iter()); + let names = resolutions.filter_map(|(&(ref i, _), resolution)| { if *i == ident { return None; } // Never suggest the same name match *resolution.borrow() { NameResolution { binding: Some(name_binding), .. } => { @@ -813,11 +929,24 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { Some(name) => format!(". Did you mean to use `{}`?", name), None => "".to_owned(), }; - let module_str = module_to_string(module); - let msg = if let Some(module_str) = module_str { - format!("no `{}` in `{}`{}", ident, module_str, lev_suggestion) - } else { - format!("no `{}` in the root{}", ident, lev_suggestion) + let msg = match module { + ModuleOrUniformRoot::Module(module) => { + let module_str = module_to_string(module); + if let Some(module_str) = module_str { + format!("no `{}` in `{}`{}", ident, module_str, lev_suggestion) + } else { + format!("no `{}` in the root{}", ident, lev_suggestion) + } + } + ModuleOrUniformRoot::UniformRoot(_) => { + if !ident.is_path_segment_keyword() { + format!("no `{}` external crate{}", ident, lev_suggestion) + } else { + // HACK(eddyb) this shows up for `self` & `super`, which + // should work instead - for now keep the same error message. + format!("no `{}` in the root{}", ident, lev_suggestion) + } + } }; Some((span, msg)) } else { @@ -868,26 +997,20 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { } } - if warn_if_binding_comes_from_local_crate { - let mut warned = false; + if module_path.len() <= 1 { + // HACK(eddyb) `lint_if_path_starts_with_module` needs at least + // 2 segments, so the `resolve_path` above won't trigger it. + let mut full_path = module_path.clone(); + full_path.push(ident); self.per_ns(|this, ns| { - let binding = match result[ns].get().ok() { - Some(b) => b, - None => return - }; - if let NameBindingKind::Import { directive: d, .. } = binding.kind { - if let ImportDirectiveSubclass::ExternCrate(..) = d.subclass { - return - } - } - if warned { - return + if let Ok(binding) = result[ns].get() { + this.lint_if_path_starts_with_module( + directive.crate_lint(), + &full_path, + directive.span, + Some(binding), + ); } - warned = true; - this.lint_path_starts_with_module( - directive.root_id, - directive.root_span, - ); }); } @@ -904,7 +1027,15 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { } fn resolve_glob_import(&mut self, directive: &'b ImportDirective<'b>) { - let module = directive.imported_module.get().unwrap(); + let module = match directive.imported_module.get().unwrap() { + ModuleOrUniformRoot::Module(module) => module, + ModuleOrUniformRoot::UniformRoot(_) => { + self.session.span_err(directive.span, + "cannot glob-import all possible crates"); + return; + } + }; + self.populate_module_if_necessary(module); if let Some(Def::Trait(_)) = module.def() { @@ -1026,8 +1157,10 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { }; let mut err = self.session.struct_span_err(binding.span, &msg); - let imported_module = directive.imported_module.get() - .expect("module should exist"); + let imported_module = match directive.imported_module.get() { + Some(ModuleOrUniformRoot::Module(module)) => module, + _ => bug!("module should exist"), + }; let resolutions = imported_module.parent.expect("parent should exist") .resolutions.borrow(); let enum_path_segment_index = directive.module_path.len() - 1; diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 6925ed2afb83b..b66946affadb3 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -101,7 +101,7 @@ impl Path { // or starts with something like `self`/`super`/`$crate`/etc. pub fn make_root(&self) -> Option { if let Some(ident) = self.segments.get(0).map(|seg| seg.ident) { - if ident.is_path_segment_keyword() && ident.name != keywords::Crate.name() { + if ident.is_path_segment_keyword() { return None; } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index b779b2eb689e8..56e69b9df9e04 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -507,6 +507,9 @@ declare_features! ( // Support for arbitrary delimited token streams in non-macro attributes. (active, unrestricted_attribute_tokens, "1.30.0", Some(44690), None), + + // Allows `use x::y;` to resolve through `self::x`, not just `::x`. + (active, uniform_paths, "1.30.0", Some(53130), None), ); declare_features! ( diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index 68121d42b69c6..626a610017d42 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -11,6 +11,8 @@ use ast; use attr; use std::cell::Cell; +use std::iter; +use edition::Edition; use ext::hygiene::{Mark, SyntaxContext}; use symbol::{Symbol, keywords}; use syntax_pos::{DUMMY_SP, Span}; @@ -43,7 +45,13 @@ thread_local! { static INJECTED_CRATE_NAME: Cell> = Cell::new(None); } -pub fn maybe_inject_crates_ref(mut krate: ast::Crate, alt_std_name: Option<&str>) -> ast::Crate { +pub fn maybe_inject_crates_ref( + mut krate: ast::Crate, + alt_std_name: Option<&str>, + edition: Edition, +) -> ast::Crate { + let rust_2018 = edition >= Edition::Edition2018; + // the first name in this list is the crate name of the crate with the prelude let names: &[&str] = if attr::contains_name(&krate.attrs, "no_core") { return krate; @@ -58,14 +66,27 @@ pub fn maybe_inject_crates_ref(mut krate: ast::Crate, alt_std_name: Option<&str> }; // .rev() to preserve ordering above in combination with insert(0, ...) - for name in names.iter().rev() { + let alt_std_name = alt_std_name.map(Symbol::intern); + for orig_name in names.iter().rev() { + let orig_name = Symbol::intern(orig_name); + let mut rename = orig_name; + // HACK(eddyb) gensym the injected crates on the Rust 2018 edition, + // so they don't accidentally interfere with the new import paths. + if rust_2018 { + rename = orig_name.gensymed(); + } + let orig_name = if rename != orig_name { + Some(orig_name) + } else { + None + }; krate.module.items.insert(0, P(ast::Item { attrs: vec![attr::mk_attr_outer(DUMMY_SP, attr::mk_attr_id(), attr::mk_word_item(ast::Ident::from_str("macro_use")))], vis: dummy_spanned(ast::VisibilityKind::Inherited), - node: ast::ItemKind::ExternCrate(alt_std_name.map(Symbol::intern)), - ident: ast::Ident::from_str(name), + node: ast::ItemKind::ExternCrate(alt_std_name.or(orig_name)), + ident: ast::Ident::with_empty_ctxt(rename), id: ast::DUMMY_NODE_ID, span: DUMMY_SP, tokens: None, @@ -91,9 +112,11 @@ pub fn maybe_inject_crates_ref(mut krate: ast::Crate, alt_std_name: Option<&str> vis: respan(span.shrink_to_lo(), ast::VisibilityKind::Inherited), node: ast::ItemKind::Use(P(ast::UseTree { prefix: ast::Path { - segments: [name, "prelude", "v1"].into_iter().map(|name| { - ast::PathSegment::from_ident(ast::Ident::from_str(name)) - }).collect(), + segments: iter::once(keywords::CrateRoot.ident()) + .chain( + [name, "prelude", "v1"].iter().cloned() + .map(ast::Ident::from_str) + ).map(ast::PathSegment::from_ident).collect(), span, }, kind: ast::UseTreeKind::Glob, diff --git a/src/test/compile-fail/dollar-crate-is-keyword.rs b/src/test/compile-fail/dollar-crate-is-keyword.rs index 70597a230a886..4a667f020ad34 100644 --- a/src/test/compile-fail/dollar-crate-is-keyword.rs +++ b/src/test/compile-fail/dollar-crate-is-keyword.rs @@ -10,7 +10,11 @@ macro_rules! m { () => { - struct $crate {} //~ ERROR expected identifier, found reserved identifier `$crate` + // Avoid having more than one `$crate`-named item in the same module, + // as even though they error, they still parse as `$crate` and conflict. + mod foo { + struct $crate {} //~ ERROR expected identifier, found reserved identifier `$crate` + } use $crate; // OK //~^ WARN `$crate` may not be imported diff --git a/src/test/compile-fail/import-crate-var.rs b/src/test/compile-fail/import-crate-var.rs index e58ba2c88917f..b09883d9adeea 100644 --- a/src/test/compile-fail/import-crate-var.rs +++ b/src/test/compile-fail/import-crate-var.rs @@ -9,15 +9,14 @@ // except according to those terms. // aux-build:import_crate_var.rs -// error-pattern: `$crate` may not be imported -// error-pattern: `use $crate;` was erroneously allowed and will become a hard error -// error-pattern: compilation successful #![feature(rustc_attrs)] #[macro_use] extern crate import_crate_var; #[rustc_error] -fn main() { +fn main() { //~ ERROR compilation successful m!(); + //~^ WARN `$crate` may not be imported + //~| NOTE `use $crate;` was erroneously allowed and will become a hard error } diff --git a/src/test/compile-fail/keyword-extern-as-identifier.rs b/src/test/compile-fail/keyword-extern-as-identifier.rs index e5927d09b4165..3e44585395788 100644 --- a/src/test/compile-fail/keyword-extern-as-identifier.rs +++ b/src/test/compile-fail/keyword-extern-as-identifier.rs @@ -11,5 +11,5 @@ #![feature(extern_in_paths)] fn main() { - let extern = 0; //~ ERROR expected unit struct/variant or constant, found module `extern` + let extern = 0; //~ ERROR cannot find unit struct/variant or constant `extern` in this scope } diff --git a/src/test/compile-fail/rfc-2126-extern-absolute-paths/single-segment.rs b/src/test/compile-fail/rfc-2126-extern-absolute-paths/single-segment.rs index e9de0f01b30d9..69fc4b4f7f8fc 100644 --- a/src/test/compile-fail/rfc-2126-extern-absolute-paths/single-segment.rs +++ b/src/test/compile-fail/rfc-2126-extern-absolute-paths/single-segment.rs @@ -11,10 +11,8 @@ // aux-build:xcrate.rs // edition:2018 -use crate; //~ ERROR unresolved import `crate` - //~^ NOTE crate root imports need to be explicitly named: `use crate as name;` -use *; //~ ERROR unresolved import `*` - //~^ NOTE cannot glob-import all possible crates +use crate; //~ ERROR crate root imports need to be explicitly named: `use crate as name;` +use *; //~ ERROR cannot glob-import all possible crates fn main() { let s = ::xcrate; //~ ERROR expected value, found module `xcrate` diff --git a/src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs b/src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs index ebc42aa9d4492..017844a0252e2 100644 --- a/src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs +++ b/src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs @@ -14,8 +14,7 @@ use extern; //~ ERROR unresolved import `extern` //~^ NOTE no `extern` in the root -use extern::*; //~ ERROR unresolved import `extern::*` - //~^ NOTE cannot glob-import all possible crates +use extern::*; //~ ERROR cannot glob-import all possible crates fn main() { let s = extern::xcrate; //~ ERROR expected value, found module `extern::xcrate` diff --git a/src/test/pretty/cast-lt.pp b/src/test/pretty/cast-lt.pp index f1b4b4f5a0c8a..b8d920754ad79 100644 --- a/src/test/pretty/cast-lt.pp +++ b/src/test/pretty/cast-lt.pp @@ -1,7 +1,7 @@ #![feature(prelude_import)] #![no_std] #[prelude_import] -use std::prelude::v1::*; +use ::std::prelude::v1::*; #[macro_use] extern crate std; // Copyright 2017 The Rust Project Developers. See the COPYRIGHT diff --git a/src/test/pretty/issue-4264.pp b/src/test/pretty/issue-4264.pp index a4380d9212fdf..5f42b86c82a81 100644 --- a/src/test/pretty/issue-4264.pp +++ b/src/test/pretty/issue-4264.pp @@ -1,5 +1,5 @@ #[prelude_import] -use std::prelude::v1::*; +use ::std::prelude::v1::*; #[macro_use] extern crate std; // Copyright 2014 The Rust Project Developers. See the COPYRIGHT diff --git a/src/test/run-pass/issue-52140/auxiliary/some_crate.rs b/src/test/run-pass/issue-52140/auxiliary/some_crate.rs new file mode 100644 index 0000000000000..bf8dee0863a1b --- /dev/null +++ b/src/test/run-pass/issue-52140/auxiliary/some_crate.rs @@ -0,0 +1,15 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +pub fn hello() { + println!("Hello, world!"); +} diff --git a/src/test/run-pass/issue-52140/main.rs b/src/test/run-pass/issue-52140/main.rs new file mode 100644 index 0000000000000..6fae6adc44248 --- /dev/null +++ b/src/test/run-pass/issue-52140/main.rs @@ -0,0 +1,21 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:some_crate.rs +// edition:2018 + +mod foo { + pub use some_crate; +} + +fn main() { + ::some_crate::hello(); + foo::some_crate::hello(); +} diff --git a/src/test/run-pass/issue-52141/auxiliary/some_crate.rs b/src/test/run-pass/issue-52141/auxiliary/some_crate.rs new file mode 100644 index 0000000000000..bf8dee0863a1b --- /dev/null +++ b/src/test/run-pass/issue-52141/auxiliary/some_crate.rs @@ -0,0 +1,15 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +pub fn hello() { + println!("Hello, world!"); +} diff --git a/src/test/run-pass/issue-52141/main.rs b/src/test/run-pass/issue-52141/main.rs new file mode 100644 index 0000000000000..8f3fc9f969894 --- /dev/null +++ b/src/test/run-pass/issue-52141/main.rs @@ -0,0 +1,24 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:some_crate.rs +// edition:2018 + +use some_crate as some_name; + +mod foo { + pub use crate::some_name::*; +} + +fn main() { + ::some_crate::hello(); + some_name::hello(); + foo::hello(); +} diff --git a/src/test/run-pass/issue-52705/auxiliary/png.rs b/src/test/run-pass/issue-52705/auxiliary/png.rs new file mode 100644 index 0000000000000..48d53a2cbac0a --- /dev/null +++ b/src/test/run-pass/issue-52705/auxiliary/png.rs @@ -0,0 +1,13 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![crate_type = "lib"] + +pub struct DecodingError; diff --git a/src/test/run-pass/issue-52705/main.rs b/src/test/run-pass/issue-52705/main.rs new file mode 100644 index 0000000000000..101f67e3e7b98 --- /dev/null +++ b/src/test/run-pass/issue-52705/main.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:png.rs +// edition:2018 + +mod png { + use png as png_ext; + + fn foo() -> png_ext::DecodingError { unimplemented!() } +} + +fn main() { + println!("Hello, world!"); +} diff --git a/src/test/run-pass/uniform-paths/basic-nested.rs b/src/test/run-pass/uniform-paths/basic-nested.rs new file mode 100644 index 0000000000000..086fd58cffa9e --- /dev/null +++ b/src/test/run-pass/uniform-paths/basic-nested.rs @@ -0,0 +1,52 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +// This test is similar to `basic.rs`, but nested in modules. + +mod foo { + // Test that ambiguity errors are not emitted between `self::test` and + // `::test`, assuming the latter (crate) is not in `extern_prelude`. + mod test { + pub struct Foo(pub ()); + } + pub use test::Foo; + + // Test that qualified paths can refer to both the external crate and local item. + mod std { + pub struct io(pub ()); + } + pub use ::std::io as std_io; + pub use self::std::io as local_io; +} + +// Test that we can refer to the external crate unqualified +// (when there isn't a local item with the same name). +use std::io; + +mod bar { + // Also test the unqualified external crate import in a nested module, + // to show that the above import doesn't resolve through a local `std` + // 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; +} + + +fn main() { + foo::Foo(()); + foo::std_io::stdout(); + foo::local_io(()); + io::stdout(); + bar::io::stdout(); +} diff --git a/src/test/run-pass/uniform-paths/basic.rs b/src/test/run-pass/uniform-paths/basic.rs new file mode 100644 index 0000000000000..59a0404e4c360 --- /dev/null +++ b/src/test/run-pass/uniform-paths/basic.rs @@ -0,0 +1,33 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +// Test that ambiguity errors are not emitted between `self::test` and +// `::test`, assuming the latter (crate) is not in `extern_prelude`. +mod test { + pub struct Foo(pub ()); +} +use test::Foo; + +// Test that qualified paths can refer to both the external crate and local item. +mod std { + pub struct io(pub ()); +} +use ::std::io as std_io; +use self::std::io as local_io; + +fn main() { + Foo(()); + std_io::stdout(); + local_io(()); +} diff --git a/src/test/run-pass/uniform-paths/macros-nested.rs b/src/test/run-pass/uniform-paths/macros-nested.rs new file mode 100644 index 0000000000000..9cf05cd956039 --- /dev/null +++ b/src/test/run-pass/uniform-paths/macros-nested.rs @@ -0,0 +1,62 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +// This test is similar to `macros.rs`, but nested in modules. + +mod foo { + // Test that ambiguity errors are not emitted between `self::test` and + // `::test`, assuming the latter (crate) is not in `extern_prelude`. + macro_rules! m1 { + () => { + mod test { + pub struct Foo(pub ()); + } + } + } + pub use test::Foo; + m1!(); + + // Test that qualified paths can refer to both the external crate and local item. + macro_rules! m2 { + () => { + mod std { + pub struct io(pub ()); + } + } + } + pub use ::std::io as std_io; + pub use self::std::io as local_io; + m2!(); +} + +// Test that we can refer to the external crate unqualified +// (when there isn't a local item with the same name). +use std::io; + +mod bar { + // Also test the unqualified external crate import in a nested module, + // to show that the above import doesn't resolve through a local `std` + // 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; +} + + +fn main() { + foo::Foo(()); + foo::std_io::stdout(); + foo::local_io(()); + io::stdout(); + bar::io::stdout(); +} diff --git a/src/test/run-pass/uniform-paths/macros.rs b/src/test/run-pass/uniform-paths/macros.rs new file mode 100644 index 0000000000000..ad74f5d0876bb --- /dev/null +++ b/src/test/run-pass/uniform-paths/macros.rs @@ -0,0 +1,45 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +// This test is similar to `basic.rs`, but with macros defining local items. + +// Test that ambiguity errors are not emitted between `self::test` and +// `::test`, assuming the latter (crate) is not in `extern_prelude`. +macro_rules! m1 { + () => { + mod test { + pub struct Foo(pub ()); + } + } +} +use test::Foo; +m1!(); + +// Test that qualified paths can refer to both the external crate and local item. +macro_rules! m2 { + () => { + mod std { + pub struct io(pub ()); + } + } +} +use ::std::io as std_io; +use self::std::io as local_io; +m2!(); + +fn main() { + Foo(()); + std_io::stdout(); + local_io(()); +} diff --git a/src/test/run-pass/uniform-paths/same-crate.rs b/src/test/run-pass/uniform-paths/same-crate.rs new file mode 100644 index 0000000000000..a3eefa7134c47 --- /dev/null +++ b/src/test/run-pass/uniform-paths/same-crate.rs @@ -0,0 +1,109 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +pub const A: usize = 0; + +pub mod foo { + pub const B: usize = 1; + + pub mod bar { + pub const C: usize = 2; + + pub enum E { + V1(usize), + V2(String), + } + + pub fn test() -> String { + format!("{} {} {}", crate::A, crate::foo::B, C) + } + + pub fn test_use() -> String { + use crate::A; + use crate::foo::B; + + format!("{} {} {}", A, B, C) + } + + pub fn test_enum() -> String { + use E::*; + match E::V1(10) { + V1(i) => { format!("V1: {}", i) } + V2(s) => { format!("V2: {}", s) } + } + } + } + + pub fn test() -> String { + format!("{} {} {}", crate::A, B, bar::C) + } + + pub fn test_use() -> String { + use crate::A; + use bar::C; + + format!("{} {} {}", A, B, C) + } + + pub fn test_enum() -> String { + use bar::E::*; + match bar::E::V1(10) { + V1(i) => { format!("V1: {}", i) } + V2(s) => { format!("V2: {}", s) } + } + } +} + +pub fn test() -> String { + format!("{} {} {}", A, foo::B, foo::bar::C) +} + +pub fn test_use() -> String { + use foo::B; + use foo::bar::C; + + format!("{} {} {}", A, B, C) +} + +pub fn test_enum() -> String { + use foo::bar::E::*; + match foo::bar::E::V1(10) { + V1(i) => { format!("V1: {}", i) } + V2(s) => { format!("V2: {}", s) } + } +} + +fn main() { + let output = [ + test(), + foo::test(), + foo::bar::test(), + test_use(), + foo::test_use(), + foo::bar::test_use(), + test_enum(), + foo::test_enum(), + foo::bar::test_enum(), + ].join("\n"); + assert_eq!(output, "\ +0 1 2 +0 1 2 +0 1 2 +0 1 2 +0 1 2 +0 1 2 +V1: 10 +V1: 10 +V1: 10"); +} diff --git a/src/test/ui/feature-gate-uniform-paths.rs b/src/test/ui/feature-gate-uniform-paths.rs new file mode 100644 index 0000000000000..140655d52bd46 --- /dev/null +++ b/src/test/ui/feature-gate-uniform-paths.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub mod foo { + pub use bar::Bar; + //~^ ERROR unresolved import `bar` + + pub mod bar { + pub struct Bar; + } +} + +fn main() { + let _ = foo::Bar; +} diff --git a/src/test/ui/feature-gate-uniform-paths.stderr b/src/test/ui/feature-gate-uniform-paths.stderr new file mode 100644 index 0000000000000..68faacfcbe75e --- /dev/null +++ b/src/test/ui/feature-gate-uniform-paths.stderr @@ -0,0 +1,9 @@ +error[E0432]: unresolved import `bar` + --> $DIR/feature-gate-uniform-paths.rs:12:13 + | +LL | pub use bar::Bar; + | ^^^ Did you mean `self::bar`? + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0432`. 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 new file mode 100644 index 0000000000000..5f29e7bc99e94 --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.rs @@ -0,0 +1,31 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +// This test is similar to `ambiguity-macros.rs`, but nested in a module. + +mod foo { + pub use std::io; + //~^ ERROR import from `std` is ambiguous + + macro_rules! m { + () => { + mod std { + pub struct io; + } + } + } + m!(); +} + +fn main() {} 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 new file mode 100644 index 0000000000000..d400987dfee3c --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros-nested.stderr @@ -0,0 +1,16 @@ +error: import from `std` is ambiguous + --> $DIR/ambiguity-macros-nested.rs:18:13 + | +LL | pub use std::io; + | ^^^ could refer to external crate `::std` +... +LL | / mod std { +LL | | pub struct io; +LL | | } + | |_____________- could also refer to `self::std` + | + = help: write `::std` or `self::std` explicitly instead + = note: relative `use` paths enabled by `#![feature(uniform_paths)]` + +error: aborting due to previous error + diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.rs b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.rs new file mode 100644 index 0000000000000..547b2508b96ab --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.rs @@ -0,0 +1,29 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +// This test is similar to `ambiguity.rs`, but with macros defining local items. + +use std::io; +//~^ ERROR import from `std` is ambiguous + +macro_rules! m { + () => { + mod std { + pub struct io; + } + } +} +m!(); + +fn main() {} diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.stderr b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.stderr new file mode 100644 index 0000000000000..24a2061a3cb2a --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-macros.stderr @@ -0,0 +1,16 @@ +error: import from `std` is ambiguous + --> $DIR/ambiguity-macros.rs:17:5 + | +LL | use std::io; + | ^^^ could refer to external crate `::std` +... +LL | / mod std { +LL | | pub struct io; +LL | | } + | |_________- could also refer to `self::std` + | + = help: write `::std` or `self::std` explicitly instead + = note: relative `use` paths enabled by `#![feature(uniform_paths)]` + +error: aborting due to previous error + diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.rs b/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.rs new file mode 100644 index 0000000000000..fe00fd94ee942 --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.rs @@ -0,0 +1,26 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +// This test is similar to `ambiguity.rs`, but nested in a module. + +mod foo { + pub use std::io; + //~^ ERROR import from `std` is ambiguous + + mod std { + pub struct io; + } +} + +fn main() {} diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.stderr b/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.stderr new file mode 100644 index 0000000000000..5104aba8b44a2 --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity-nested.stderr @@ -0,0 +1,16 @@ +error: import from `std` is ambiguous + --> $DIR/ambiguity-nested.rs:18:13 + | +LL | pub use std::io; + | ^^^ could refer to external crate `::std` +... +LL | / mod std { +LL | | pub struct io; +LL | | } + | |_____- could also refer to `self::std` + | + = help: write `::std` or `self::std` explicitly instead + = note: relative `use` paths enabled by `#![feature(uniform_paths)]` + +error: aborting due to previous error + diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity.rs b/src/test/ui/rust-2018/uniform-paths/ambiguity.rs new file mode 100644 index 0000000000000..49ab2f0c19168 --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity.rs @@ -0,0 +1,22 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// edition:2018 + +#![feature(uniform_paths)] + +use std::io; +//~^ ERROR import from `std` is ambiguous + +mod std { + pub struct io; +} + +fn main() {} diff --git a/src/test/ui/rust-2018/uniform-paths/ambiguity.stderr b/src/test/ui/rust-2018/uniform-paths/ambiguity.stderr new file mode 100644 index 0000000000000..2e227dce96cb0 --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/ambiguity.stderr @@ -0,0 +1,16 @@ +error: import from `std` is ambiguous + --> $DIR/ambiguity.rs:15:5 + | +LL | use std::io; + | ^^^ could refer to external crate `::std` +... +LL | / mod std { +LL | | pub struct io; +LL | | } + | |_- could also refer to `self::std` + | + = help: write `::std` or `self::std` explicitly instead + = note: relative `use` paths enabled by `#![feature(uniform_paths)]` + +error: aborting due to previous error + 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 new file mode 100644 index 0000000000000..b8489c61dadee --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.rs @@ -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 or the MIT license +// , 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); +} 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 new file mode 100644 index 0000000000000..656af91a8c7fe --- /dev/null +++ b/src/test/ui/rust-2018/uniform-paths/block-scoped-shadow.stderr @@ -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 +