diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 003450cd6fd79..b62c544778fd9 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -28,9 +28,9 @@ use rustc::hir::def::*; use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId}; use rustc::ty::{self, VariantKind}; -use syntax::ast::Name; +use syntax::ast::{Name, NodeId}; use syntax::attr::AttrMetaMethods; -use syntax::parse::token::{special_idents, SELF_KEYWORD_NAME, SUPER_KEYWORD_NAME}; +use syntax::parse::token::keywords; use syntax::codemap::{Span, DUMMY_SP}; use rustc::hir; @@ -100,6 +100,37 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { block.stmts.iter().any(is_item) } + fn sanity_check_import(&self, view_path: &hir::ViewPath, id: NodeId) { + let path = match view_path.node { + ViewPathSimple(_, ref path) | + ViewPathGlob (ref path) | + ViewPathList(ref path, _) => path + }; + + // Check for type parameters + let found_param = path.segments.iter().any(|segment| { + !segment.parameters.types().is_empty() || + !segment.parameters.lifetimes().is_empty() || + !segment.parameters.bindings().is_empty() + }); + if found_param { + self.session.span_err(path.span, + "type or lifetime parameter is found in import path"); + } + + // Checking for special identifiers in path + // prevent `self` or `super` at beginning of global path + if path.global && path.segments.len() > 0 { + let first = path.segments[0].identifier.name; + if first == keywords::Super.to_name() || first == keywords::SelfValue.to_name() { + self.session.add_lint( + lint::builtin::SUPER_OR_SELF_IN_GLOBAL_PATH, id, path.span, + format!("expected identifier, found keyword `{}`", first) + ); + } + } + } + /// Constructs the reduced graph for one item. fn build_reduced_graph_for_item(&mut self, item: &Item, parent_ref: &mut Module<'b>) { let parent = *parent_ref; @@ -117,10 +148,8 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { // 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 is_global; let module_path: Vec = match view_path.node { ViewPathSimple(_, ref full_path) => { - is_global = full_path.global; full_path.segments .split_last() .unwrap() @@ -132,7 +161,6 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { ViewPathGlob(ref module_ident_path) | ViewPathList(ref module_ident_path, _) => { - is_global = module_ident_path.global; module_ident_path.segments .iter() .map(|seg| seg.identifier.name) @@ -140,22 +168,10 @@ impl<'b, 'tcx:'b> Resolver<'b, 'tcx> { } }; - // Checking for special identifiers in path - // prevent `self` or `super` at beginning of global path - if is_global && (module_path.first() == Some(&SELF_KEYWORD_NAME) || - module_path.first() == Some(&SUPER_KEYWORD_NAME)) { - self.session.add_lint( - lint::builtin::SUPER_OR_SELF_IN_GLOBAL_PATH, - item.id, - item.span, - format!("expected identifier, found keyword `{}`", - module_path.first().unwrap().as_str())); - } + self.sanity_check_import(view_path, item.id); // Build up the import directives. - let is_prelude = item.attrs.iter().any(|attr| { - attr.name() == special_idents::prelude_import.name.as_str() - }); + let is_prelude = item.attrs.iter().any(|attr| attr.name() == "prelude_import"); match view_path.node { ViewPathSimple(binding, ref full_path) => { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f41aa835a753b..61ec268ca17b7 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -81,6 +81,8 @@ type ItemInfo = (Ident, ItemKind, Option >); pub enum PathParsingMode { /// A path with no type parameters; e.g. `foo::bar::Baz` NoTypesAllowed, + /// Same as `NoTypesAllowed`, but may end with `::{` or `::*`, which are left unparsed + ImportPrefix, /// A path with a lifetime and type parameters, with no double colons /// before the type parameters; e.g. `foo::bar<'a>::Baz` LifetimeAndTypesWithoutColons, @@ -589,20 +591,6 @@ impl<'a> Parser<'a> { } } - pub fn parse_path_list_item(&mut self) -> PResult<'a, ast::PathListItem> { - let lo = self.span.lo; - let node = if self.eat_keyword(keywords::SelfValue) { - let rename = self.parse_rename()?; - ast::PathListItemKind::Mod { id: ast::DUMMY_NODE_ID, rename: rename } - } else { - let ident = self.parse_ident()?; - let rename = self.parse_rename()?; - ast::PathListItemKind::Ident { name: ident, rename: rename, id: ast::DUMMY_NODE_ID } - }; - let hi = self.last_span.hi; - Ok(spanned(lo, hi, node)) - } - /// Check if the next token is `tok`, and return `true` if so. /// /// This method will automatically add `tok` to `expected_tokens` if `tok` is not @@ -1761,8 +1749,8 @@ impl<'a> Parser<'a> { LifetimeAndTypesWithColons => { self.parse_path_segments_with_colons()? } - NoTypesAllowed => { - self.parse_path_segments_without_types()? + NoTypesAllowed | ImportPrefix => { + self.parse_path_segments_without_types(mode == ImportPrefix)? } }; path.segments.extend(segments); @@ -1799,8 +1787,8 @@ impl<'a> Parser<'a> { LifetimeAndTypesWithColons => { self.parse_path_segments_with_colons()? } - NoTypesAllowed => { - self.parse_path_segments_without_types()? + NoTypesAllowed | ImportPrefix => { + self.parse_path_segments_without_types(mode == ImportPrefix)? } }; @@ -1918,7 +1906,8 @@ impl<'a> Parser<'a> { /// Examples: /// - `a::b::c` - pub fn parse_path_segments_without_types(&mut self) -> PResult<'a, Vec> { + pub fn parse_path_segments_without_types(&mut self, import_prefix: bool) + -> PResult<'a, Vec> { let mut segments = Vec::new(); loop { // First, parse an identifier. @@ -1930,9 +1919,11 @@ impl<'a> Parser<'a> { parameters: ast::PathParameters::none() }); - // If we do not see a `::`, stop. - if !self.eat(&token::ModSep) { + // If we do not see a `::` or see `::{`/`::*`, stop. + if !self.check(&token::ModSep) || import_prefix && self.is_import_coupler() { return Ok(segments); + } else { + self.bump(); } } } @@ -6119,106 +6110,67 @@ impl<'a> Parser<'a> { self.parse_item_(attrs, true, false) } + fn parse_path_list_items(&mut self) -> PResult<'a, Vec> { + self.parse_unspanned_seq(&token::OpenDelim(token::Brace), + &token::CloseDelim(token::Brace), + SeqSep::trailing_allowed(token::Comma), |this| { + let lo = this.span.lo; + let node = if this.eat_keyword(keywords::SelfValue) { + let rename = this.parse_rename()?; + ast::PathListItemKind::Mod { id: ast::DUMMY_NODE_ID, rename: rename } + } else { + let ident = this.parse_ident()?; + let rename = this.parse_rename()?; + ast::PathListItemKind::Ident { name: ident, rename: rename, id: ast::DUMMY_NODE_ID } + }; + let hi = this.last_span.hi; + Ok(spanned(lo, hi, node)) + }) + } + + /// `::{` 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 view_path : MOD? non_global_path as IDENT - /// | MOD? non_global_path MOD_SEP LBRACE RBRACE - /// | MOD? non_global_path MOD_SEP LBRACE ident_seq RBRACE - /// | MOD? non_global_path MOD_SEP STAR - /// | MOD? non_global_path + /// 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> { let lo = self.span.lo; - - // Allow a leading :: because the paths are absolute either way. - // This occurs with "use $crate::..." in macros. - let is_global = self.eat(&token::ModSep); - - if self.check(&token::OpenDelim(token::Brace)) { - // use {foo,bar} - let idents = self.parse_unspanned_seq( - &token::OpenDelim(token::Brace), - &token::CloseDelim(token::Brace), - SeqSep::trailing_allowed(token::Comma), - |p| p.parse_path_list_item())?; - let path = ast::Path { + if self.check(&token::OpenDelim(token::Brace)) || self.is_import_coupler() { + // `{foo, bar}` or `::{foo, bar}` + let prefix = ast::Path { + global: self.eat(&token::ModSep), + segments: Vec::new(), span: mk_sp(lo, self.span.hi), - global: is_global, - segments: Vec::new() }; - return Ok(P(spanned(lo, self.span.hi, ViewPathList(path, idents)))); - } - - let first_ident = self.parse_ident()?; - let mut path = vec!(first_ident); - if let token::ModSep = self.token { - // foo::bar or foo::{a,b,c} or foo::* - while self.check(&token::ModSep) { + let items = self.parse_path_list_items()?; + Ok(P(spanned(lo, self.span.hi, ViewPathList(prefix, items)))) + } else { + let prefix = self.parse_path(ImportPrefix)?; + if self.is_import_coupler() { + // `foo::bar::{a, b}` or `foo::bar::*` self.bump(); - - match self.token { - token::Ident(..) => { - let ident = self.parse_ident()?; - path.push(ident); - } - - // foo::bar::{a,b,c} - token::OpenDelim(token::Brace) => { - let idents = self.parse_unspanned_seq( - &token::OpenDelim(token::Brace), - &token::CloseDelim(token::Brace), - SeqSep::trailing_allowed(token::Comma), - |p| p.parse_path_list_item() - )?; - let path = ast::Path { - span: mk_sp(lo, self.span.hi), - global: is_global, - segments: path.into_iter().map(|identifier| { - ast::PathSegment { - identifier: identifier, - parameters: ast::PathParameters::none(), - } - }).collect() - }; - return Ok(P(spanned(lo, self.span.hi, ViewPathList(path, idents)))); - } - - // foo::bar::* - token::BinOp(token::Star) => { + if self.check(&token::BinOp(token::Star)) { self.bump(); - let path = ast::Path { - span: mk_sp(lo, self.span.hi), - global: is_global, - segments: path.into_iter().map(|identifier| { - ast::PathSegment { - identifier: identifier, - parameters: ast::PathParameters::none(), - } - }).collect() - }; - return Ok(P(spanned(lo, self.span.hi, ViewPathGlob(path)))); - } - - // fall-through for case foo::bar::; - token::Semi => { - self.span_err(self.span, "expected identifier or `{` or `*`, found `;`"); - } - - _ => break + Ok(P(spanned(lo, self.span.hi, ViewPathGlob(prefix)))) + } else { + let items = self.parse_path_list_items()?; + Ok(P(spanned(lo, self.span.hi, 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(spanned(lo, self.last_span.hi, ViewPathSimple(rename, prefix)))) } } - let mut rename_to = path[path.len() - 1]; - let path = ast::Path { - span: mk_sp(lo, self.last_span.hi), - global: is_global, - segments: path.into_iter().map(|identifier| { - ast::PathSegment { - identifier: identifier, - parameters: ast::PathParameters::none(), - } - }).collect() - }; - rename_to = self.parse_rename()?.unwrap_or(rename_to); - Ok(P(spanned(lo, self.last_span.hi, ViewPathSimple(rename_to, path)))) } fn parse_rename(&mut self) -> PResult<'a, Option> { diff --git a/src/test/compile-fail/import-prefix-macro-1.rs b/src/test/compile-fail/import-prefix-macro-1.rs new file mode 100644 index 0000000000000..beb15a11a9695 --- /dev/null +++ b/src/test/compile-fail/import-prefix-macro-1.rs @@ -0,0 +1,26 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod a { + pub mod b { + pub mod c { + pub struct S; + pub struct Z; + } + } +} + +macro_rules! import { + ($p: path) => (use $p {S, Z}); //~ERROR expected one of `::`, `;`, or `as`, found `{` +} + +import! { a::b::c } + +fn main() {} diff --git a/src/test/compile-fail/import-prefix-macro-2.rs b/src/test/compile-fail/import-prefix-macro-2.rs new file mode 100644 index 0000000000000..56c6273aa9a29 --- /dev/null +++ b/src/test/compile-fail/import-prefix-macro-2.rs @@ -0,0 +1,26 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod a { + pub mod b { + pub mod c { + pub struct S; + pub struct Z; + } + } +} + +macro_rules! import { + ($p: path) => (use ::$p {S, Z}); //~ERROR expected identifier, found `a::b::c` +} + +import! { a::b::c } + +fn main() {} diff --git a/src/test/compile-fail/import-ty-params.rs b/src/test/compile-fail/import-ty-params.rs new file mode 100644 index 0000000000000..66d4d6d06211d --- /dev/null +++ b/src/test/compile-fail/import-ty-params.rs @@ -0,0 +1,25 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod a { + pub mod b { + pub mod c { + pub struct S(T); + } + } +} + +macro_rules! import { + ($p: path) => (use $p;); +} + +import! { a::b::c::S } //~ERROR type or lifetime parameter is found in import path + +fn main() {} diff --git a/src/test/compile-fail/self_type_keyword-2.rs b/src/test/compile-fail/self_type_keyword-2.rs new file mode 100644 index 0000000000000..613f54eb33134 --- /dev/null +++ b/src/test/compile-fail/self_type_keyword-2.rs @@ -0,0 +1,13 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use self::Self as Foo; //~ ERROR unresolved import `self::Self` + +pub fn main() {} diff --git a/src/test/compile-fail/self_type_keyword.rs b/src/test/compile-fail/self_type_keyword.rs index 6296673787407..b28f48bb1056e 100644 --- a/src/test/compile-fail/self_type_keyword.rs +++ b/src/test/compile-fail/self_type_keyword.rs @@ -39,9 +39,6 @@ pub fn main() { } } -use self::Self as Foo; -//~^ ERROR expected identifier, found keyword `Self` - use std::option::Option as Self; //~^ ERROR expected identifier, found keyword `Self` diff --git a/src/test/parse-fail/use-ends-with-mod-sep.rs b/src/test/parse-fail/use-ends-with-mod-sep.rs index 143886e23377f..e6a10d43e2994 100644 --- a/src/test/parse-fail/use-ends-with-mod-sep.rs +++ b/src/test/parse-fail/use-ends-with-mod-sep.rs @@ -10,4 +10,4 @@ // compile-flags: -Z parse-only -use std::any::; //~ ERROR expected identifier or `{` or `*`, found `;` +use std::any::; //~ ERROR expected identifier, found `;` diff --git a/src/test/run-pass/import-prefix-macro.rs b/src/test/run-pass/import-prefix-macro.rs new file mode 100644 index 0000000000000..cfe4ff78e6258 --- /dev/null +++ b/src/test/run-pass/import-prefix-macro.rs @@ -0,0 +1,35 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod a { + pub mod b { + pub mod c { + pub struct S; + pub struct Z; + } + pub struct W; + } +} + +macro_rules! import { + (1 $p: path) => (use $p;); + (2 $p: path) => (use $p::{Z};); + (3 $p: path) => (use $p::*;); +} + +import! { 1 a::b::c::S } +import! { 2 a::b::c } +import! { 3 a::b } + +fn main() { + let s = S; + let z = Z; + let w = W; +}