Skip to content

Commit 44eeb21

Browse files
committedJun 16, 2017
Auto merge of #42578 - estebank:recover-binop, r=nikomatsakis
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`. Fix #22644.
2 parents a311496 + ad260ff commit 44eeb21

File tree

5 files changed

+140
-9
lines changed

5 files changed

+140
-9
lines changed
 

‎src/librustc_errors/diagnostic.rs

+4
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,10 @@ impl Diagnostic {
248248
self.message.iter().map(|i| i.0.to_owned()).collect::<String>()
249249
}
250250

251+
pub fn set_message(&mut self, message: &str) {
252+
self.message = vec![(message.to_owned(), Style::NoStyle)];
253+
}
254+
251255
pub fn styled_message(&self) -> &Vec<(String, Style)> {
252256
&self.message
253257
}

‎src/libsyntax/parse/parser.rs

+91-9
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ fn maybe_append(mut lhs: Vec<Attribute>, rhs: Option<Vec<Attribute>>)
150150
lhs
151151
}
152152

153-
#[derive(PartialEq)]
153+
#[derive(Clone, PartialEq)]
154154
enum PrevTokenKind {
155155
DocComment,
156156
Comma,
@@ -162,6 +162,7 @@ enum PrevTokenKind {
162162

163163
/* ident is handled by common.rs */
164164

165+
#[derive(Clone)]
165166
pub struct Parser<'a> {
166167
pub sess: &'a ParseSess,
167168
/// the current token:
@@ -193,11 +194,13 @@ pub struct Parser<'a> {
193194
}
194195

195196

197+
#[derive(Clone)]
196198
struct TokenCursor {
197199
frame: TokenCursorFrame,
198200
stack: Vec<TokenCursorFrame>,
199201
}
200202

203+
#[derive(Clone)]
201204
struct TokenCursorFrame {
202205
delim: token::DelimToken,
203206
span: Span,
@@ -397,6 +400,7 @@ impl Error {
397400
}
398401
}
399402

403+
#[derive(Debug)]
400404
pub enum LhsExpr {
401405
NotYetParsed,
402406
AttributesParsed(ThinVec<Attribute>),
@@ -1721,7 +1725,7 @@ impl<'a> Parser<'a> {
17211725

17221726
let segments = match mode {
17231727
PathStyle::Type => {
1724-
self.parse_path_segments_without_colons()?
1728+
self.parse_path_segments_without_colons(true)?
17251729
}
17261730
PathStyle::Expr => {
17271731
self.parse_path_segments_with_colons()?
@@ -1742,6 +1746,16 @@ impl<'a> Parser<'a> {
17421746
/// bounds are permitted and whether `::` must precede type parameter
17431747
/// groups.
17441748
pub fn parse_path(&mut self, mode: PathStyle) -> PResult<'a, ast::Path> {
1749+
self.parse_path_common(mode, true)
1750+
}
1751+
1752+
pub fn parse_path_without_generics(&mut self, mode: PathStyle) -> PResult<'a, ast::Path> {
1753+
self.parse_path_common(mode, false)
1754+
}
1755+
1756+
fn parse_path_common(&mut self, mode: PathStyle, parse_generics: bool)
1757+
-> PResult<'a, ast::Path>
1758+
{
17451759
maybe_whole!(self, NtPath, |x| x);
17461760

17471761
let lo = self.meta_var_span.unwrap_or(self.span);
@@ -1752,7 +1766,7 @@ impl<'a> Parser<'a> {
17521766
// A bound set is a set of type parameter bounds.
17531767
let mut segments = match mode {
17541768
PathStyle::Type => {
1755-
self.parse_path_segments_without_colons()?
1769+
self.parse_path_segments_without_colons(parse_generics)?
17561770
}
17571771
PathStyle::Expr => {
17581772
self.parse_path_segments_with_colons()?
@@ -1797,7 +1811,9 @@ impl<'a> Parser<'a> {
17971811
/// - `a::b<T,U>::c<V,W>`
17981812
/// - `a::b<T,U>::c(V) -> W`
17991813
/// - `a::b<T,U>::c(V)`
1800-
pub fn parse_path_segments_without_colons(&mut self) -> PResult<'a, Vec<PathSegment>> {
1814+
pub fn parse_path_segments_without_colons(&mut self, parse_generics: bool)
1815+
-> PResult<'a, Vec<PathSegment>>
1816+
{
18011817
let mut segments = Vec::new();
18021818
loop {
18031819
// First, parse an identifier.
@@ -1816,7 +1832,7 @@ impl<'a> Parser<'a> {
18161832
}
18171833

18181834
// Parse types, optionally.
1819-
let parameters = if self.eat_lt() {
1835+
let parameters = if parse_generics && self.eat_lt() {
18201836
let (lifetimes, types, bindings) = self.parse_generic_args()?;
18211837
self.expect_gt()?;
18221838
ast::AngleBracketedParameterData {
@@ -2795,8 +2811,9 @@ impl<'a> Parser<'a> {
27952811
}
27962812
// Special cases:
27972813
if op == AssocOp::As {
2798-
let rhs = self.parse_ty_no_plus()?;
2799-
lhs = self.mk_expr(lhs_span.to(rhs.span), ExprKind::Cast(lhs, rhs), ThinVec::new());
2814+
// Save the state of the parser before parsing type normally, in case there is a
2815+
// LessThan comparison after this cast.
2816+
lhs = self.parse_assoc_op_as(lhs, lhs_span)?;
28002817
continue
28012818
} else if op == AssocOp::Colon {
28022819
let rhs = self.parse_ty_no_plus()?;
@@ -2894,11 +2911,74 @@ impl<'a> Parser<'a> {
28942911
Ok(lhs)
28952912
}
28962913

2914+
fn parse_assoc_op_as(&mut self, lhs: P<Expr>, lhs_span: Span) -> PResult<'a, P<Expr>> {
2915+
let rp = self.clone();
2916+
match self.parse_ty_no_plus() {
2917+
Ok(rhs) => {
2918+
Ok(self.mk_expr(lhs_span.to(rhs.span),
2919+
ExprKind::Cast(lhs, rhs),
2920+
ThinVec::new()))
2921+
}
2922+
Err(mut err) => {
2923+
let rp_err = self.clone();
2924+
let sp = rp_err.span.clone();
2925+
2926+
// Rewind to before attempting to parse the type with generics, to get
2927+
// arround #22644.
2928+
mem::replace(self, rp);
2929+
let lo = self.span;
2930+
match self.parse_path_without_generics(PathStyle::Type) {
2931+
Ok(path) => {
2932+
// Successfully parsed the type leaving a `<` yet to parse
2933+
err.cancel();
2934+
let codemap = self.sess.codemap();
2935+
let suggestion_span = lhs_span.to(self.prev_span);
2936+
let suggestion = match codemap.span_to_snippet(suggestion_span) {
2937+
Ok(lstring) => format!("({})", lstring),
2938+
_ => format!("(<expression> as <type>)")
2939+
};
2940+
let warn_message = match codemap.span_to_snippet(self.prev_span) {
2941+
Ok(lstring) => format!("`{}`", lstring),
2942+
_ => "a type".to_string(),
2943+
};
2944+
let msg = format!("`<` is interpreted as a start of generic \
2945+
arguments for {}, not a comparison",
2946+
warn_message);
2947+
let mut err = self.sess.span_diagnostic.struct_span_err(sp, &msg);
2948+
err.span_label(sp, "interpreted as generic argument");
2949+
err.span_label(self.span, "not interpreted as comparison");
2950+
err.span_suggestion(suggestion_span,
2951+
"if you want to compare the casted value then write:",
2952+
suggestion);
2953+
err.emit();
2954+
2955+
let path = TyKind::Path(None, path);
2956+
let span = lo.to(self.prev_span);
2957+
let rhs = P(Ty { node: path, span: span, id: ast::DUMMY_NODE_ID });
2958+
// Letting the parser accept the recovered type to avoid further errors,
2959+
// but the code will still not compile due to the error emitted above.
2960+
Ok(self.mk_expr(lhs_span.to(rhs.span),
2961+
ExprKind::Cast(lhs, rhs),
2962+
ThinVec::new()))
2963+
}
2964+
Err(mut path_err) => {
2965+
// Still couldn't parse, return original error and parser state
2966+
path_err.cancel();
2967+
mem::replace(self, rp_err);
2968+
Err(err)
2969+
}
2970+
}
2971+
}
2972+
}
2973+
}
2974+
28972975
/// Produce an error if comparison operators are chained (RFC #558).
28982976
/// We only need to check lhs, not rhs, because all comparison ops
28992977
/// have same precedence and are left-associative
29002978
fn check_no_chained_comparison(&mut self, lhs: &Expr, outer_op: &AssocOp) {
2901-
debug_assert!(outer_op.is_comparison());
2979+
debug_assert!(outer_op.is_comparison(),
2980+
"check_no_chained_comparison: {:?} is not comparison",
2981+
outer_op);
29022982
match lhs.node {
29032983
ExprKind::Binary(op, _, _) if op.node.is_comparison() => {
29042984
// respan to include both operators
@@ -2922,7 +3002,9 @@ impl<'a> Parser<'a> {
29223002
fn parse_prefix_range_expr(&mut self,
29233003
already_parsed_attrs: Option<ThinVec<Attribute>>)
29243004
-> PResult<'a, P<Expr>> {
2925-
debug_assert!(self.token == token::DotDot || self.token == token::DotDotDot);
3005+
debug_assert!(self.token == token::DotDot || self.token == token::DotDotDot,
3006+
"parse_prefix_range_expr: token {:?} is not DotDot or DotDotDot",
3007+
self.token);
29263008
let tok = self.token.clone();
29273009
let attrs = self.parse_or_use_outer_attributes(already_parsed_attrs)?;
29283010
let lo = self.span;

‎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/ui/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+
}

‎src/test/ui/issue-22644.stderr

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
2+
--> $DIR/issue-22644.rs:16:33
3+
|
4+
16 | println!("{}", a as usize < b);
5+
| - ^ interpreted as generic argument
6+
| |
7+
| not interpreted as comparison
8+
|
9+
help: if you want to compare the casted value then write:
10+
| println!("{}", (a as usize) < b);
11+
12+
error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
13+
--> $DIR/issue-22644.rs:17:33
14+
|
15+
17 | println!("{}", a as usize < 4);
16+
| - ^ interpreted as generic argument
17+
| |
18+
| not interpreted as comparison
19+
|
20+
help: if you want to compare the casted value then write:
21+
| println!("{}", (a as usize) < 4);
22+
23+
error: aborting due to previous error(s)
24+

0 commit comments

Comments
 (0)
Please sign in to comment.