Skip to content

Commit

Permalink
Parse & reject postfix operators after casts
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
daboross committed Feb 16, 2020
1 parent 8ba3ca0 commit 940f657
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 2 deletions.
41 changes: 39 additions & 2 deletions src/librustc_parse/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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>> {
Expand Down
63 changes: 63 additions & 0 deletions src/test/ui/parser/issue-35813-postfix-after-cast.rs
Original file line number Diff line number Diff line change
@@ -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
}
74 changes: 74 additions & 0 deletions src/test/ui/parser/issue-35813-postfix-after-cast.stderr
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 940f657

Please sign in to comment.