Skip to content

Commit 02e40d9

Browse files
committed
Rollup merge of rust-lang#33044 - petrochenkov:prefix, r=eddyb
syntax: Parse import prefixes as paths Fixes rust-lang#10415 r? @eddyb (This partially intersects with rust-lang#33041)
2 parents 4046f39 + e7bc939 commit 02e40d9

File tree

9 files changed

+224
-134
lines changed

9 files changed

+224
-134
lines changed

src/librustc_resolve/build_reduced_graph.rs

+35-19
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ use rustc::hir::def::*;
2828
use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
2929
use rustc::ty::{self, VariantKind};
3030

31-
use syntax::ast::Name;
31+
use syntax::ast::{Name, NodeId};
3232
use syntax::attr::AttrMetaMethods;
33-
use syntax::parse::token::{special_idents, SELF_KEYWORD_NAME, SUPER_KEYWORD_NAME};
33+
use syntax::parse::token::keywords;
3434
use syntax::codemap::{Span, DUMMY_SP};
3535

3636
use rustc::hir;
@@ -100,6 +100,37 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> {
100100
block.stmts.iter().any(is_item)
101101
}
102102

103+
fn sanity_check_import(&self, view_path: &hir::ViewPath, id: NodeId) {
104+
let path = match view_path.node {
105+
ViewPathSimple(_, ref path) |
106+
ViewPathGlob (ref path) |
107+
ViewPathList(ref path, _) => path
108+
};
109+
110+
// Check for type parameters
111+
let found_param = path.segments.iter().any(|segment| {
112+
!segment.parameters.types().is_empty() ||
113+
!segment.parameters.lifetimes().is_empty() ||
114+
!segment.parameters.bindings().is_empty()
115+
});
116+
if found_param {
117+
self.session.span_err(path.span,
118+
"type or lifetime parameter is found in import path");
119+
}
120+
121+
// Checking for special identifiers in path
122+
// prevent `self` or `super` at beginning of global path
123+
if path.global && path.segments.len() > 0 {
124+
let first = path.segments[0].identifier.name;
125+
if first == keywords::Super.to_name() || first == keywords::SelfValue.to_name() {
126+
self.session.add_lint(
127+
lint::builtin::SUPER_OR_SELF_IN_GLOBAL_PATH, id, path.span,
128+
format!("expected identifier, found keyword `{}`", first)
129+
);
130+
}
131+
}
132+
}
133+
103134
/// Constructs the reduced graph for one item.
104135
fn build_reduced_graph_for_item(&mut self, item: &Item, parent_ref: &mut Module<'b>) {
105136
let parent = *parent_ref;
@@ -114,10 +145,8 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> {
114145
// Extract and intern the module part of the path. For
115146
// globs and lists, the path is found directly in the AST;
116147
// for simple paths we have to munge the path a little.
117-
let is_global;
118148
let module_path: Vec<Name> = match view_path.node {
119149
ViewPathSimple(_, ref full_path) => {
120-
is_global = full_path.global;
121150
full_path.segments
122151
.split_last()
123152
.unwrap()
@@ -129,30 +158,17 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> {
129158

130159
ViewPathGlob(ref module_ident_path) |
131160
ViewPathList(ref module_ident_path, _) => {
132-
is_global = module_ident_path.global;
133161
module_ident_path.segments
134162
.iter()
135163
.map(|seg| seg.identifier.name)
136164
.collect()
137165
}
138166
};
139167

140-
// Checking for special identifiers in path
141-
// prevent `self` or `super` at beginning of global path
142-
if is_global && (module_path.first() == Some(&SELF_KEYWORD_NAME) ||
143-
module_path.first() == Some(&SUPER_KEYWORD_NAME)) {
144-
self.session.add_lint(
145-
lint::builtin::SUPER_OR_SELF_IN_GLOBAL_PATH,
146-
item.id,
147-
item.span,
148-
format!("expected identifier, found keyword `{}`",
149-
module_path.first().unwrap().as_str()));
150-
}
168+
self.sanity_check_import(view_path, item.id);
151169

152170
// Build up the import directives.
153-
let is_prelude = item.attrs.iter().any(|attr| {
154-
attr.name() == special_idents::prelude_import.name.as_str()
155-
});
171+
let is_prelude = item.attrs.iter().any(|attr| attr.name() == "prelude_import");
156172

157173
match view_path.node {
158174
ViewPathSimple(binding, ref full_path) => {

src/libsyntax/parse/parser.rs

+63-111
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ type ItemInfo = (Ident, ItemKind, Option<Vec<Attribute> >);
8181
pub enum PathParsingMode {
8282
/// A path with no type parameters; e.g. `foo::bar::Baz`
8383
NoTypesAllowed,
84+
/// Same as `NoTypesAllowed`, but may end with `::{` or `::*`, which are left unparsed
85+
ImportPrefix,
8486
/// A path with a lifetime and type parameters, with no double colons
8587
/// before the type parameters; e.g. `foo::bar<'a>::Baz<T>`
8688
LifetimeAndTypesWithoutColons,
@@ -591,20 +593,6 @@ impl<'a> Parser<'a> {
591593
}
592594
}
593595

594-
pub fn parse_path_list_item(&mut self) -> PResult<'a, ast::PathListItem> {
595-
let lo = self.span.lo;
596-
let node = if self.eat_keyword(keywords::SelfValue) {
597-
let rename = self.parse_rename()?;
598-
ast::PathListItemKind::Mod { id: ast::DUMMY_NODE_ID, rename: rename }
599-
} else {
600-
let ident = self.parse_ident()?;
601-
let rename = self.parse_rename()?;
602-
ast::PathListItemKind::Ident { name: ident, rename: rename, id: ast::DUMMY_NODE_ID }
603-
};
604-
let hi = self.last_span.hi;
605-
Ok(spanned(lo, hi, node))
606-
}
607-
608596
/// Check if the next token is `tok`, and return `true` if so.
609597
///
610598
/// This method will automatically add `tok` to `expected_tokens` if `tok` is not
@@ -1763,8 +1751,8 @@ impl<'a> Parser<'a> {
17631751
LifetimeAndTypesWithColons => {
17641752
self.parse_path_segments_with_colons()?
17651753
}
1766-
NoTypesAllowed => {
1767-
self.parse_path_segments_without_types()?
1754+
NoTypesAllowed | ImportPrefix => {
1755+
self.parse_path_segments_without_types(mode == ImportPrefix)?
17681756
}
17691757
};
17701758
path.segments.extend(segments);
@@ -1801,8 +1789,8 @@ impl<'a> Parser<'a> {
18011789
LifetimeAndTypesWithColons => {
18021790
self.parse_path_segments_with_colons()?
18031791
}
1804-
NoTypesAllowed => {
1805-
self.parse_path_segments_without_types()?
1792+
NoTypesAllowed | ImportPrefix => {
1793+
self.parse_path_segments_without_types(mode == ImportPrefix)?
18061794
}
18071795
};
18081796

@@ -1920,7 +1908,8 @@ impl<'a> Parser<'a> {
19201908

19211909
/// Examples:
19221910
/// - `a::b::c`
1923-
pub fn parse_path_segments_without_types(&mut self) -> PResult<'a, Vec<ast::PathSegment>> {
1911+
pub fn parse_path_segments_without_types(&mut self, import_prefix: bool)
1912+
-> PResult<'a, Vec<ast::PathSegment>> {
19241913
let mut segments = Vec::new();
19251914
loop {
19261915
// First, parse an identifier.
@@ -1932,9 +1921,11 @@ impl<'a> Parser<'a> {
19321921
parameters: ast::PathParameters::none()
19331922
});
19341923

1935-
// If we do not see a `::`, stop.
1936-
if !self.eat(&token::ModSep) {
1924+
// If we do not see a `::` or see `::{`/`::*`, stop.
1925+
if !self.check(&token::ModSep) || import_prefix && self.is_import_coupler() {
19371926
return Ok(segments);
1927+
} else {
1928+
self.bump();
19381929
}
19391930
}
19401931
}
@@ -6127,106 +6118,67 @@ impl<'a> Parser<'a> {
61276118
self.parse_item_(attrs, true, false)
61286119
}
61296120

6121+
fn parse_path_list_items(&mut self) -> PResult<'a, Vec<ast::PathListItem>> {
6122+
self.parse_unspanned_seq(&token::OpenDelim(token::Brace),
6123+
&token::CloseDelim(token::Brace),
6124+
SeqSep::trailing_allowed(token::Comma), |this| {
6125+
let lo = this.span.lo;
6126+
let node = if this.eat_keyword(keywords::SelfValue) {
6127+
let rename = this.parse_rename()?;
6128+
ast::PathListItemKind::Mod { id: ast::DUMMY_NODE_ID, rename: rename }
6129+
} else {
6130+
let ident = this.parse_ident()?;
6131+
let rename = this.parse_rename()?;
6132+
ast::PathListItemKind::Ident { name: ident, rename: rename, id: ast::DUMMY_NODE_ID }
6133+
};
6134+
let hi = this.last_span.hi;
6135+
Ok(spanned(lo, hi, node))
6136+
})
6137+
}
6138+
6139+
/// `::{` or `::*`
6140+
fn is_import_coupler(&mut self) -> bool {
6141+
self.check(&token::ModSep) &&
6142+
self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace) ||
6143+
*t == token::BinOp(token::Star))
6144+
}
61306145

6131-
/// Matches view_path : MOD? non_global_path as IDENT
6132-
/// | MOD? non_global_path MOD_SEP LBRACE RBRACE
6133-
/// | MOD? non_global_path MOD_SEP LBRACE ident_seq RBRACE
6134-
/// | MOD? non_global_path MOD_SEP STAR
6135-
/// | MOD? non_global_path
6146+
/// Matches ViewPath:
6147+
/// MOD_SEP? non_global_path
6148+
/// MOD_SEP? non_global_path as IDENT
6149+
/// MOD_SEP? non_global_path MOD_SEP STAR
6150+
/// MOD_SEP? non_global_path MOD_SEP LBRACE item_seq RBRACE
6151+
/// MOD_SEP? LBRACE item_seq RBRACE
61366152
fn parse_view_path(&mut self) -> PResult<'a, P<ViewPath>> {
61376153
let lo = self.span.lo;
6138-
6139-
// Allow a leading :: because the paths are absolute either way.
6140-
// This occurs with "use $crate::..." in macros.
6141-
let is_global = self.eat(&token::ModSep);
6142-
6143-
if self.check(&token::OpenDelim(token::Brace)) {
6144-
// use {foo,bar}
6145-
let idents = self.parse_unspanned_seq(
6146-
&token::OpenDelim(token::Brace),
6147-
&token::CloseDelim(token::Brace),
6148-
SeqSep::trailing_allowed(token::Comma),
6149-
|p| p.parse_path_list_item())?;
6150-
let path = ast::Path {
6154+
if self.check(&token::OpenDelim(token::Brace)) || self.is_import_coupler() {
6155+
// `{foo, bar}` or `::{foo, bar}`
6156+
let prefix = ast::Path {
6157+
global: self.eat(&token::ModSep),
6158+
segments: Vec::new(),
61516159
span: mk_sp(lo, self.span.hi),
6152-
global: is_global,
6153-
segments: Vec::new()
61546160
};
6155-
return Ok(P(spanned(lo, self.span.hi, ViewPathList(path, idents))));
6156-
}
6157-
6158-
let first_ident = self.parse_ident()?;
6159-
let mut path = vec!(first_ident);
6160-
if let token::ModSep = self.token {
6161-
// foo::bar or foo::{a,b,c} or foo::*
6162-
while self.check(&token::ModSep) {
6161+
let items = self.parse_path_list_items()?;
6162+
Ok(P(spanned(lo, self.span.hi, ViewPathList(prefix, items))))
6163+
} else {
6164+
let prefix = self.parse_path(ImportPrefix)?;
6165+
if self.is_import_coupler() {
6166+
// `foo::bar::{a, b}` or `foo::bar::*`
61636167
self.bump();
6164-
6165-
match self.token {
6166-
token::Ident(..) => {
6167-
let ident = self.parse_ident()?;
6168-
path.push(ident);
6169-
}
6170-
6171-
// foo::bar::{a,b,c}
6172-
token::OpenDelim(token::Brace) => {
6173-
let idents = self.parse_unspanned_seq(
6174-
&token::OpenDelim(token::Brace),
6175-
&token::CloseDelim(token::Brace),
6176-
SeqSep::trailing_allowed(token::Comma),
6177-
|p| p.parse_path_list_item()
6178-
)?;
6179-
let path = ast::Path {
6180-
span: mk_sp(lo, self.span.hi),
6181-
global: is_global,
6182-
segments: path.into_iter().map(|identifier| {
6183-
ast::PathSegment {
6184-
identifier: identifier,
6185-
parameters: ast::PathParameters::none(),
6186-
}
6187-
}).collect()
6188-
};
6189-
return Ok(P(spanned(lo, self.span.hi, ViewPathList(path, idents))));
6190-
}
6191-
6192-
// foo::bar::*
6193-
token::BinOp(token::Star) => {
6168+
if self.check(&token::BinOp(token::Star)) {
61946169
self.bump();
6195-
let path = ast::Path {
6196-
span: mk_sp(lo, self.span.hi),
6197-
global: is_global,
6198-
segments: path.into_iter().map(|identifier| {
6199-
ast::PathSegment {
6200-
identifier: identifier,
6201-
parameters: ast::PathParameters::none(),
6202-
}
6203-
}).collect()
6204-
};
6205-
return Ok(P(spanned(lo, self.span.hi, ViewPathGlob(path))));
6206-
}
6207-
6208-
// fall-through for case foo::bar::;
6209-
token::Semi => {
6210-
self.span_err(self.span, "expected identifier or `{` or `*`, found `;`");
6211-
}
6212-
6213-
_ => break
6170+
Ok(P(spanned(lo, self.span.hi, ViewPathGlob(prefix))))
6171+
} else {
6172+
let items = self.parse_path_list_items()?;
6173+
Ok(P(spanned(lo, self.span.hi, ViewPathList(prefix, items))))
62146174
}
6175+
} else {
6176+
// `foo::bar` or `foo::bar as baz`
6177+
let rename = self.parse_rename()?.
6178+
unwrap_or(prefix.segments.last().unwrap().identifier);
6179+
Ok(P(spanned(lo, self.last_span.hi, ViewPathSimple(rename, prefix))))
62156180
}
62166181
}
6217-
let mut rename_to = path[path.len() - 1];
6218-
let path = ast::Path {
6219-
span: mk_sp(lo, self.last_span.hi),
6220-
global: is_global,
6221-
segments: path.into_iter().map(|identifier| {
6222-
ast::PathSegment {
6223-
identifier: identifier,
6224-
parameters: ast::PathParameters::none(),
6225-
}
6226-
}).collect()
6227-
};
6228-
rename_to = self.parse_rename()?.unwrap_or(rename_to);
6229-
Ok(P(spanned(lo, self.last_span.hi, ViewPathSimple(rename_to, path))))
62306182
}
62316183

62326184
fn parse_rename(&mut self) -> PResult<'a, Option<Ident>> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
mod a {
12+
pub mod b {
13+
pub mod c {
14+
pub struct S;
15+
pub struct Z;
16+
}
17+
}
18+
}
19+
20+
macro_rules! import {
21+
($p: path) => (use $p {S, Z}); //~ERROR expected one of `::`, `;`, or `as`, found `{`
22+
}
23+
24+
import! { a::b::c }
25+
26+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
mod a {
12+
pub mod b {
13+
pub mod c {
14+
pub struct S;
15+
pub struct Z;
16+
}
17+
}
18+
}
19+
20+
macro_rules! import {
21+
($p: path) => (use ::$p {S, Z}); //~ERROR expected identifier, found `a::b::c`
22+
}
23+
24+
import! { a::b::c }
25+
26+
fn main() {}

0 commit comments

Comments
 (0)