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.
This commit is contained in:
David Ross 2020-02-08 21:34:38 -08:00
parent 8ba3ca0e6b
commit 940f65782c
3 changed files with 176 additions and 2 deletions

View file

@ -551,7 +551,7 @@ impl<'a> Parser<'a> {
// Save the state of the parser before parsing type normally, in case there is a
// LessThan comparison after this cast.
let parser_snapshot_before_type = self.clone();
match self.parse_ty_no_plus() {
let type_result = match self.parse_ty_no_plus() {
Ok(rhs) => Ok(mk_expr(self, rhs)),
Err(mut type_err) => {
// Rewind to before attempting to parse the type with generics, to recover
@ -616,7 +616,44 @@ impl<'a> Parser<'a> {
}
}
}
}
};
// Disallow postfix operators such as `.`, `?` or index (`[]`) after casts.
// Parses the postfix operator and emits an error.
let expr = type_result?;
let span = expr.span;
// The resulting parse tree for `&x as T[0]` has a precedence of `((&x) as T)[0]`.
let with_postfix = self.parse_dot_or_call_expr_with_(expr, span)?;
if !matches!(with_postfix.kind, ExprKind::Cast(_, _)) {
let expr_str = self.span_to_snippet(span);
let msg = format!(
"casts followed by {} are not supported",
match with_postfix.kind {
ExprKind::Index(_, _) => "index operators",
ExprKind::Try(_) => "try operators",
ExprKind::Field(_, _) => "field access expressions",
ExprKind::MethodCall(_, _) => "method call expressions",
ExprKind::Await(_) => "awaits",
_ => "expressions",
}
);
let mut err = self.struct_span_err(with_postfix.span, &msg);
let suggestion = "try surrounding the expression with parentheses";
if let Ok(expr_str) = expr_str {
err.span_suggestion(
span,
suggestion,
format!("({})", expr_str),
Applicability::MachineApplicable,
)
} else {
err.span_help(span, suggestion)
}
.emit();
};
Ok(with_postfix)
}
fn parse_assoc_op_ascribe(&mut self, lhs: P<Expr>, lhs_span: Span) -> PResult<'a, P<Expr>> {

View file

@ -0,0 +1,63 @@
// edition:2018
#![crate_type = "lib"]
use std::future::Future;
use std::pin::Pin;
// This tests the parser for "x as Y[z]". It errors, but we want to give useful
// errors and parse such that further code gives useful errors.
pub fn index_after_as_cast() {
vec![1, 2, 3] as Vec<i32>[0];
//~^ ERROR: casts followed by index operators are not supported
}
pub fn index_after_cast_to_index() {
(&[0]) as &[i32][0];
//~^ ERROR: casts followed by index operators are not supported
}
// this tests that the precedence for `!x as Y.Z` is still what we expect
pub fn precedence() {
let x: i32 = &vec![1, 2, 3] as &Vec<i32>[0];
//~^ ERROR: casts followed by index operators are not supported
}
pub fn complex() {
let _ = format!(
"{}",
if true { 33 } else { 44 } as i32.max(0)
//~^ ERROR: casts followed by method call expressions are not supported
);
}
pub fn in_condition() {
if 5u64 as i32.max(0) == 0 {
//~^ ERROR: casts followed by method call expressions are not supported
}
}
pub fn inside_block() {
let _ = if true {
5u64 as u32.max(0) == 0
//~^ ERROR: casts followed by method call expressions are not supported
} else { false };
}
static bar: &[i32] = &(&[1,2,3] as &[i32][0..1]);
//~^ ERROR: casts followed by index operators are not supported
pub async fn cast_then_await() {
Box::pin(noop()) as Pin<Box<dyn Future<Output = ()>>>.await;
//~^ ERROR: casts followed by awaits are not supported
}
pub async fn noop() {}
#[derive(Default)]
pub struct Foo {
pub bar: u32,
}
pub fn struct_field() {
Foo::default() as Foo.bar;
//~^ ERROR: casts followed by field access expressions are not supported
}

View file

@ -0,0 +1,74 @@
error: casts followed by index operators are not supported
--> $DIR/issue-35813-postfix-after-cast.rs:9:5
|
LL | vec![1, 2, 3] as Vec<i32>[0];
| -------------------------^^^
| |
| help: try surrounding the expression with parentheses: `(vec![1, 2, 3] as Vec<i32>)`
error: casts followed by index operators are not supported
--> $DIR/issue-35813-postfix-after-cast.rs:14:5
|
LL | (&[0]) as &[i32][0];
| ----------------^^^
| |
| help: try surrounding the expression with parentheses: `((&[0]) as &[i32])`
error: casts followed by index operators are not supported
--> $DIR/issue-35813-postfix-after-cast.rs:20:18
|
LL | let x: i32 = &vec![1, 2, 3] as &Vec<i32>[0];
| ---------------------------^^^
| |
| help: try surrounding the expression with parentheses: `(&vec![1, 2, 3] as &Vec<i32>)`
error: casts followed by method call expressions are not supported
--> $DIR/issue-35813-postfix-after-cast.rs:33:8
|
LL | if 5u64 as i32.max(0) == 0 {
| -----------^^^^^^^
| |
| help: try surrounding the expression with parentheses: `(5u64 as i32)`
error: casts followed by method call expressions are not supported
--> $DIR/issue-35813-postfix-after-cast.rs:40:9
|
LL | 5u64 as u32.max(0) == 0
| -----------^^^^^^^
| |
| help: try surrounding the expression with parentheses: `(5u64 as u32)`
error: casts followed by index operators are not supported
--> $DIR/issue-35813-postfix-after-cast.rs:45:24
|
LL | static bar: &[i32] = &(&[1,2,3] as &[i32][0..1]);
| ------------------^^^^^^
| |
| help: try surrounding the expression with parentheses: `(&[1,2,3] as &[i32])`
error: casts followed by awaits are not supported
--> $DIR/issue-35813-postfix-after-cast.rs:49:5
|
LL | Box::pin(noop()) as Pin<Box<dyn Future<Output = ()>>>.await;
| -----------------------------------------------------^^^^^^
| |
| help: try surrounding the expression with parentheses: `(Box::pin(noop()) as Pin<Box<dyn Future<Output = ()>>>)`
error: casts followed by field access expressions are not supported
--> $DIR/issue-35813-postfix-after-cast.rs:61:5
|
LL | Foo::default() as Foo.bar;
| ---------------------^^^^
| |
| help: try surrounding the expression with parentheses: `(Foo::default() as Foo)`
error: casts followed by method call expressions are not supported
--> $DIR/issue-35813-postfix-after-cast.rs:27:9
|
LL | if true { 33 } else { 44 } as i32.max(0)
| ---------------------------------^^^^^^^
| |
| help: try surrounding the expression with parentheses: `(if true { 33 } else { 44 } as i32)`
error: aborting due to 9 previous errors