Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

syntax: Parse import prefixes as paths #33044

Merged
merged 1 commit into from
Apr 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used anymore at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed all existing uses in #33041, but a new one is introduced in #32875
Merging these two variants into one with ImportPrefix's semantics does no harm, I guess. I'll do it as a part of #33041

/// 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