Skip to content

Commit 940f657

Browse files
committed
Parse & reject postfix operators after casts
This adds parsing for expressions like 'x as Ty[0]' which will immediately error out, but still give the rest of the parser a valid parse tree to continue.
1 parent 8ba3ca0 commit 940f657

File tree

3 files changed

+176
-2
lines changed

3 files changed

+176
-2
lines changed

src/librustc_parse/parser/expr.rs

+39-2
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ impl<'a> Parser<'a> {
551551
// Save the state of the parser before parsing type normally, in case there is a
552552
// LessThan comparison after this cast.
553553
let parser_snapshot_before_type = self.clone();
554-
match self.parse_ty_no_plus() {
554+
let type_result = match self.parse_ty_no_plus() {
555555
Ok(rhs) => Ok(mk_expr(self, rhs)),
556556
Err(mut type_err) => {
557557
// Rewind to before attempting to parse the type with generics, to recover
@@ -616,7 +616,44 @@ impl<'a> Parser<'a> {
616616
}
617617
}
618618
}
619-
}
619+
};
620+
621+
// Disallow postfix operators such as `.`, `?` or index (`[]`) after casts.
622+
// Parses the postfix operator and emits an error.
623+
let expr = type_result?;
624+
let span = expr.span;
625+
626+
// The resulting parse tree for `&x as T[0]` has a precedence of `((&x) as T)[0]`.
627+
let with_postfix = self.parse_dot_or_call_expr_with_(expr, span)?;
628+
if !matches!(with_postfix.kind, ExprKind::Cast(_, _)) {
629+
let expr_str = self.span_to_snippet(span);
630+
631+
let msg = format!(
632+
"casts followed by {} are not supported",
633+
match with_postfix.kind {
634+
ExprKind::Index(_, _) => "index operators",
635+
ExprKind::Try(_) => "try operators",
636+
ExprKind::Field(_, _) => "field access expressions",
637+
ExprKind::MethodCall(_, _) => "method call expressions",
638+
ExprKind::Await(_) => "awaits",
639+
_ => "expressions",
640+
}
641+
);
642+
let mut err = self.struct_span_err(with_postfix.span, &msg);
643+
let suggestion = "try surrounding the expression with parentheses";
644+
if let Ok(expr_str) = expr_str {
645+
err.span_suggestion(
646+
span,
647+
suggestion,
648+
format!("({})", expr_str),
649+
Applicability::MachineApplicable,
650+
)
651+
} else {
652+
err.span_help(span, suggestion)
653+
}
654+
.emit();
655+
};
656+
Ok(with_postfix)
620657
}
621658

622659
fn parse_assoc_op_ascribe(&mut self, lhs: P<Expr>, lhs_span: Span) -> PResult<'a, P<Expr>> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// edition:2018
2+
#![crate_type = "lib"]
3+
use std::future::Future;
4+
use std::pin::Pin;
5+
6+
// This tests the parser for "x as Y[z]". It errors, but we want to give useful
7+
// errors and parse such that further code gives useful errors.
8+
pub fn index_after_as_cast() {
9+
vec![1, 2, 3] as Vec<i32>[0];
10+
//~^ ERROR: casts followed by index operators are not supported
11+
}
12+
13+
pub fn index_after_cast_to_index() {
14+
(&[0]) as &[i32][0];
15+
//~^ ERROR: casts followed by index operators are not supported
16+
}
17+
18+
// this tests that the precedence for `!x as Y.Z` is still what we expect
19+
pub fn precedence() {
20+
let x: i32 = &vec![1, 2, 3] as &Vec<i32>[0];
21+
//~^ ERROR: casts followed by index operators are not supported
22+
}
23+
24+
pub fn complex() {
25+
let _ = format!(
26+
"{}",
27+
if true { 33 } else { 44 } as i32.max(0)
28+
//~^ ERROR: casts followed by method call expressions are not supported
29+
);
30+
}
31+
32+
pub fn in_condition() {
33+
if 5u64 as i32.max(0) == 0 {
34+
//~^ ERROR: casts followed by method call expressions are not supported
35+
}
36+
}
37+
38+
pub fn inside_block() {
39+
let _ = if true {
40+
5u64 as u32.max(0) == 0
41+
//~^ ERROR: casts followed by method call expressions are not supported
42+
} else { false };
43+
}
44+
45+
static bar: &[i32] = &(&[1,2,3] as &[i32][0..1]);
46+
//~^ ERROR: casts followed by index operators are not supported
47+
48+
pub async fn cast_then_await() {
49+
Box::pin(noop()) as Pin<Box<dyn Future<Output = ()>>>.await;
50+
//~^ ERROR: casts followed by awaits are not supported
51+
}
52+
53+
pub async fn noop() {}
54+
55+
#[derive(Default)]
56+
pub struct Foo {
57+
pub bar: u32,
58+
}
59+
60+
pub fn struct_field() {
61+
Foo::default() as Foo.bar;
62+
//~^ ERROR: casts followed by field access expressions are not supported
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
error: casts followed by index operators are not supported
2+
--> $DIR/issue-35813-postfix-after-cast.rs:9:5
3+
|
4+
LL | vec![1, 2, 3] as Vec<i32>[0];
5+
| -------------------------^^^
6+
| |
7+
| help: try surrounding the expression with parentheses: `(vec![1, 2, 3] as Vec<i32>)`
8+
9+
error: casts followed by index operators are not supported
10+
--> $DIR/issue-35813-postfix-after-cast.rs:14:5
11+
|
12+
LL | (&[0]) as &[i32][0];
13+
| ----------------^^^
14+
| |
15+
| help: try surrounding the expression with parentheses: `((&[0]) as &[i32])`
16+
17+
error: casts followed by index operators are not supported
18+
--> $DIR/issue-35813-postfix-after-cast.rs:20:18
19+
|
20+
LL | let x: i32 = &vec![1, 2, 3] as &Vec<i32>[0];
21+
| ---------------------------^^^
22+
| |
23+
| help: try surrounding the expression with parentheses: `(&vec![1, 2, 3] as &Vec<i32>)`
24+
25+
error: casts followed by method call expressions are not supported
26+
--> $DIR/issue-35813-postfix-after-cast.rs:33:8
27+
|
28+
LL | if 5u64 as i32.max(0) == 0 {
29+
| -----------^^^^^^^
30+
| |
31+
| help: try surrounding the expression with parentheses: `(5u64 as i32)`
32+
33+
error: casts followed by method call expressions are not supported
34+
--> $DIR/issue-35813-postfix-after-cast.rs:40:9
35+
|
36+
LL | 5u64 as u32.max(0) == 0
37+
| -----------^^^^^^^
38+
| |
39+
| help: try surrounding the expression with parentheses: `(5u64 as u32)`
40+
41+
error: casts followed by index operators are not supported
42+
--> $DIR/issue-35813-postfix-after-cast.rs:45:24
43+
|
44+
LL | static bar: &[i32] = &(&[1,2,3] as &[i32][0..1]);
45+
| ------------------^^^^^^
46+
| |
47+
| help: try surrounding the expression with parentheses: `(&[1,2,3] as &[i32])`
48+
49+
error: casts followed by awaits are not supported
50+
--> $DIR/issue-35813-postfix-after-cast.rs:49:5
51+
|
52+
LL | Box::pin(noop()) as Pin<Box<dyn Future<Output = ()>>>.await;
53+
| -----------------------------------------------------^^^^^^
54+
| |
55+
| help: try surrounding the expression with parentheses: `(Box::pin(noop()) as Pin<Box<dyn Future<Output = ()>>>)`
56+
57+
error: casts followed by field access expressions are not supported
58+
--> $DIR/issue-35813-postfix-after-cast.rs:61:5
59+
|
60+
LL | Foo::default() as Foo.bar;
61+
| ---------------------^^^^
62+
| |
63+
| help: try surrounding the expression with parentheses: `(Foo::default() as Foo)`
64+
65+
error: casts followed by method call expressions are not supported
66+
--> $DIR/issue-35813-postfix-after-cast.rs:27:9
67+
|
68+
LL | if true { 33 } else { 44 } as i32.max(0)
69+
| ---------------------------------^^^^^^^
70+
| |
71+
| help: try surrounding the expression with parentheses: `(if true { 33 } else { 44 } as i32)`
72+
73+
error: aborting due to 9 previous errors
74+

0 commit comments

Comments
 (0)