about summary refs log tree commit diff
path: root/src/librustc_resolve
diff options
context:
space:
mode:
authorPietro Albini <pietro@pietroalbini.org>2017-09-26 23:04:00 +0200
committerPietro Albini <pietro@pietroalbini.org>2017-11-30 13:10:26 +0100
commit91ba8b42fcce0491fa8a1b95e4088bcabf9abf4a (patch)
tree8816e095bc7a534e8d9ecbe542a5e640d35b6c2c /src/librustc_resolve
parentd6b010f98beae1591ea6e8e21008de97d6cf5be4 (diff)
downloadrust-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.rs270
-rw-r--r--src/librustc_resolve/check_unused.rs59
-rw-r--r--src/librustc_resolve/lib.rs40
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)
     {