Skip to content

Commit a6d3215

Browse files
committed
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`.
1 parent 13eb0ec commit a6d3215

File tree

3 files changed

+111
-8
lines changed

3 files changed

+111
-8
lines changed

src/libsyntax/parse/parser.rs

+90-8
Original file line numberDiff line numberDiff line change
@@ -193,11 +193,13 @@ pub struct Parser<'a> {
193193
}
194194

195195

196+
#[derive(Clone)]
196197
struct TokenCursor {
197198
frame: TokenCursorFrame,
198199
stack: Vec<TokenCursorFrame>,
199200
}
200201

202+
#[derive(Clone)]
201203
struct TokenCursorFrame {
202204
delim: token::DelimToken,
203205
span: Span,
@@ -397,6 +399,7 @@ impl Error {
397399
}
398400
}
399401

402+
#[derive(Debug)]
400403
pub enum LhsExpr {
401404
NotYetParsed,
402405
AttributesParsed(ThinVec<Attribute>),
@@ -438,6 +441,8 @@ fn dummy_arg(span: Span) -> Arg {
438441
Arg { ty: P(ty), pat: pat, id: ast::DUMMY_NODE_ID }
439442
}
440443

444+
type RewindPoint = (token::Token, Span, Option<Span>, Span, TokenCursor, Vec<TokenType>);
445+
441446
impl<'a> Parser<'a> {
442447
pub fn new(sess: &'a ParseSess,
443448
tokens: TokenStream,
@@ -786,6 +791,13 @@ impl<'a> Parser<'a> {
786791
}
787792
}
788793

794+
fn is_lt(&mut self) -> bool {
795+
match self.token {
796+
token::Lt | token::BinOp(token::Shl) => true,
797+
_ => false,
798+
}
799+
}
800+
789801
/// Attempt to consume a `<`. If `<<` is seen, replace it with a single
790802
/// `<` and continue. If a `<` is not seen, return false.
791803
///
@@ -1724,7 +1736,7 @@ impl<'a> Parser<'a> {
17241736

17251737
let segments = match mode {
17261738
PathStyle::Type => {
1727-
self.parse_path_segments_without_colons()?
1739+
self.parse_path_segments_without_colons(false)?
17281740
}
17291741
PathStyle::Expr => {
17301742
self.parse_path_segments_with_colons()?
@@ -1745,6 +1757,16 @@ impl<'a> Parser<'a> {
17451757
/// bounds are permitted and whether `::` must precede type parameter
17461758
/// groups.
17471759
pub fn parse_path(&mut self, mode: PathStyle) -> PResult<'a, ast::Path> {
1760+
self.parse_path_common(mode, false)
1761+
}
1762+
1763+
pub fn parse_path_without_generics(&mut self, mode: PathStyle) -> PResult<'a, ast::Path> {
1764+
self.parse_path_common(mode, true)
1765+
}
1766+
1767+
fn parse_path_common(&mut self, mode: PathStyle, dont_parse_generics: bool)
1768+
-> PResult<'a, ast::Path>
1769+
{
17481770
maybe_whole!(self, NtPath, |x| x);
17491771

17501772
let lo = self.meta_var_span.unwrap_or(self.span);
@@ -1755,7 +1777,7 @@ impl<'a> Parser<'a> {
17551777
// A bound set is a set of type parameter bounds.
17561778
let mut segments = match mode {
17571779
PathStyle::Type => {
1758-
self.parse_path_segments_without_colons()?
1780+
self.parse_path_segments_without_colons(dont_parse_generics)?
17591781
}
17601782
PathStyle::Expr => {
17611783
self.parse_path_segments_with_colons()?
@@ -1800,7 +1822,9 @@ impl<'a> Parser<'a> {
18001822
/// - `a::b<T,U>::c<V,W>`
18011823
/// - `a::b<T,U>::c(V) -> W`
18021824
/// - `a::b<T,U>::c(V)`
1803-
pub fn parse_path_segments_without_colons(&mut self) -> PResult<'a, Vec<PathSegment>> {
1825+
pub fn parse_path_segments_without_colons(&mut self, dont_parse_generics: bool)
1826+
-> PResult<'a, Vec<PathSegment>>
1827+
{
18041828
let mut segments = Vec::new();
18051829
loop {
18061830
// First, parse an identifier.
@@ -1819,7 +1843,8 @@ impl<'a> Parser<'a> {
18191843
}
18201844

18211845
// Parse types, optionally.
1822-
let parameters = if self.eat_lt() {
1846+
let parameters = if self.is_lt() && !dont_parse_generics {
1847+
let _ = self.eat_lt();
18231848
let (lifetimes, types, bindings) = self.parse_generic_args()?;
18241849
self.expect_gt()?;
18251850
ast::AngleBracketedParameterData {
@@ -2798,8 +2823,40 @@ impl<'a> Parser<'a> {
27982823
}
27992824
// Special cases:
28002825
if op == AssocOp::As {
2801-
let rhs = self.parse_ty_no_plus()?;
2802-
lhs = self.mk_expr(lhs_span.to(rhs.span), ExprKind::Cast(lhs, rhs), ThinVec::new());
2826+
// Save the state of the parser before parsing type normally, in case there is a
2827+
// LessThan comparison after this cast.
2828+
let rp = self.get_rewind_point();
2829+
match self.parse_ty_no_plus() {
2830+
Ok(rhs) => {
2831+
lhs = self.mk_expr(lhs_span.to(rhs.span),
2832+
ExprKind::Cast(lhs, rhs), ThinVec::new());
2833+
}
2834+
Err(mut err) => {
2835+
// Rewind to before attempting to parse the type with generics, to get
2836+
// arround #22644.
2837+
let rp_err = self.get_rewind_point();
2838+
self.rewind(rp);
2839+
let lo = self.span;
2840+
let path = match self.parse_path_without_generics(PathStyle::Type) {
2841+
Ok(path) => {
2842+
// Successfully parsed the type leaving a `<` yet to parse
2843+
err.cancel();
2844+
path
2845+
}
2846+
Err(mut path_err) => {
2847+
// Still couldn't parse, return original error and parser state
2848+
path_err.cancel();
2849+
self.rewind(rp_err);
2850+
return Err(err);
2851+
}
2852+
};
2853+
let path = TyKind::Path(None, path);
2854+
let span = lo.to(self.prev_span);
2855+
let rhs = P(Ty { node: path, span: span, id: ast::DUMMY_NODE_ID });
2856+
lhs = self.mk_expr(lhs_span.to(rhs.span),
2857+
ExprKind::Cast(lhs, rhs), ThinVec::new());
2858+
}
2859+
};
28032860
continue
28042861
} else if op == AssocOp::Colon {
28052862
let rhs = self.parse_ty_no_plus()?;
@@ -2901,7 +2958,9 @@ impl<'a> Parser<'a> {
29012958
/// We only need to check lhs, not rhs, because all comparison ops
29022959
/// have same precedence and are left-associative
29032960
fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: &AssocOp) {
2904-
debug_assert!(outer_op.is_comparison());
2961+
debug_assert!(outer_op.is_comparison(),
2962+
"check_no_chained_comparison: {:?} is not comparison",
2963+
outer_op);
29052964
match lhs.node {
29062965
ExprKind::Binary(op, _, _) if op.node.is_comparison() => {
29072966
// respan to include both operators
@@ -2925,7 +2984,9 @@ impl<'a> Parser<'a> {
29252984
fn parse_prefix_range_expr(&mut self,
29262985
already_parsed_attrs: Option<ThinVec<Attribute>>)
29272986
-> PResult<'a, P<Expr>> {
2928-
debug_assert!(self.token == token::DotDot || self.token == token::DotDotDot);
2987+
debug_assert!(self.token == token::DotDot || self.token == token::DotDotDot,
2988+
"parse_prefix_range_expr: token {:?} is not DotDot or DotDotDot",
2989+
self.token);
29292990
let tok = self.token.clone();
29302991
let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?;
29312992
let lo = self.span;
@@ -6174,4 +6235,25 @@ impl<'a> Parser<'a> {
61746235
_ => Err(self.fatal("expected string literal"))
61756236
}
61766237
}
6238+
6239+
fn get_rewind_point(&mut self) -> RewindPoint {
6240+
(
6241+
self.token.clone(),
6242+
self.span,
6243+
self.meta_var_span,
6244+
self.prev_span,
6245+
self.token_cursor.clone(),
6246+
self.expected_tokens.clone(),
6247+
)
6248+
}
6249+
6250+
fn rewind(&mut self, rp: RewindPoint) {
6251+
let (token, span, meta_var_span, prev_span, token_cursor, expected_tokens,) = rp;
6252+
self.token = token;
6253+
self.span = span;
6254+
self.meta_var_span = meta_var_span;
6255+
self.prev_span = prev_span;
6256+
self.token_cursor = token_cursor;
6257+
self.expected_tokens = expected_tokens;
6258+
}
61776259
}

src/libsyntax/tokenstream.rs

+3
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,17 @@ impl TokenStream {
227227
}
228228
}
229229

230+
#[derive(Clone)]
230231
pub struct Cursor(CursorKind);
231232

233+
#[derive(Clone)]
232234
enum CursorKind {
233235
Empty,
234236
Tree(TokenTree, bool /* consumed? */),
235237
Stream(StreamCursor),
236238
}
237239

240+
#[derive(Clone)]
238241
struct StreamCursor {
239242
stream: RcSlice<TokenStream>,
240243
index: usize,

src/test/run-pass/issue-22644.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
let a : u32 = 0;
13+
let b : usize = 0;
14+
15+
println!("{}", a as usize > b);
16+
println!("{}", a as usize < b);
17+
println!("{}", a as usize < 4);
18+
}

0 commit comments

Comments
 (0)