diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index 20b9df0a2d9b6..d7d3145770ca1 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -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, lhs_span: Span) -> PResult<'a, P> { diff --git a/src/test/ui/parser/issue-35813-postfix-after-cast.rs b/src/test/ui/parser/issue-35813-postfix-after-cast.rs new file mode 100644 index 0000000000000..dd608b263ec6c --- /dev/null +++ b/src/test/ui/parser/issue-35813-postfix-after-cast.rs @@ -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[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[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>>.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 +} diff --git a/src/test/ui/parser/issue-35813-postfix-after-cast.stderr b/src/test/ui/parser/issue-35813-postfix-after-cast.stderr new file mode 100644 index 0000000000000..9459e076ea0c5 --- /dev/null +++ b/src/test/ui/parser/issue-35813-postfix-after-cast.stderr @@ -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[0]; + | -------------------------^^^ + | | + | help: try surrounding the expression with parentheses: `(vec![1, 2, 3] as Vec)` + +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[0]; + | ---------------------------^^^ + | | + | help: try surrounding the expression with parentheses: `(&vec![1, 2, 3] as &Vec)` + +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>>.await; + | -----------------------------------------------------^^^^^^ + | | + | help: try surrounding the expression with parentheses: `(Box::pin(noop()) as Pin>>)` + +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 +