Implement RFC 2128 (use_nested_groups)

This commit adds support for nested groups inside `use` declarations,
such as `use foo::{bar, sub::{baz::Foo, *}};`.
This commit is contained in:
Pietro Albini 2017-09-26 23:04:00 +02:00
parent d6b010f98b
commit 91ba8b42fc
No known key found for this signature in database
GPG Key ID: E8C1042DD1624519
29 changed files with 960 additions and 589 deletions

View File

@ -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}};`

View File

@ -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);

View File

@ -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()),

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -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 {

View File

@ -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) {

View File

@ -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,6 +101,139 @@ impl<'a> Resolver<'a> {
}
}
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;
}
// 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;
}
}
} else {
// Disallow `self`
if source.name == keywords::SelfValue.name() {
resolve_error(self,
use_tree.span,
ResolutionError::SelfImportsOnlyAllowedWithin);
}
// 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;
}
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();
}
}
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);
}
}
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;
@ -110,136 +242,16 @@ impl<'a> Resolver<'a> {
let vis = self.resolve_visibility(&item.vis);
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()
}
ViewPathGlob(ref module_ident_path) |
ViewPathList(ref module_ident_path, _) => {
module_ident_path.segments
.iter()
.map(|seg| respan(seg.span, seg.identifier))
.collect()
}
ItemKind::Use(ref use_tree) => {
// Just an empty prefix to start out
let prefix = ast::Path {
segments: vec![],
span: use_tree.span,
};
// 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();
}
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,
);
}
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,
);
}
}
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,
);
}
}
self.build_reduced_graph_for_use_tree(
use_tree, item.id, vis, &prefix, false, item, expansion,
);
}
ItemKind::ExternCrate(as_name) => {

View File

@ -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;
}
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);
}
}
if let ast::ItemKind::Use(..) = item.node {
if item.vis == ast::Visibility::Public || item.span.source_equal(&DUMMY_SP) {
return;
}
_ => {}
}
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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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,
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum UseTreeKind {
Simple(Ident),
Glob,
Nested(Vec<(UseTree, 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 struct UseTree {
pub kind: UseTreeKind,
pub prefix: Path,
pub span: Span,
}
impl ViewPath_ {
pub fn path(&self) -> &Path {
match *self {
ViewPathSimple(_, ref path) |
ViewPathGlob (ref path) |
ViewPathList(ref path, _) => path
}
}
}
/// 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";`

View File

@ -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,
}))
}
}

View File

@ -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,

View File

@ -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))

View File

@ -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()
/// `{` 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)
}
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
}
}
/// 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;
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 {
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))
return self.unexpected();
}
} else {
// `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 {
return self.unexpected();
}
} else {
// `use path::foo;` or `use path::foo as bar;`
let rename = self.parse_rename()?.
unwrap_or(prefix.segments.last().unwrap().identifier);
UseTreeKind::Simple(rename)
}
};
Ok(UseTree {
span: lo.to(self.prev_span),
kind,
prefix,
})
}
/// `::{` 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))
}
/// 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>> {
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)
} else {
ViewPathList(prefix, self.parse_path_list_items()?)
};
Ok(P(respan(lo.to(self.span), view_path_kind)))
} 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))))
} else {
let items = self.parse_path_list_items()?;
Ok(P(respan(lo.to(self.span), ViewPathList(prefix, items))))
}
} else {
// `foo::bar` or `foo::bar as baz`
let rename = self.parse_rename()?.
unwrap_or(prefix.segments.last().unwrap().identifier);
Ok(P(respan(lo.to(self.prev_span), ViewPathSimple(rename, 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>> {

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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,

View File

@ -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() {}

View File

@ -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() {}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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() {}

View File

@ -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