Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 32 additions & 13 deletions src/closures.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_ast::{ast, ptr};
use rustc_ast::{Label, ast, ptr};
use rustc_span::Span;
use thin_vec::thin_vec;
use tracing::debug;
Expand Down Expand Up @@ -55,7 +55,7 @@ pub(crate) fn rewrite_closure(
// 1 = space between `|...|` and body.
let body_shape = shape.offset_left(extra_offset, span)?;

if let ast::ExprKind::Block(ref block, _) = body.kind {
if let ast::ExprKind::Block(ref block, ref label) = body.kind {
// The body of the closure is an empty block.
if block.stmts.is_empty() && !block_contains_comment(context, block) {
return body
Expand All @@ -72,7 +72,7 @@ pub(crate) fn rewrite_closure(

result.or_else(|_| {
// Either we require a block, or tried without and failed.
rewrite_closure_block(block, &prefix, context, body_shape)
rewrite_closure_block(block, label, &prefix, context, body_shape)
})
} else {
rewrite_closure_expr(body, &prefix, context, body_shape).or_else(|_| {
Expand Down Expand Up @@ -104,8 +104,8 @@ fn get_inner_expr<'a>(
prefix: &str,
context: &RewriteContext<'_>,
) -> &'a ast::Expr {
if let ast::ExprKind::Block(ref block, _) = expr.kind {
if !needs_block(block, prefix, context) {
if let ast::ExprKind::Block(ref block, ref label) = expr.kind {
if !needs_block(block, label, prefix, context) {
// block.stmts.len() == 1 except with `|| {{}}`;
// https://github.com/rust-lang/rustfmt/issues/3844
if let Some(expr) = block.stmts.first().and_then(stmt_expr) {
Expand All @@ -118,7 +118,12 @@ fn get_inner_expr<'a>(
}

// Figure out if a block is necessary.
fn needs_block(block: &ast::Block, prefix: &str, context: &RewriteContext<'_>) -> bool {
fn needs_block(
block: &ast::Block,
label: &Option<Label>,
prefix: &str,
context: &RewriteContext<'_>,
) -> bool {
let has_attributes = block.stmts.first().map_or(false, |first_stmt| {
!get_attrs_from_stmt(first_stmt).is_empty()
});
Expand All @@ -128,6 +133,7 @@ fn needs_block(block: &ast::Block, prefix: &str, context: &RewriteContext<'_>) -
|| has_attributes
|| block_contains_comment(context, block)
|| prefix.contains('\n')
|| label.is_some()
}

fn veto_block(e: &ast::Expr) -> bool {
Expand Down Expand Up @@ -231,15 +237,25 @@ fn rewrite_closure_expr(
// Rewrite closure whose body is block.
fn rewrite_closure_block(
block: &ast::Block,
label: &Option<Label>,
prefix: &str,
context: &RewriteContext<'_>,
shape: Shape,
) -> RewriteResult {
Ok(format!(
"{} {}",
prefix,
block.rewrite_result(context, shape)?
))
if let Some(label) = label {
Ok(format!(
"{} {}: {}",
prefix,
context.snippet(label.ident.span),
block.rewrite_result(context, shape)?
))
} else {
Ok(format!(
"{} {}",
prefix,
block.rewrite_result(context, shape)?
))
}
}

// Return type is (prefix, extra_offset)
Expand Down Expand Up @@ -353,6 +369,8 @@ pub(crate) fn rewrite_last_closure(
expr: &ast::Expr,
shape: Shape,
) -> RewriteResult {
debug!("rewrite_last_closure {:?}", expr);

if let ast::ExprKind::Closure(ref closure) = expr.kind {
let ast::Closure {
ref binder,
Expand All @@ -366,10 +384,11 @@ pub(crate) fn rewrite_last_closure(
fn_arg_span: _,
} = **closure;
let body = match body.kind {
ast::ExprKind::Block(ref block, _)
ast::ExprKind::Block(ref block, ref label)
if !is_unsafe_block(block)
&& !context.inside_macro()
&& is_simple_block(context, block, Some(&body.attrs)) =>
&& is_simple_block(context, block, Some(&body.attrs))
&& label.is_none() =>
{
stmt_expr(&block.stmts[0]).unwrap_or(body)
}
Expand Down
246 changes: 246 additions & 0 deletions tests/source/closure-block-labels.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
macro_rules! test_macro {
(|$transaction: ident| $body: expr) => {{
let $transaction = some_value;
let _ = $body;
}};
}

fn test_func<T>(func: impl FnOnce(u8) -> T) {
}

fn test_func2<T>(func: impl FnOnce(u8) -> T, value: u8) {
}

// _0: in-macro
// _1: last argument in function invocation
// _2: non-last argument in function invocation
// _3: simple expression

// test0: the cause reported in issue: label is used, and there is usage, multiple statements
pub fn rustfnmt_test0_0(condition: bool) {
test_macro!(|transaction| 'block: {
if condition {
break 'block 0;
}

todo!()
});
}

pub fn rustfnmt_test0_1(condition: bool) {
test_func(|transaction| 'block: {
if condition {
break 'block 0;
}

todo!()
});
}

pub fn rustfnmt_test0_2(condition: bool) {
test_func2(|transaction| 'block: {
if condition {
break 'block 0;
}

todo!()
}, 0);
}

pub fn rustfnmt_test0_3(condition: bool) {
let x = |transaction| 'block: {
if condition {
break 'block 0;
}

todo!()
};
}

// test0: the cause reported in issue: label is used, and there is usage, multiple statements
pub fn rustfnmt_test0_0(condition: bool) {
test_macro!(|transaction| 'block: {
if condition {
break 'block 0;
}

todo!()
});
}

pub fn rustfnmt_test0_1(condition: bool) {
test_func(|transaction| 'block: {
if condition {
break 'block 0;
}

todo!()
});
}

pub fn rustfnmt_test0_2(condition: bool) {
test_func2(|transaction| 'block: {
if condition {
break 'block 0;
}

todo!()
}, 0);
}

pub fn rustfnmt_test0_3(condition: bool) {
let x = |transaction| 'block: {
if condition {
break 'block 0;
}

todo!()
};
}


// test1: label is unused, and there is usage, multiple statements
pub fn rustfnmt_test1_0(condition: bool) {
test_macro!(|transaction| 'block: {
if condition {
todo!("");
}

todo!()
});
}

pub fn rustfnmt_test1_1(condition: bool) {
test_func(|transaction| 'block: {
if condition {
todo!("");
}

todo!()
});
}

pub fn rustfnmt_test1_2(condition: bool) {
test_func2(|transaction| 'block: {
if condition {
todo!("");
}

todo!()
}, 0);
}

pub fn rustfnmt_test1_3(condition: bool) {
let x = |transaction| 'block: {
if condition {
todo!("");
}

todo!()
};
}



// test2: label is used, single expression
pub fn rustfnmt_test2_0(condition: bool) {
test_macro!(|transaction| 'block: {
break 'block 0;
});
}

pub fn rustfnmt_test2_1(condition: bool) {
test_func(|transaction| 'block: {
break 'block 0;
});
}

pub fn rustfnmt_test2_2(condition: bool) {
test_func2(|transaction| 'block: {
break 'block 0;
}, 0);
}

pub fn rustfnmt_test2_3(condition: bool) {
let x = |transaction| 'block: {
break 'block 0;
};
}

// test3: label is unused, single general multi-line expression
pub fn rustfnmt_test3_0(condition: bool) {
test_macro!(|transaction| 'block: {
vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
]
});
}

pub fn rustfnmt_test3_1(condition: bool) {
test_func(|transaction| 'block: {
vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
]
});
}

pub fn rustfnmt_test3_2(condition: bool) {
test_func2(|transaction| 'block: {
vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
]
}, 0);
}

pub fn rustfnmt_test3_3(condition: bool) {
let x = |transaction| 'block: {
vec![
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0,
]
};
}

// test4: label is unused, single block statement-expression
pub fn rustfnmt_test4_0(condition: bool) {
test_macro!(|transaction| 'block: {
if condition {
break 'block 1;
} else {
break 'block 0;
}
});
}

pub fn rustfnmt_test4_1(condition: bool) {
test_func(|transaction| 'block: {
if condition {
break 'block 1;
} else {
break 'block 0;
}
});
}

pub fn rustfnmt_test4_2(condition: bool) {
test_func2(|transaction| 'block: {
if condition {
break 'block 1;
} else {
break 'block 0;
}
}, 1);
}

pub fn rustfnmt_test4_3(condition: bool) {
let x = |transaction| 'block: {
if condition {
break 'block 1;
} else {
break 'block 0;
}
};
}
Loading
Loading