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<Attribute>)
+                       -> 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<hir::ItemId> {
         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<hir::ItemId>) {
+        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<hir::Item> {
         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<T>;` 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<T>);
+        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::<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 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<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 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<Variant_>;
 
-#[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<Ident>,
-    pub id: NodeId,
-}
-
-pub type PathListItem = Spanned<PathListItem_>;
-
-pub type ViewPath = Spanned<ViewPath_>;
-
 #[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<PathListItem>)
+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<ViewPath>),
+    Use(P<UseTree>),
     /// 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<ast::ViewPath>) -> P<ast::Item>;
+                vis: ast::Visibility, vp: P<ast::UseTree>) -> P<ast::Item>;
     fn item_use_simple(&self, sp: Span, vis: ast::Visibility, path: ast::Path) -> P<ast::Item>;
     fn item_use_simple_(&self, sp: Span, vis: ast::Visibility,
                         ident: ast::Ident, path: ast::Path) -> P<ast::Item>;
@@ -1142,7 +1142,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
     }
 
     fn item_use(&self, sp: Span,
-                vis: ast::Visibility, vp: P<ast::ViewPath>) -> P<ast::Item> {
+                vis: ast::Visibility, vp: P<ast::UseTree>) -> P<ast::Item> {
         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<ast::Item> {
-        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<ast::Ident>, imports: &[ast::Ident]) -> P<ast::Item> {
         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<ast::Ident>) -> P<ast::Item> {
-        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<ViewPath>) -> P<ViewPath> {
-        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<T: Folder>(meta_items: Vec<MetaItem>, fld: &mut T) -
     meta_items.move_map(|x| fld.fold_meta_item(x))
 }
 
-pub fn noop_fold_view_path<T: Folder>(view_path: P<ViewPath>, fld: &mut T) -> P<ViewPath> {
-    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<T: Folder>(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<T: Folder>(attrs: Vec<Attribute>, fld: &mut T) -> Vec<Attribute> {
@@ -874,8 +862,8 @@ pub fn noop_fold_block<T: Folder>(b: P<Block>, folder: &mut T) -> P<Block> {
 pub fn noop_fold_item_kind<T: Folder>(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<ast::PathListItem>> {
-        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<ViewPath>> {
+    /// 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<Ident>> {
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<Strin
             span,
         }],
         vis: ast::Visibility::Inherited,
-        node: ast::ItemKind::Use(P(codemap::dummy_spanned(ast::ViewPathGlob(ast::Path {
-            segments: ["{{root}}", name, "prelude", "v1"].into_iter().map(|name| {
-                ast::PathSegment::from_ident(ast::Ident::from_str(name), DUMMY_SP)
-            }).collect(),
+        node: ast::ItemKind::Use(P(ast::UseTree {
+            prefix: ast::Path {
+                segments: ["{{root}}", name, "prelude", "v1"].into_iter().map(|name| {
+                    ast::PathSegment::from_ident(ast::Ident::from_str(name), DUMMY_SP)
+                }).collect(),
+                span,
+            },
+            kind: ast::UseTreeKind::Glob,
             span,
-        })))),
+        })),
         id: ast::DUMMY_NODE_ID,
         ident: keywords::Invalid.ident(),
         span,
diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs
index 5a5a1ce3777e6..a4ac5826f99c1 100644
--- a/src/libsyntax/test.rs
+++ b/src/libsyntax/test.rs
@@ -455,9 +455,11 @@ fn mk_std(cx: &TestCtxt) -> P<ast::Item> {
     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<ast::Item>, Option<P<ast::Item>>) {
         // building `use <ident> = __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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![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"