From a6d32153a6a70390e1c0c24907179d1e2a1f1ef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 9 Jun 2017 20:30:33 -0700 Subject: [PATCH] Learn to parse `a as usize < b` Parsing `a as usize > b` always works, but `a as usize < b` was a parsing error because the parser would think the `<` started a generic type argument for `usize`. The parser now attempts to parse as before, and if a DiagnosticError is returned, try to parse again as a type with no generic arguments. If this fails, return the original `DiagnosticError`. --- src/libsyntax/parse/parser.rs | 98 +++++++++++++++++++++++++++++--- src/libsyntax/tokenstream.rs | 3 + src/test/run-pass/issue-22644.rs | 18 ++++++ 3 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 src/test/run-pass/issue-22644.rs diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index d9cb2b4ab7db0..a6ecd304dbd16 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -193,11 +193,13 @@ pub struct Parser<'a> { } +#[derive(Clone)] struct TokenCursor { frame: TokenCursorFrame, stack: Vec, } +#[derive(Clone)] struct TokenCursorFrame { delim: token::DelimToken, span: Span, @@ -397,6 +399,7 @@ impl Error { } } +#[derive(Debug)] pub enum LhsExpr { NotYetParsed, AttributesParsed(ThinVec), @@ -438,6 +441,8 @@ fn dummy_arg(span: Span) -> Arg { Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID } } +type RewindPoint = (token::Token, Span, Option, Span, TokenCursor, Vec); + impl<'a> Parser<'a> { pub fn new(sess: &'a ParseSess, tokens: TokenStream, @@ -786,6 +791,13 @@ impl<'a> Parser<'a> { } } + fn is_lt(&mut self) -> bool { + match self.token { + token::Lt | token::BinOp(token::Shl) => true, + _ => false, + } + } + /// Attempt to consume a `<`. If `<<` is seen, replace it with a single /// `<` and continue. If a `<` is not seen, return false. /// @@ -1724,7 +1736,7 @@ impl<'a> Parser<'a> { let segments = match mode { PathStyle::Type => { - self.parse_path_segments_without_colons()? + self.parse_path_segments_without_colons(false)? } PathStyle::Expr => { self.parse_path_segments_with_colons()? @@ -1745,6 +1757,16 @@ impl<'a> Parser<'a> { /// 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, false) + } + + pub fn parse_path_without_generics(&mut self, mode: PathStyle) -> PResult<'a, ast::Path> { + self.parse_path_common(mode, true) + } + + fn parse_path_common(&mut self, mode: PathStyle, dont_parse_generics: bool) + -> PResult<'a, ast::Path> + { maybe_whole!(self, NtPath, |x| x); let lo = self.meta_var_span.unwrap_or(self.span); @@ -1755,7 +1777,7 @@ impl<'a> Parser<'a> { // A bound set is a set of type parameter bounds. let mut segments = match mode { PathStyle::Type => { - self.parse_path_segments_without_colons()? + self.parse_path_segments_without_colons(dont_parse_generics)? } PathStyle::Expr => { self.parse_path_segments_with_colons()? @@ -1800,7 +1822,9 @@ impl<'a> Parser<'a> { /// - `a::b::c` /// - `a::b::c(V) -> W` /// - `a::b::c(V)` - pub fn parse_path_segments_without_colons(&mut self) -> PResult<'a, Vec> { + pub fn parse_path_segments_without_colons(&mut self, dont_parse_generics: bool) + -> PResult<'a, Vec> + { let mut segments = Vec::new(); loop { // First, parse an identifier. @@ -1819,7 +1843,8 @@ impl<'a> Parser<'a> { } // Parse types, optionally. - let parameters = if self.eat_lt() { + let parameters = if self.is_lt() && !dont_parse_generics { + let _ = self.eat_lt(); let (lifetimes, types, bindings) = self.parse_generic_args()?; self.expect_gt()?; ast::AngleBracketedParameterData { @@ -2798,8 +2823,40 @@ impl<'a> Parser<'a> { } // Special cases: if op == AssocOp::As { - let rhs = self.parse_ty_no_plus()?; - lhs = self.mk_expr(lhs_span.to(rhs.span), ExprKind::Cast(lhs, rhs), ThinVec::new()); + // Save the state of the parser before parsing type normally, in case there is a + // LessThan comparison after this cast. + let rp = self.get_rewind_point(); + match self.parse_ty_no_plus() { + Ok(rhs) => { + lhs = self.mk_expr(lhs_span.to(rhs.span), + ExprKind::Cast(lhs, rhs), ThinVec::new()); + } + Err(mut err) => { + // Rewind to before attempting to parse the type with generics, to get + // arround #22644. + let rp_err = self.get_rewind_point(); + self.rewind(rp); + let lo = self.span; + let path = match self.parse_path_without_generics(PathStyle::Type) { + Ok(path) => { + // Successfully parsed the type leaving a `<` yet to parse + err.cancel(); + path + } + Err(mut path_err) => { + // Still couldn't parse, return original error and parser state + path_err.cancel(); + self.rewind(rp_err); + return Err(err); + } + }; + let path = TyKind::Path(None, path); + let span = lo.to(self.prev_span); + let rhs = P(Ty { node: path, span: span, id: ast::DUMMY_NODE_ID }); + lhs = self.mk_expr(lhs_span.to(rhs.span), + ExprKind::Cast(lhs, rhs), ThinVec::new()); + } + }; continue } else if op == AssocOp::Colon { let rhs = self.parse_ty_no_plus()?; @@ -2901,7 +2958,9 @@ impl<'a> Parser<'a> { /// We only need to check lhs, not rhs, because all comparison ops /// have same precedence and are left-associative fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: &AssocOp) { - debug_assert!(outer_op.is_comparison()); + debug_assert!(outer_op.is_comparison(), + "check_no_chained_comparison: {:?} is not comparison", + outer_op); match lhs.node { ExprKind::Binary(op, _, _) if op.node.is_comparison() => { // respan to include both operators @@ -2925,7 +2984,9 @@ impl<'a> Parser<'a> { fn parse_prefix_range_expr(&mut self, already_parsed_attrs: Option>) -> PResult<'a, P> { - debug_assert!(self.token == token::DotDot || self.token == token::DotDotDot); + debug_assert!(self.token == token::DotDot || self.token == token::DotDotDot, + "parse_prefix_range_expr: token {:?} is not DotDot or DotDotDot", + self.token); let tok = self.token.clone(); let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?; let lo = self.span; @@ -6174,4 +6235,25 @@ impl<'a> Parser<'a> { _ => Err(self.fatal("expected string literal")) } } + + fn get_rewind_point(&mut self) -> RewindPoint { + ( + self.token.clone(), + self.span, + self.meta_var_span, + self.prev_span, + self.token_cursor.clone(), + self.expected_tokens.clone(), + ) + } + + fn rewind(&mut self, rp: RewindPoint) { + let (token, span, meta_var_span, prev_span, token_cursor, expected_tokens,) = rp; + self.token = token; + self.span = span; + self.meta_var_span = meta_var_span; + self.prev_span = prev_span; + self.token_cursor = token_cursor; + self.expected_tokens = expected_tokens; + } } diff --git a/src/libsyntax/tokenstream.rs b/src/libsyntax/tokenstream.rs index 339e7c0b628ad..963482fc223f1 100644 --- a/src/libsyntax/tokenstream.rs +++ b/src/libsyntax/tokenstream.rs @@ -227,14 +227,17 @@ impl TokenStream { } } +#[derive(Clone)] pub struct Cursor(CursorKind); +#[derive(Clone)] enum CursorKind { Empty, Tree(TokenTree, bool /* consumed? */), Stream(StreamCursor), } +#[derive(Clone)] struct StreamCursor { stream: RcSlice, index: usize, diff --git a/src/test/run-pass/issue-22644.rs b/src/test/run-pass/issue-22644.rs new file mode 100644 index 0000000000000..9269180396c54 --- /dev/null +++ b/src/test/run-pass/issue-22644.rs @@ -0,0 +1,18 @@ +// Copyright 2017 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. + +fn main() { + let a : u32 = 0; + let b : usize = 0; + + println!("{}", a as usize > b); + println!("{}", a as usize < b); + println!("{}", a as usize < 4); +}