From 9b5a974bd5c398e5706e463045121b20f0f6abb9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Esteban=20K=C3=BCber?= Date: Wed, 22 Jul 2020 12:01:56 -0700 Subject: [PATCH] Correctly parse `{} && false` in tail expression Fix #74233. --- src/librustc_ast/util/parser.rs | 1 - src/librustc_parse/parser/expr.rs | 11 ++++++-- src/librustc_typeck/check/demand.rs | 1 + src/librustc_typeck/check/mod.rs | 8 ++++++ src/test/ui/parser/expr-as-stmt-2.rs | 10 +++++++ src/test/ui/parser/expr-as-stmt-2.stderr | 33 ++++++++++++++++++++++++ src/test/ui/parser/expr-as-stmt.fixed | 6 ----- src/test/ui/parser/expr-as-stmt.rs | 6 ----- src/test/ui/parser/expr-as-stmt.stderr | 12 ++------- 9 files changed, 63 insertions(+), 25 deletions(-) create mode 100644 src/test/ui/parser/expr-as-stmt-2.rs create mode 100644 src/test/ui/parser/expr-as-stmt-2.stderr diff --git a/src/librustc_ast/util/parser.rs b/src/librustc_ast/util/parser.rs index e5bcc571d41..2ee94965756 100644 --- a/src/librustc_ast/util/parser.rs +++ b/src/librustc_ast/util/parser.rs @@ -222,7 +222,6 @@ pub fn can_continue_expr_unambiguously(&self) -> bool { Greater | // `{ 42 } > 3` GreaterEqual | // `{ 42 } >= 3` AssignOp(_) | // `{ 42 } +=` - LAnd | // `{ 42 } &&foo` As | // `{ 42 } as usize` // Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery. diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index 3926122606e..223986635a0 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -295,11 +295,18 @@ fn should_continue_as_assoc_expr(&mut self, lhs: &Expr) -> bool { // want to keep their span info to improve diagnostics in these cases in a later stage. (true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3` (true, Some(AssocOp::Subtract)) | // `{ 42 } -5` - (true, Some(AssocOp::LAnd)) | // `{ 42 } &&x` (#61475) (true, Some(AssocOp::Add)) // `{ 42 } + 42 // If the next token is a keyword, then the tokens above *are* unambiguously incorrect: // `if x { a } else { b } && if y { c } else { d }` - if !self.look_ahead(1, |t| t.is_reserved_ident()) => { + if !self.look_ahead(1, |t| t.is_used_keyword()) => { + // These cases are ambiguous and can't be identified in the parser alone. + let sp = self.sess.source_map().start_point(self.token.span); + self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span); + false + } + (true, Some(AssocOp::LAnd)) => { + // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`. Separated from the + // above due to #74233. // These cases are ambiguous and can't be identified in the parser alone. let sp = self.sess.source_map().start_point(self.token.span); self.sess.ambiguous_block_expr_parse.borrow_mut().insert(sp, lhs.span); diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs index be83ab259c2..46303a99278 100644 --- a/src/librustc_typeck/check/demand.rs +++ b/src/librustc_typeck/check/demand.rs @@ -34,6 +34,7 @@ pub fn emit_coerce_suggestions( } self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty); self.suggest_missing_await(err, expr, expected, expr_ty); + self.suggest_missing_parentheses(err, expr); self.note_need_for_fn_pointer(err, expected, expr_ty); } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 04e02704296..57de1780c65 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -5403,6 +5403,14 @@ fn suggest_missing_await( } } + fn suggest_missing_parentheses(&self, err: &mut DiagnosticBuilder<'_>, expr: &hir::Expr<'_>) { + let sp = self.tcx.sess.source_map().start_point(expr.span); + if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) { + // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` + self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None); + } + } + fn note_need_for_fn_pointer( &self, err: &mut DiagnosticBuilder<'_>, diff --git a/src/test/ui/parser/expr-as-stmt-2.rs b/src/test/ui/parser/expr-as-stmt-2.rs new file mode 100644 index 00000000000..3a18bdc3b73 --- /dev/null +++ b/src/test/ui/parser/expr-as-stmt-2.rs @@ -0,0 +1,10 @@ +// This is not autofixable because we give extra suggestions to end the first expression with `;`. +fn foo(a: Option, b: Option) -> bool { + if let Some(x) = a { true } else { false } + //~^ ERROR mismatched types + //~| ERROR mismatched types + && //~ ERROR mismatched types + if let Some(y) = a { true } else { false } +} + +fn main() {} diff --git a/src/test/ui/parser/expr-as-stmt-2.stderr b/src/test/ui/parser/expr-as-stmt-2.stderr new file mode 100644 index 00000000000..ee07c367633 --- /dev/null +++ b/src/test/ui/parser/expr-as-stmt-2.stderr @@ -0,0 +1,33 @@ +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:3:26 + | +LL | if let Some(x) = a { true } else { false } + | ---------------------^^^^------------------ help: consider using a semicolon here + | | | + | | expected `()`, found `bool` + | expected this to be `()` + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:3:40 + | +LL | if let Some(x) = a { true } else { false } + | -----------------------------------^^^^^--- help: consider using a semicolon here + | | | + | | expected `()`, found `bool` + | expected this to be `()` + +error[E0308]: mismatched types + --> $DIR/expr-as-stmt-2.rs:6:5 + | +LL | fn foo(a: Option, b: Option) -> bool { + | ---- expected `bool` because of return type +LL | if let Some(x) = a { true } else { false } + | ------------------------------------------ help: parentheses are required to parse this as an expression: `(if let Some(x) = a { true } else { false })` +... +LL | / && +LL | | if let Some(y) = a { true } else { false } + | |______________________________________________^ expected `bool`, found `&&bool` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/parser/expr-as-stmt.fixed b/src/test/ui/parser/expr-as-stmt.fixed index 1ce6f9c2503..02816ef2791 100644 --- a/src/test/ui/parser/expr-as-stmt.fixed +++ b/src/test/ui/parser/expr-as-stmt.fixed @@ -25,12 +25,6 @@ fn baz() -> i32 { //~^ ERROR mismatched types } -fn qux(a: Option, b: Option) -> bool { - (if let Some(x) = a { true } else { false }) - && //~ ERROR expected expression - if let Some(y) = a { true } else { false } -} - fn moo(x: u32) -> bool { (match x { _ => 1, diff --git a/src/test/ui/parser/expr-as-stmt.rs b/src/test/ui/parser/expr-as-stmt.rs index b526c17488e..93baa8278f8 100644 --- a/src/test/ui/parser/expr-as-stmt.rs +++ b/src/test/ui/parser/expr-as-stmt.rs @@ -25,12 +25,6 @@ fn baz() -> i32 { //~^ ERROR mismatched types } -fn qux(a: Option, b: Option) -> bool { - if let Some(x) = a { true } else { false } - && //~ ERROR expected expression - if let Some(y) = a { true } else { false } -} - fn moo(x: u32) -> bool { match x { _ => 1, diff --git a/src/test/ui/parser/expr-as-stmt.stderr b/src/test/ui/parser/expr-as-stmt.stderr index 4d93e130901..324aed0ad7c 100644 --- a/src/test/ui/parser/expr-as-stmt.stderr +++ b/src/test/ui/parser/expr-as-stmt.stderr @@ -22,16 +22,8 @@ LL | { 42 } + foo; | | | help: parentheses are required to parse this as an expression: `({ 42 })` -error: expected expression, found `&&` - --> $DIR/expr-as-stmt.rs:30:5 - | -LL | if let Some(x) = a { true } else { false } - | ------------------------------------------ help: parentheses are required to parse this as an expression: `(if let Some(x) = a { true } else { false })` -LL | && - | ^^ expected expression - error: expected expression, found `>` - --> $DIR/expr-as-stmt.rs:37:7 + --> $DIR/expr-as-stmt.rs:31:7 | LL | } > 0 | ^ expected expression @@ -75,7 +67,7 @@ LL | { 3 } * 3 | | | help: parentheses are required to parse this as an expression: `({ 3 })` -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0308, E0614. For more information about an error, try `rustc --explain E0308`. -- 2.44.0