Skip to content

Commit

Permalink
Parenthesize break values containing leading label
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Jun 24, 2024
1 parent 5f37433 commit eccfeca
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 5 deletions.
79 changes: 78 additions & 1 deletion compiler/rustc_ast/src/util/classify.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! Routines the parser and pretty-printer use to classify AST nodes.

use crate::ast::ExprKind::*;
use crate::{ast, token::Delimiter};
use crate::ast::{self, MatchKind};
use crate::token::Delimiter;

/// This classification determines whether various syntactic positions break out
/// of parsing the current expression (true) or continue parsing more of the
Expand Down Expand Up @@ -81,6 +82,82 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
}
}

/// Returns whether the leftmost token of the given expression is the label of a
/// labeled loop or block, such as in `'inner: loop { break 'inner 1 } + 1`.
///
/// Such expressions are not allowed as the value of an unlabeled break.
///
/// ```ignore (illustrative)
/// 'outer: {
/// break 'inner: loop { break 'inner 1 } + 1; // invalid syntax
///
/// break 'outer 'inner: loop { break 'inner 1 } + 1; // okay
///
/// break ('inner: loop { break 'inner 1 } + 1); // okay
///
/// break ('inner: loop { break 'inner 1 }) + 1; // okay
/// }
/// ```
pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
loop {
match &expr.kind {
Block(_, label) | ForLoop { label, .. } | Loop(_, label, _) | While(_, _, label) => {
return label.is_some();
}

Assign(e, _, _)
| AssignOp(_, e, _)
| Binary(_, e, _)
| Call(e, _)
| Cast(e, _)
| Field(e, _)
| Index(e, _, _)
| Match(e, _, MatchKind::Postfix)
| Range(Some(e), _, _)
| Try(e) => {
expr = e;
}
MethodCall(method_call) => {
expr = &method_call.receiver;
}

AddrOf(..)
| Array(..)
| Await(..)
| Become(..)
| Break(..)
| Closure(..)
| ConstBlock(..)
| Continue(..)
| FormatArgs(..)
| Gen(..)
| If(..)
| IncludedBytes(..)
| InlineAsm(..)
| Let(..)
| Lit(..)
| MacCall(..)
| Match(_, _, MatchKind::Prefix)
| OffsetOf(..)
| Paren(..)
| Path(..)
| Range(None, _, _)
| Repeat(..)
| Ret(..)
| Struct(..)
| TryBlock(..)
| Tup(..)
| Type(..)
| Unary(..)
| Underscore
| Yeet(..)
| Yield(..)
| Err(..)
| Dummy => return false,
}
}
}

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.
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_ast_pretty/src/pprust/state/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use ast::{ForLoopKind, MatchKind};
use itertools::{Itertools, Position};
use rustc_ast::ptr::P;
use rustc_ast::token;
use rustc_ast::util::classify;
use rustc_ast::util::literal::escape_byte_str_symbol;
use rustc_ast::util::parser::{self, AssocOp, Fixity};
use rustc_ast::{self as ast, BlockCheckMode};
Expand Down Expand Up @@ -610,9 +611,12 @@ impl<'a> State<'a> {
}
if let Some(expr) = opt_expr {
self.space();
self.print_expr_maybe_paren(
self.print_expr_cond_paren(
expr,
parser::PREC_JUMP,
// Parenthesize if required by precedence, or in the
// case of `break 'inner: loop { break 'inner 1 } + 1`
expr.precedence().order() < parser::PREC_JUMP
|| (opt_label.is_none() && classify::leading_labeled_expr(expr)),
fixup.subsequent_subexpression(),
);
}
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/unpretty/expanded-interpolation.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ fn break_labeled_loop() {
'outer: loop { break 'outer 'inner: loop { break 'inner 1; } + 1; };

let paren_around_break_value =
'outer: loop { break 'inner: loop { break 'inner 1; } + 1; };
'outer: loop { break ('inner: loop { break 'inner 1; } + 1); };

macro_rules! breaking { ($value:expr) => { break $value }; }

let paren_around_break_value =
loop { break 'inner: loop { break 'inner 1; } + 1; };
loop { break ('inner: loop { break 'inner 1; } + 1); };
}

fn if_let() {
Expand Down

0 comments on commit eccfeca

Please sign in to comment.