diff options
| author | Pietro Albini <pietro@pietroalbini.org> | 2017-09-26 23:04:00 +0200 |
|---|---|---|
| committer | Pietro Albini <pietro@pietroalbini.org> | 2017-11-30 13:10:26 +0100 |
| commit | 91ba8b42fcce0491fa8a1b95e4088bcabf9abf4a (patch) | |
| tree | 8816e095bc7a534e8d9ecbe542a5e640d35b6c2c /src/librustc_resolve | |
| parent | d6b010f98beae1591ea6e8e21008de97d6cf5be4 (diff) | |
| download | rust-91ba8b42fcce0491fa8a1b95e4088bcabf9abf4a.tar.gz rust-91ba8b42fcce0491fa8a1b95e4088bcabf9abf4a.zip | |
Implement RFC 2128 (use_nested_groups)
This commit adds support for nested groups inside `use` declarations,
such as `use foo::{bar, sub::{baz::Foo, *}};`.
Diffstat (limited to 'src/librustc_resolve')
| -rw-r--r-- | src/librustc_resolve/build_reduced_graph.rs | 270 | ||||
| -rw-r--r-- | src/librustc_resolve/check_unused.rs | 59 | ||||
| -rw-r--r-- | src/librustc_resolve/lib.rs | 40 |
3 files changed, 205 insertions, 164 deletions
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 46513a5740a..afca6ea2c07 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::<Vec<Span>>(); - - 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::<Vec<_>>(); + 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 5820acf1b90..0fb3d96cd50 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<NodeMap<Span>>, + 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 3243152527f..44db030b2b2 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) { |
