From f4931437c249ed5275ae20eb4ac58e3e4484fa98 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 12 May 2024 12:49:49 -0700 Subject: [PATCH 1/3] Clean up unneeded warnings from let-else syntax test --- tests/ui/parser/bad-let-else-statement.rs | 59 ++-- tests/ui/parser/bad-let-else-statement.stderr | 300 +++--------------- 2 files changed, 69 insertions(+), 290 deletions(-) diff --git a/tests/ui/parser/bad-let-else-statement.rs b/tests/ui/parser/bad-let-else-statement.rs index de41a07568fb1..8ed55f949598e 100644 --- a/tests/ui/parser/bad-let-else-statement.rs +++ b/tests/ui/parser/bad-let-else-statement.rs @@ -3,8 +3,7 @@ #![feature(explicit_tail_calls)] fn a() { - let foo = { - //~^ WARN irrefutable `let...else` pattern + let 0 = { 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -22,8 +21,7 @@ fn b() { } fn c() { - let foo = if true { - //~^ WARN irrefutable `let...else` pattern + let 0 = if true { 1 } else { 0 @@ -43,8 +41,7 @@ fn d() { } fn e() { - let foo = match true { - //~^ WARN irrefutable `let...else` pattern + let 0 = match true { true => 1, false => 0 } else { @@ -53,10 +50,12 @@ fn e() { }; } -struct X {a: i32} fn f() { - let foo = X { - //~^ WARN irrefutable `let...else` pattern + struct X { + a: i32, + } + + let X { a: 0 } = X { a: 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -74,8 +73,7 @@ fn g() { } fn h() { - let foo = const { - //~^ WARN irrefutable `let...else` pattern + let 0 = const { 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -84,8 +82,7 @@ fn h() { } fn i() { - let foo = &{ - //~^ WARN irrefutable `let...else` pattern + let 0 = &{ 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -94,8 +91,8 @@ fn i() { } fn j() { - let bar = 0; - let foo = bar = { //~ ERROR: cannot assign twice + let mut bar = 0; + let foo = bar = { //~^ WARN irrefutable `let...else` pattern 1 } else { @@ -105,8 +102,7 @@ fn j() { } fn k() { - let foo = 1 + { - //~^ WARN irrefutable `let...else` pattern + let 0 = 1 + { 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -115,8 +111,8 @@ fn k() { } fn l() { - let foo = 1..{ - //~^ WARN irrefutable `let...else` pattern + const RANGE: std::ops::Range = 0..0; + let RANGE = 1..{ 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -125,8 +121,7 @@ fn l() { } fn m() { - let foo = return { - //~^ WARN irrefutable `let...else` pattern + let 0 = return { () } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -135,8 +130,7 @@ fn m() { } fn n() { - let foo = -{ - //~^ WARN irrefutable `let...else` pattern + let 0 = -{ 1 } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -145,8 +139,7 @@ fn n() { } fn o() -> Result<(), ()> { - let foo = do yeet { - //~^ WARN irrefutable `let...else` pattern + let 0 = do yeet { () } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -155,8 +148,7 @@ fn o() -> Result<(), ()> { } fn p() { - let foo = become { - //~^ WARN irrefutable `let...else` pattern + let 0 = become { () } else { //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed @@ -185,22 +177,23 @@ fn r() { fn s() { macro_rules! a { - () => { {} } - //~^ WARN irrefutable `let...else` pattern - //~| WARN irrefutable `let...else` pattern + () => { + { 1 } + }; } macro_rules! b { (1) => { - let x = a!() else { return; }; + let 0 = a!() else { return; }; }; (2) => { - let x = a! {} else { return; }; + let 0 = a! {} else { return; }; //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed }; } - b!(1); b!(2); + b!(1); + b!(2); } fn main() {} diff --git a/tests/ui/parser/bad-let-else-statement.stderr b/tests/ui/parser/bad-let-else-statement.stderr index 3f7e176b3e31f..60b4600ff35d8 100644 --- a/tests/ui/parser/bad-let-else-statement.stderr +++ b/tests/ui/parser/bad-let-else-statement.stderr @@ -1,19 +1,18 @@ error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:9:5 + --> $DIR/bad-let-else-statement.rs:8:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = ({ -LL | +LL ~ let 0 = ({ LL | 1 LL ~ }) else { | error: `for...else` loops are not supported - --> $DIR/bad-let-else-statement.rs:18:7 + --> $DIR/bad-let-else-statement.rs:17:7 | LL | let foo = for i in 1..2 { | --- `else` is attached to this loop @@ -28,22 +27,22 @@ LL | | }; = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:30:5 + --> $DIR/bad-let-else-statement.rs:28:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = (if true { -LL | - ... +LL ~ let 0 = (if true { +LL | 1 +LL | } else { LL | 0 LL ~ }) else { | error: `loop...else` loops are not supported - --> $DIR/bad-let-else-statement.rs:39:7 + --> $DIR/bad-let-else-statement.rs:37:7 | LL | let foo = loop { | ---- `else` is attached to this loop @@ -58,36 +57,34 @@ LL | | }; = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:50:5 + --> $DIR/bad-let-else-statement.rs:47:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = (match true { -LL | +LL ~ let 0 = (match true { LL | true => 1, LL | false => 0 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:61:5 + --> $DIR/bad-let-else-statement.rs:60:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = (X { -LL | +LL ~ let X { a: 0 } = (X { LL | a: 1 LL ~ }) else { | error: `while...else` loops are not supported - --> $DIR/bad-let-else-statement.rs:70:7 + --> $DIR/bad-let-else-statement.rs:69:7 | LL | let foo = while false { | ----- `else` is attached to this loop @@ -102,35 +99,33 @@ LL | | }; = note: consider moving this `else` clause to a separate `if` statement and use a `bool` variable to control if it should run error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:80:5 + --> $DIR/bad-let-else-statement.rs:78:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = (const { -LL | +LL ~ let 0 = (const { LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:90:5 + --> $DIR/bad-let-else-statement.rs:87:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = &({ -LL | +LL ~ let 0 = &({ LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:101:5 + --> $DIR/bad-let-else-statement.rs:98:5 | LL | } else { | ^ @@ -144,91 +139,85 @@ LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:111:5 + --> $DIR/bad-let-else-statement.rs:107:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = 1 + ({ -LL | +LL ~ let 0 = 1 + ({ LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:121:5 + --> $DIR/bad-let-else-statement.rs:117:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = 1..({ -LL | +LL ~ let RANGE = 1..({ LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:131:5 + --> $DIR/bad-let-else-statement.rs:126:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = return ({ -LL | +LL ~ let 0 = return ({ LL | () LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:141:5 + --> $DIR/bad-let-else-statement.rs:135:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = -({ -LL | +LL ~ let 0 = -({ LL | 1 LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:151:5 + --> $DIR/bad-let-else-statement.rs:144:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = do yeet ({ -LL | +LL ~ let 0 = do yeet ({ LL | () LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:161:5 + --> $DIR/bad-let-else-statement.rs:153:5 | LL | } else { | ^ | help: wrap the expression in parentheses | -LL ~ let foo = become ({ -LL | +LL ~ let 0 = become ({ LL | () LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:171:5 + --> $DIR/bad-let-else-statement.rs:163:5 | LL | } else { | ^ @@ -242,7 +231,7 @@ LL ~ }) else { | error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:181:31 + --> $DIR/bad-let-else-statement.rs:173:31 | LL | let bad = format_args! {""} else { return; }; | ^ @@ -253,98 +242,22 @@ LL | let bad = format_args! ("") else { return; }; | ~ ~ error: right curly brace `}` before `else` in a `let...else` statement not allowed - --> $DIR/bad-let-else-statement.rs:198:25 + --> $DIR/bad-let-else-statement.rs:190:25 | -LL | let x = a! {} else { return; }; +LL | let 0 = a! {} else { return; }; | ^ ... -LL | b!(1); b!(2); - | ----- in this macro invocation +LL | b!(2); + | ----- in this macro invocation | = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) help: use parentheses instead of braces for this macro | -LL | let x = a! () else { return; }; +LL | let 0 = a! () else { return; }; | ~~ warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:6:5 - | -LL | / let foo = { -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - = note: `#[warn(irrefutable_let_patterns)]` on by default - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:25:5 - | -LL | / let foo = if true { -LL | | -LL | | 1 -LL | | } else { -LL | | 0 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:46:5 - | -LL | / let foo = match true { -LL | | -LL | | true => 1, -LL | | false => 0 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:58:5 - | -LL | / let foo = X { -LL | | -LL | | a: 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:77:5 - | -LL | / let foo = const { -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:87:5 - | -LL | / let foo = &{ -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:98:5 + --> $DIR/bad-let-else-statement.rs:95:5 | LL | / let foo = bar = { LL | | @@ -354,96 +267,10 @@ LL | | } else { | = note: this pattern will always match, so the `else` clause is useless = help: consider removing the `else` clause - -error[E0384]: cannot assign twice to immutable variable `bar` - --> $DIR/bad-let-else-statement.rs:98:15 - | -LL | let bar = 0; - | --- - | | - | first assignment to `bar` - | help: consider making this binding mutable: `mut bar` -LL | let foo = bar = { - | _______________^ -LL | | -LL | | 1 -LL | | } else { - | |_____^ cannot assign twice to immutable variable - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:108:5 - | -LL | / let foo = 1 + { -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:118:5 - | -LL | / let foo = 1..{ -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:128:5 - | -LL | / let foo = return { -LL | | -LL | | () -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:138:5 - | -LL | / let foo = -{ -LL | | -LL | | 1 -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:148:5 - | -LL | / let foo = do yeet { -LL | | -LL | | () -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:158:5 - | -LL | / let foo = become { -LL | | -LL | | () -LL | | } else { - | |_____^ - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause + = note: `#[warn(irrefutable_let_patterns)]` on by default warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:168:5 + --> $DIR/bad-let-else-statement.rs:160:5 | LL | / let foo = |x: i32| { LL | | @@ -455,7 +282,7 @@ LL | | } else { = help: consider removing the `else` clause warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:178:5 + --> $DIR/bad-let-else-statement.rs:170:5 | LL | let ok = format_args!("") else { return; }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -464,7 +291,7 @@ LL | let ok = format_args!("") else { return; }; = help: consider removing the `else` clause warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:181:5 + --> $DIR/bad-let-else-statement.rs:173:5 | LL | let bad = format_args! {""} else { return; }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -472,46 +299,5 @@ LL | let bad = format_args! {""} else { return; }; = note: this pattern will always match, so the `else` clause is useless = help: consider removing the `else` clause -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:188:19 - | -LL | () => { {} } - | ___________________^ -LL | | -LL | | -LL | | } -... | -LL | | (1) => { -LL | | let x = a!() else { return; }; - | |____________^ -... -LL | b!(1); b!(2); - | ----- in this macro invocation - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - = note: this warning originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) - -warning: irrefutable `let...else` pattern - --> $DIR/bad-let-else-statement.rs:188:19 - | -LL | () => { {} } - | ___________________^ -LL | | -LL | | -LL | | } -... | -LL | | (2) => { -LL | | let x = a! {} else { return; }; - | |____________^ -... -LL | b!(1); b!(2); - | ----- in this macro invocation - | - = note: this pattern will always match, so the `else` clause is useless - = help: consider removing the `else` clause - = note: this warning originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 20 previous errors; 18 warnings emitted +error: aborting due to 19 previous errors; 4 warnings emitted -For more information about this error, try `rustc --explain E0384`. From 75a34ca26294cb79bd8b126c0fc18f4c207faaf0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 12 May 2024 13:02:54 -0700 Subject: [PATCH 2/3] Add test of trailing brace in a cast expression --- tests/ui/parser/bad-let-else-statement.rs | 14 ++++++++++++++ tests/ui/parser/bad-let-else-statement.stderr | 14 +++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/tests/ui/parser/bad-let-else-statement.rs b/tests/ui/parser/bad-let-else-statement.rs index 8ed55f949598e..2dce9ed24d2d1 100644 --- a/tests/ui/parser/bad-let-else-statement.rs +++ b/tests/ui/parser/bad-let-else-statement.rs @@ -196,4 +196,18 @@ fn s() { b!(2); } +fn t() { + macro_rules! primitive { + (8) => { u8 }; + } + + let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! { + //~^ WARN irrefutable `let...else` pattern + 8 + } else { + // FIXME: right curly brace `}` before `else` in a `let...else` statement not allowed + return; + }; +} + fn main() {} diff --git a/tests/ui/parser/bad-let-else-statement.stderr b/tests/ui/parser/bad-let-else-statement.stderr index 60b4600ff35d8..76097aaca8327 100644 --- a/tests/ui/parser/bad-let-else-statement.stderr +++ b/tests/ui/parser/bad-let-else-statement.stderr @@ -299,5 +299,17 @@ LL | let bad = format_args! {""} else { return; }; = note: this pattern will always match, so the `else` clause is useless = help: consider removing the `else` clause -error: aborting due to 19 previous errors; 4 warnings emitted +warning: irrefutable `let...else` pattern + --> $DIR/bad-let-else-statement.rs:204:5 + | +LL | / let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! { +LL | | +LL | | 8 +LL | | } else { + | |_____^ + | + = note: this pattern will always match, so the `else` clause is useless + = help: consider removing the `else` clause + +error: aborting due to 19 previous errors; 5 warnings emitted From a36b94d0887d42b692935c918c8cc869ca6c61b4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 12 May 2024 12:28:10 -0700 Subject: [PATCH 3/3] Disallow cast with trailing braced macro in let-else --- compiler/rustc_ast/src/util/classify.rs | 95 ++++++++++++++++++- compiler/rustc_parse/src/parser/stmt.rs | 28 +++--- tests/ui/parser/bad-let-else-statement.rs | 2 +- tests/ui/parser/bad-let-else-statement.stderr | 16 +++- 4 files changed, 124 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index f6e9e1a87c4bb..382c903625fbd 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -81,8 +81,17 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool { } } +pub enum TrailingBrace<'a> { + /// Trailing brace in a macro call, like the one in `x as *const brace! {}`. + /// We will suggest changing the macro call to a different delimiter. + MacCall(&'a ast::MacCall), + /// Trailing brace in any other expression, such as `a + B {}`. We will + /// suggest wrapping the innermost expression in parentheses: `a + (B {})`. + Expr(&'a ast::Expr), +} + /// If an expression ends with `}`, returns the innermost expression ending in the `}` -pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { +pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { loop { match &expr.kind { AddrOf(_, _, e) @@ -111,10 +120,14 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { | Struct(..) | TryBlock(..) | While(..) - | ConstBlock(_) => break Some(expr), + | ConstBlock(_) => break Some(TrailingBrace::Expr(expr)), + + Cast(_, ty) => { + break type_trailing_braced_mac_call(ty).map(TrailingBrace::MacCall); + } MacCall(mac) => { - break (mac.args.delim == Delimiter::Brace).then_some(expr); + break (mac.args.delim == Delimiter::Brace).then_some(TrailingBrace::MacCall(mac)); } InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) => { @@ -131,7 +144,6 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { | MethodCall(_) | Tup(_) | Lit(_) - | Cast(_, _) | Type(_, _) | Await(_, _) | Field(_, _) @@ -148,3 +160,78 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> { } } } + +/// If the type's last token is `}`, it must be due to a braced macro call, such +/// as in `*const brace! { ... }`. Returns that trailing macro call. +fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { + loop { + match &ty.kind { + ast::TyKind::MacCall(mac) => { + break (mac.args.delim == Delimiter::Brace).then_some(mac); + } + + ast::TyKind::Ptr(mut_ty) | ast::TyKind::Ref(_, mut_ty) => { + ty = &mut_ty.ty; + } + + ast::TyKind::BareFn(fn_ty) => match &fn_ty.decl.output { + ast::FnRetTy::Default(_) => break None, + ast::FnRetTy::Ty(ret) => ty = ret, + }, + + ast::TyKind::Path(_, path) => match path_return_type(path) { + Some(trailing_ty) => ty = trailing_ty, + None => break None, + }, + + ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds, _) => { + match bounds.last() { + Some(ast::GenericBound::Trait(bound, _)) => { + match path_return_type(&bound.trait_ref.path) { + Some(trailing_ty) => ty = trailing_ty, + None => break None, + } + } + Some(ast::GenericBound::Outlives(_)) | None => break None, + } + } + + ast::TyKind::Slice(..) + | ast::TyKind::Array(..) + | ast::TyKind::Never + | ast::TyKind::Tup(..) + | ast::TyKind::Paren(..) + | ast::TyKind::Typeof(..) + | ast::TyKind::Infer + | ast::TyKind::ImplicitSelf + | ast::TyKind::CVarArgs + | ast::TyKind::Pat(..) + | ast::TyKind::Dummy + | ast::TyKind::Err(..) => break None, + + // These end in brace, but cannot occur in a let-else statement. + // They are only parsed as fields of a data structure. For the + // purpose of denying trailing braces in the expression of a + // let-else, we can disregard these. + ast::TyKind::AnonStruct(..) | ast::TyKind::AnonUnion(..) => break None, + } + } +} + +/// Returns the trailing return type in the given path, if it has one. +/// +/// ```ignore (illustrative) +/// ::std::ops::FnOnce(&str) -> fn() -> *const c_void +/// ^^^^^^^^^^^^^^^^^^^^^ +/// ``` +fn path_return_type(path: &ast::Path) -> Option<&ast::Ty> { + let last_segment = path.segments.last()?; + let args = last_segment.args.as_ref()?; + match &**args { + ast::GenericArgs::Parenthesized(args) => match &args.output { + ast::FnRetTy::Default(_) => None, + ast::FnRetTy::Ty(ret) => Some(ret), + }, + ast::GenericArgs::AngleBracketed(_) => None, + } +} diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index d70afebf1b2da..941b145e2dba2 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -15,7 +15,7 @@ use ast::Label; use rustc_ast as ast; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, TokenKind}; -use rustc_ast::util::classify; +use rustc_ast::util::classify::{self, TrailingBrace}; use rustc_ast::{AttrStyle, AttrVec, LocalKind, MacCall, MacCallStmt, MacStmtStyle}; use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, HasAttrs, Local, Recovered, Stmt}; use rustc_ast::{StmtKind, DUMMY_NODE_ID}; @@ -407,18 +407,24 @@ impl<'a> Parser<'a> { fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) { if let Some(trailing) = classify::expr_trailing_brace(init) { - let sugg = match &trailing.kind { - ExprKind::MacCall(mac) => errors::WrapInParentheses::MacroArgs { - left: mac.args.dspan.open, - right: mac.args.dspan.close, - }, - _ => errors::WrapInParentheses::Expression { - left: trailing.span.shrink_to_lo(), - right: trailing.span.shrink_to_hi(), - }, + let (span, sugg) = match trailing { + TrailingBrace::MacCall(mac) => ( + mac.span(), + errors::WrapInParentheses::MacroArgs { + left: mac.args.dspan.open, + right: mac.args.dspan.close, + }, + ), + TrailingBrace::Expr(expr) => ( + expr.span, + errors::WrapInParentheses::Expression { + left: expr.span.shrink_to_lo(), + right: expr.span.shrink_to_hi(), + }, + ), }; self.dcx().emit_err(errors::InvalidCurlyInLetElse { - span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)), + span: span.with_lo(span.hi() - BytePos(1)), sugg, }); } diff --git a/tests/ui/parser/bad-let-else-statement.rs b/tests/ui/parser/bad-let-else-statement.rs index 2dce9ed24d2d1..ff6619cbc9864 100644 --- a/tests/ui/parser/bad-let-else-statement.rs +++ b/tests/ui/parser/bad-let-else-statement.rs @@ -205,7 +205,7 @@ fn t() { //~^ WARN irrefutable `let...else` pattern 8 } else { - // FIXME: right curly brace `}` before `else` in a `let...else` statement not allowed + //~^ ERROR right curly brace `}` before `else` in a `let...else` statement not allowed return; }; } diff --git a/tests/ui/parser/bad-let-else-statement.stderr b/tests/ui/parser/bad-let-else-statement.stderr index 76097aaca8327..0bf6a346dfb17 100644 --- a/tests/ui/parser/bad-let-else-statement.stderr +++ b/tests/ui/parser/bad-let-else-statement.stderr @@ -241,6 +241,20 @@ help: use parentheses instead of braces for this macro LL | let bad = format_args! ("") else { return; }; | ~ ~ +error: right curly brace `}` before `else` in a `let...else` statement not allowed + --> $DIR/bad-let-else-statement.rs:207:5 + | +LL | } else { + | ^ + | +help: use parentheses instead of braces for this macro + | +LL ~ let foo = &std::ptr::null as &'static dyn std::ops::Fn() -> *const primitive! ( +LL | +LL | 8 +LL ~ ) else { + | + error: right curly brace `}` before `else` in a `let...else` statement not allowed --> $DIR/bad-let-else-statement.rs:190:25 | @@ -311,5 +325,5 @@ LL | | } else { = note: this pattern will always match, so the `else` clause is useless = help: consider removing the `else` clause -error: aborting due to 19 previous errors; 5 warnings emitted +error: aborting due to 20 previous errors; 5 warnings emitted