Skip to content

reserved width for unsafe in match arm pattern - #7097

Open
AsthaMishra wants to merge 3 commits into
rust-lang:mainfrom
AsthaMishra:fix-issue-6848
Open

reserved width for unsafe in match arm pattern#7097
AsthaMishra wants to merge 3 commits into
rust-lang:mainfrom
AsthaMishra:fix-issue-6848

Conversation

@AsthaMishra

@AsthaMishra AsthaMishra commented Sep 3, 2026

Copy link
Copy Markdown
Contributor
  • I did not use an LLM to create a change in this PR.
  • I used an LLM to create a change in this PR, and I have explained below how it was used.

Issue : unsafe is a block with label None and when patterns code executes and label: None is found, execution goes to fallback arm where only 5 column spaces are reserved (which is correct for async, const, gen and try blocks) but unsafe needs 12 column spaces, this is what causing max_width violation and when run with --config error_on_line_overflow=true it does give line overflow error for max-width 80

 // Patterns
    let pat_shape = match &arm.body.as_ref().unknown_error()?.kind {
        ast::ExprKind::Block(_, Some(label)) => {
            // Some block with a label ` => 'label: {`
            // 7 = ` => : {`
            let label_len = label.ident.as_str().len();
            shape
                .sub_width(7 + label_len, arm.span)?
                .offset_left(pipe_offset, arm.span)?
        }
        _ => {
            // 5 = ` => {`
            shape
                .sub_width(5, arm.span)?
                .offset_left(pipe_offset, arm.span)?
        }
    };

Fix : added an arm for unsafe block to reserve required space for unsafe

 ast::ExprKind::Block(block, None) if is_unsafe_block(block) => {
            // 12 = ` => unsafe {`
            shape
                .sub_width(12, arm.span)?
                .offset_left(pipe_offset, arm.span)?
        }

Fixes : #6848

@rustbot rustbot added the S-waiting-on-review Status: awaiting review from the assignee but also interested parties. label Sep 3, 2026
@AsthaMishra

Copy link
Copy Markdown
Contributor Author

one question? should this change be gated?

@ytmimi

ytmimi commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

@AsthaMishra Thank you for working on this. Yeah, let's gate this fix on style_edition=2027

@ytmimi

ytmimi commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Also are we sure that things are correct for other types of blocks? Let's add test cases for them just to be sure.

try {}
}
_ => {}
}

@ytmimi ytmimi Sep 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we're forcing const, async, gen, and try blocks to be wrapped in an outer block. Does that always happen or just when we can't fit everything onto one line?

unsafe 6 chars
const 5 chars
async 5 chars
gen 3 chars
try 3 chars

All of these are getting tested against the same ( ExampleTypeX::VariantAlphaSampleXYZ, ExampleTypeX::VariantBetaXYZ) tuple. To make sure we're exhaustively testing this I would like to test the following cases for each type of block:

  1. 1 char below the max_width limit when accounting for the pattern, =>, keyword, {, and any whitespace in between..
  2. everything properly fits on 1 line at exactly the max_width limit when accounting for the pattern, =>, keyword, {, and any whitespace in between..
  3. 1 char over the max_width limit when accounting for the pattern, =>, keyword, {, and any whitespace in between.

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we're forcing const, async, gen, and try blocks to be wrapped in an outer block. Does that always happen or just when we can't fit everything onto one line?

outer block only added when we can't fit everything in one line

@AsthaMishra AsthaMishra Sep 6, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test cases added for above mentioned pointers. There was one more bug , when empty blocks exceeds max-width, we get line overflow error.

reason -

rustfmt/src/matches.rs

Lines 530 to 537 in 2d897e2

Ok(ref body_str)
if is_block
|| (!body_str.contains('\n')
&& unicode_str_width(body_str) <= body_shape.width) =>
{
return combine_orig_body(body_str);
}
_ => rewrite,

if is_block || (!body_str.contains('\n') && unicode_str_width(body_str) <= body_shape.width)

code sees if is_block is true and 'OR' condition is bypassed i.e. if there is no new line in body_str and if it fits in the available column space. this is only for what comes after => in same line.

Fix: add a way to enforce execution of (!body_str.contains('\n') && unicode_str_width(body_str) <= body_shape.width for empty blocks

  let enforce_empty_block_width =
            is_empty_block && context.config.style_edition() >= StyleEdition::Edition2027;
        match rewrite {
            Ok(ref body_str)
                if (is_block && !enforce_empty_block_width)
                    || (!body_str.contains('\n')
                        && unicode_str_width(body_str) <= body_shape.width) =>
            {
                return combine_orig_body(body_str);
            }
            _ => rewrite,
        }

@AsthaMishra
AsthaMishra requested a review from ytmimi September 8, 2026 19:58
Comment thread src/matches.rs
Comment on lines +289 to +290
ast::ExprKind::Block(block, None)
if is_unsafe_block(block)

@ytmimi ytmimi Sep 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it ever possible to have an unsafe block with a label?

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'a: unsafe{ 5 } gives compile error - after 'a -> a loop or block is expected.

// everything properly fits on 1 line at exactly the max_width limit
(ExampleTypeX::VariantABCDE, ExampleTypeX::VariantBetaXYZ) => unsafe {},
// 1 char over the max_width limit
(ExampleTypeX::VariantABCDEF, ExampleTypeX::VariantBetaXYZ) => unsafe {},

@ytmimi ytmimi Sep 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to understand why there's this inconsistency between the unsafe formatting and const, async, gen and try examples below.

Do we need to update the pattern matching for these other kinds of blocks in rewrite_match_arm?

View changes since the review

@AsthaMishra AsthaMishra Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pattern matching seems correct we are doing same thing in expr.rs as well and for ast::ExprKind::Block unsafe cases are handled in if branch.

we are just missing pattern arm for unsafe block,

rustfmt/src/matches.rs

Lines 280 to 295 in 7bc6cd7

let pat_shape = match &arm.body.as_ref().unknown_error()?.kind {
ast::ExprKind::Block(_, Some(label)) => {
// Some block with a label ` => 'label: {`
// 7 = ` => : {`
let label_len = label.ident.as_str().len();
shape
.sub_width(7 + label_len, arm.span)?
.offset_left(pipe_offset, arm.span)?
}
_ => {
// 5 = ` => {`
shape
.sub_width(5, arm.span)?
.offset_left(pipe_offset, arm.span)?
}
};

lable block works, - ast::ExprKind::Block(_, Some(label))
const , async, gen and try - these works - default arm

Comment thread src/matches.rs
Comment on lines +293 to +296
// 12 = ` => unsafe {`
shape
.sub_width(12, arm.span)?
.offset_left(pipe_offset, arm.span)?

@ytmimi ytmimi Sep 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we know the block is empty, should we:

// 13 = ` => unsafe {}`
sub_width(13, arm.span)

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes we can

Comment on lines +41 to +44
(
ExampleTypeX::VariantABCDEFGHIJKL,
ExampleTypeX::VariantBetaXYZ,
) => unsafe { non_empty_block() },

@ytmimi ytmimi Sep 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on these tests it seems that we'll never wrap the pattern like this for const, async, gen, or try blocks.

View changes since the review

@AsthaMishra AsthaMishra Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on these tests it seems that we'll never wrap the pattern like this for const, async, gen, or try blocks.

View changes since the review

yes, wrapping only happens for when is_block = false.

rustfmt/src/matches.rs

Lines 460 to 471 in 7bc6cd7

if is_block {
let mut result = pats_str.to_owned();
result.push_str(" =>");
if !arrow_comment.is_empty() {
result.push_str(&nested_indent_str);
result.push_str(&arrow_comment);
}
result.push_str(&nested_indent_str);
result.push_str(body_str);
result.push_str(comma);
return Ok(result);
}

if is_block i.e. label block and unsafe block - wrapping is not done

for const, async, gen, try wrapping happens at 490

rustfmt/src/matches.rs

Lines 473 to 493 in 7bc6cd7

let indent_str = shape.indent.to_string_with_newline(context.config);
let (body_prefix, body_suffix) =
if context.config.match_arm_blocks() && !context.inside_macro() {
let comma = if context.config.match_block_trailing_comma() {
","
} else {
""
};
let semicolon = if context.config.style_edition() <= StyleEdition::Edition2021 {
""
} else {
if semicolon_for_expr(context, body) {
";"
} else {
""
}
};
("{", format!("{}{}}}{}", semicolon, indent_str, comma))
} else {
("", String::from(","))
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review Status: awaiting review from the assignee but also interested parties.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

max_width not obeyed in match arm tuple

3 participants