Skip to content

Commit 70412af

Browse files
committed
Auto merge of #43060 - petrochenkov:asless, r=estebank
syntax: Apply `x as usize < y` recovery to type ascription as well Also correct spans, add some comments. r? @estebank
2 parents 33e353e + 4323877 commit 70412af

File tree

5 files changed

+194
-78
lines changed

5 files changed

+194
-78
lines changed

src/libsyntax/parse/parser.rs

+65-51
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,14 @@ fn maybe_append(mut lhs: Vec<Attribute>, rhs: Option<Vec<Attribute>>)
150150
lhs
151151
}
152152

153-
#[derive(Clone, PartialEq)]
153+
#[derive(Clone, Copy, PartialEq)]
154154
enum PrevTokenKind {
155155
DocComment,
156156
Comma,
157157
Plus,
158158
Interpolated,
159159
Eof,
160+
Ident,
160161
Other,
161162
}
162163

@@ -1040,6 +1041,7 @@ impl<'a> Parser<'a> {
10401041
token::BinOp(token::Plus) => PrevTokenKind::Plus,
10411042
token::Interpolated(..) => PrevTokenKind::Interpolated,
10421043
token::Eof => PrevTokenKind::Eof,
1044+
token::Ident(..) => PrevTokenKind::Ident,
10431045
_ => PrevTokenKind::Other,
10441046
};
10451047

@@ -1079,6 +1081,16 @@ impl<'a> Parser<'a> {
10791081
None => token::CloseDelim(self.token_cursor.frame.delim),
10801082
})
10811083
}
1084+
fn look_ahead_span(&self, dist: usize) -> Span {
1085+
if dist == 0 {
1086+
return self.span
1087+
}
1088+
1089+
match self.token_cursor.frame.tree_cursor.look_ahead(dist - 1) {
1090+
Some(TokenTree::Token(span, _)) | Some(TokenTree::Delimited(span, _)) => span,
1091+
None => self.look_ahead_span(dist - 1),
1092+
}
1093+
}
10821094
pub fn fatal(&self, m: &str) -> DiagnosticBuilder<'a> {
10831095
self.sess.span_diagnostic.struct_span_fatal(self.span, m)
10841096
}
@@ -2777,10 +2789,15 @@ impl<'a> Parser<'a> {
27772789
self.expected_tokens.push(TokenType::Operator);
27782790
while let Some(op) = AssocOp::from_token(&self.token) {
27792791

2780-
let lhs_span = if self.prev_token_kind == PrevTokenKind::Interpolated {
2781-
self.prev_span
2782-
} else {
2783-
lhs.span
2792+
// Adjust the span for interpolated LHS to point to the `$lhs` token and not to what
2793+
// it refers to. Interpolated identifiers are unwrapped early and never show up here
2794+
// as `PrevTokenKind::Interpolated` so if LHS is a single identifier we always process
2795+
// it as "interpolated", it doesn't change the answer for non-interpolated idents.
2796+
let lhs_span = match (self.prev_token_kind, &lhs.node) {
2797+
(PrevTokenKind::Interpolated, _) => self.prev_span,
2798+
(PrevTokenKind::Ident, &ExprKind::Path(None, ref path))
2799+
if path.segments.len() == 1 => self.prev_span,
2800+
_ => lhs.span,
27842801
};
27852802

27862803
let cur_op_span = self.span;
@@ -2798,13 +2815,10 @@ impl<'a> Parser<'a> {
27982815
}
27992816
// Special cases:
28002817
if op == AssocOp::As {
2801-
// Save the state of the parser before parsing type normally, in case there is a
2802-
// LessThan comparison after this cast.
2803-
lhs = self.parse_assoc_op_as(lhs, lhs_span)?;
2818+
lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?;
28042819
continue
28052820
} else if op == AssocOp::Colon {
2806-
let rhs = self.parse_ty_no_plus()?;
2807-
lhs = self.mk_expr(lhs_span.to(rhs.span), ExprKind::Type(lhs, rhs), ThinVec::new());
2821+
lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Type)?;
28082822
continue
28092823
} else if op == AssocOp::DotDot || op == AssocOp::DotDotDot {
28102824
// If we didn’t have to handle `x..`/`x...`, it would be pretty easy to
@@ -2898,61 +2912,61 @@ impl<'a> Parser<'a> {
28982912
Ok(lhs)
28992913
}
29002914

2901-
fn parse_assoc_op_as(&mut self, lhs: P<Expr>, lhs_span: Span) -> PResult<'a, P<Expr>> {
2902-
let rp = self.clone();
2915+
fn parse_assoc_op_cast(&mut self, lhs: P<Expr>, lhs_span: Span,
2916+
expr_kind: fn(P<Expr>, P<Ty>) -> ExprKind)
2917+
-> PResult<'a, P<Expr>> {
2918+
let mk_expr = |this: &mut Self, rhs: P<Ty>| {
2919+
this.mk_expr(lhs_span.to(rhs.span), expr_kind(lhs, rhs), ThinVec::new())
2920+
};
2921+
2922+
// Save the state of the parser before parsing type normally, in case there is a
2923+
// LessThan comparison after this cast.
2924+
let parser_snapshot_before_type = self.clone();
29032925
match self.parse_ty_no_plus() {
29042926
Ok(rhs) => {
2905-
Ok(self.mk_expr(lhs_span.to(rhs.span),
2906-
ExprKind::Cast(lhs, rhs),
2907-
ThinVec::new()))
2927+
Ok(mk_expr(self, rhs))
29082928
}
2909-
Err(mut err) => {
2910-
let rp_err = self.clone();
2911-
let sp = rp_err.span.clone();
2929+
Err(mut type_err) => {
2930+
// Rewind to before attempting to parse the type with generics, to recover
2931+
// from situations like `x as usize < y` in which we first tried to parse
2932+
// `usize < y` as a type with generic arguments.
2933+
let parser_snapshot_after_type = self.clone();
2934+
mem::replace(self, parser_snapshot_before_type);
29122935

2913-
// Rewind to before attempting to parse the type with generics, to get
2914-
// arround #22644.
2915-
mem::replace(self, rp);
2916-
let lo = self.span;
29172936
match self.parse_path_without_generics(PathStyle::Type) {
29182937
Ok(path) => {
2919-
// Successfully parsed the type leaving a `<` yet to parse
2920-
err.cancel();
2921-
let codemap = self.sess.codemap();
2922-
let suggestion_span = lhs_span.to(self.prev_span);
2923-
let warn_message = match codemap.span_to_snippet(self.prev_span) {
2924-
Ok(lstring) => format!("`{}`", lstring),
2925-
_ => "a type".to_string(),
2926-
};
2938+
// Successfully parsed the type path leaving a `<` yet to parse.
2939+
type_err.cancel();
2940+
2941+
// Report non-fatal diagnostics, keep `x as usize` as an expression
2942+
// in AST and continue parsing.
29272943
let msg = format!("`<` is interpreted as a start of generic \
2928-
arguments for {}, not a comparison",
2929-
warn_message);
2930-
let mut err = self.sess.span_diagnostic.struct_span_err(sp, &msg);
2931-
err.span_label(sp, "interpreted as generic argument");
2944+
arguments for `{}`, not a comparison", path);
2945+
let mut err = self.sess.span_diagnostic.struct_span_err(self.span, &msg);
2946+
err.span_label(self.look_ahead_span(1).to(parser_snapshot_after_type.span),
2947+
"interpreted as generic arguments");
29322948
err.span_label(self.span, "not interpreted as comparison");
2933-
let suggestion = match codemap.span_to_snippet(suggestion_span) {
2934-
Ok(lstring) => format!("({})", lstring),
2935-
_ => format!("(<expression> as <type>)")
2936-
};
2937-
err.span_suggestion(suggestion_span,
2949+
2950+
let expr = mk_expr(self, P(Ty {
2951+
span: path.span,
2952+
node: TyKind::Path(None, path),
2953+
id: ast::DUMMY_NODE_ID
2954+
}));
2955+
2956+
let expr_str = self.sess.codemap().span_to_snippet(expr.span)
2957+
.unwrap_or(pprust::expr_to_string(&expr));
2958+
err.span_suggestion(expr.span,
29382959
"if you want to compare the casted value then write:",
2939-
suggestion);
2960+
format!("({})", expr_str));
29402961
err.emit();
29412962

2942-
let path = TyKind::Path(None, path);
2943-
let span = lo.to(self.prev_span);
2944-
let rhs = P(Ty { node: path, span: span, id: ast::DUMMY_NODE_ID });
2945-
// Letting the parser accept the recovered type to avoid further errors,
2946-
// but the code will still not compile due to the error emitted above.
2947-
Ok(self.mk_expr(lhs_span.to(rhs.span),
2948-
ExprKind::Cast(lhs, rhs),
2949-
ThinVec::new()))
2963+
Ok(expr)
29502964
}
29512965
Err(mut path_err) => {
2952-
// Still couldn't parse, return original error and parser state
2966+
// Couldn't parse as a path, return original error and parser state.
29532967
path_err.cancel();
2954-
mem::replace(self, rp_err);
2955-
Err(err)
2968+
mem::replace(self, parser_snapshot_after_type);
2969+
Err(type_err)
29562970
}
29572971
}
29582972
}

src/test/ui/issue-22644.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,16 @@
1010

1111
fn main() {
1212
let a : u32 = 0;
13-
let b : usize = 0;
13+
let long_name : usize = 0;
14+
15+
println!("{}", a as usize > long_name);
16+
println!("{}", a as usize < long_name);
17+
println!("{}{}", a as usize < long_name, long_name);
18+
println!("{}", a as usize < 4);
19+
println!("{}", a: usize > long_name);
20+
println!("{}{}", a: usize < long_name, long_name);
21+
println!("{}", a: usize < 4);
1422

15-
println!("{}", a as usize > b);
16-
println!("{}", a as usize < b);
1723
println!("{}", a
1824
as
1925
usize
@@ -28,4 +34,6 @@ fn main() {
2834
usize
2935
<
3036
5);
37+
38+
println!("{}", a: &mut 4);
3139
}

src/test/ui/issue-22644.stderr

+80-24
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,104 @@
11
error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
2-
--> $DIR/issue-22644.rs:16:33
2+
--> $DIR/issue-22644.rs:16:31
33
|
4-
16 | println!("{}", a as usize < b);
5-
| - ^ interpreted as generic argument
4+
16 | println!("{}", a as usize < long_name);
5+
| ^ --------- interpreted as generic arguments
66
| |
77
| not interpreted as comparison
88
|
99
help: if you want to compare the casted value then write:
1010
|
11-
16 | println!("{}", (a as usize) < b);
11+
16 | println!("{}", (a as usize) < long_name);
1212
| ^^^^^^^^^^^^
1313

1414
error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
15-
--> $DIR/issue-22644.rs:21:20
15+
--> $DIR/issue-22644.rs:17:33
1616
|
17-
20 | <
18-
| - not interpreted as comparison
19-
21 | 4);
20-
| ^ interpreted as generic argument
17+
17 | println!("{}{}", a as usize < long_name, long_name);
18+
| ^ -------------------- interpreted as generic arguments
19+
| |
20+
| not interpreted as comparison
2121
|
2222
help: if you want to compare the casted value then write:
2323
|
24-
17 | println!("{}", (a
25-
18 | as
26-
19 | usize)
24+
17 | println!("{}{}", (a as usize) < long_name, long_name);
25+
| ^^^^^^^^^^^^
26+
27+
error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
28+
--> $DIR/issue-22644.rs:18:31
29+
|
30+
18 | println!("{}", a as usize < 4);
31+
| ^ - interpreted as generic arguments
32+
| |
33+
| not interpreted as comparison
34+
|
35+
help: if you want to compare the casted value then write:
36+
|
37+
18 | println!("{}", (a as usize) < 4);
38+
| ^^^^^^^^^^^^
39+
40+
error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
41+
--> $DIR/issue-22644.rs:20:31
42+
|
43+
20 | println!("{}{}", a: usize < long_name, long_name);
44+
| ^ -------------------- interpreted as generic arguments
45+
| |
46+
| not interpreted as comparison
47+
|
48+
help: if you want to compare the casted value then write:
49+
|
50+
20 | println!("{}{}", (a: usize) < long_name, long_name);
51+
| ^^^^^^^^^^
52+
53+
error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
54+
--> $DIR/issue-22644.rs:21:29
2755
|
56+
21 | println!("{}", a: usize < 4);
57+
| ^ - interpreted as generic arguments
58+
| |
59+
| not interpreted as comparison
60+
|
61+
help: if you want to compare the casted value then write:
62+
|
63+
21 | println!("{}", (a: usize) < 4);
64+
| ^^^^^^^^^^
2865

2966
error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
30-
--> $DIR/issue-22644.rs:30:20
67+
--> $DIR/issue-22644.rs:26:20
3168
|
32-
29 | <
33-
| - not interpreted as comparison
34-
30 | 5);
35-
| ^ interpreted as generic argument
69+
26 | <
70+
| ^ not interpreted as comparison
71+
27 | 4);
72+
| - interpreted as generic arguments
3673
|
3774
help: if you want to compare the casted value then write:
3875
|
39-
22 | println!("{}", (a
40-
23 |
41-
24 |
42-
25 | as
43-
26 |
44-
27 |
76+
23 | println!("{}", (a
77+
24 | as
78+
25 | usize)
79+
|
80+
81+
error: `<` is interpreted as a start of generic arguments for `usize`, not a comparison
82+
--> $DIR/issue-22644.rs:35:20
83+
|
84+
35 | <
85+
| ^ not interpreted as comparison
86+
36 | 5);
87+
| - interpreted as generic arguments
88+
|
89+
help: if you want to compare the casted value then write:
90+
|
91+
28 | println!("{}", (a
92+
29 |
93+
30 |
94+
31 | as
95+
32 |
96+
33 |
4597
...
4698

47-
error: aborting due to 3 previous errors
99+
error: expected type, found `4`
100+
--> $DIR/issue-22644.rs:38:28
101+
|
102+
38 | println!("{}", a: &mut 4);
103+
| ^
48104

src/test/ui/issue-42954.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
macro_rules! is_plainly_printable {
12+
($i: ident) => {
13+
$i as u32 < 0
14+
};
15+
}
16+
17+
fn main() {
18+
let c = 'a';
19+
is_plainly_printable!(c);
20+
}

src/test/ui/issue-42954.stderr

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: `<` is interpreted as a start of generic arguments for `u32`, not a comparison
2+
--> $DIR/issue-42954.rs:13:19
3+
|
4+
13 | $i as u32 < 0
5+
| ^ - interpreted as generic arguments
6+
| |
7+
| not interpreted as comparison
8+
...
9+
19 | is_plainly_printable!(c);
10+
| ------------------------- in this macro invocation
11+
|
12+
help: if you want to compare the casted value then write:
13+
|
14+
13 | ($i as u32) < 0
15+
| ^^^^^^^^^^^
16+
17+
error: aborting due to previous error
18+

0 commit comments

Comments
 (0)