diff options
| author | Esteban Küber <esteban@kuber.com.ar> | 2017-06-09 20:30:33 -0700 |
|---|---|---|
| committer | Esteban Küber <esteban@kuber.com.ar> | 2017-06-11 13:40:04 -0700 |
| commit | a6d32153a6a70390e1c0c24907179d1e2a1f1ef9 (patch) | |
| tree | e3ea0ded6219ea9c40a9385468c4bc387114125e /src/libsyntax/parse/parser.rs | |
| parent | 13eb0ec9f17c89a3cb06a8f3ae627c076839fa52 (diff) | |
| download | rust-a6d32153a6a70390e1c0c24907179d1e2a1f1ef9.tar.gz rust-a6d32153a6a70390e1c0c24907179d1e2a1f1ef9.zip | |
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`.
Diffstat (limited to 'src/libsyntax/parse/parser.rs')
| -rw-r--r-- | src/libsyntax/parse/parser.rs | 98 |
1 files changed, 90 insertions, 8 deletions
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index d9cb2b4ab7d..a6ecd304dbd 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<TokenCursorFrame>, } +#[derive(Clone)] struct TokenCursorFrame { delim: token::DelimToken, span: Span, @@ -397,6 +399,7 @@ impl Error { } } +#[derive(Debug)] pub enum LhsExpr { NotYetParsed, AttributesParsed(ThinVec<Attribute>), @@ -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>, Span, TokenCursor, Vec<TokenType>); + 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<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) -> PResult<'a, Vec<PathSegment>> { + pub fn parse_path_segments_without_colons(&mut self, dont_parse_generics: bool) + -> PResult<'a, Vec<PathSegment>> + { 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<ThinVec<Attribute>>) -> PResult<'a, P<Expr>> { - 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; + } } |
