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;
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;
// 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<Name> = match view_path.node {
ViewPathSimple(_, ref full_path) => {
- is_global = full_path.global;
full_path.segments
.split_last()
.unwrap()
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)
}
};
- // 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) => {
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<T>`
LifetimeAndTypesWithoutColons,
}
}
- 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
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);
LifetimeAndTypesWithColons => {
self.parse_path_segments_with_colons()?
}
- NoTypesAllowed => {
- self.parse_path_segments_without_types()?
+ NoTypesAllowed | ImportPrefix => {
+ self.parse_path_segments_without_types(mode == ImportPrefix)?
}
};
/// Examples:
/// - `a::b::c`
- pub fn parse_path_segments_without_types(&mut self) -> PResult<'a, Vec<ast::PathSegment>> {
+ pub fn parse_path_segments_without_types(&mut self, import_prefix: bool)
+ -> PResult<'a, Vec<ast::PathSegment>> {
let mut segments = Vec::new();
loop {
// First, parse an identifier.
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();
}
}
}
self.parse_item_(attrs, true, false)
}
+ 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.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<ViewPath>> {
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<Ident>> {