From 93931a4f243665a1e50748072b9fac1adbcebb7c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 1 Jul 2024 19:42:22 -0700 Subject: [PATCH 1/2] Add fixup test for break with leading label --- tests/test_expr.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 961e3c4ea3..364386c5f8 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -672,6 +672,7 @@ fn test_fixup() { quote! { match m { _ => ({}) - 1 } }, quote! { if let _ = (a && b) && c {} }, quote! { if let _ = (S {}) {} }, + quote! { break ('a: loop { break 'a 1 } + 1) }, ] { let original: Expr = syn::parse2(tokens).unwrap(); From 4e71c1caa413122a644f9e3fcb8222dfc20d9f8d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 19 May 2024 20:17:44 -0700 Subject: [PATCH 2/2] Parenthesize labeled loops inside break value --- src/classify.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++ src/expr.rs | 11 ++++++++-- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/src/classify.rs b/src/classify.rs index 1b0ff30040..cc176d06dc 100644 --- a/src/classify.rs +++ b/src/classify.rs @@ -248,6 +248,59 @@ pub(crate) fn confusable_with_adjacent_lt(mut expr: &Expr) -> bool { } } +/// Whether the expression's first token is the label of a loop/block. +#[cfg(all(feature = "printing", feature = "full"))] +pub(crate) fn expr_leading_label(mut expr: &Expr) -> bool { + loop { + match expr { + Expr::Block(e) => return e.label.is_some(), + Expr::ForLoop(e) => return e.label.is_some(), + Expr::Loop(e) => return e.label.is_some(), + Expr::While(e) => return e.label.is_some(), + + Expr::Assign(e) => expr = &e.left, + Expr::Await(e) => expr = &e.base, + Expr::Binary(e) => expr = &e.left, + Expr::Call(e) => expr = &e.func, + Expr::Cast(e) => expr = &e.expr, + Expr::Field(e) => expr = &e.base, + Expr::Index(e) => expr = &e.expr, + Expr::MethodCall(e) => expr = &e.receiver, + Expr::Range(e) => match &e.start { + Some(start) => expr = start, + None => return false, + }, + Expr::Try(e) => expr = &e.expr, + + Expr::Array(_) + | Expr::Async(_) + | Expr::Break(_) + | Expr::Closure(_) + | Expr::Const(_) + | Expr::Continue(_) + | Expr::Group(_) + | Expr::If(_) + | Expr::Infer(_) + | Expr::Let(_) + | Expr::Lit(_) + | Expr::Macro(_) + | Expr::Match(_) + | Expr::Paren(_) + | Expr::Path(_) + | Expr::Reference(_) + | Expr::Repeat(_) + | Expr::Return(_) + | Expr::Struct(_) + | Expr::TryBlock(_) + | Expr::Tuple(_) + | Expr::Unary(_) + | Expr::Unsafe(_) + | Expr::Verbatim(_) + | Expr::Yield(_) => return false, + } + } +} + /// Whether the expression's last token is `}`. #[cfg(feature = "full")] pub(crate) fn expr_trailing_brace(mut expr: &Expr) -> bool { diff --git a/src/expr.rs b/src/expr.rs index 30bf9c76c6..e684068caa 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -3286,8 +3286,15 @@ pub(crate) mod printing { outer_attrs_to_tokens(&e.attrs, tokens); e.break_token.to_tokens(tokens); e.label.to_tokens(tokens); - if let Some(expr) = &e.expr { - print_expr(expr, tokens, fixup.subsequent_subexpression()); + if let Some(value) = &e.expr { + print_subexpression( + value, + // Parenthesize `break 'inner: loop { break 'inner 1 } + 1` + // ^---------------------------------^ + e.label.is_none() && classify::expr_leading_label(value), + tokens, + fixup.subsequent_subexpression(), + ); } }