Simplify parsing of paths

This commit is contained in:
Vadim Petrochenkov 2017-07-20 02:39:34 +03:00
parent 5cc1baa290
commit da77a1a0ac
6 changed files with 152 additions and 255 deletions

View File

@ -153,6 +153,19 @@ pub enum PathParameters {
Parenthesized(ParenthesizedParameterData),
}
impl PathParameters {
pub fn span(&self, fallback: Span) -> Span {
match *self {
AngleBracketed(ref data) => {
data.lifetimes.get(0).map(|x| x.span).or_else(||
data.types.get(0).map(|x| x.span)).or_else(||
data.bindings.get(0).map(|x| x.span)).unwrap_or(fallback)
}
Parenthesized(ref data) => data.span
}
}
}
/// A path like `Foo<'a, T>`
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Default)]
pub struct AngleBracketedParameterData {
@ -173,6 +186,12 @@ impl Into<Option<P<PathParameters>>> for AngleBracketedParameterData {
}
}
impl Into<Option<P<PathParameters>>> for ParenthesizedParameterData {
fn into(self) -> Option<P<PathParameters>> {
Some(P(PathParameters::Parenthesized(self)))
}
}
/// A path like `Foo(A,B) -> C`
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub struct ParenthesizedParameterData {

View File

@ -9,7 +9,7 @@
// except according to those terms.
use abi::{self, Abi};
use ast::{AngleBracketedParameterData, AttrStyle, BareFnTy};
use ast::{AngleBracketedParameterData, ParenthesizedParameterData, AttrStyle, BareFnTy};
use ast::{RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
use ast::Unsafety;
use ast::{Mod, Arg, Arm, Attribute, BindingMode, TraitItemKind};
@ -72,19 +72,27 @@ bitflags! {
type ItemInfo = (Ident, ItemKind, Option<Vec<Attribute> >);
/// How to parse a path. There are three different kinds of paths, all of which
/// are parsed somewhat differently.
/// How to parse a path.
#[derive(Copy, Clone, PartialEq)]
pub enum PathStyle {
/// A path with no type parameters, e.g. `foo::bar::Baz`, used in imports or visibilities.
Mod,
/// A path with a lifetime and type parameters, with no double colons
/// before the type parameters; e.g. `foo::bar<'a>::Baz<T>`, used in types.
/// Paths using this style can be passed into macros expecting `path` nonterminals.
Type,
/// A path with a lifetime and type parameters with double colons before
/// the type parameters; e.g. `foo::bar::<'a>::Baz::<T>`, used in expressions or patterns.
/// In some contexts, notably in expressions, paths with generic arguments are ambiguous
/// with something else. For example, in expressions `segment < ....` can be interpreted
/// as a comparison and `segment ( ....` can be interpreted as a function call.
/// In all such contexts the non-path interpretation is preferred by default for practical
/// reasons, but the path interpretation can be forced by the disambiguator `::`, e.g.
/// `x<y>` - comparisons, `x::<y>` - unambiguously a path.
Expr,
/// In other contexts, notably in types, no ambiguity exists and paths can be written
/// without the disambiguator, e.g. `x<y>` - unambiguously a path.
/// Paths with disambiguators are rejected for now, but may be allowed in the future.
Type,
/// A path with generic arguments disallowed, e.g. `foo::bar::Baz`, used in imports,
/// visibilities or attributes.
/// Technically, this variant is unnecessary and e.g. `Expr` can be used instead
/// (paths in "mod" contexts have to be checked later for absence of generic arguments
/// anyway, due to macros), but it is used to avoid weird suggestions about expected
/// tokens when something goes wrong.
Mod,
}
#[derive(Clone, Copy, Debug, PartialEq)]
@ -1397,7 +1405,7 @@ impl<'a> Parser<'a> {
TyKind::Infer
} else if self.eat_lt() {
// Qualified path
let (qself, path) = self.parse_qualified_path(PathStyle::Type)?;
let (qself, path) = self.parse_qpath(PathStyle::Type)?;
TyKind::Path(Some(qself), path)
} else if self.token.is_path_start() {
// Simple path
@ -1683,108 +1691,58 @@ impl<'a> Parser<'a> {
}
/// Parses qualified path.
///
/// Assumes that the leading `<` has been parsed already.
///
/// Qualifed paths are a part of the universal function call
/// syntax (UFCS).
///
/// `qualified_path = <type [as trait_ref]>::path`
///
/// See `parse_path` for `mode` meaning.
///
/// # Examples:
///
/// # Examples
/// `<T as U>::a`
/// `<T as U>::F::a::<S>`
pub fn parse_qualified_path(&mut self, mode: PathStyle)
-> PResult<'a, (QSelf, ast::Path)> {
let span = self.prev_span;
let self_type = self.parse_ty()?;
/// `<T as U>::F::a<S>` (without disambiguator)
/// `<T as U>::F::a::<S>` (with disambiguator)
fn parse_qpath(&mut self, style: PathStyle) -> PResult<'a, (QSelf, ast::Path)> {
let lo = self.prev_span;
let ty = self.parse_ty()?;
let mut path = if self.eat_keyword(keywords::As) {
self.parse_path(PathStyle::Type)?
} else {
ast::Path {
span: span,
segments: vec![]
}
ast::Path { segments: Vec::new(), span: syntax_pos::DUMMY_SP }
};
let qself = QSelf {
ty: self_type,
position: path.segments.len()
};
self.expect(&token::Gt)?;
self.expect(&token::ModSep)?;
let segments = match mode {
PathStyle::Type => {
self.parse_path_segments_without_colons(true)?
}
PathStyle::Expr => {
self.parse_path_segments_with_colons()?
}
PathStyle::Mod => {
self.parse_path_segments_without_types()?
}
};
path.segments.extend(segments);
let qself = QSelf { ty, position: path.segments.len() };
self.parse_path_segments(&mut path.segments, style)?;
path.span.hi = self.prev_span.hi;
Ok((qself, path))
Ok((qself, ast::Path { segments: path.segments, span: lo.to(self.prev_span) }))
}
/// Parses a path and optional type parameter bounds, depending on the
/// mode. The `mode` parameter determines whether lifetimes, types, and/or
/// bounds are permitted and whether `::` must precede type parameter
/// groups.
pub fn parse_path(&mut self, mode: PathStyle) -> PResult<'a, ast::Path> {
self.parse_path_common(mode, true)
}
pub fn parse_path_without_generics(&mut self, mode: PathStyle) -> PResult<'a, ast::Path> {
self.parse_path_common(mode, false)
}
fn parse_path_common(&mut self, mode: PathStyle, parse_generics: bool)
-> PResult<'a, ast::Path>
/// Parses simple paths.
///
/// `path = [::] segment+`
/// `segment = ident | ident[::]<args> | ident[::](args) [-> type]`
///
/// # Examples
/// `a::b::C<D>` (without disambiguator)
/// `a::b::C::<D>` (with disambiguator)
/// `Fn(Args)` (without disambiguator)
/// `Fn::(Args)` (with disambiguator)
pub fn parse_path(&mut self, style: PathStyle) -> PResult<'a, ast::Path>
{
maybe_whole!(self, NtPath, |x| x);
let lo = self.meta_var_span.unwrap_or(self.span);
let is_global = self.eat(&token::ModSep);
// Parse any number of segments and bound sets. A segment is an
// identifier followed by an optional lifetime and a set of types.
// A bound set is a set of type parameter bounds.
let mut segments = match mode {
PathStyle::Type => {
self.parse_path_segments_without_colons(parse_generics)?
}
PathStyle::Expr => {
self.parse_path_segments_with_colons()?
}
PathStyle::Mod => {
self.parse_path_segments_without_types()?
}
};
if is_global {
segments.insert(0, PathSegment::crate_root(lo));
let mut segments = Vec::new();
if self.eat(&token::ModSep) {
segments.push(PathSegment::crate_root(lo));
}
self.parse_path_segments(&mut segments, style)?;
// Assemble the result.
Ok(ast::Path {
span: lo.to(self.prev_span),
segments: segments,
})
Ok(ast::Path { segments, span: lo.to(self.prev_span) })
}
/// Like `parse_path`, but also supports parsing `Word` meta items into paths for back-compat.
/// This is used when parsing derive macro paths in `#[derive]` attributes.
pub fn parse_path_allowing_meta(&mut self, mode: PathStyle) -> PResult<'a, ast::Path> {
pub fn parse_path_allowing_meta(&mut self, style: PathStyle) -> PResult<'a, ast::Path> {
let meta_ident = match self.token {
token::Interpolated(ref nt) => match nt.0 {
token::NtMeta(ref meta) => match meta.node {
@ -1799,134 +1757,79 @@ impl<'a> Parser<'a> {
self.bump();
return Ok(ast::Path::from_ident(self.prev_span, ident));
}
self.parse_path(mode)
self.parse_path(style)
}
/// Examples:
/// - `a::b<T,U>::c<V,W>`
/// - `a::b<T,U>::c(V) -> W`
/// - `a::b<T,U>::c(V)`
pub fn parse_path_segments_without_colons(&mut self, parse_generics: bool)
-> PResult<'a, Vec<PathSegment>>
{
let mut segments = Vec::new();
fn parse_path_segments(&mut self, segments: &mut Vec<PathSegment>, style: PathStyle)
-> PResult<'a, ()> {
loop {
// First, parse an identifier.
let ident_span = self.span;
let identifier = self.parse_path_segment_ident()?;
segments.push(self.parse_path_segment(style)?);
if self.check(&token::ModSep) && self.look_ahead(1, |t| *t == token::Lt) {
self.bump();
let prev_span = self.prev_span;
if self.is_import_coupler() || !self.eat(&token::ModSep) {
return Ok(());
}
}
}
let mut err = self.diagnostic().struct_span_err(prev_span,
"unexpected token: `::`");
err.help(
"use `<...>` instead of `::<...>` if you meant to specify type arguments");
err.emit();
fn parse_path_segment(&mut self, style: PathStyle) -> PResult<'a, PathSegment> {
let ident_span = self.span;
let ident = self.parse_path_segment_ident()?;
let is_args_start = |token: &token::Token| match *token {
token::Lt | token::BinOp(token::Shl) | token::OpenDelim(token::Paren) => true,
_ => false,
};
let check_args_start = |this: &mut Self| {
this.expected_tokens.extend_from_slice(
&[TokenType::Token(token::Lt), TokenType::Token(token::OpenDelim(token::Paren))]
);
is_args_start(&this.token)
};
Ok(if style == PathStyle::Type && check_args_start(self) ||
style != PathStyle::Mod && self.check(&token::ModSep)
&& self.look_ahead(1, |t| is_args_start(t)) {
// Generic arguments are found - `<`, `(`, `::<` or `::(`.
let lo = self.span;
if self.eat(&token::ModSep) {
// These errors are not strictly necessary and may be removed in the future.
if style == PathStyle::Type {
let mut err = self.diagnostic().struct_span_err(self.prev_span,
"unnecessary path disambiguator");
err.span_label(self.prev_span, "try removing `::`");
err.emit();
} else if self.token == token::OpenDelim(token::Paren) {
self.diagnostic().span_err(self.prev_span,
"`::` is not supported before parenthesized generic arguments")
}
}
// Parse types, optionally.
let parameters = if parse_generics && self.eat_lt() {
let parameters = if self.eat_lt() {
// `<'a, T, A = U>`
let (lifetimes, types, bindings) = self.parse_generic_args()?;
self.expect_gt()?;
let _span = lo.to(self.prev_span);
AngleBracketedParameterData { lifetimes, types, bindings }.into()
} else if self.eat(&token::OpenDelim(token::Paren)) {
let lo = self.prev_span;
let inputs = self.parse_seq_to_end(
&token::CloseDelim(token::Paren),
SeqSep::trailing_allowed(token::Comma),
|p| p.parse_ty())?;
let output_ty = if self.eat(&token::RArrow) {
} else {
// `(T, U) -> R`
self.bump(); // `(`
let inputs = self.parse_seq_to_end(&token::CloseDelim(token::Paren),
SeqSep::trailing_allowed(token::Comma),
|p| p.parse_ty())?;
let output = if self.eat(&token::RArrow) {
Some(self.parse_ty_no_plus()?)
} else {
None
};
let hi = self.prev_span;
Some(P(ast::PathParameters::Parenthesized(ast::ParenthesizedParameterData {
span: lo.to(hi),
inputs: inputs,
output: output_ty,
})))
} else {
None
let span = lo.to(self.prev_span);
ParenthesizedParameterData { inputs, output, span }.into()
};
// Assemble and push the result.
segments.push(PathSegment {
identifier: identifier,
span: ident_span,
parameters: parameters
});
// Continue only if we see a `::`
if !self.eat(&token::ModSep) {
return Ok(segments);
}
}
}
/// Examples:
/// - `a::b::<T,U>::c`
pub fn parse_path_segments_with_colons(&mut self) -> PResult<'a, Vec<PathSegment>> {
let mut segments = Vec::new();
loop {
// First, parse an identifier.
let ident_span = self.span;
let identifier = self.parse_path_segment_ident()?;
// If we do not see a `::`, stop.
if !self.eat(&token::ModSep) {
segments.push(PathSegment::from_ident(identifier, ident_span));
return Ok(segments);
}
// Check for a type segment.
if self.eat_lt() {
// Consumed `a::b::<`, go look for types
let (lifetimes, types, bindings) = self.parse_generic_args()?;
self.expect_gt()?;
segments.push(PathSegment {
identifier: identifier,
span: ident_span,
parameters: AngleBracketedParameterData { lifetimes, types, bindings }.into(),
});
// Consumed `a::b::<T,U>`, check for `::` before proceeding
if !self.eat(&token::ModSep) {
return Ok(segments);
}
} else {
// Consumed `a::`, go look for `b`
segments.push(PathSegment::from_ident(identifier, ident_span));
}
}
}
/// Examples:
/// - `a::b::c`
pub fn parse_path_segments_without_types(&mut self)
-> PResult<'a, Vec<PathSegment>> {
let mut segments = Vec::new();
loop {
// First, parse an identifier.
let ident_span = self.span;
let identifier = self.parse_path_segment_ident()?;
// Assemble and push the result.
segments.push(PathSegment::from_ident(identifier, ident_span));
// If we do not see a `::` or see `::{`/`::*`, stop.
if !self.check(&token::ModSep) || self.is_import_coupler() {
return Ok(segments);
} else {
self.bump();
}
}
PathSegment { identifier: ident, span: ident_span, parameters }
} else {
// Generic arguments are not found.
PathSegment::from_ident(ident, ident_span)
})
}
fn check_lifetime(&mut self) -> bool {
@ -2030,10 +1933,6 @@ impl<'a> Parser<'a> {
}
}
pub fn mk_field(&mut self, expr: P<Expr>, ident: ast::SpannedIdent) -> ast::ExprKind {
ExprKind::Field(expr, ident)
}
pub fn mk_tup_field(&mut self, expr: P<Expr>, idx: codemap::Spanned<usize>) -> ast::ExprKind {
ExprKind::TupField(expr, idx)
}
@ -2178,8 +2077,7 @@ impl<'a> Parser<'a> {
}
_ => {
if self.eat_lt() {
let (qself, path) =
self.parse_qualified_path(PathStyle::Expr)?;
let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
hi = path.span;
return Ok(self.mk_expr(lo.to(hi), ExprKind::Path(Some(qself), path), attrs));
}
@ -2439,50 +2337,33 @@ impl<'a> Parser<'a> {
)
}
// Assuming we have just parsed `.foo` (i.e., a dot and an ident), continue
// parsing into an expression.
fn parse_dot_suffix(&mut self, ident: Ident, ident_span: Span, self_value: P<Expr>, lo: Span)
-> PResult<'a, P<Expr>> {
let (lifetimes, types, bindings) = if self.eat(&token::ModSep) {
self.expect_lt()?;
let args = self.parse_generic_args()?;
self.expect_gt()?;
args
} else {
(Vec::new(), Vec::new(), Vec::new())
};
// Assuming we have just parsed `.`, continue parsing into an expression.
fn parse_dot_suffix(&mut self, self_arg: P<Expr>, lo: Span) -> PResult<'a, P<Expr>> {
let segment = self.parse_path_segment(PathStyle::Expr)?;
Ok(match self.token {
// expr.f() method call.
token::OpenDelim(token::Paren) => {
let mut es = self.parse_unspanned_seq(
// Method call `expr.f()`
let mut args = self.parse_unspanned_seq(
&token::OpenDelim(token::Paren),
&token::CloseDelim(token::Paren),
SeqSep::trailing_allowed(token::Comma),
|p| Ok(p.parse_expr()?)
)?;
let hi = self.prev_span;
args.insert(0, self_arg);
es.insert(0, self_value);
let seg = PathSegment {
identifier: ident,
span: ident_span.to(ident_span),
parameters: AngleBracketedParameterData { lifetimes, types, bindings }.into(),
};
self.mk_expr(lo.to(hi), ExprKind::MethodCall(seg, es), ThinVec::new())
let span = lo.to(self.prev_span);
self.mk_expr(span, ExprKind::MethodCall(segment, args), ThinVec::new())
}
// Field access.
_ => {
if let Some(generic_arg_span) = lifetimes.get(0).map(|x| x.span).or_else(||
types.get(0).map(|x| x.span)).or_else(||
bindings.get(0).map(|x| x.span)) {
self.span_err(generic_arg_span,
// Field access `expr.f`
if let Some(parameters) = segment.parameters {
self.span_err(parameters.span(segment.span),
"field expressions may not have generic arguments");
}
let id = respan(ident_span.to(ident_span), ident);
let field = self.mk_field(self_value, id);
self.mk_expr(lo.to(ident_span), field, ThinVec::new())
let span = lo.to(self.prev_span);
let ident = respan(segment.span, segment.identifier);
self.mk_expr(span, ExprKind::Field(self_arg, ident), ThinVec::new())
}
})
}
@ -2500,10 +2381,8 @@ impl<'a> Parser<'a> {
// expr.f
if self.eat(&token::Dot) {
match self.token {
token::Ident(i) => {
let ident_span = self.span;
self.bump();
e = self.parse_dot_suffix(i, ident_span, e, lo)?;
token::Ident(..) => {
e = self.parse_dot_suffix(e, lo)?;
}
token::Literal(token::Integer(n), suf) => {
let sp = self.span;
@ -2561,9 +2440,6 @@ impl<'a> Parser<'a> {
// FIXME Could factor this out into non_fatal_unexpected or something.
let actual = self.this_token_to_string();
self.span_err(self.span, &format!("unexpected token: `{}`", actual));
let dot_span = self.prev_span;
e = self.parse_dot_suffix(keywords::Invalid.ident(), dot_span, e, lo)?;
}
}
continue;
@ -2928,7 +2804,7 @@ impl<'a> Parser<'a> {
let parser_snapshot_after_type = self.clone();
mem::replace(self, parser_snapshot_before_type);
match self.parse_path_without_generics(PathStyle::Type) {
match self.parse_path(PathStyle::Expr) {
Ok(path) => {
// Successfully parsed the type path leaving a `<` yet to parse.
type_err.cancel();
@ -3455,8 +3331,7 @@ impl<'a> Parser<'a> {
let lo = self.span;
let (qself, path) = if self.eat_lt() {
// Parse a qualified path
let (qself, path) =
self.parse_qualified_path(PathStyle::Expr)?;
let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
(Some(qself), path)
} else {
// Parse an unqualified path
@ -3554,7 +3429,7 @@ impl<'a> Parser<'a> {
// Parse pattern starting with a path
let (qself, path) = if self.eat_lt() {
// Parse a qualified path
let (qself, path) = self.parse_qualified_path(PathStyle::Expr)?;
let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
(Some(qself), path)
} else {
// Parse an unqualified path

View File

@ -14,10 +14,10 @@ struct Foo<T> {
fn main() {
let f = Some(Foo { _a: 42 }).map(|a| a as Foo::<i32>);
//~^ ERROR unexpected token: `::`
//~| HELP use `<...>` instead of `::<...>` if you meant to specify type arguments
//~^ ERROR unnecessary path disambiguator
//~| NOTE try removing `::`
let g: Foo::<i32> = Foo { _a: 42 };
//~^ ERROR unexpected token: `::`
//~| HELP use `<...>` instead of `::<...>` if you meant to specify type arguments
//~^ ERROR unnecessary path disambiguator
//~| NOTE try removing `::`
}

View File

@ -17,5 +17,6 @@ fn main() {
let y = 42;
let x = y.; //~ ERROR unexpected token
let x = y.(); //~ ERROR unexpected token
//~^ ERROR expected function, found `{integer}`
let x = y.foo; //~ ERROR `{integer}` is a primitive type and therefore doesn't have fields [E061
}

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// compile-flags: -Z parse-only
// compile-flags: -Z parse-only -Z continue-parse-after-error
struct Foo {
x: isize,
@ -22,4 +22,6 @@ fn main() {
};
f.x::<isize>;
//~^ ERROR field expressions may not have generic arguments
f.x::<>;
//~^ ERROR field expressions may not have generic arguments
}

View File

@ -24,7 +24,7 @@ fn bar() {
let b = Box::Bar::<isize,usize>::new(); // OK
let b = Box::Bar::()::new();
//~^ ERROR expected identifier, found `(`
//~^ ERROR `::` is not supported before parenthesized generic arguments
}
fn main() { }