Skip to content

Commit 4323877

Browse files
committed
syntax: Apply recovery for casts to type ascription
Fix spans, add some comments
1 parent 5fa1c1b commit 4323877

File tree

5 files changed

+155
-79
lines changed

5 files changed

+155
-79
lines changed

src/libsyntax/parse/parser.rs

+53-46
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,16 @@ impl<'a> Parser<'a> {
10811081
None => token::CloseDelim(self.token_cursor.frame.delim),
10821082
})
10831083
}
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+
}
10841094
pub fn fatal(&self, m: &str) -> DiagnosticBuilder<'a> {
10851095
self.sess.span_diagnostic.struct_span_fatal(self.span, m)
10861096
}
@@ -2805,13 +2815,10 @@ impl<'a> Parser<'a> {
28052815
}
28062816
// Special cases:
28072817
if op == AssocOp::As {
2808-
// Save the state of the parser before parsing type normally, in case there is a
2809-
// LessThan comparison after this cast.
2810-
lhs = self.parse_assoc_op_as(lhs, lhs_span)?;
2818+
lhs = self.parse_assoc_op_cast(lhs, lhs_span, ExprKind::Cast)?;
28112819
continue
28122820
} else if op == AssocOp::Colon {
2813-
let rhs = self.parse_ty_no_plus()?;
2814-
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)?;
28152822
continue
28162823
} else if op == AssocOp::DotDot || op == AssocOp::DotDotDot {
28172824
// If we didn’t have to handle `x..`/`x...`, it would be pretty easy to
@@ -2905,61 +2912,61 @@ impl<'a> Parser<'a> {
29052912
Ok(lhs)
29062913
}
29072914

2908-
fn parse_assoc_op_as(&mut self, lhs: P<Expr>, lhs_span: Span) -> PResult<'a, P<Expr>> {
2909-
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();
29102925
match self.parse_ty_no_plus() {
29112926
Ok(rhs) => {
2912-
Ok(self.mk_expr(lhs_span.to(rhs.span),
2913-
ExprKind::Cast(lhs, rhs),
2914-
ThinVec::new()))
2927+
Ok(mk_expr(self, rhs))
29152928
}
2916-
Err(mut err) => {
2917-
let rp_err = self.clone();
2918-
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);
29192935

2920-
// Rewind to before attempting to parse the type with generics, to get
2921-
// arround #22644.
2922-
mem::replace(self, rp);
2923-
let lo = self.span;
29242936
match self.parse_path_without_generics(PathStyle::Type) {
29252937
Ok(path) => {
2926-
// Successfully parsed the type leaving a `<` yet to parse
2927-
err.cancel();
2928-
let codemap = self.sess.codemap();
2929-
let suggestion_span = lhs_span.to(self.prev_span);
2930-
let warn_message = match codemap.span_to_snippet(self.prev_span) {
2931-
Ok(lstring) => format!("`{}`", lstring),
2932-
_ => "a type".to_string(),
2933-
};
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.
29342943
let msg = format!("`<` is interpreted as a start of generic \
2935-
arguments for {}, not a comparison",
2936-
warn_message);
2937-
let mut err = self.sess.span_diagnostic.struct_span_err(sp, &msg);
2938-
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");
29392948
err.span_label(self.span, "not interpreted as comparison");
2940-
let suggestion = match codemap.span_to_snippet(suggestion_span) {
2941-
Ok(lstring) => format!("({})", lstring),
2942-
_ => format!("(<expression> as <type>)")
2943-
};
2944-
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,
29452959
"if you want to compare the casted value then write:",
2946-
suggestion);
2960+
format!("({})", expr_str));
29472961
err.emit();
29482962

2949-
let path = TyKind::Path(None, path);
2950-
let span = lo.to(self.prev_span);
2951-
let rhs = P(Ty { node: path, span: span, id: ast::DUMMY_NODE_ID });
2952-
// Letting the parser accept the recovered type to avoid further errors,
2953-
// but the code will still not compile due to the error emitted above.
2954-
Ok(self.mk_expr(lhs_span.to(rhs.span),
2955-
ExprKind::Cast(lhs, rhs),
2956-
ThinVec::new()))
2963+
Ok(expr)
29572964
}
29582965
Err(mut path_err) => {
2959-
// Still couldn't parse, return original error and parser state
2966+
// Couldn't parse as a path, return original error and parser state.
29602967
path_err.cancel();
2961-
mem::replace(self, rp_err);
2962-
Err(err)
2968+
mem::replace(self, parser_snapshot_after_type);
2969+
Err(type_err)
29632970
}
29642971
}
29652972
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
macro_rules! is_plainly_printable {
1212
($i: ident) => {
13-
$i as u32 < 0
13+
$i as u32 < 0
1414
};
1515
}
1616

src/test/ui/issue-42954.stderr

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
error: `<` is interpreted as a start of generic arguments for `u32`, not a comparison
22
--> $DIR/issue-42954.rs:13:19
33
|
4-
13 | $i as u32 < 0
5-
| - ^ interpreted as generic argument
6-
| |
7-
| not interpreted as comparison
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
811
|
912
help: if you want to compare the casted value then write:
10-
| ($i as u32) < 0
13+
|
14+
13 | ($i as u32) < 0
15+
| ^^^^^^^^^^^
1116

1217
error: aborting due to previous error
1318

0 commit comments

Comments
 (0)