Skip to content

Commit

Permalink
syntax: Parse import prefixes as paths
Browse files Browse the repository at this point in the history
  • Loading branch information
petrochenkov committed Apr 17, 2016
1 parent 6fa61b8 commit e7bc939
Show file tree
Hide file tree
Showing 9 changed files with 224 additions and 134 deletions.
54 changes: 35 additions & 19 deletions src/librustc_resolve/build_reduced_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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<Name> = match view_path.node {
ViewPathSimple(_, ref full_path) => {
is_global = full_path.global;
full_path.segments
.split_last()
.unwrap()
Expand All @@ -132,30 +161,17 @@ 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)
.collect()
}
};

// 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) => {
Expand Down
174 changes: 63 additions & 111 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ type ItemInfo = (Ident, ItemKind, Option<Vec<Attribute> >);
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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)?
}
};

Expand Down Expand Up @@ -1918,7 +1906,8 @@ impl<'a> Parser<'a> {

/// 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.
Expand All @@ -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();
}
}
}
Expand Down Expand Up @@ -6119,106 +6110,67 @@ impl<'a> Parser<'a> {
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>> {
Expand Down
26 changes: 26 additions & 0 deletions src/test/compile-fail/import-prefix-macro-1.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

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() {}
26 changes: 26 additions & 0 deletions src/test/compile-fail/import-prefix-macro-2.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

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() {}
Loading

0 comments on commit e7bc939

Please sign in to comment.