Skip to content

Commit 804b15b

Browse files
committed
Auto merge of #45846 - pietroalbini:use-nested-groups, r=petrochenkov
Add nested groups in imports This PR adds support for nested groups in imports (rust-lang/rfcs#2128, tracking issue #44494). r? @petrochenkov
2 parents d8a60c9 + f7f6951 commit 804b15b

30 files changed

+949
-578
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# `use_nested_groups`
2+
3+
The tracking issue for this feature is: [#44494]
4+
5+
[#44494]: https://github.com/rust-lang/rust/issues/44494
6+
7+
------------------------
8+
9+
The `use_nested_groups` feature allows you to import multiple items from a
10+
complex module tree easily, by nesting different imports in the same
11+
declaration. For example:
12+
13+
```rust
14+
#![feature(use_nested_groups)]
15+
# #![allow(unused_imports, dead_code)]
16+
#
17+
# mod foo {
18+
# pub mod bar {
19+
# pub type Foo = ();
20+
# }
21+
# pub mod baz {
22+
# pub mod quux {
23+
# pub type Bar = ();
24+
# }
25+
# }
26+
# }
27+
28+
use foo::{
29+
bar::{self, Foo},
30+
baz::{*, quux::Bar},
31+
};
32+
#
33+
# fn main() {}
34+
```
35+
36+
## Snippet for the book's new features appendix
37+
38+
When stabilizing, add this to
39+
`src/doc/book/second-edition/src/appendix-07-newest-features.md`:
40+
41+
### Nested groups in `use` declarations
42+
43+
If you have a complex module tree with many different submodules and you need
44+
to import a few items from each one, it might be useful to group all the
45+
imports in the same declaration to keep your code clean and avoid repeating the
46+
base modules' name.
47+
48+
The `use` declaration supports nesting to help you in those cases, both with
49+
simple imports and glob ones. For example this snippets imports `bar`, `Foo`,
50+
all the items in `baz` and `Bar`:
51+
52+
```rust
53+
# #![feature(use_nested_groups)]
54+
# #![allow(unused_imports, dead_code)]
55+
#
56+
# mod foo {
57+
# pub mod bar {
58+
# pub type Foo = ();
59+
# }
60+
# pub mod baz {
61+
# pub mod quux {
62+
# pub type Bar = ();
63+
# }
64+
# }
65+
# }
66+
#
67+
use foo::{
68+
bar::{self, Foo},
69+
baz::{*, quux::Bar},
70+
};
71+
#
72+
# fn main() {}
73+
```
74+
75+
## Updated reference
76+
77+
When stabilizing, replace the shortcut list in
78+
`src/doc/reference/src/items/use-declarations.md` with this updated one:
79+
80+
* Simultaneously binding a list of paths with a common prefix, using the
81+
glob-like brace syntax `use a::b::{c, d, e::f, g::h::i};`
82+
* Simultaneously binding a list of paths with a common prefix and their common
83+
parent module, using the `self` keyword, such as `use a::b::{self, c, d::e};`
84+
* Rebinding the target name as a new local name, using the syntax `use p::q::r
85+
as x;`. This can also be used with the last two features:
86+
`use a::b::{self as ab, c as abc}`.
87+
* Binding all paths matching a given prefix, using the asterisk wildcard syntax
88+
`use a::b::*;`.
89+
* Nesting groups of the previous features multiple times, such as
90+
`use a::b::{self as ab, c d::{*, e::f}};`

src/librustc/hir/lowering.rs

+130-78
Original file line numberDiff line numberDiff line change
@@ -1768,80 +1768,14 @@ impl<'a> LoweringContext<'a> {
17681768
-> hir::Item_ {
17691769
match *i {
17701770
ItemKind::ExternCrate(string) => hir::ItemExternCrate(string),
1771-
ItemKind::Use(ref view_path) => {
1772-
let path = match view_path.node {
1773-
ViewPathSimple(_, ref path) => path,
1774-
ViewPathGlob(ref path) => path,
1775-
ViewPathList(ref path, ref path_list_idents) => {
1776-
for &Spanned { node: ref import, span } in path_list_idents {
1777-
// `use a::{self as x, b as y};` lowers to
1778-
// `use a as x; use a::b as y;`
1779-
let mut ident = import.name;
1780-
let suffix = if ident.name == keywords::SelfValue.name() {
1781-
if let Some(last) = path.segments.last() {
1782-
ident = last.identifier;
1783-
}
1784-
None
1785-
} else {
1786-
Some(ident.name)
1787-
};
1788-
1789-
let mut path = self.lower_path_extra(import.id, path, suffix,
1790-
ParamMode::Explicit, true);
1791-
path.span = span;
1792-
1793-
self.allocate_hir_id_counter(import.id, import);
1794-
let LoweredNodeId {
1795-
node_id: import_node_id,
1796-
hir_id: import_hir_id,
1797-
} = self.lower_node_id(import.id);
1798-
1799-
self.with_hir_id_owner(import_node_id, |this| {
1800-
let vis = match *vis {
1801-
hir::Visibility::Public => hir::Visibility::Public,
1802-
hir::Visibility::Crate => hir::Visibility::Crate,
1803-
hir::Visibility::Inherited => hir::Visibility::Inherited,
1804-
hir::Visibility::Restricted { ref path, id: _ } => {
1805-
hir::Visibility::Restricted {
1806-
path: path.clone(),
1807-
// We are allocating a new NodeId here
1808-
id: this.next_id().node_id,
1809-
}
1810-
}
1811-
};
1812-
1813-
this.items.insert(import_node_id, hir::Item {
1814-
id: import_node_id,
1815-
hir_id: import_hir_id,
1816-
name: import.rename.unwrap_or(ident).name,
1817-
attrs: attrs.clone(),
1818-
node: hir::ItemUse(P(path), hir::UseKind::Single),
1819-
vis,
1820-
span,
1821-
});
1822-
});
1823-
}
1824-
path
1825-
}
1771+
ItemKind::Use(ref use_tree) => {
1772+
// Start with an empty prefix
1773+
let prefix = Path {
1774+
segments: vec![],
1775+
span: use_tree.span,
18261776
};
1827-
let path = P(self.lower_path(id, path, ParamMode::Explicit, true));
1828-
let kind = match view_path.node {
1829-
ViewPathSimple(ident, _) => {
1830-
*name = ident.name;
1831-
hir::UseKind::Single
1832-
}
1833-
ViewPathGlob(_) => {
1834-
hir::UseKind::Glob
1835-
}
1836-
ViewPathList(..) => {
1837-
// Privatize the degenerate import base, used only to check
1838-
// the stability of `use a::{};`, to avoid it showing up as
1839-
// a reexport by accident when `pub`, e.g. in documentation.
1840-
*vis = hir::Inherited;
1841-
hir::UseKind::ListStem
1842-
}
1843-
};
1844-
hir::ItemUse(path, kind)
1777+
1778+
self.lower_use_tree(use_tree, &prefix, id, vis, name, attrs)
18451779
}
18461780
ItemKind::Static(ref t, m, ref e) => {
18471781
let value = self.lower_body(None, |this| this.lower_expr(e));
@@ -1963,6 +1897,112 @@ impl<'a> LoweringContext<'a> {
19631897
// not cause an assertion failure inside the `lower_defaultness` function
19641898
}
19651899

1900+
fn lower_use_tree(&mut self,
1901+
tree: &UseTree,
1902+
prefix: &Path,
1903+
id: NodeId,
1904+
vis: &mut hir::Visibility,
1905+
name: &mut Name,
1906+
attrs: &hir::HirVec<Attribute>)
1907+
-> hir::Item_ {
1908+
let path = &tree.prefix;
1909+
1910+
match tree.kind {
1911+
UseTreeKind::Simple(ident) => {
1912+
*name = ident.name;
1913+
1914+
// First apply the prefix to the path
1915+
let mut path = Path {
1916+
segments: prefix.segments
1917+
.iter()
1918+
.chain(path.segments.iter())
1919+
.cloned()
1920+
.collect(),
1921+
span: path.span.to(prefix.span),
1922+
};
1923+
1924+
// Correctly resolve `self` imports
1925+
if path.segments.last().unwrap().identifier.name == keywords::SelfValue.name() {
1926+
let _ = path.segments.pop();
1927+
if ident.name == keywords::SelfValue.name() {
1928+
*name = path.segments.last().unwrap().identifier.name;
1929+
}
1930+
}
1931+
1932+
let path = P(self.lower_path(id, &path, ParamMode::Explicit, true));
1933+
hir::ItemUse(path, hir::UseKind::Single)
1934+
}
1935+
UseTreeKind::Glob => {
1936+
let path = P(self.lower_path(id, &Path {
1937+
segments: prefix.segments
1938+
.iter()
1939+
.chain(path.segments.iter())
1940+
.cloned()
1941+
.collect(),
1942+
span: path.span,
1943+
}, ParamMode::Explicit, true));
1944+
hir::ItemUse(path, hir::UseKind::Glob)
1945+
}
1946+
UseTreeKind::Nested(ref trees) => {
1947+
let prefix = Path {
1948+
segments: prefix.segments
1949+
.iter()
1950+
.chain(path.segments.iter())
1951+
.cloned()
1952+
.collect(),
1953+
span: prefix.span.to(path.span),
1954+
};
1955+
1956+
// Add all the nested PathListItems in the HIR
1957+
for &(ref use_tree, id) in trees {
1958+
self.allocate_hir_id_counter(id, &use_tree);
1959+
let LoweredNodeId {
1960+
node_id: new_id,
1961+
hir_id: new_hir_id,
1962+
} = self.lower_node_id(id);
1963+
1964+
let mut vis = vis.clone();
1965+
let mut name = name.clone();
1966+
let item = self.lower_use_tree(
1967+
use_tree, &prefix, new_id, &mut vis, &mut name, &attrs,
1968+
);
1969+
1970+
self.with_hir_id_owner(new_id, |this| {
1971+
let vis = match vis {
1972+
hir::Visibility::Public => hir::Visibility::Public,
1973+
hir::Visibility::Crate => hir::Visibility::Crate,
1974+
hir::Visibility::Inherited => hir::Visibility::Inherited,
1975+
hir::Visibility::Restricted { ref path, id: _ } => {
1976+
hir::Visibility::Restricted {
1977+
path: path.clone(),
1978+
// We are allocating a new NodeId here
1979+
id: this.next_id().node_id,
1980+
}
1981+
}
1982+
};
1983+
1984+
this.items.insert(new_id, hir::Item {
1985+
id: new_id,
1986+
hir_id: new_hir_id,
1987+
name: name,
1988+
attrs: attrs.clone(),
1989+
node: item,
1990+
vis,
1991+
span: use_tree.span,
1992+
});
1993+
});
1994+
}
1995+
1996+
// Privatize the degenerate import base, used only to check
1997+
// the stability of `use a::{};`, to avoid it showing up as
1998+
// a reexport by accident when `pub`, e.g. in documentation.
1999+
let path = P(self.lower_path(id, &prefix, ParamMode::Explicit, true));
2000+
*vis = hir::Inherited;
2001+
hir::ItemUse(path, hir::UseKind::ListStem)
2002+
}
2003+
}
2004+
}
2005+
19662006
fn lower_trait_item(&mut self, i: &TraitItem) -> hir::TraitItem {
19672007
self.with_parent_def(i.id, |this| {
19682008
let LoweredNodeId { node_id, hir_id } = this.lower_node_id(i.id);
@@ -2129,18 +2169,30 @@ impl<'a> LoweringContext<'a> {
21292169

21302170
fn lower_item_id(&mut self, i: &Item) -> SmallVector<hir::ItemId> {
21312171
match i.node {
2132-
ItemKind::Use(ref view_path) => {
2133-
if let ViewPathList(_, ref imports) = view_path.node {
2134-
return iter::once(i.id).chain(imports.iter().map(|import| import.node.id))
2135-
.map(|id| hir::ItemId { id: id }).collect();
2136-
}
2172+
ItemKind::Use(ref use_tree) => {
2173+
let mut vec = SmallVector::one(hir::ItemId { id: i.id });
2174+
self.lower_item_id_use_tree(use_tree, &mut vec);
2175+
return vec;
21372176
}
21382177
ItemKind::MacroDef(..) => return SmallVector::new(),
21392178
_ => {}
21402179
}
21412180
SmallVector::one(hir::ItemId { id: i.id })
21422181
}
21432182

2183+
fn lower_item_id_use_tree(&self, tree: &UseTree, vec: &mut SmallVector<hir::ItemId>) {
2184+
match tree.kind {
2185+
UseTreeKind::Nested(ref nested_vec) => {
2186+
for &(ref nested, id) in nested_vec {
2187+
vec.push(hir::ItemId { id, });
2188+
self.lower_item_id_use_tree(nested, vec);
2189+
}
2190+
}
2191+
UseTreeKind::Glob => {}
2192+
UseTreeKind::Simple(..) => {}
2193+
}
2194+
}
2195+
21442196
pub fn lower_item(&mut self, i: &Item) -> Option<hir::Item> {
21452197
let mut name = i.ident.name;
21462198
let mut vis = self.lower_visibility(&i.vis, None);

src/librustc/hir/map/def_collector.rs

+7-15
Original file line numberDiff line numberDiff line change
@@ -118,21 +118,8 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
118118
ItemKind::MacroDef(..) => DefPathData::MacroDef(i.ident.name.as_str()),
119119
ItemKind::Mac(..) => return self.visit_macro_invoc(i.id, false),
120120
ItemKind::GlobalAsm(..) => DefPathData::Misc,
121-
ItemKind::Use(ref view_path) => {
122-
match view_path.node {
123-
ViewPathGlob(..) => {}
124-
125-
// FIXME(eddyb) Should use the real name. Which namespace?
126-
ViewPathSimple(..) => {}
127-
ViewPathList(_, ref imports) => {
128-
for import in imports {
129-
self.create_def(import.node.id,
130-
DefPathData::Misc,
131-
ITEM_LIKE_SPACE);
132-
}
133-
}
134-
}
135-
DefPathData::Misc
121+
ItemKind::Use(..) => {
122+
return visit::walk_item(self, i);
136123
}
137124
};
138125
let def = self.create_def(i.id, def_data, ITEM_LIKE_SPACE);
@@ -180,6 +167,11 @@ impl<'a> visit::Visitor<'a> for DefCollector<'a> {
180167
});
181168
}
182169

170+
fn visit_use_tree(&mut self, use_tree: &'a UseTree, id: NodeId, _nested: bool) {
171+
self.create_def(id, DefPathData::Misc, ITEM_LIKE_SPACE);
172+
visit::walk_use_tree(self, use_tree, id);
173+
}
174+
183175
fn visit_foreign_item(&mut self, foreign_item: &'a ForeignItem) {
184176
let def = self.create_def(foreign_item.id,
185177
DefPathData::ValueNs(foreign_item.ident.name.as_str()),

src/librustc/lint/context.rs

-6
Original file line numberDiff line numberDiff line change
@@ -981,12 +981,6 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContext<'a> {
981981
ast_visit::walk_path(self, p);
982982
}
983983

984-
fn visit_path_list_item(&mut self, prefix: &'a ast::Path, item: &'a ast::PathListItem) {
985-
run_lints!(self, check_path_list_item, early_passes, item);
986-
self.check_id(item.node.id);
987-
ast_visit::walk_path_list_item(self, prefix, item);
988-
}
989-
990984
fn visit_attribute(&mut self, attr: &'a ast::Attribute) {
991985
run_lints!(self, check_attribute, early_passes, attr);
992986
}

src/librustc/lint/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,6 @@ pub trait EarlyLintPass: LintPass {
248248
fn check_lifetime(&mut self, _: &EarlyContext, _: &ast::Lifetime) { }
249249
fn check_lifetime_def(&mut self, _: &EarlyContext, _: &ast::LifetimeDef) { }
250250
fn check_path(&mut self, _: &EarlyContext, _: &ast::Path, _: ast::NodeId) { }
251-
fn check_path_list_item(&mut self, _: &EarlyContext, _: &ast::PathListItem) { }
252251
fn check_attribute(&mut self, _: &EarlyContext, _: &ast::Attribute) { }
253252

254253
/// Called when entering a syntax node that can have lint attributes such

0 commit comments

Comments
 (0)