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:
parent
d6b010f98b
commit
91ba8b42fc
@ -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}};`
|
@ -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);
|
||||
|
@ -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()),
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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) => {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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";`
|
||||
|
@ -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,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
|
@ -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>> {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
22
src/test/compile-fail/absolute-paths-in-nested-use-groups.rs
Normal file
22
src/test/compile-fail/absolute-paths-in-nested-use-groups.rs
Normal 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() {}
|
31
src/test/compile-fail/feature-gate-use_nested_groups.rs
Normal file
31
src/test/compile-fail/feature-gate-use_nested_groups.rs
Normal 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() {}
|
35
src/test/run-pass/use-nested-groups.rs
Normal file
35
src/test/run-pass/use-nested-groups.rs
Normal 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;
|
||||
}
|
22
src/test/ui/owl-import-generates-unused-import-lint.rs
Normal file
22
src/test/ui/owl-import-generates-unused-import-lint.rs
Normal 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;
|
||||
}
|
14
src/test/ui/owl-import-generates-unused-import-lint.stderr
Normal file
14
src/test/ui/owl-import-generates-unused-import-lint.stderr
Normal 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
|
||||
|
@ -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() {}
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user