diff --git a/src/doc/unstable-book/src/language-features/use-nested-groups.md b/src/doc/unstable-book/src/language-features/use-nested-groups.md new file mode 100644 index 0000000000000..47b635bad736f --- /dev/null +++ b/src/doc/unstable-book/src/language-features/use-nested-groups.md @@ -0,0 +1,90 @@ +# `use_nested_groups` + +The tracking issue for this feature is: [#44494] + +[#44494]: https://github.com/rust-lang/rust/issues/44494 + +------------------------ + +The `use_nested_groups` feature allows you to import multiple items from a +complex module tree easily, by nesting different imports in the same +declaration. For example: + +```rust +#![feature(use_nested_groups)] +# #![allow(unused_imports, dead_code)] +# +# mod foo { +# pub mod bar { +# pub type Foo = (); +# } +# pub mod baz { +# pub mod quux { +# pub type Bar = (); +# } +# } +# } + +use foo::{ + bar::{self, Foo}, + baz::{*, quux::Bar}, +}; +# +# fn main() {} +``` + +## Snippet for the book's new features appendix + +When stabilizing, add this to +`src/doc/book/second-edition/src/appendix-07-newest-features.md`: + +### Nested groups in `use` declarations + +If you have a complex module tree with many different submodules and you need +to import a few items from each one, it might be useful to group all the +imports in the same declaration to keep your code clean and avoid repeating the +base modules' name. + +The `use` declaration supports nesting to help you in those cases, both with +simple imports and glob ones. For example this snippets imports `bar`, `Foo`, +all the items in `baz` and `Bar`: + +```rust +# #![feature(use_nested_groups)] +# #![allow(unused_imports, dead_code)] +# +# mod foo { +# pub mod bar { +# pub type Foo = (); +# } +# pub mod baz { +# pub mod quux { +# pub type Bar = (); +# } +# } +# } +# +use foo::{ + bar::{self, Foo}, + baz::{*, quux::Bar}, +}; +# +# fn main() {} +``` + +## Updated reference + +When stabilizing, replace the shortcut list in +`src/doc/reference/src/items/use-declarations.md` with this updated one: + +* Simultaneously binding a list of paths with a common prefix, using the + glob-like brace syntax `use a::b::{c, d, e::f, g::h::i};` +* Simultaneously binding a list of paths with a common prefix and their common + parent module, using the `self` keyword, such as `use a::b::{self, c, d::e};` +* Rebinding the target name as a new local name, using the syntax `use p::q::r + as x;`. This can also be used with the last two features: + `use a::b::{self as ab, c as abc}`. +* Binding all paths matching a given prefix, using the asterisk wildcard syntax + `use a::b::*;`. +* Nesting groups of the previous features multiple times, such as + `use a::b::{self as ab, c d::{*, e::f}};` diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 7fd6f4a8b4278..cafbbe4c11718 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -1768,80 +1768,14 @@ impl<'a> LoweringContext<'a> { -> hir::Item_ { match *i { ItemKind::ExternCrate(string) => hir::ItemExternCrate(string), - ItemKind::Use(ref view_path) => { - let path = match view_path.node { - ViewPathSimple(_, ref path) => path, - ViewPathGlob(ref path) => path, - ViewPathList(ref path, ref path_list_idents) => { - for &Spanned { node: ref import, span } in path_list_idents { - // `use a::{self as x, b as y};` lowers to - // `use a as x; use a::b as y;` - let mut ident = import.name; - let suffix = if ident.name == keywords::SelfValue.name() { - if let Some(last) = path.segments.last() { - ident = last.identifier; - } - None - } else { - Some(ident.name) - }; - - let mut path = self.lower_path_extra(import.id, path, suffix, - ParamMode::Explicit, true); - path.span = span; - - self.allocate_hir_id_counter(import.id, import); - let LoweredNodeId { - node_id: import_node_id, - hir_id: import_hir_id, - } = self.lower_node_id(import.id); - - self.with_hir_id_owner(import_node_id, |this| { - let vis = match *vis { - hir::Visibility::Public => hir::Visibility::Public, - hir::Visibility::Crate => hir::Visibility::Crate, - hir::Visibility::Inherited => hir::Visibility::Inherited, - hir::Visibility::Restricted { ref path, id: _ } => { - hir::Visibility::Restricted { - path: path.clone(), - // We are allocating a new NodeId here - id: this.next_id().node_id, - } - } - }; - - this.items.insert(import_node_id, hir::Item { - id: import_node_id, - hir_id: import_hir_id, - name: import.rename.unwrap_or(ident).name, - attrs: attrs.clone(), - node: hir::ItemUse(P(path), hir::UseKind::Single), - vis, - span, - }); - }); - } - path - } + ItemKind::Use(ref use_tree) => { + // Start with an empty prefix + let prefix = Path { + segments: vec![], + span: use_tree.span, }; - let path = P(self.lower_path(id, path, ParamMode::Explicit, true)); - let kind = match view_path.node { - ViewPathSimple(ident, _) => { - *name = ident.name; - hir::UseKind::Single - } - ViewPathGlob(_) => { - hir::UseKind::Glob - } - ViewPathList(..) => { - // Privatize the degenerate import base, used only to check - // the stability of `use a::{};`, to avoid it showing up as - // a reexport by accident when `pub`, e.g. in documentation. - *vis = hir::Inherited; - hir::UseKind::ListStem - } - }; - hir::ItemUse(path, kind) + + self.lower_use_tree(use_tree, &prefix, id, vis, name, attrs) } ItemKind::Static(ref t, m, ref e) => { let value = self.lower_body(None, |this| this.lower_expr(e)); @@ -1963,6 +1897,112 @@ impl<'a> LoweringContext<'a> { // not cause an assertion failure inside the `lower_defaultness` function } + fn lower_use_tree(&mut self, + tree: &UseTree, + prefix: &Path, + id: NodeId, + vis: &mut hir::Visibility, + name: &mut Name, + attrs: &hir::HirVec) + -> hir::Item_ { + let path = &tree.prefix; + + match tree.kind { + UseTreeKind::Simple(ident) => { + *name = ident.name; + + // First apply the prefix to the path + let mut path = Path { + segments: prefix.segments + .iter() + .chain(path.segments.iter()) + .cloned() + .collect(), + span: path.span.to(prefix.span), + }; + + // Correctly resolve `self` imports + if path.segments.last().unwrap().identifier.name == keywords::SelfValue.name() { + let _ = path.segments.pop(); + if ident.name == keywords::SelfValue.name() { + *name = path.segments.last().unwrap().identifier.name; + } + } + + let path = P(self.lower_path(id, &path, ParamMode::Explicit, true)); + hir::ItemUse(path, hir::UseKind::Single) + } + UseTreeKind::Glob => { + let path = P(self.lower_path(id, &Path { + segments: prefix.segments + .iter() + .chain(path.segments.iter()) + .cloned() + .collect(), + span: path.span, + }, ParamMode::Explicit, true)); + hir::ItemUse(path, hir::UseKind::Glob) + } + UseTreeKind::Nested(ref trees) => { + let prefix = Path { + segments: prefix.segments + .iter() + .chain(path.segments.iter()) + .cloned() + .collect(), + span: prefix.span.to(path.span), + }; + + // Add all the nested PathListItems in the HIR + for &(ref use_tree, id) in trees { + self.allocate_hir_id_counter(id, &use_tree); + let LoweredNodeId { + node_id: new_id, + hir_id: new_hir_id, + } = self.lower_node_id(id); + + let mut vis = vis.clone(); + let mut name = name.clone(); + let item = self.lower_use_tree( + use_tree, &prefix, new_id, &mut vis, &mut name, &attrs, + ); + + self.with_hir_id_owner(new_id, |this| { + let vis = match vis { + hir::Visibility::Public => hir::Visibility::Public, + hir::Visibility::Crate => hir::Visibility::Crate, + hir::Visibility::Inherited => hir::Visibility::Inherited, + hir::Visibility::Restricted { ref path, id: _ } => { + hir::Visibility::Restricted { + path: path.clone(), + // We are allocating a new NodeId here + id: this.next_id().node_id, + } + } + }; + + this.items.insert(new_id, hir::Item { + id: new_id, + hir_id: new_hir_id, + name: name, + attrs: attrs.clone(), + node: item, + vis, + span: use_tree.span, + }); + }); + } + + // Privatize the degenerate import base, used only to check + // the stability of `use a::{};`, to avoid it showing up as + // a reexport by accident when `pub`, e.g. in documentation. + let path = P(self.lower_path(id, &prefix, ParamMode::Explicit, true)); + *vis = hir::Inherited; + hir::ItemUse(path, hir::UseKind::ListStem) + } + } + } + fn lower_trait_item(&mut self, i: &TraitItem) -> hir::TraitItem { self.with_parent_def(i.id, |this| { let LoweredNodeId { node_id, hir_id } = this.lower_node_id(i.id); @@ -2129,11 +2169,10 @@ impl<'a> LoweringContext<'a> { fn lower_item_id(&mut self, i: &Item) -> SmallVector { match i.node { - ItemKind::Use(ref view_path) => { - if let ViewPathList(_, ref imports) = view_path.node { - return iter::once(i.id).chain(imports.iter().map(|import| import.node.id)) - .map(|id| hir::ItemId { id: id }).collect(); - } + ItemKind::Use(ref use_tree) => { + let mut vec = SmallVector::one(hir::ItemId { id: i.id }); + self.lower_item_id_use_tree(use_tree, &mut vec); + return vec; } ItemKind::MacroDef(..) => return SmallVector::new(), _ => {} @@ -2141,6 +2180,19 @@ impl<'a> LoweringContext<'a> { SmallVector::one(hir::ItemId { id: i.id }) } + fn lower_item_id_use_tree(&self, tree: &UseTree, vec: &mut SmallVector) { + match tree.kind { + UseTreeKind::Nested(ref nested_vec) => { + for &(ref nested, id) in nested_vec { + vec.push(hir::ItemId { id, }); + self.lower_item_id_use_tree(nested, vec); + } + } + UseTreeKind::Glob => {} + UseTreeKind::Simple(..) => {} + } + } + pub fn lower_item(&mut self, i: &Item) -> Option { let mut name = i.ident.name; let mut vis = self.lower_visibility(&i.vis, None); diff --git a/src/librustc/hir/map/def_collector.rs b/src/librustc/hir/map/def_collector.rs index d8590c1de94e9..17a4c66edb9c9 100644 --- a/src/librustc/hir/map/def_collector.rs +++ b/src/librustc/hir/map/def_collector.rs @@ -118,21 +118,8 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { ItemKind::MacroDef(..) => DefPathData::MacroDef(i.ident.name.as_str()), ItemKind::Mac(..) => return self.visit_macro_invoc(i.id, false), ItemKind::GlobalAsm(..) => DefPathData::Misc, - ItemKind::Use(ref view_path) => { - match view_path.node { - ViewPathGlob(..) => {} - - // FIXME(eddyb) Should use the real name. Which namespace? - ViewPathSimple(..) => {} - ViewPathList(_, ref imports) => { - for import in imports { - self.create_def(import.node.id, - DefPathData::Misc, - ITEM_LIKE_SPACE); - } - } - } - DefPathData::Misc + ItemKind::Use(..) => { + return visit::walk_item(self, i); } }; let def = self.create_def(i.id, def_data, ITEM_LIKE_SPACE); @@ -180,6 +167,11 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> { }); } + fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) { + self.create_def(id, DefPathData::Misc, ITEM_LIKE_SPACE); + visit::walk_use_tree(self, use_tree, id); + } + fn visit_foreign_item(&mut self, foreign_item: &'a ForeignItem) { let def = self.create_def(foreign_item.id, DefPathData::ValueNs(foreign_item.ident.name.as_str()), diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index 4496e07b13814..2b9d5f27c661e 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -981,12 +981,6 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> { ast_visit::walk_path(self, p); } - fn visit_path_list_item(&mut self, prefix: &'a ast::Path, item: &'a ast::PathListItem) { - run_lints!(self, check_path_list_item, early_passes, item); - self.check_id(item.node.id); - ast_visit::walk_path_list_item(self, prefix, item); - } - fn visit_attribute(&mut self, attr: &'a ast::Attribute) { run_lints!(self, check_attribute, early_passes, attr); } diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index d648099d74d36..b5cc6556dace1 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -248,7 +248,6 @@ pub trait EarlyLintPass: LintPass { fn check_lifetime(&mut self, _: &EarlyContext, _: &ast::Lifetime) { } fn check_lifetime_def(&mut self, _: &EarlyContext, _: &ast::LifetimeDef) { } fn check_path(&mut self, _: &EarlyContext, _: &ast::Path, _: ast::NodeId) { } - fn check_path_list_item(&mut self, _: &EarlyContext, _: &ast::PathListItem) { } fn check_attribute(&mut self, _: &EarlyContext, _: &ast::Attribute) { } /// Called when entering a syntax node that can have lint attributes such diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 32f724f7541c7..4e066ecf999e3 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -330,6 +330,43 @@ declare_lint! { #[derive(Copy, Clone)] pub struct UnusedImportBraces; +impl UnusedImportBraces { + fn check_use_tree(&self, cx: &EarlyContext, use_tree: &ast::UseTree, item: &ast::Item) { + if let ast::UseTreeKind::Nested(ref items) = use_tree.kind { + // Recursively check nested UseTrees + for &(ref tree, _) in items { + self.check_use_tree(cx, tree, item); + } + + // Trigger the lint only if there is one nested item + if items.len() != 1 { + return; + } + + // Trigger the lint if the nested item is a non-self single item + let node_ident; + match items[0].0.kind { + ast::UseTreeKind::Simple(ident) => { + if ident.name == keywords::SelfValue.name() { + return; + } else { + node_ident = ident; + } + } + ast::UseTreeKind::Glob => { + node_ident = ast::Ident::from_str("*"); + } + ast::UseTreeKind::Nested(_) => { + return; + } + } + + let msg = format!("braces around {} is unnecessary", node_ident.name); + cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg); + } + } +} + impl LintPass for UnusedImportBraces { fn get_lints(&self) -> LintArray { lint_array!(UNUSED_IMPORT_BRACES) @@ -338,13 +375,8 @@ impl LintPass for UnusedImportBraces { impl EarlyLintPass for UnusedImportBraces { fn check_item(&mut self, cx: &EarlyContext, item: &ast::Item) { - if let ast::ItemKind::Use(ref view_path) = item.node { - if let ast::ViewPathList(_, ref items) = view_path.node { - if items.len() == 1 && items[0].node.name.name != keywords::SelfValue.name() { - let msg = format!("braces around {} is unnecessary", items[0].node.name); - cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg); - } - } + if let ast::ItemKind::Use(ref use_tree) = item.node { + self.check_use_tree(cx, use_tree, item); } } } diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 3136aeaf8ca1f..97cea5c9d6452 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -181,15 +181,27 @@ impl<'a> Visitor<'a> for AstValidator<'a> { visit::walk_ty(self, ty) } + fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) { + // Check if the path in this `use` is not generic, such as `use foo::bar;` While this + // can't happen normally thanks to the parser, a generic might sneak in if the `use` is + // built using a macro. + // + // macro_use foo { + // ($p:path) => { use $p; } + // } + // foo!(bar::baz); + use_tree.prefix.segments.iter().find(|segment| { + segment.parameters.is_some() + }).map(|segment| { + self.err_handler().span_err(segment.parameters.as_ref().unwrap().span(), + "generic arguments in import path"); + }); + + visit::walk_use_tree(self, use_tree, id); + } + fn visit_item(&mut self, item: &'a Item) { match item.node { - ItemKind::Use(ref view_path) => { - let path = view_path.node.path(); - path.segments.iter().find(|segment| segment.parameters.is_some()).map(|segment| { - self.err_handler().span_err(segment.parameters.as_ref().unwrap().span(), - "generic arguments in import path"); - }); - } ItemKind::Impl(.., Some(..), _, ref impl_items) => { self.invalid_visibility(&item.vis, item.span, None); for impl_item in impl_items { diff --git a/src/librustc_passes/hir_stats.rs b/src/librustc_passes/hir_stats.rs index c6bc045f0de3b..6f93fa133b9e4 100644 --- a/src/librustc_passes/hir_stats.rs +++ b/src/librustc_passes/hir_stats.rs @@ -358,13 +358,6 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { self.record("Mac", Id::None, mac); } - fn visit_path_list_item(&mut self, - prefix: &'v ast::Path, - item: &'v ast::PathListItem) { - self.record("PathListItem", Id::None, item); - ast_visit::walk_path_list_item(self, prefix, item) - } - fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v ast::PathSegment) { diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 46513a5740aed..afca6ea2c0751 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -32,9 +32,8 @@ use std::rc::Rc; use syntax::ast::{Name, Ident}; use syntax::attr; -use syntax::ast::{self, Block, ForeignItem, ForeignItemKind, Item, ItemKind}; -use syntax::ast::{Mutability, StmtKind, TraitItem, TraitItemKind}; -use syntax::ast::{Variant, ViewPathGlob, ViewPathList, ViewPathSimple}; +use syntax::ast::{self, Block, ForeignItem, ForeignItemKind, Item, ItemKind, NodeId}; +use syntax::ast::{Mutability, StmtKind, TraitItem, TraitItemKind, Variant}; use syntax::codemap::respan; use syntax::ext::base::SyntaxExtension; use syntax::ext::base::Determinacy::Undetermined; @@ -102,144 +101,157 @@ impl<'a> Resolver<'a> { } } - /// Constructs the reduced graph for one item. - fn build_reduced_graph_for_item(&mut self, item: &Item, expansion: Mark) { - let parent = self.current_module; - let ident = item.ident; - let sp = item.span; - let vis = self.resolve_visibility(&item.vis); + fn build_reduced_graph_for_use_tree(&mut self, + use_tree: &ast::UseTree, + id: NodeId, + vis: ty::Visibility, + prefix: &ast::Path, + nested: bool, + item: &Item, + expansion: Mark) { + let is_prelude = attr::contains_name(&item.attrs, "prelude_import"); + let path = &use_tree.prefix; + + let mut module_path: Vec<_> = prefix.segments.iter() + .chain(path.segments.iter()) + .map(|seg| respan(seg.span, seg.identifier)) + .collect(); + + match use_tree.kind { + ast::UseTreeKind::Simple(mut ident) => { + let mut source = module_path.pop().unwrap().node; + let mut type_ns_only = false; + + if nested { + // Correctly handle `self` + if source.name == keywords::SelfValue.name() { + type_ns_only = true; + + let last_segment = *module_path.last().unwrap(); + if last_segment.node.name == keywords::CrateRoot.name() { + resolve_error( + self, + use_tree.span, + ResolutionError:: + SelfImportOnlyInImportListWithNonEmptyPrefix + ); + return; + } - match item.node { - ItemKind::Use(ref view_path) => { - // Extract and intern the module part of the path. For - // globs and lists, the path is found directly in the AST; - // for simple paths we have to munge the path a little. - let module_path: Vec<_> = match view_path.node { - ViewPathSimple(_, ref full_path) => { - full_path.segments - .split_last() - .unwrap() - .1 - .iter() - .map(|seg| respan(seg.span, seg.identifier)) - .collect() + // Replace `use foo::self;` with `use foo;` + let _ = module_path.pop(); + source = last_segment.node; + if ident.name == keywords::SelfValue.name() { + ident = last_segment.node; + } } - - ViewPathGlob(ref module_ident_path) | - ViewPathList(ref module_ident_path, _) => { - module_ident_path.segments - .iter() - .map(|seg| respan(seg.span, seg.identifier)) - .collect() + } else { + // Disallow `self` + if source.name == keywords::SelfValue.name() { + resolve_error(self, + use_tree.span, + ResolutionError::SelfImportsOnlyAllowedWithin); } - }; - // Build up the import directives. - let is_prelude = attr::contains_name(&item.attrs, "prelude_import"); - - match view_path.node { - ViewPathSimple(mut binding, ref full_path) => { - let mut source = full_path.segments.last().unwrap().identifier; - let source_name = source.name; - if source_name == "mod" || source_name == "self" { - resolve_error(self, - view_path.span, - ResolutionError::SelfImportsOnlyAllowedWithin); - } else if source_name == keywords::DollarCrate.name() && - full_path.segments.len() == 1 { - let crate_root = self.resolve_crate_root(source.ctxt); - let crate_name = match crate_root.kind { - ModuleKind::Def(_, name) => name, - ModuleKind::Block(..) => unreachable!(), - }; - source.name = crate_name; - if binding.name == keywords::DollarCrate.name() { - binding.name = crate_name; - } - - self.session.struct_span_warn(item.span, "`$crate` may not be imported") - .note("`use $crate;` was erroneously allowed and \ - will become a hard error in a future release") - .emit(); + // Disallow `use $crate;` + if source.name == keywords::DollarCrate.name() && path.segments.len() == 1 { + let crate_root = self.resolve_crate_root(source.ctxt); + let crate_name = match crate_root.kind { + ModuleKind::Def(_, name) => name, + ModuleKind::Block(..) => unreachable!(), + }; + source.name = crate_name; + if ident.name == keywords::DollarCrate.name() { + ident.name = crate_name; } - let subclass = SingleImport { - target: binding, - source, - result: self.per_ns(|_, _| Cell::new(Err(Undetermined))), - type_ns_only: false, - }; - self.add_import_directive( - module_path, subclass, view_path.span, item.id, vis, expansion, - ); + self.session.struct_span_warn(item.span, "`$crate` may not be imported") + .note("`use $crate;` was erroneously allowed and \ + will become a hard error in a future release") + .emit(); } - ViewPathList(_, ref source_items) => { - // Make sure there's at most one `mod` import in the list. - let mod_spans = source_items.iter().filter_map(|item| { - if item.node.name.name == keywords::SelfValue.name() { - Some(item.span) - } else { - None - } - }).collect::>(); - - if mod_spans.len() > 1 { - let mut e = resolve_struct_error(self, - mod_spans[0], - ResolutionError::SelfImportCanOnlyAppearOnceInTheList); - for other_span in mod_spans.iter().skip(1) { - e.span_note(*other_span, "another `self` import appears here"); - } - e.emit(); - } + } - for source_item in source_items { - let node = source_item.node; - let (module_path, ident, rename, type_ns_only) = { - if node.name.name != keywords::SelfValue.name() { - let rename = node.rename.unwrap_or(node.name); - (module_path.clone(), - respan(source_item.span, node.name), - rename, - false) - } else { - let ident = *module_path.last().unwrap(); - if ident.node.name == keywords::CrateRoot.name() { - resolve_error( - self, - source_item.span, - ResolutionError:: - SelfImportOnlyInImportListWithNonEmptyPrefix - ); - continue; - } - let module_path = module_path.split_last().unwrap().1; - let rename = node.rename.unwrap_or(ident.node); - (module_path.to_vec(), ident, rename, true) - } - }; - let subclass = SingleImport { - target: rename, - source: ident.node, - result: self.per_ns(|_, _| Cell::new(Err(Undetermined))), - type_ns_only, - }; - let id = source_item.node.id; - self.add_import_directive( - module_path, subclass, source_item.span, id, vis, expansion, - ); + let subclass = SingleImport { + target: ident, + source, + result: self.per_ns(|_, _| Cell::new(Err(Undetermined))), + type_ns_only, + }; + self.add_import_directive( + module_path, subclass, use_tree.span, id, vis, expansion, + ); + } + ast::UseTreeKind::Glob => { + let subclass = GlobImport { + is_prelude, + max_vis: Cell::new(ty::Visibility::Invisible), + }; + self.add_import_directive( + module_path, subclass, use_tree.span, id, vis, expansion, + ); + } + ast::UseTreeKind::Nested(ref items) => { + let prefix = ast::Path { + segments: module_path.iter() + .map(|s| ast::PathSegment { + identifier: s.node, + span: s.span, + parameters: None, + }) + .collect(), + span: path.span, + }; + + // Ensure there is at most one `self` in the list + let self_spans = items.iter().filter_map(|&(ref use_tree, _)| { + if let ast::UseTreeKind::Simple(ident) = use_tree.kind { + if ident.name == keywords::SelfValue.name() { + return Some(use_tree.span); } } - ViewPathGlob(_) => { - let subclass = GlobImport { - is_prelude, - max_vis: Cell::new(ty::Visibility::Invisible), - }; - self.add_import_directive( - module_path, subclass, view_path.span, item.id, vis, expansion, - ); + + None + }).collect::>(); + if self_spans.len() > 1 { + let mut e = resolve_struct_error(self, + self_spans[0], + ResolutionError::SelfImportCanOnlyAppearOnceInTheList); + + for other_span in self_spans.iter().skip(1) { + e.span_note(*other_span, "another `self` import appears here"); } + + e.emit(); } + + for &(ref tree, id) in items { + self.build_reduced_graph_for_use_tree( + tree, id, vis, &prefix, true, item, expansion + ); + } + } + } + } + + /// Constructs the reduced graph for one item. + fn build_reduced_graph_for_item(&mut self, item: &Item, expansion: Mark) { + let parent = self.current_module; + let ident = item.ident; + let sp = item.span; + let vis = self.resolve_visibility(&item.vis); + + match item.node { + ItemKind::Use(ref use_tree) => { + // Just an empty prefix to start out + let prefix = ast::Path { + segments: vec![], + span: use_tree.span, + }; + + self.build_reduced_graph_for_use_tree( + use_tree, item.id, vis, &prefix, false, item, expansion, + ); } ItemKind::ExternCrate(as_name) => { diff --git a/src/librustc_resolve/check_unused.rs b/src/librustc_resolve/check_unused.rs index 5820acf1b9006..0fb3d96cd50d4 100644 --- a/src/librustc_resolve/check_unused.rs +++ b/src/librustc_resolve/check_unused.rs @@ -26,7 +26,7 @@ use resolve_imports::ImportDirectiveSubclass; use rustc::{lint, ty}; use rustc::util::nodemap::NodeMap; -use syntax::ast::{self, ViewPathGlob, ViewPathList, ViewPathSimple}; +use syntax::ast; use syntax::visit::{self, Visitor}; use syntax_pos::{Span, MultiSpan, DUMMY_SP}; @@ -35,6 +35,8 @@ struct UnusedImportCheckVisitor<'a, 'b: 'a> { resolver: &'a mut Resolver<'b>, /// All the (so far) unused imports, grouped path list unused_imports: NodeMap>, + base_id: ast::NodeId, + item_span: Span, } // Deref and DerefMut impls allow treating UnusedImportCheckVisitor as Resolver. @@ -77,40 +79,41 @@ impl<'a, 'b> UnusedImportCheckVisitor<'a, 'b> { impl<'a, 'b> Visitor<'a> for UnusedImportCheckVisitor<'a, 'b> { fn visit_item(&mut self, item: &'a ast::Item) { - visit::walk_item(self, item); + self.item_span = item.span; + // Ignore is_public import statements because there's no way to be sure // whether they're used or not. Also ignore imports with a dummy span // because this means that they were generated in some fashion by the // compiler and we don't need to consider them. - if item.vis == ast::Visibility::Public || item.span.source_equal(&DUMMY_SP) { - return; + if let ast::ItemKind::Use(..) = item.node { + if item.vis == ast::Visibility::Public || item.span.source_equal(&DUMMY_SP) { + return; + } } - match item.node { - ast::ItemKind::Use(ref p) => { - match p.node { - ViewPathSimple(..) => { - self.check_import(item.id, item.id, p.span) - } - - ViewPathList(_, ref list) => { - if list.len() == 0 { - self.unused_imports - .entry(item.id) - .or_insert_with(NodeMap) - .insert(item.id, item.span); - } - for i in list { - self.check_import(item.id, i.node.id, i.span); - } - } - ViewPathGlob(_) => { - self.check_import(item.id, item.id, p.span); - } - } + visit::walk_item(self, item); + } + + fn visit_use_tree(&mut self, use_tree: &'a ast::UseTree, id: ast::NodeId, nested: bool) { + // Use the base UseTree's NodeId as the item id + // This allows the grouping of all the lints in the same item + if !nested { + self.base_id = id; + } + + if let ast::UseTreeKind::Nested(ref items) = use_tree.kind { + if items.len() == 0 { + self.unused_imports + .entry(self.base_id) + .or_insert_with(NodeMap) + .insert(id, self.item_span); } - _ => {} + } else { + let base_id = self.base_id; + self.check_import(base_id, id, use_tree.span); } + + visit::walk_use_tree(self, use_tree, id); } } @@ -135,6 +138,8 @@ pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) { let mut visitor = UnusedImportCheckVisitor { resolver, unused_imports: NodeMap(), + base_id: ast::DUMMY_NODE_ID, + item_span: DUMMY_SP, }; visit::walk_crate(&mut visitor, krate); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 3243152527ffc..44db030b2b242 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1937,14 +1937,12 @@ impl<'a> Resolver<'a> { }); } - ItemKind::Use(ref view_path) => { - match view_path.node { - ast::ViewPathList(ref prefix, ref items) if items.is_empty() => { - // Resolve prefix of an import with empty braces (issue #28388). - self.smart_resolve_path(item.id, None, prefix, PathSource::ImportPrefix); - } - _ => {} - } + ItemKind::Use(ref use_tree) => { + let path = Path { + segments: vec![], + span: use_tree.span, + }; + self.resolve_use_tree(item, use_tree, &path); } ItemKind::ExternCrate(_) | ItemKind::MacroDef(..) | ItemKind::GlobalAsm(_)=> { @@ -1955,6 +1953,32 @@ impl<'a> Resolver<'a> { } } + fn resolve_use_tree(&mut self, item: &Item, use_tree: &ast::UseTree, prefix: &Path) { + match use_tree.kind { + ast::UseTreeKind::Nested(ref items) => { + let path = Path { + segments: prefix.segments + .iter() + .chain(use_tree.prefix.segments.iter()) + .cloned() + .collect(), + span: prefix.span.to(use_tree.prefix.span), + }; + + if items.len() == 0 { + // Resolve prefix of an import with empty braces (issue #28388). + self.smart_resolve_path(item.id, None, &path, PathSource::ImportPrefix); + } else { + for &(ref tree, _) in items { + self.resolve_use_tree(item, tree, &path); + } + } + } + ast::UseTreeKind::Simple(_) => {}, + ast::UseTreeKind::Glob => {}, + } + } + fn with_type_parameter_rib<'b, F>(&'b mut self, type_parameters: TypeParameters<'a, 'b>, f: F) where F: FnOnce(&mut Resolver) { diff --git a/src/librustc_save_analysis/dump_visitor.rs b/src/librustc_save_analysis/dump_visitor.rs index d4257e35823b3..602c70f9a1f4b 100644 --- a/src/librustc_save_analysis/dump_visitor.rs +++ b/src/librustc_save_analysis/dump_visitor.rs @@ -38,7 +38,7 @@ use syntax::symbol::keywords; use syntax::visit::{self, Visitor}; use syntax::print::pprust::{bounds_to_string, generics_to_string, path_to_string, ty_to_string}; use syntax::ptr::P; -use syntax::codemap::Spanned; +use syntax::codemap::{Spanned, DUMMY_SP}; use syntax_pos::*; use {escape, generated_code, lower_attributes, PathCollector, SaveContext}; @@ -1229,6 +1229,106 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> DumpVisitor<'l, 'tcx, 'll, O> { ast::ImplItemKind::Macro(_) => {} } } + + fn process_use_tree(&mut self, + use_tree: &'l ast::UseTree, + id: NodeId, + parent_item: &'l ast::Item, + prefix: &ast::Path) { + let path = &use_tree.prefix; + let access = access_from!(self.save_ctxt, parent_item); + + match use_tree.kind { + ast::UseTreeKind::Simple(ident) => { + let path = ast::Path { + segments: prefix.segments + .iter() + .chain(path.segments.iter()) + .cloned() + .collect(), + span: path.span, + }; + + let sub_span = self.span.span_for_last_ident(path.span); + let mod_id = match self.lookup_def_id(id) { + Some(def_id) => { + self.process_def_kind(id, path.span, sub_span, def_id); + Some(def_id) + } + None => None, + }; + + // 'use' always introduces an alias, if there is not an explicit + // one, there is an implicit one. + let sub_span = match self.span.sub_span_after_keyword(use_tree.span, + keywords::As) { + Some(sub_span) => Some(sub_span), + None => sub_span, + }; + + if !self.span.filter_generated(sub_span, path.span) { + let span = + self.span_from_span(sub_span.expect("No span found for use")); + self.dumper.import(&access, Import { + kind: ImportKind::Use, + ref_id: mod_id.map(|id| ::id_from_def_id(id)), + span, + name: ident.to_string(), + value: String::new(), + }); + } + self.write_sub_paths_truncated(&path); + } + ast::UseTreeKind::Glob => { + let path = ast::Path { + segments: prefix.segments + .iter() + .chain(path.segments.iter()) + .cloned() + .collect(), + span: path.span, + }; + + // Make a comma-separated list of names of imported modules. + let mut names = vec![]; + let glob_map = &self.save_ctxt.analysis.glob_map; + let glob_map = glob_map.as_ref().unwrap(); + if glob_map.contains_key(&id) { + for n in glob_map.get(&id).unwrap() { + names.push(n.to_string()); + } + } + + let sub_span = self.span.sub_span_of_token(use_tree.span, + token::BinOp(token::Star)); + if !self.span.filter_generated(sub_span, use_tree.span) { + let span = + self.span_from_span(sub_span.expect("No span found for use glob")); + self.dumper.import(&access, Import { + kind: ImportKind::GlobUse, + ref_id: None, + span, + name: "*".to_owned(), + value: names.join(", "), + }); + } + self.write_sub_paths(&path); + } + ast::UseTreeKind::Nested(ref nested_items) => { + let prefix = ast::Path { + segments: prefix.segments + .iter() + .chain(path.segments.iter()) + .cloned() + .collect(), + span: path.span, + }; + for &(ref tree, id) in nested_items { + self.process_use_tree(tree, id, parent_item, &prefix); + } + } + } + } } impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tcx, 'll, O> { @@ -1275,86 +1375,12 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc use syntax::ast::ItemKind::*; self.process_macro_use(item.span); match item.node { - Use(ref use_item) => { - let access = access_from!(self.save_ctxt, item); - - match use_item.node { - ast::ViewPathSimple(ident, ref path) => { - let sub_span = self.span.span_for_last_ident(path.span); - let mod_id = match self.lookup_def_id(item.id) { - Some(def_id) => { - self.process_def_kind(item.id, path.span, sub_span, def_id); - Some(def_id) - } - None => None, - }; - - // 'use' always introduces an alias, if there is not an explicit - // one, there is an implicit one. - let sub_span = match self.span - .sub_span_after_keyword(use_item.span, keywords::As) - { - Some(sub_span) => Some(sub_span), - None => sub_span, - }; - - if !self.span.filter_generated(sub_span, path.span) { - let span = - self.span_from_span(sub_span.expect("No span found for use")); - self.dumper.import( - &access, - Import { - kind: ImportKind::Use, - ref_id: mod_id.map(|id| ::id_from_def_id(id)), - span, - name: ident.to_string(), - value: String::new(), - }, - ); - } - self.write_sub_paths_truncated(path); - } - ast::ViewPathGlob(ref path) => { - // Make a comma-separated list of names of imported modules. - let mut names = vec![]; - let glob_map = &self.save_ctxt.analysis.glob_map; - let glob_map = glob_map.as_ref().unwrap(); - if glob_map.contains_key(&item.id) { - for n in glob_map.get(&item.id).unwrap() { - names.push(n.to_string()); - } - } - - let sub_span = self.span - .sub_span_of_token(item.span, token::BinOp(token::Star)); - if !self.span.filter_generated(sub_span, item.span) { - let span = - self.span_from_span(sub_span.expect("No span found for use glob")); - self.dumper.import( - &access, - Import { - kind: ImportKind::GlobUse, - ref_id: None, - span, - name: "*".to_owned(), - value: names.join(", "), - }, - ); - } - self.write_sub_paths(path); - } - ast::ViewPathList(ref path, ref list) => { - for plid in list { - let id = plid.node.id; - if let Some(def_id) = self.lookup_def_id(id) { - let span = plid.span; - self.process_def_kind(id, span, Some(span), def_id); - } - } - - self.write_sub_paths(path); - } - } + Use(ref use_tree) => { + let prefix = ast::Path { + segments: vec![], + span: DUMMY_SP, + }; + self.process_use_tree(use_tree, item.id, item, &prefix); } ExternCrate(_) => { let alias_span = self.span.span_for_last_ident(item.span); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index ad9d58651207d..3c1d6ea18f7c2 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -12,7 +12,6 @@ pub use self::TyParamBound::*; pub use self::UnsafeSource::*; -pub use self::ViewPath_::*; pub use self::PathParameters::*; pub use symbol::{Ident, Symbol as Name}; pub use util::ThinVec; @@ -1705,46 +1704,20 @@ pub struct Variant_ { pub type Variant = Spanned; -#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)] -pub struct PathListItem_ { - pub name: Ident, - /// renamed in list, e.g. `use foo::{bar as baz};` - pub rename: Option, - pub id: NodeId, -} - -pub type PathListItem = Spanned; - -pub type ViewPath = Spanned; - #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] -pub enum ViewPath_ { - - /// `foo::bar::baz as quux` - /// - /// or just - /// - /// `foo::bar::baz` (with `as baz` implicitly on the right) - ViewPathSimple(Ident, Path), - - /// `foo::bar::*` - ViewPathGlob(Path), - - /// `foo::bar::{a,b,c}` - ViewPathList(Path, Vec) +pub enum UseTreeKind { + Simple(Ident), + Glob, + Nested(Vec<(UseTree, NodeId)>), } -impl ViewPath_ { - pub fn path(&self) -> &Path { - match *self { - ViewPathSimple(_, ref path) | - ViewPathGlob (ref path) | - ViewPathList(ref path, _) => path - } - } +#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] +pub struct UseTree { + pub kind: UseTreeKind, + pub prefix: Path, + pub span: Span, } - /// Distinguishes between Attributes that decorate items and Attributes that /// are contained as statements within items. These two cases need to be /// distinguished for pretty-printing. @@ -1913,7 +1886,7 @@ pub enum ItemKind { /// A use declaration (`use` or `pub use`) item. /// /// E.g. `use foo;`, `use foo::bar;` or `use foo::bar as FooBar;` - Use(P), + Use(P), /// A static item (`static` or `pub static`). /// /// E.g. `static FOO: i32 = 42;` or `static FOO: &'static str = "bar";` diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index 48d789372a07b..25eef6db93036 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -291,7 +291,7 @@ pub trait AstBuilder { -> ast::MetaItem; fn item_use(&self, sp: Span, - vis: ast::Visibility, vp: P) -> P; + vis: ast::Visibility, vp: P) -> P; fn item_use_simple(&self, sp: Span, vis: ast::Visibility, path: ast::Path) -> P; fn item_use_simple_(&self, sp: Span, vis: ast::Visibility, ident: ast::Ident, path: ast::Path) -> P; @@ -1142,7 +1142,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { } fn item_use(&self, sp: Span, - vis: ast::Visibility, vp: P) -> P { + vis: ast::Visibility, vp: P) -> P { P(ast::Item { id: ast::DUMMY_NODE_ID, ident: keywords::Invalid.ident(), @@ -1161,33 +1161,36 @@ impl<'a> AstBuilder for ExtCtxt<'a> { fn item_use_simple_(&self, sp: Span, vis: ast::Visibility, ident: ast::Ident, path: ast::Path) -> P { - self.item_use(sp, vis, - P(respan(sp, - ast::ViewPathSimple(ident, - path)))) + self.item_use(sp, vis, P(ast::UseTree { + span: sp, + prefix: path, + kind: ast::UseTreeKind::Simple(ident), + })) } fn item_use_list(&self, sp: Span, vis: ast::Visibility, path: Vec, imports: &[ast::Ident]) -> P { let imports = imports.iter().map(|id| { - let item = ast::PathListItem_ { - name: *id, - rename: None, - id: ast::DUMMY_NODE_ID, - }; - respan(sp, item) + (ast::UseTree { + span: sp, + prefix: self.path(sp, vec![*id]), + kind: ast::UseTreeKind::Simple(*id), + }, ast::DUMMY_NODE_ID) }).collect(); - self.item_use(sp, vis, - P(respan(sp, - ast::ViewPathList(self.path(sp, path), - imports)))) + self.item_use(sp, vis, P(ast::UseTree { + span: sp, + prefix: self.path(sp, path), + kind: ast::UseTreeKind::Nested(imports), + })) } fn item_use_glob(&self, sp: Span, vis: ast::Visibility, path: Vec) -> P { - self.item_use(sp, vis, - P(respan(sp, - ast::ViewPathGlob(self.path(sp, path))))) + self.item_use(sp, vis, P(ast::UseTree { + span: sp, + prefix: self.path(sp, path), + kind: ast::UseTreeKind::Glob, + })) } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 89d1a3699e8a6..8507c9653dbef 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -428,6 +428,9 @@ declare_features! ( // In-band lifetime bindings (e.g. `fn foo(x: &'a u8) -> &'a u8`) (active, in_band_lifetimes, "1.23.0", Some(44524)), + + // Nested groups in `use` (RFC 2128) + (active, use_nested_groups, "1.23.0", Some(44494)), ); declare_features! ( @@ -1661,6 +1664,29 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { visit::walk_path(self, path); } + fn visit_use_tree(&mut self, use_tree: &'a ast::UseTree, id: NodeId, nested: bool) { + if nested { + match use_tree.kind { + ast::UseTreeKind::Simple(_) => { + if use_tree.prefix.segments.len() != 1 { + gate_feature_post!(&self, use_nested_groups, use_tree.span, + "paths in `use` groups are experimental"); + } + } + ast::UseTreeKind::Glob => { + gate_feature_post!(&self, use_nested_groups, use_tree.span, + "glob imports in `use` groups are experimental"); + } + ast::UseTreeKind::Nested(_) => { + gate_feature_post!(&self, use_nested_groups, use_tree.span, + "nested groups in `use` are experimental"); + } + } + } + + visit::walk_use_tree(self, use_tree, id); + } + fn visit_vis(&mut self, vis: &'a ast::Visibility) { if let ast::Visibility::Crate(span, ast::CrateSugar::JustCrate) = *vis { gate_feature_post!(&self, crate_visibility_modifier, span, diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index cc63bffec48a1..1a92f057e5e87 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -56,8 +56,8 @@ pub trait Folder : Sized { noop_fold_meta_item(meta_item, self) } - fn fold_view_path(&mut self, view_path: P) -> P { - noop_fold_view_path(view_path, self) + fn fold_use_tree(&mut self, use_tree: UseTree) -> UseTree { + noop_fold_use_tree(use_tree, self) } fn fold_foreign_item(&mut self, ni: ForeignItem) -> ForeignItem { @@ -310,30 +310,18 @@ pub fn noop_fold_meta_items(meta_items: Vec, fld: &mut T) - meta_items.move_map(|x| fld.fold_meta_item(x)) } -pub fn noop_fold_view_path(view_path: P, fld: &mut T) -> P { - view_path.map(|Spanned {node, span}| Spanned { - node: match node { - ViewPathSimple(ident, path) => { - ViewPathSimple(fld.fold_ident(ident), fld.fold_path(path)) - } - ViewPathGlob(path) => { - ViewPathGlob(fld.fold_path(path)) - } - ViewPathList(path, path_list_idents) => { - let path = fld.fold_path(path); - let path_list_idents = path_list_idents.move_map(|path_list_ident| Spanned { - node: PathListItem_ { - id: fld.new_id(path_list_ident.node.id), - rename: path_list_ident.node.rename.map(|ident| fld.fold_ident(ident)), - name: fld.fold_ident(path_list_ident.node.name), - }, - span: fld.new_span(path_list_ident.span) - }); - ViewPathList(path, path_list_idents) - } +pub fn noop_fold_use_tree(use_tree: UseTree, fld: &mut T) -> UseTree { + UseTree { + span: fld.new_span(use_tree.span), + prefix: fld.fold_path(use_tree.prefix), + kind: match use_tree.kind { + UseTreeKind::Simple(ident) => UseTreeKind::Simple(fld.fold_ident(ident)), + UseTreeKind::Glob => UseTreeKind::Glob, + UseTreeKind::Nested(items) => UseTreeKind::Nested(items.move_map(|(tree, id)| { + (fld.fold_use_tree(tree), fld.new_id(id)) + })), }, - span: fld.new_span(span) - }) + } } pub fn fold_attrs(attrs: Vec, fld: &mut T) -> Vec { @@ -874,8 +862,8 @@ pub fn noop_fold_block(b: P, folder: &mut T) -> P { pub fn noop_fold_item_kind(i: ItemKind, folder: &mut T) -> ItemKind { match i { ItemKind::ExternCrate(string) => ItemKind::ExternCrate(string), - ItemKind::Use(view_path) => { - ItemKind::Use(folder.fold_view_path(view_path)) + ItemKind::Use(use_tree) => { + ItemKind::Use(use_tree.map(|tree| folder.fold_use_tree(tree))) } ItemKind::Static(t, m, e) => { ItemKind::Static(folder.fold_ty(t), m, folder.fold_expr(e)) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 0f32d588b372f..07956ecb5aff8 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -35,8 +35,8 @@ use ast::StrStyle; use ast::SelfKind; use ast::{TraitItem, TraitRef, TraitObjectSyntax}; use ast::{Ty, TyKind, TypeBinding, TyParam, TyParamBounds}; -use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple}; use ast::{Visibility, WhereClause, CrateSugar}; +use ast::{UseTree, UseTreeKind}; use ast::{BinOpKind, UnOp}; use ast::{RangeEnd, RangeSyntax}; use {ast, attr}; @@ -1861,7 +1861,7 @@ impl<'a> Parser<'a> { loop { segments.push(self.parse_path_segment(style, enable_warning)?); - if self.is_import_coupler() || !self.eat(&token::ModSep) { + if self.is_import_coupler(false) || !self.eat(&token::ModSep) { return Ok(()); } } @@ -5964,7 +5964,7 @@ impl<'a> Parser<'a> { if self.eat_keyword(keywords::Use) { // USE ITEM - let item_ = ItemKind::Use(self.parse_view_path()?); + let item_ = ItemKind::Use(P(self.parse_use_tree(false)?)); self.expect(&token::Semi)?; let prev_span = self.prev_span; @@ -6407,74 +6407,101 @@ impl<'a> Parser<'a> { })) } - fn parse_path_list_items(&mut self) -> PResult<'a, Vec> { - self.parse_unspanned_seq(&token::OpenDelim(token::Brace), - &token::CloseDelim(token::Brace), - SeqSep::trailing_allowed(token::Comma), |this| { - let lo = this.span; - let ident = if this.eat_keyword(keywords::SelfValue) { - keywords::SelfValue.ident() - } else { - this.parse_ident()? - }; - let rename = this.parse_rename()?; - let node = ast::PathListItem_ { - name: ident, - rename, - id: ast::DUMMY_NODE_ID - }; - Ok(respan(lo.to(this.prev_span), node)) - }) + /// `{` or `::{` or `*` or `::*` + /// `::{` or `::*` (also `{` or `*` if unprefixed is true) + fn is_import_coupler(&mut self, unprefixed: bool) -> bool { + self.is_import_coupler_inner(&token::OpenDelim(token::Brace), unprefixed) || + self.is_import_coupler_inner(&token::BinOp(token::Star), unprefixed) } - /// `::{` or `::*` - fn is_import_coupler(&mut self) -> bool { - self.check(&token::ModSep) && - self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace) || - *t == token::BinOp(token::Star)) + fn is_import_coupler_inner(&mut self, token: &token::Token, unprefixed: bool) -> bool { + if self.check(&token::ModSep) { + self.look_ahead(1, |t| t == token) + } else if unprefixed { + self.check(token) + } else { + false + } } - /// Matches ViewPath: - /// MOD_SEP? non_global_path - /// MOD_SEP? non_global_path as IDENT - /// MOD_SEP? non_global_path MOD_SEP STAR - /// MOD_SEP? non_global_path MOD_SEP LBRACE item_seq RBRACE - /// MOD_SEP? LBRACE item_seq RBRACE - fn parse_view_path(&mut self) -> PResult<'a, P> { + /// Parse UseTree + /// + /// USE_TREE = `*` | + /// `{` USE_TREE_LIST `}` | + /// PATH `::` `*` | + /// PATH `::` `{` USE_TREE_LIST `}` | + /// PATH [`as` IDENT] + fn parse_use_tree(&mut self, nested: bool) -> PResult<'a, UseTree> { let lo = self.span; - if self.check(&token::OpenDelim(token::Brace)) || self.check(&token::BinOp(token::Star)) || - self.is_import_coupler() { - // `{foo, bar}`, `::{foo, bar}`, `*`, or `::*`. - self.eat(&token::ModSep); - let prefix = ast::Path { - segments: vec![PathSegment::crate_root(lo)], - span: lo.to(self.span), - }; - let view_path_kind = if self.eat(&token::BinOp(token::Star)) { - ViewPathGlob(prefix) + + let mut prefix = ast::Path { + segments: vec![], + span: lo.to(self.span), + }; + + let kind = if self.is_import_coupler(true) { + // `use *;` or `use ::*;` or `use {...};` `use ::{...};` + + // Remove the first `::` + if self.eat(&token::ModSep) { + prefix.segments.push(PathSegment::crate_root(self.prev_span)); + } else if !nested { + prefix.segments.push(PathSegment::crate_root(self.span)); + } + + if self.eat(&token::BinOp(token::Star)) { + // `use *;` + UseTreeKind::Glob + } else if self.check(&token::OpenDelim(token::Brace)) { + // `use {...};` + UseTreeKind::Nested(self.parse_use_tree_list()?) } else { - ViewPathList(prefix, self.parse_path_list_items()?) - }; - Ok(P(respan(lo.to(self.span), view_path_kind))) + return self.unexpected(); + } } else { - let prefix = self.parse_path(PathStyle::Mod)?.default_to_global(); - if self.is_import_coupler() { - // `foo::bar::{a, b}` or `foo::bar::*` - self.bump(); - if self.check(&token::BinOp(token::Star)) { - self.bump(); - Ok(P(respan(lo.to(self.span), ViewPathGlob(prefix)))) + // `use path::...;` + let mut parsed = self.parse_path(PathStyle::Mod)?; + if !nested { + parsed = parsed.default_to_global(); + } + + prefix.segments.append(&mut parsed.segments); + prefix.span = prefix.span.to(parsed.span); + + if self.eat(&token::ModSep) { + if self.eat(&token::BinOp(token::Star)) { + // `use path::*;` + UseTreeKind::Glob + } else if self.check(&token::OpenDelim(token::Brace)) { + // `use path::{...};` + UseTreeKind::Nested(self.parse_use_tree_list()?) } else { - let items = self.parse_path_list_items()?; - Ok(P(respan(lo.to(self.span), ViewPathList(prefix, items)))) + return self.unexpected(); } } else { - // `foo::bar` or `foo::bar as baz` + // `use path::foo;` or `use path::foo as bar;` let rename = self.parse_rename()?. unwrap_or(prefix.segments.last().unwrap().identifier); - Ok(P(respan(lo.to(self.prev_span), ViewPathSimple(rename, prefix)))) + UseTreeKind::Simple(rename) } - } + }; + + Ok(UseTree { + span: lo.to(self.prev_span), + kind, + prefix, + }) + } + + /// Parse UseTreeKind::Nested(list) + /// + /// USE_TREE_LIST = Ø | (USE_TREE `,`)* USE_TREE [`,`] + fn parse_use_tree_list(&mut self) -> PResult<'a, Vec<(UseTree, ast::NodeId)>> { + self.parse_unspanned_seq(&token::OpenDelim(token::Brace), + &token::CloseDelim(token::Brace), + SeqSep::trailing_allowed(token::Comma), |this| { + Ok((this.parse_use_tree(true)?, ast::DUMMY_NODE_ID)) + }) } fn parse_rename(&mut self) -> PResult<'a, Option> { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 38627b40544f5..a2d3ed4deb652 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1185,9 +1185,9 @@ impl<'a> State<'a> { self.end()?; // end inner head-block self.end()?; // end outer head-block } - ast::ItemKind::Use(ref vp) => { + ast::ItemKind::Use(ref tree) => { self.head(&visibility_qualified(&item.vis, "use"))?; - self.print_view_path(vp)?; + self.print_use_tree(tree)?; self.s.word(";")?; self.end()?; // end inner head-block self.end()?; // end outer head-block @@ -2918,45 +2918,39 @@ impl<'a> State<'a> { Ok(()) } - pub fn print_view_path(&mut self, vp: &ast::ViewPath) -> io::Result<()> { - match vp.node { - ast::ViewPathSimple(ident, ref path) => { - self.print_path(path, false, 0, true)?; + pub fn print_use_tree(&mut self, tree: &ast::UseTree) -> io::Result<()> { + match tree.kind { + ast::UseTreeKind::Simple(ref ident) => { + self.print_path(&tree.prefix, false, 0, true)?; - if path.segments.last().unwrap().identifier.name != - ident.name { + if tree.prefix.segments.last().unwrap().identifier.name != ident.name { self.s.space()?; self.word_space("as")?; - self.print_ident(ident)?; + self.print_ident(*ident)?; } - - Ok(()) } - - ast::ViewPathGlob(ref path) => { - self.print_path(path, false, 0, true)?; - self.s.word("::*") + ast::UseTreeKind::Glob => { + if !tree.prefix.segments.is_empty() { + self.print_path(&tree.prefix, false, 0, true)?; + self.s.word("::")?; + } + self.s.word("*")?; } - - ast::ViewPathList(ref path, ref idents) => { - if path.segments.is_empty() { + ast::UseTreeKind::Nested(ref items) => { + if tree.prefix.segments.is_empty() { self.s.word("{")?; } else { - self.print_path(path, false, 0, true)?; + self.print_path(&tree.prefix, false, 0, true)?; self.s.word("::{")?; } - self.commasep(Inconsistent, &idents[..], |s, w| { - s.print_ident(w.node.name)?; - if let Some(ident) = w.node.rename { - s.s.space()?; - s.word_space("as")?; - s.print_ident(ident)?; - } - Ok(()) + self.commasep(Inconsistent, &items[..], |this, &(ref tree, _)| { + this.print_use_tree(tree) })?; - self.s.word("}") + self.s.word("}")?; } } + + Ok(()) } pub fn print_mutability(&mut self, diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index 7aa94de9d3d5b..ae22230198f57 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -13,7 +13,7 @@ use attr; use ext::hygiene::{Mark, SyntaxContext}; use symbol::{Symbol, keywords}; use syntax_pos::{DUMMY_SP, Span}; -use codemap::{self, ExpnInfo, NameAndSpan, MacroAttribute}; +use codemap::{ExpnInfo, NameAndSpan, MacroAttribute}; use ptr::P; use tokenstream::TokenStream; @@ -75,12 +75,16 @@ pub fn maybe_inject_crates_ref(mut krate: ast::Crate, alt_std_name: Option P { let id_test = Ident::from_str("test"); let sp = ignored_span(cx, DUMMY_SP); let (vi, vis, ident) = if cx.is_libtest { - (ast::ItemKind::Use( - P(nospan(ast::ViewPathSimple(id_test, - path_node(vec![id_test]))))), + (ast::ItemKind::Use(P(ast::UseTree { + span: DUMMY_SP, + prefix: path_node(vec![id_test]), + kind: ast::UseTreeKind::Simple(id_test), + })), ast::Visibility::Public, keywords::Invalid.ident()) } else { (ast::ItemKind::ExternCrate(None), ast::Visibility::Inherited, id_test) @@ -547,9 +549,11 @@ fn mk_test_module(cx: &mut TestCtxt) -> (P, Option>) { // building `use = __test::main` let reexport_ident = Ident::with_empty_ctxt(s); - let use_path = - nospan(ast::ViewPathSimple(reexport_ident, - path_node(vec![mod_ident, Ident::from_str("main")]))); + let use_path = ast::UseTree { + span: DUMMY_SP, + prefix: path_node(vec![mod_ident, Ident::from_str("main")]), + kind: ast::UseTreeKind::Simple(reexport_ident), + }; expander.fold_item(P(ast::Item { id: ast::DUMMY_NODE_ID, diff --git a/src/libsyntax/util/node_count.rs b/src/libsyntax/util/node_count.rs index 0a5d0c2e7fe01..ac5642e53cf67 100644 --- a/src/libsyntax/util/node_count.rs +++ b/src/libsyntax/util/node_count.rs @@ -133,9 +133,9 @@ impl<'ast> Visitor<'ast> for NodeCounter { self.count += 1; walk_path(self, path) } - fn visit_path_list_item(&mut self, prefix: &Path, item: &PathListItem) { + fn visit_use_tree(&mut self, use_tree: &UseTree, id: NodeId, _nested: bool) { self.count += 1; - walk_path_list_item(self, prefix, item) + walk_use_tree(self, use_tree, id) } fn visit_path_parameters(&mut self, path_span: Span, path_parameters: &PathParameters) { self.count += 1; diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index c2e90f0bb13a3..9a06ed0ba0297 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -120,8 +120,8 @@ pub trait Visitor<'ast>: Sized { fn visit_path(&mut self, path: &'ast Path, _id: NodeId) { walk_path(self, path) } - fn visit_path_list_item(&mut self, prefix: &'ast Path, item: &'ast PathListItem) { - walk_path_list_item(self, prefix, item) + fn visit_use_tree(&mut self, use_tree: &'ast UseTree, id: NodeId, _nested: bool) { + walk_use_tree(self, use_tree, id) } fn visit_path_segment(&mut self, path_span: Span, path_segment: &'ast PathSegment) { walk_path_segment(self, path_span, path_segment) @@ -236,22 +236,8 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) { ItemKind::ExternCrate(opt_name) => { walk_opt_name(visitor, item.span, opt_name) } - ItemKind::Use(ref vp) => { - match vp.node { - ViewPathSimple(ident, ref path) => { - visitor.visit_ident(vp.span, ident); - visitor.visit_path(path, item.id); - } - ViewPathGlob(ref path) => { - visitor.visit_path(path, item.id); - } - ViewPathList(ref prefix, ref list) => { - visitor.visit_path(prefix, item.id); - for item in list { - visitor.visit_path_list_item(prefix, item) - } - } - } + ItemKind::Use(ref use_tree) => { + visitor.visit_use_tree(use_tree, item.id, false) } ItemKind::Static(ref typ, _, ref expr) | ItemKind::Const(ref typ, ref expr) => { @@ -381,11 +367,22 @@ pub fn walk_path<'a, V: Visitor<'a>>(visitor: &mut V, path: &'a Path) { } } -pub fn walk_path_list_item<'a, V: Visitor<'a>>(visitor: &mut V, - _prefix: &Path, - item: &'a PathListItem) { - visitor.visit_ident(item.span, item.node.name); - walk_opt_ident(visitor, item.span, item.node.rename); +pub fn walk_use_tree<'a, V: Visitor<'a>>( + visitor: &mut V, use_tree: &'a UseTree, id: NodeId, +) { + visitor.visit_path(&use_tree.prefix, id); + + match use_tree.kind { + UseTreeKind::Simple(ident) => { + visitor.visit_ident(use_tree.span, ident); + } + UseTreeKind::Glob => {}, + UseTreeKind::Nested(ref use_trees) => { + for &(ref nested_tree, nested_id) in use_trees { + visitor.visit_use_tree(nested_tree, nested_id, true); + } + } + } } pub fn walk_path_segment<'a, V: Visitor<'a>>(visitor: &mut V, diff --git a/src/test/compile-fail/absolute-paths-in-nested-use-groups.rs b/src/test/compile-fail/absolute-paths-in-nested-use-groups.rs new file mode 100644 index 0000000000000..8e5ba489c565e --- /dev/null +++ b/src/test/compile-fail/absolute-paths-in-nested-use-groups.rs @@ -0,0 +1,22 @@ +// Copyright 2017 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. + +#![feature(use_nested_groups)] +#![allow(unused_imports)] + +mod foo {} + +use foo::{ + ::bar, //~ ERROR crate root in paths can only be used in start position + super::bar, //~ ERROR `super` in paths can only be used in start position + self::bar, //~ ERROR `self` in paths can only be used in start position +}; + +fn main() {} diff --git a/src/test/compile-fail/feature-gate-use_nested_groups.rs b/src/test/compile-fail/feature-gate-use_nested_groups.rs new file mode 100644 index 0000000000000..56413a999d7f7 --- /dev/null +++ b/src/test/compile-fail/feature-gate-use_nested_groups.rs @@ -0,0 +1,31 @@ +// Copyright 2017 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. + +#![allow(unused_imports, dead_code)] + +mod a { + pub enum B {} + pub enum C {} + + pub mod d { + pub enum E {} + pub enum F {} + + pub mod g { + pub enum H {} + } + } +} + +use a::{B, d::{*, g::H}}; //~ ERROR glob imports in `use` groups are experimental + //~^ ERROR nested groups in `use` are experimental + //~^^ ERROR paths in `use` groups are experimental + +fn main() {} diff --git a/src/test/run-pass/use-nested-groups.rs b/src/test/run-pass/use-nested-groups.rs new file mode 100644 index 0000000000000..74a82afd462b8 --- /dev/null +++ b/src/test/run-pass/use-nested-groups.rs @@ -0,0 +1,35 @@ +// Copyright 2017 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. + +#![feature(use_nested_groups)] + +mod a { + pub enum B {} + + pub mod d { + pub enum E {} + pub enum F {} + + pub mod g { + pub enum H {} + pub enum I {} + } + } +} + +use a::{B, d::{self, *, g::H}}; + +fn main() { + let _: B; + let _: E; + let _: F; + let _: H; + let _: d::g::I; +} diff --git a/src/test/ui/owl-import-generates-unused-import-lint.rs b/src/test/ui/owl-import-generates-unused-import-lint.rs new file mode 100644 index 0000000000000..dc30c31835299 --- /dev/null +++ b/src/test/ui/owl-import-generates-unused-import-lint.rs @@ -0,0 +1,22 @@ +// Copyright 2017 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. + +#![feature(use_nested_groups)] +#![deny(unused_imports)] + +mod foo { + pub enum Bar {} +} + +use foo::{*, *}; //~ ERROR unused import: `*` + +fn main() { + let _: Bar; +} diff --git a/src/test/ui/owl-import-generates-unused-import-lint.stderr b/src/test/ui/owl-import-generates-unused-import-lint.stderr new file mode 100644 index 0000000000000..79089b2a93c73 --- /dev/null +++ b/src/test/ui/owl-import-generates-unused-import-lint.stderr @@ -0,0 +1,14 @@ +error: unused import: `*` + --> $DIR/owl-import-generates-unused-import-lint.rs:18:14 + | +18 | use foo::{*, *}; //~ ERROR unused import: `*` + | ^ + | +note: lint level defined here + --> $DIR/owl-import-generates-unused-import-lint.rs:12:9 + | +12 | #![deny(unused_imports)] + | ^^^^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/similar-tokens.rs b/src/test/ui/similar-tokens.rs index b16d584ed4246..eb7eab9e42dd7 100644 --- a/src/test/ui/similar-tokens.rs +++ b/src/test/ui/similar-tokens.rs @@ -14,6 +14,6 @@ mod x { } // `.` is similar to `,` so list parsing should continue to closing `}` -use x::{A. B}; //~ ERROR expected one of `,` or `as`, found `.` +use x::{A. B}; //~ ERROR expected one of `,`, `::`, or `as`, found `.` fn main() {} diff --git a/src/test/ui/similar-tokens.stderr b/src/test/ui/similar-tokens.stderr index 1b9eb1d8ef547..b4968b1018ff4 100644 --- a/src/test/ui/similar-tokens.stderr +++ b/src/test/ui/similar-tokens.stderr @@ -1,8 +1,8 @@ -error: expected one of `,` or `as`, found `.` +error: expected one of `,`, `::`, or `as`, found `.` --> $DIR/similar-tokens.rs:17:10 | -17 | use x::{A. B}; //~ ERROR expected one of `,` or `as`, found `.` - | ^ expected one of `,` or `as` here +17 | use x::{A. B}; //~ ERROR expected one of `,`, `::`, or `as`, found `.` + | ^ expected one of `,`, `::`, or `as` here error: aborting due to previous error diff --git a/src/tools/toolstate.toml b/src/tools/toolstate.toml index f1684f4c5acbe..9dd420c14586d 100644 --- a/src/tools/toolstate.toml +++ b/src/tools/toolstate.toml @@ -29,7 +29,7 @@ miri = "Broken" clippy = "Broken" # ping @nrc -rls = "Testing" +rls = "Broken" # ping @nrc -rustfmt = "Testing" +rustfmt = "Broken"