From cf18df7e23d4a4fa828354aca87fcc065868013a Mon Sep 17 00:00:00 2001 From: Sjael Date: Thu, 18 Jan 2024 19:01:39 -0800 Subject: [PATCH 1/6] first draft, tests OK --- src/expr.rs | 34 +++++++++++++++++++++++++++-- tests/source/expr.rs | 2 +- tests/source/single-line-if-else.rs | 16 ++++++++++++++ tests/target/single-line-if-else.rs | 21 ++++++++++++++++++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 7808f891336..6eea315cf6e 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1102,8 +1102,14 @@ impl<'a> Rewrite for ControlFlow<'a> { }; let block_str = { let old_val = context.is_if_else_block.replace(self.else_block.is_some()); - let result = - rewrite_block_with_visitor(context, "", self.block, None, None, block_shape, true); + let allow_single_line = + allow_single_line_if(&cond_str, self.block) && self.keyword == "if"; + + let result = if allow_single_line { + rewrite_block_inner(self.block, None, None, true, context, block_shape) + } else { + rewrite_block_with_visitor(context, "", self.block, None, None, block_shape, true) + }; context.is_if_else_block.replace(old_val); result? }; @@ -1158,6 +1164,30 @@ impl<'a> Rewrite for ControlFlow<'a> { } } +fn allow_single_line_if(result: &str, block: &ast::Block) -> bool { + if result.contains('\n') { + return false; + } + + if block.stmts.len() == 0 { + return true; + } + if block.stmts.len() == 1 { + return is_simple_stmt(&block.stmts[0]); + } + false +} + +fn is_simple_stmt(stmt: &ast::Stmt) -> bool { + match stmt.kind { + ast::StmtKind::Expr(ref expr) => match expr.kind { + ast::ExprKind::Ret(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Break(..) => true, + _ => false, + }, + _ => false, + } +} + fn rewrite_label(opt_label: Option) -> Cow<'static, str> { match opt_label { Some(label) => Cow::from(format!("{}: ", label.ident)), diff --git a/tests/source/expr.rs b/tests/source/expr.rs index 879c551ea49..056a94b2676 100644 --- a/tests/source/expr.rs +++ b/tests/source/expr.rs @@ -79,7 +79,7 @@ fn bar() { let bar = 5 ; let nonsense = (10 .. 0)..(0..10); - loop{if true {break}} + loop{if true {break;}} let x = (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa && aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, diff --git a/tests/source/single-line-if-else.rs b/tests/source/single-line-if-else.rs index bcde390d116..832a53dedb8 100644 --- a/tests/source/single-line-if-else.rs +++ b/tests/source/single-line-if-else.rs @@ -35,6 +35,22 @@ fn main() { do_something() } + let a = if x { 1 } else { 3 }; + + // if may be formatted on a single line if it is "short" + // and only contain a single expression + if true { return } + + if true { + return + } + + if true { return; } + + if a { let y = 1; return y } + + for i in 0..2 { if g == true { continue } } + let x = if veeeeeeeeery_loooooong_condition() { aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa } else { bbbbbbbbbb }; let x = if veeeeeeeeery_loooooong_condition() { aaaaaaaaaaaaaaaaaaaaaaaaa } else { diff --git a/tests/target/single-line-if-else.rs b/tests/target/single-line-if-else.rs index 98fd793cba2..002f576b01a 100644 --- a/tests/target/single-line-if-else.rs +++ b/tests/target/single-line-if-else.rs @@ -42,6 +42,27 @@ fn main() { do_something() } + let a = if x { 1 } else { 3 }; + + // if may be formatted on a single line if it is "short" + // and only contain a single expression + if true { return } + + if true { return } + + if true { + return; + } + + if a { + let y = 1; + return y; + } + + for i in 0..2 { + if g == true { continue } + } + let x = if veeeeeeeeery_loooooong_condition() { aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa } else { From 50c1c8348292145193ab8aef730d8c1dd2fa7453 Mon Sep 17 00:00:00 2001 From: Sjael Date: Wed, 24 Jan 2024 09:45:39 -0800 Subject: [PATCH 2/6] single_line_if config option --- Configurations.md | 28 ++++++++++++++++++++++++++ src/config/config_type.rs | 10 ++++++++++ src/config/mod.rs | 7 +++++++ src/config/options.rs | 6 ++++++ src/expr.rs | 41 +++++++++++++++++++++++++++++---------- 5 files changed, 82 insertions(+), 10 deletions(-) diff --git a/Configurations.md b/Configurations.md index af7fa012063..925cbaed3c1 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2481,6 +2481,34 @@ fn main() { See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) +## `single_line_simple_if` + +Allows simple single expression if blocks to format on one line. Useful in the case of keeping `let-else` guards format consistent with `if` guards. + +Note that line will still break if: +1. The condition length is over the `max_width`/`single_line_simple_if_max_width length` +2. The block contains a trailing semicolon +3. The block contains a single or multi-lined comment + +- **Default value**: `false` +- **Possible values**: `true`, `false` +- **Stable**: No + +#### `false` (default): + +## `single_line_simple_if_max_width` + +Maximum line length for single line if with a simple inner expression. Useful in the case of keeping `let-else` guards format consistent with `if` guards. + +A value of `0` (zero) results in if-else expressions always being broken into multiple lines. Note this occurs when `use_small_heuristics` is set to `Off`. + +- **Default value**: `50` +- **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) +- **Stable**: Yes + +By default this option is set as a percentage of [`max_width`](#max_width) provided by [`use_small_heuristics`](#use_small_heuristics), but a value set directly for `single_line_if_else_max_width` will take precedence. + +See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) ## `space_after_colon` diff --git a/src/config/config_type.rs b/src/config/config_type.rs index 7551241fc00..f4e4d3690f6 100644 --- a/src/config/config_type.rs +++ b/src/config/config_type.rs @@ -122,6 +122,7 @@ macro_rules! create_config { | "fn_call_width" | "single_line_if_else_max_width" | "single_line_let_else_max_width" + | "single_line_simple_if_max_width" | "attr_fn_like_width" | "struct_lit_width" | "struct_variant_width" @@ -273,6 +274,7 @@ macro_rules! create_config { | "fn_call_width" | "single_line_if_else_max_width" | "single_line_let_else_max_width" + | "single_line_simple_if_max_width" | "attr_fn_like_width" | "struct_lit_width" | "struct_variant_width" @@ -421,6 +423,14 @@ macro_rules! create_config { "single_line_let_else_max_width", ); self.single_line_let_else_max_width.2 = single_line_let_else_max_width; + + let single_line_simple_if_max_width = get_width_value( + self.was_set().single_line_simple_if_max_width(), + self.single_line_simple_if_max_width.2, + heuristics.single_line_simple_if_max_width, + "single_line_simple_if_max_width", + ); + self.single_line_simple_if_max_width.2 = single_line_simple_if_max_width; } fn set_heuristics(&mut self) { diff --git a/src/config/mod.rs b/src/config/mod.rs index c7c2d4e48a8..da2186f73fa 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -61,6 +61,8 @@ create_config! { single_line_let_else_max_width: usize, 50, true, "Maximum line length for single line \ let-else statements. A value of zero means always format the divergent `else` block \ over multiple lines."; + single_line_simple_if_max_width: usize, 50, false, "Maximum line length for single line \ + if statement. A value of zero means always format to multiple lines."; // Comments. macros, and strings wrap_comments: bool, false, false, "Break comments to fit on the line"; @@ -81,6 +83,7 @@ create_config! { "Format hexadecimal integer literals"; // Single line expressions and items + single_line_simple_if: bool, false, false, "Simple if statements can format to a single line"; empty_item_single_line: bool, true, false, "Put empty-body functions and impls on a single line"; struct_lit_single_line: bool, true, false, @@ -490,6 +493,8 @@ mod test { single_line_let_else_max_width: usize, 50, false, "Maximum line length for single \ line let-else statements. A value of zero means always format the divergent \ `else` block over multiple lines."; + single_line_simple_if_max_width: usize, 50, false, "Maximum line length for \ + single line if statement. A value of zero means always format to multiple lines."; // Options that are used by the tests stable_option: bool, false, true, "A stable option"; @@ -634,6 +639,7 @@ array_width = 60 chain_width = 60 single_line_if_else_max_width = 50 single_line_let_else_max_width = 50 +single_line_simple_if_max_width = 50 wrap_comments = false format_code_in_doc_comments = false doc_comment_code_block_width = 100 @@ -645,6 +651,7 @@ format_macro_matchers = false format_macro_bodies = true skip_macro_invocations = [] hex_literal_case = "Preserve" +single_line_simple_if = false empty_item_single_line = true struct_lit_single_line = true fn_single_line = false diff --git a/src/config/options.rs b/src/config/options.rs index e7fd2cfbd31..f1d70a2c737 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -241,6 +241,9 @@ pub struct WidthHeuristics { // Maximum line length for single line let-else statements. A value of zero means // always format the divergent `else` block over multiple lines. pub(crate) single_line_let_else_max_width: usize, + // Maximum line length for single line if statement. + // A value of zero means always format to multiple lines. + pub(crate) single_line_simple_if_max_width: usize, } impl fmt::Display for WidthHeuristics { @@ -261,6 +264,7 @@ impl WidthHeuristics { chain_width: usize::max_value(), single_line_if_else_max_width: 0, single_line_let_else_max_width: 0, + single_line_simple_if_max_width: 0, } } @@ -274,6 +278,7 @@ impl WidthHeuristics { chain_width: max_width, single_line_if_else_max_width: max_width, single_line_let_else_max_width: max_width, + single_line_simple_if_max_width: max_width, } } @@ -296,6 +301,7 @@ impl WidthHeuristics { chain_width: (60.0 * max_width_ratio).round() as usize, single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize, single_line_let_else_max_width: (50.0 * max_width_ratio).round() as usize, + single_line_simple_if_max_width: (50.0 * max_width_ratio).round() as usize, } } } diff --git a/src/expr.rs b/src/expr.rs index 6eea315cf6e..41c7efce231 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1102,16 +1102,31 @@ impl<'a> Rewrite for ControlFlow<'a> { }; let block_str = { let old_val = context.is_if_else_block.replace(self.else_block.is_some()); - let allow_single_line = - allow_single_line_if(&cond_str, self.block) && self.keyword == "if"; + let max_width = if context.config.single_line_simple_if() { + std::cmp::min( + shape.width, + context.config.single_line_simple_if_max_width(), + ) + } else { + shape.width + }; + let available_space = max_width.saturating_sub(used_width); + let allow_single_line = allow_single_line_if(&cond_str, self.block) + && self.keyword == "if" + && available_space > 0; - let result = if allow_single_line { - rewrite_block_inner(self.block, None, None, true, context, block_shape) + let mut result = if allow_single_line && context.config.single_line_simple_if() { + rewrite_block_inner(self.block, None, None, true, context, block_shape)? } else { - rewrite_block_with_visitor(context, "", self.block, None, None, block_shape, true) + rewrite_block_with_visitor(context, "", self.block, None, None, block_shape, true)? }; + + let block_exceeds_width = result.len() > available_space; + if allow_single_line && !result.contains('\n') && block_exceeds_width { + result = rewrite_block_inner(self.block, None, None, false, context, block_shape)?; + } context.is_if_else_block.replace(old_val); - result? + result }; let mut result = format!("{cond_str}{block_str}"); @@ -1168,20 +1183,26 @@ fn allow_single_line_if(result: &str, block: &ast::Block) -> bool { if result.contains('\n') { return false; } - if block.stmts.len() == 0 { return true; } if block.stmts.len() == 1 { - return is_simple_stmt(&block.stmts[0]); + return is_simple_control_flow_stmt(&block.stmts[0]); } false } -fn is_simple_stmt(stmt: &ast::Stmt) -> bool { +fn is_simple_control_flow_stmt(stmt: &ast::Stmt) -> bool { match stmt.kind { ast::StmtKind::Expr(ref expr) => match expr.kind { - ast::ExprKind::Ret(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Break(..) => true, + ast::ExprKind::Continue(..) => true, + ast::ExprKind::Break(_, ref opt_expr) | ast::ExprKind::Ret(ref opt_expr) => { + if let Some(_) = *opt_expr { + false + } else { + true + } + } _ => false, }, _ => false, From 2b9164e0f3e0c6151083723d3332e4f7b4c3ab78 Mon Sep 17 00:00:00 2001 From: Sjael Date: Wed, 24 Jan 2024 09:46:18 -0800 Subject: [PATCH 3/6] single_line_if tests --- tests/source/single-line-if-else.rs | 16 -- tests/source/single-line-simple-if.rs | 109 +++++++++++++ tests/source/single_line_if/false_0.rs | 43 +++++ tests/source/single_line_if/false_100.rs | 43 +++++ tests/source/single_line_if/false_50.rs | 43 +++++ tests/source/single_line_if/true_0.rs | 45 ++++++ tests/source/single_line_if/true_100.rs | 45 ++++++ tests/source/single_line_if/true_50.rs | 45 ++++++ tests/target/single-line-if-else.rs | 21 --- tests/target/single-line-simple-if.rs | 198 +++++++++++++++++++++++ tests/target/single_line_if/false_0.rs | 69 ++++++++ tests/target/single_line_if/false_100.rs | 61 +++++++ tests/target/single_line_if/false_50.rs | 61 +++++++ tests/target/single_line_if/true_0.rs | 65 ++++++++ tests/target/single_line_if/true_100.rs | 57 +++++++ tests/target/single_line_if/true_50.rs | 57 +++++++ 16 files changed, 941 insertions(+), 37 deletions(-) create mode 100644 tests/source/single-line-simple-if.rs create mode 100644 tests/source/single_line_if/false_0.rs create mode 100644 tests/source/single_line_if/false_100.rs create mode 100644 tests/source/single_line_if/false_50.rs create mode 100644 tests/source/single_line_if/true_0.rs create mode 100644 tests/source/single_line_if/true_100.rs create mode 100644 tests/source/single_line_if/true_50.rs create mode 100644 tests/target/single-line-simple-if.rs create mode 100644 tests/target/single_line_if/false_0.rs create mode 100644 tests/target/single_line_if/false_100.rs create mode 100644 tests/target/single_line_if/false_50.rs create mode 100644 tests/target/single_line_if/true_0.rs create mode 100644 tests/target/single_line_if/true_100.rs create mode 100644 tests/target/single_line_if/true_50.rs diff --git a/tests/source/single-line-if-else.rs b/tests/source/single-line-if-else.rs index 832a53dedb8..bcde390d116 100644 --- a/tests/source/single-line-if-else.rs +++ b/tests/source/single-line-if-else.rs @@ -35,22 +35,6 @@ fn main() { do_something() } - let a = if x { 1 } else { 3 }; - - // if may be formatted on a single line if it is "short" - // and only contain a single expression - if true { return } - - if true { - return - } - - if true { return; } - - if a { let y = 1; return y } - - for i in 0..2 { if g == true { continue } } - let x = if veeeeeeeeery_loooooong_condition() { aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa } else { bbbbbbbbbb }; let x = if veeeeeeeeery_loooooong_condition() { aaaaaaaaaaaaaaaaaaaaaaaaa } else { diff --git a/tests/source/single-line-simple-if.rs b/tests/source/single-line-simple-if.rs new file mode 100644 index 00000000000..a7ab739d242 --- /dev/null +++ b/tests/source/single-line-simple-if.rs @@ -0,0 +1,109 @@ +// rustfmt-single_line_simple_if: true +// rustfmt-unstable_features: true + +fn main() { + // if statements may be formatted on a single line if they are "short" + // and only contain a single expression of 'return', 'continue' or 'break' + if true { continue } + + if true { + continue + } + + // Default max width is 50 + if width == 50_characters_or_shorter { continue } + if width == 51_characters_long_and_above { return } + + if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { return } + + // More than 1 stmt means a new line, it is no longer 'simple' + if a { let y = 1; return y; } + + // Adds a semicolon to 'return/continue/break' when put on a new line + // (unless config has trailing_semicolon = false) + if a { let y = 1; return y } + + // Will not work on fn or method calls (may change) + if true { do_something() } + + // Will not work on an expression with trailing semicolon pre-format + if true { return; } + + // Will not single line if there is an else block, even with single expressions + if true { return } else { break } + + // Will not be single line if returns/breaks with a value + for i in 0..2{ + if true { break } + if true { break 2 } + if true { return } + if true { return 3 } + } + + // Will not be single line if comment is in the block + if true { + // nope + return + } + if true { /* nope 2 */ return } + + // Only works on if blocks, not other control flow + for i in 0..2 { if i == 1 { continue } } + + for i in 0..2 { + loop { if i == 1 { continue } } + } + + // New line formatted here as 'loop' != 'return/continue/break' + if i == 1 { loop { return } } + + // Works on labelled break/continue + 'gamer: loop { if true{ break 'gamer } } + + 'gamer: loop { if true{ break 'gamer; } } + + let result = 'block: { + if foo() { break 'block 1 } + if bar() { break 'block 2; } + 3 + }; + + #[allow(unused)] + // Comments after attributes dont mess it up + if true { return } + #[cfg(target_os = "linux")] + // Comments after attributes dont mess it up + if name == super_duper_ultra_really_name { return } + #[cfg(target_os = "linux")] + /* Multiple lines dont mess this up */ + /* Multiple lines dont mess this up */ + if name == super_duper_ultra_really_name { return } + + // Works as intended with nested ifs and indents + if true { + if true { continue } + if true { if true { continue } } + } else if false { + if true { if true { if width == 50_characters_or_shorter { continue } if width == 51_characters_long_and_above { return } } } + } else { + if true { return; } + } + + // Works with complex conditions + if matches!(x, Ok(Some(value))) { continue } + if matches!(x, Ok(Some(value))) { kick_ball() } + if matches!(x, Ok(Some(value))) && value.some_method_call(input) { break } + if matches!(x, Ok(Some(value))) && value.some_method_call(input) { run_fast() } + if matches!(x, Ok(Some(value))) && value.some_method_call(input) && single_line_if_is_allowed_at_all_ever { return } + if matches!(x, Ok(Some(value))) && value.some_method_call(input) && single_line_if_is_allowed_at_all_ever { play_catch() } + + // Nested complex conditions + if true { + if matches!(x, Ok(Some(value))) { continue } + if true { if matches!(x, Ok(Some(value))) && value.some_method_call(input) { break } } + } else if false { + if true { if true { if matches!(x, Ok(Some(value))) { continue } } } + } else { + if true { if true { if matches!(x, Ok(Some(value))) && value.some_method_call(input) && single_line_if_is_allowed_at_all_ever { return } } } + } +} \ No newline at end of file diff --git a/tests/source/single_line_if/false_0.rs b/tests/source/single_line_if/false_0.rs new file mode 100644 index 00000000000..40b090a6f1c --- /dev/null +++ b/tests/source/single_line_if/false_0.rs @@ -0,0 +1,43 @@ +// rustfmt-single_line_if_else_max_width: 0 + +fn main() { + let a = if 1 > 2 { + unreachable!() + } else { + 10 + }; + + let a = if x { 1 } else if y { 2 } else { 3 }; + + if true { continue } + + if true { + continue + } + + if width == 50_characters_or_shorter { continue } + if width == 51_characters_long_and_above { return } + + if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { return } + + if true { return } else { break } + + let x = if true { return } else { break }; + + let b = if cond() { + 5 + } else { + // Brief comment. + 10 + }; + + let c = if cond() { + statement(); + + 5 + } else { + 10 + }; + + if cond() { statement(); } else { other_statement(); } +} \ No newline at end of file diff --git a/tests/source/single_line_if/false_100.rs b/tests/source/single_line_if/false_100.rs new file mode 100644 index 00000000000..99316c40751 --- /dev/null +++ b/tests/source/single_line_if/false_100.rs @@ -0,0 +1,43 @@ +// rustfmt-single_line_if_else_max_width: 100 + +fn main() { + let a = if 1 > 2 { + unreachable!() + } else { + 10 + }; + + let a = if x { 1 } else if y { 2 } else { 3 }; + + if true { continue } + + if true { + continue + } + + if width == 50_characters_or_shorter { continue } + if width == 51_characters_long_and_above { return } + + if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { return } + + if true { return } else { break } + + let x = if true { return } else { break }; + + let b = if cond() { + 5 + } else { + // Brief comment. + 10 + }; + + let c = if cond() { + statement(); + + 5 + } else { + 10 + }; + + if cond() { statement(); } else { other_statement(); } +} diff --git a/tests/source/single_line_if/false_50.rs b/tests/source/single_line_if/false_50.rs new file mode 100644 index 00000000000..7a271a40580 --- /dev/null +++ b/tests/source/single_line_if/false_50.rs @@ -0,0 +1,43 @@ +// rustfmt-single_line_if_else_max_width: 50 + +fn main() { + let a = if 1 > 2 { + unreachable!() + } else { + 10 + }; + + let a = if x { 1 } else if y { 2 } else { 3 }; + + if true { continue } + + if true { + continue + } + + if width == 50_characters_or_shorter { continue } + if width == 51_characters_long_and_above { return } + + if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { return } + + if true { return } else { break } + + let x = if true { return } else { break }; + + let b = if cond() { + 5 + } else { + // Brief comment. + 10 + }; + + let c = if cond() { + statement(); + + 5 + } else { + 10 + }; + + if cond() { statement(); } else { other_statement(); } +} diff --git a/tests/source/single_line_if/true_0.rs b/tests/source/single_line_if/true_0.rs new file mode 100644 index 00000000000..4a4f02e7ddd --- /dev/null +++ b/tests/source/single_line_if/true_0.rs @@ -0,0 +1,45 @@ +// rustfmt-single_line_simple_if: true +// rustfmt-unstable_features: true +// rustfmt-single_line_if_else_max_width: 0 + +fn main() { + let a = if 1 > 2 { + unreachable!() + } else { + 10 + }; + + let a = if x { 1 } else if y { 2 } else { 3 }; + + if true { continue } + + if true { + continue + } + + if width == 50_characters_or_shorter { continue } + if width == 51_characters_long_and_above { return } + + if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { return } + + if true { return } else { break } + + let x = if true { return } else { break }; + + let b = if cond() { + 5 + } else { + // Brief comment. + 10 + }; + + let c = if cond() { + statement(); + + 5 + } else { + 10 + }; + + if cond() { statement(); } else { other_statement(); } +} diff --git a/tests/source/single_line_if/true_100.rs b/tests/source/single_line_if/true_100.rs new file mode 100644 index 00000000000..adc4ef56ff3 --- /dev/null +++ b/tests/source/single_line_if/true_100.rs @@ -0,0 +1,45 @@ +// rustfmt-single_line_simple_if: true +// rustfmt-unstable_features: true +// rustfmt-single_line_if_else_max_width: 100 + +fn main() { + let a = if 1 > 2 { + unreachable!() + } else { + 10 + }; + + let a = if x { 1 } else if y { 2 } else { 3 }; + + if true { continue } + + if true { + continue + } + + if width == 50_characters_or_shorter { continue } + if width == 51_characters_long_and_above { return } + + if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { return } + + if true { return } else { break } + + let x = if true { return } else { break }; + + let b = if cond() { + 5 + } else { + // Brief comment. + 10 + }; + + let c = if cond() { + statement(); + + 5 + } else { + 10 + }; + + if cond() { statement(); } else { other_statement(); } +} diff --git a/tests/source/single_line_if/true_50.rs b/tests/source/single_line_if/true_50.rs new file mode 100644 index 00000000000..9f44df4e181 --- /dev/null +++ b/tests/source/single_line_if/true_50.rs @@ -0,0 +1,45 @@ +// rustfmt-single_line_simple_if: true +// rustfmt-unstable_features: true +// rustfmt-single_line_if_else_max_width: 50 + +fn main() { + let a = if 1 > 2 { + unreachable!() + } else { + 10 + }; + + let a = if x { 1 } else if y { 2 } else { 3 }; + + if true { continue } + + if true { + continue + } + + if width == 50_characters_or_shorter { continue } + if width == 51_characters_long_and_above { return } + + if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { return } + + if true { return } else { break } + + let x = if true { return } else { break }; + + let b = if cond() { + 5 + } else { + // Brief comment. + 10 + }; + + let c = if cond() { + statement(); + + 5 + } else { + 10 + }; + + if cond() { statement(); } else { other_statement(); } +} diff --git a/tests/target/single-line-if-else.rs b/tests/target/single-line-if-else.rs index 002f576b01a..98fd793cba2 100644 --- a/tests/target/single-line-if-else.rs +++ b/tests/target/single-line-if-else.rs @@ -42,27 +42,6 @@ fn main() { do_something() } - let a = if x { 1 } else { 3 }; - - // if may be formatted on a single line if it is "short" - // and only contain a single expression - if true { return } - - if true { return } - - if true { - return; - } - - if a { - let y = 1; - return y; - } - - for i in 0..2 { - if g == true { continue } - } - let x = if veeeeeeeeery_loooooong_condition() { aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa } else { diff --git a/tests/target/single-line-simple-if.rs b/tests/target/single-line-simple-if.rs new file mode 100644 index 00000000000..e0982811e91 --- /dev/null +++ b/tests/target/single-line-simple-if.rs @@ -0,0 +1,198 @@ +// rustfmt-single_line_simple_if: true +// rustfmt-unstable_features: true + +fn main() { + // if statements may be formatted on a single line if they are "short" + // and only contain a single expression of 'return', 'continue' or 'break' + if true { continue } + + if true { continue } + + // Default max width is 50 + if width == 50_characters_or_shorter { continue } + if width == 51_characters_long_and_above { + return; + } + + if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { + return; + } + + // More than 1 stmt means a new line, it is no longer 'simple' + if a { + let y = 1; + return y; + } + + // Adds a semicolon to 'return/continue/break' when put on a new line + // (unless config has trailing_semicolon = false) + if a { + let y = 1; + return y; + } + + // Will not work on fn or method calls (may change) + if true { + do_something() + } + + // Will not work on an expression with trailing semicolon pre-format + if true { + return; + } + + // Will not single line if there is an else block, even with single expressions + if true { + return; + } else { + break; + } + + // Will not be single line if returns/breaks with a value + for i in 0..2 { + if true { break } + if true { + break 2; + } + if true { return } + if true { + return 3; + } + } + + // Will not be single line if comment is in the block + if true { + // nope + return; + } + if true { + /* nope 2 */ + return; + } + + // Only works on if blocks, not other control flow + for i in 0..2 { + if i == 1 { continue } + } + + for i in 0..2 { + loop { + if i == 1 { continue } + } + } + + // New line formatted here as 'loop' != 'return/continue/break' + if i == 1 { + loop { + return; + } + } + + // Works on labelled break/continue + 'gamer: loop { + if true { break 'gamer } + } + + 'gamer: loop { + if true { + break 'gamer; + } + } + + let result = 'block: { + if foo() { + break 'block 1; + } + if bar() { + break 'block 2; + } + 3 + }; + + #[allow(unused)] + // Comments after attributes dont mess it up + if true { return } + #[cfg(target_os = "linux")] + // Comments after attributes dont mess it up + if name == super_duper_ultra_really_name { + return; + } + #[cfg(target_os = "linux")] + /* Multiple lines dont mess this up */ + /* Multiple lines dont mess this up */ + if name == super_duper_ultra_really_name { + return; + } + + // Works as intended with nested ifs and indents + if true { + if true { continue } + if true { + if true { continue } + } + } else if false { + if true { + if true { + if width == 50_characters_or_shorter { continue } + if width == 51_characters_long_and_above { + return; + } + } + } + } else { + if true { + return; + } + } + + // Works with complex conditions + if matches!(x, Ok(Some(value))) { continue } + if matches!(x, Ok(Some(value))) { + kick_ball() + } + if matches!(x, Ok(Some(value))) && value.some_method_call(input) { + break; + } + if matches!(x, Ok(Some(value))) && value.some_method_call(input) { + run_fast() + } + if matches!(x, Ok(Some(value))) + && value.some_method_call(input) + && single_line_if_is_allowed_at_all_ever + { + return; + } + if matches!(x, Ok(Some(value))) + && value.some_method_call(input) + && single_line_if_is_allowed_at_all_ever + { + play_catch() + } + + // Nested complex conditions + if true { + if matches!(x, Ok(Some(value))) { continue } + if true { + if matches!(x, Ok(Some(value))) && value.some_method_call(input) { + break; + } + } + } else if false { + if true { + if true { + if matches!(x, Ok(Some(value))) { continue } + } + } + } else { + if true { + if true { + if matches!(x, Ok(Some(value))) + && value.some_method_call(input) + && single_line_if_is_allowed_at_all_ever + { + return; + } + } + } + } +} diff --git a/tests/target/single_line_if/false_0.rs b/tests/target/single_line_if/false_0.rs new file mode 100644 index 00000000000..2d4452eecdd --- /dev/null +++ b/tests/target/single_line_if/false_0.rs @@ -0,0 +1,69 @@ +// rustfmt-single_line_if_else_max_width: 0 + +fn main() { + let a = if 1 > 2 { + unreachable!() + } else { + 10 + }; + + let a = if x { + 1 + } else if y { + 2 + } else { + 3 + }; + + if true { + continue; + } + + if true { + continue; + } + + if width == 50_characters_or_shorter { + continue; + } + if width == 51_characters_long_and_above { + return; + } + + if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { + return; + } + + if true { + return; + } else { + break; + } + + let x = if true { + return; + } else { + break; + }; + + let b = if cond() { + 5 + } else { + // Brief comment. + 10 + }; + + let c = if cond() { + statement(); + + 5 + } else { + 10 + }; + + if cond() { + statement(); + } else { + other_statement(); + } +} diff --git a/tests/target/single_line_if/false_100.rs b/tests/target/single_line_if/false_100.rs new file mode 100644 index 00000000000..90d90d96212 --- /dev/null +++ b/tests/target/single_line_if/false_100.rs @@ -0,0 +1,61 @@ +// rustfmt-single_line_if_else_max_width: 100 + +fn main() { + let a = if 1 > 2 { unreachable!() } else { 10 }; + + let a = if x { + 1 + } else if y { + 2 + } else { + 3 + }; + + if true { + continue; + } + + if true { + continue; + } + + if width == 50_characters_or_shorter { + continue; + } + if width == 51_characters_long_and_above { + return; + } + + if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { + return; + } + + if true { + return; + } else { + break; + } + + let x = if true { return } else { break }; + + let b = if cond() { + 5 + } else { + // Brief comment. + 10 + }; + + let c = if cond() { + statement(); + + 5 + } else { + 10 + }; + + if cond() { + statement(); + } else { + other_statement(); + } +} diff --git a/tests/target/single_line_if/false_50.rs b/tests/target/single_line_if/false_50.rs new file mode 100644 index 00000000000..dcc898fbcbe --- /dev/null +++ b/tests/target/single_line_if/false_50.rs @@ -0,0 +1,61 @@ +// rustfmt-single_line_if_else_max_width: 50 + +fn main() { + let a = if 1 > 2 { unreachable!() } else { 10 }; + + let a = if x { + 1 + } else if y { + 2 + } else { + 3 + }; + + if true { + continue; + } + + if true { + continue; + } + + if width == 50_characters_or_shorter { + continue; + } + if width == 51_characters_long_and_above { + return; + } + + if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { + return; + } + + if true { + return; + } else { + break; + } + + let x = if true { return } else { break }; + + let b = if cond() { + 5 + } else { + // Brief comment. + 10 + }; + + let c = if cond() { + statement(); + + 5 + } else { + 10 + }; + + if cond() { + statement(); + } else { + other_statement(); + } +} diff --git a/tests/target/single_line_if/true_0.rs b/tests/target/single_line_if/true_0.rs new file mode 100644 index 00000000000..fb03f4415c4 --- /dev/null +++ b/tests/target/single_line_if/true_0.rs @@ -0,0 +1,65 @@ +// rustfmt-single_line_simple_if: true +// rustfmt-unstable_features: true +// rustfmt-single_line_if_else_max_width: 0 + +fn main() { + let a = if 1 > 2 { + unreachable!() + } else { + 10 + }; + + let a = if x { + 1 + } else if y { + 2 + } else { + 3 + }; + + if true { continue } + + if true { continue } + + if width == 50_characters_or_shorter { continue } + if width == 51_characters_long_and_above { + return; + } + + if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { + return; + } + + if true { + return; + } else { + break; + } + + let x = if true { + return; + } else { + break; + }; + + let b = if cond() { + 5 + } else { + // Brief comment. + 10 + }; + + let c = if cond() { + statement(); + + 5 + } else { + 10 + }; + + if cond() { + statement(); + } else { + other_statement(); + } +} diff --git a/tests/target/single_line_if/true_100.rs b/tests/target/single_line_if/true_100.rs new file mode 100644 index 00000000000..a73305bfc1a --- /dev/null +++ b/tests/target/single_line_if/true_100.rs @@ -0,0 +1,57 @@ +// rustfmt-single_line_simple_if: true +// rustfmt-unstable_features: true +// rustfmt-single_line_if_else_max_width: 100 + +fn main() { + let a = if 1 > 2 { unreachable!() } else { 10 }; + + let a = if x { + 1 + } else if y { + 2 + } else { + 3 + }; + + if true { continue } + + if true { continue } + + if width == 50_characters_or_shorter { continue } + if width == 51_characters_long_and_above { + return; + } + + if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { + return; + } + + if true { + return; + } else { + break; + } + + let x = if true { return } else { break }; + + let b = if cond() { + 5 + } else { + // Brief comment. + 10 + }; + + let c = if cond() { + statement(); + + 5 + } else { + 10 + }; + + if cond() { + statement(); + } else { + other_statement(); + } +} diff --git a/tests/target/single_line_if/true_50.rs b/tests/target/single_line_if/true_50.rs new file mode 100644 index 00000000000..2ef738846aa --- /dev/null +++ b/tests/target/single_line_if/true_50.rs @@ -0,0 +1,57 @@ +// rustfmt-single_line_simple_if: true +// rustfmt-unstable_features: true +// rustfmt-single_line_if_else_max_width: 50 + +fn main() { + let a = if 1 > 2 { unreachable!() } else { 10 }; + + let a = if x { + 1 + } else if y { + 2 + } else { + 3 + }; + + if true { continue } + + if true { continue } + + if width == 50_characters_or_shorter { continue } + if width == 51_characters_long_and_above { + return; + } + + if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { + return; + } + + if true { + return; + } else { + break; + } + + let x = if true { return } else { break }; + + let b = if cond() { + 5 + } else { + // Brief comment. + 10 + }; + + let c = if cond() { + statement(); + + 5 + } else { + 10 + }; + + if cond() { + statement(); + } else { + other_statement(); + } +} From fa1d45eb5a8a4d1fefa64b4e4b3445cd818c42c7 Mon Sep 17 00:00:00 2001 From: Sjael Date: Wed, 24 Jan 2024 10:17:23 -0800 Subject: [PATCH 4/6] reverted semicolon --- tests/source/expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/source/expr.rs b/tests/source/expr.rs index 056a94b2676..879c551ea49 100644 --- a/tests/source/expr.rs +++ b/tests/source/expr.rs @@ -79,7 +79,7 @@ fn bar() { let bar = 5 ; let nonsense = (10 .. 0)..(0..10); - loop{if true {break;}} + loop{if true {break}} let x = (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa && aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, From 0c1f56d0b3dc3242c81f119cf207b90888283689 Mon Sep 17 00:00:00 2001 From: Sjael Date: Thu, 25 Jan 2024 19:50:16 -0800 Subject: [PATCH 5/6] third pass --- Configurations.md | 46 ++++++++++++- src/config/mod.rs | 4 +- src/expr.rs | 67 +++++++++++++------ tests/source/single_line_if/false_0.rs | 20 +++++- tests/source/single_line_if/false_100.rs | 20 +++++- tests/source/single_line_if/false_50.rs | 20 +++++- .../mod.rs} | 23 ++++--- tests/source/single_line_if/true_0.rs | 23 +++++-- tests/source/single_line_if/true_100.rs | 22 ++++-- tests/source/single_line_if/true_50.rs | 22 ++++-- tests/target/single_line_if/false_0.rs | 52 +++++++++++++- tests/target/single_line_if/false_100.rs | 41 +++++++++++- tests/target/single_line_if/false_50.rs | 41 +++++++++++- .../mod.rs} | 35 +++++++--- tests/target/single_line_if/true_0.rs | 50 ++++++++++++-- tests/target/single_line_if/true_100.rs | 39 +++++++++-- tests/target/single_line_if/true_50.rs | 39 +++++++++-- 17 files changed, 481 insertions(+), 83 deletions(-) rename tests/source/{single-line-simple-if.rs => single_line_if/mod.rs} (82%) rename tests/target/{single-line-simple-if.rs => single_line_if/mod.rs} (85%) diff --git a/Configurations.md b/Configurations.md index 925cbaed3c1..d4c1151f2be 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2481,7 +2481,7 @@ fn main() { See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) -## `single_line_simple_if` +## `single_line_if` Allows simple single expression if blocks to format on one line. Useful in the case of keeping `let-else` guards format consistent with `if` guards. @@ -2496,11 +2496,53 @@ Note that line will still break if: #### `false` (default): +```rust +fn main() { + if true { + break; + } + + if false { + return; + } + + if width == is_49_characters____long { + continue; + } + + if width == is_50_characters_____long { + continue; + } + + if width == is_51_characters______long { + continue; + } +} +``` + +#### `true`: + +```rust +fn main() { + if true { break } + + if false { return } + + if width == is_49_characters____long { continue } + + if width == is_50_characters_____long { continue } + + if width == is_51_characters______long { + continue; + } +} +``` + ## `single_line_simple_if_max_width` Maximum line length for single line if with a simple inner expression. Useful in the case of keeping `let-else` guards format consistent with `if` guards. -A value of `0` (zero) results in if-else expressions always being broken into multiple lines. Note this occurs when `use_small_heuristics` is set to `Off`. +A value of `0` (zero) results in if blocks always being broken into multiple lines. Note this occurs when `use_small_heuristics` is set to `Off`. - **Default value**: `50` - **Possible values**: any positive integer that is less than or equal to the value specified for [`max_width`](#max_width) diff --git a/src/config/mod.rs b/src/config/mod.rs index da2186f73fa..3388d9a5d64 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -83,7 +83,7 @@ create_config! { "Format hexadecimal integer literals"; // Single line expressions and items - single_line_simple_if: bool, false, false, "Simple if statements can format to a single line"; + single_line_if: bool, false, false, "Simple if statements can format to a single line"; empty_item_single_line: bool, true, false, "Put empty-body functions and impls on a single line"; struct_lit_single_line: bool, true, false, @@ -651,7 +651,7 @@ format_macro_matchers = false format_macro_bodies = true skip_macro_invocations = [] hex_literal_case = "Preserve" -single_line_simple_if = false +single_line_if = false empty_item_single_line = true struct_lit_single_line = true fn_single_line = false diff --git a/src/expr.rs b/src/expr.rs index 41c7efce231..c14636cdffe 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1102,31 +1102,20 @@ impl<'a> Rewrite for ControlFlow<'a> { }; let block_str = { let old_val = context.is_if_else_block.replace(self.else_block.is_some()); - let max_width = if context.config.single_line_simple_if() { - std::cmp::min( - shape.width, - context.config.single_line_simple_if_max_width(), + let result = if self.keyword == "if" { + format_if( + context, + shape, + self.block, + &cond_str, + used_width, + block_shape, ) } else { - shape.width - }; - let available_space = max_width.saturating_sub(used_width); - let allow_single_line = allow_single_line_if(&cond_str, self.block) - && self.keyword == "if" - && available_space > 0; - - let mut result = if allow_single_line && context.config.single_line_simple_if() { - rewrite_block_inner(self.block, None, None, true, context, block_shape)? - } else { - rewrite_block_with_visitor(context, "", self.block, None, None, block_shape, true)? + rewrite_block_with_visitor(context, "", self.block, None, None, block_shape, true) }; - - let block_exceeds_width = result.len() > available_space; - if allow_single_line && !result.contains('\n') && block_exceeds_width { - result = rewrite_block_inner(self.block, None, None, false, context, block_shape)?; - } context.is_if_else_block.replace(old_val); - result + result? }; let mut result = format!("{cond_str}{block_str}"); @@ -1179,6 +1168,41 @@ impl<'a> Rewrite for ControlFlow<'a> { } } +fn format_if( + context: &RewriteContext<'_>, + shape: Shape, + block: &ast::Block, + cond_str: &str, + used_width: usize, + block_shape: Shape, +) -> Option { + let max_width = if context.config.single_line_if() { + std::cmp::min( + shape.width, + context.config.single_line_simple_if_max_width(), + ) + } else { + shape.width + }; + let available_space = max_width.saturating_sub(used_width); + let allow_single_line = context.config.single_line_if() + && available_space > 0 + && allow_single_line_if(&cond_str, block); + + let result = if allow_single_line { + let mut single_line_attempt = + rewrite_block_inner(block, None, None, true, context, block_shape)?; + if !single_line_attempt.contains('\n') && single_line_attempt.len() > available_space { + single_line_attempt = + rewrite_block_inner(block, None, None, false, context, block_shape)?; + } + Some(single_line_attempt) + } else { + rewrite_block_with_visitor(context, "", block, None, None, block_shape, true) + }; + result +} + fn allow_single_line_if(result: &str, block: &ast::Block) -> bool { if result.contains('\n') { return false; @@ -1198,6 +1222,7 @@ fn is_simple_control_flow_stmt(stmt: &ast::Stmt) -> bool { ast::ExprKind::Continue(..) => true, ast::ExprKind::Break(_, ref opt_expr) | ast::ExprKind::Ret(ref opt_expr) => { if let Some(_) = *opt_expr { + // Do not allow single line if block has `return/break` with a returned value false } else { true diff --git a/tests/source/single_line_if/false_0.rs b/tests/source/single_line_if/false_0.rs index 40b090a6f1c..26f6da9bff4 100644 --- a/tests/source/single_line_if/false_0.rs +++ b/tests/source/single_line_if/false_0.rs @@ -1,6 +1,21 @@ +// rustfmt-single_line_if: false // rustfmt-single_line_if_else_max_width: 0 fn main() { + let x = if true { 1 } else { 2 }; + + funk(if test() { 1 } else { 2 }, arg2); + + if true { 1 } else { 2 } + + if test() { 1 } else { 2 } + + if let Some(a) = b { return } + + if let Some(a) = b { do_something(); return } + + if let Some(a) = b { return } else { continue } + let a = if 1 > 2 { unreachable!() } else { @@ -15,8 +30,9 @@ fn main() { continue } - if width == 50_characters_or_shorter { continue } - if width == 51_characters_long_and_above { return } + if width == is_49_characters____long { continue } + if width == is_50_characters_____long { continue } + if width == is_51_characters______long { continue } if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { return } diff --git a/tests/source/single_line_if/false_100.rs b/tests/source/single_line_if/false_100.rs index 99316c40751..bbca4ac6576 100644 --- a/tests/source/single_line_if/false_100.rs +++ b/tests/source/single_line_if/false_100.rs @@ -1,6 +1,21 @@ +// rustfmt-single_line_if: false // rustfmt-single_line_if_else_max_width: 100 fn main() { + let x = if true { 1 } else { 2 }; + + funk(if test() { 1 } else { 2 }, arg2); + + if true { 1 } else { 2 } + + if test() { 1 } else { 2 } + + if let Some(a) = b { return } + + if let Some(a) = b { do_something(); return } + + if let Some(a) = b { return } else { continue } + let a = if 1 > 2 { unreachable!() } else { @@ -15,8 +30,9 @@ fn main() { continue } - if width == 50_characters_or_shorter { continue } - if width == 51_characters_long_and_above { return } + if width == is_49_characters____long { continue } + if width == is_50_characters_____long { continue } + if width == is_51_characters______long { continue } if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { return } diff --git a/tests/source/single_line_if/false_50.rs b/tests/source/single_line_if/false_50.rs index 7a271a40580..e345eca8d5d 100644 --- a/tests/source/single_line_if/false_50.rs +++ b/tests/source/single_line_if/false_50.rs @@ -1,6 +1,21 @@ +// rustfmt-single_line_if: false // rustfmt-single_line_if_else_max_width: 50 fn main() { + let x = if true { 1 } else { 2 }; + + funk(if test() { 1 } else { 2 }, arg2); + + if true { 1 } else { 2 } + + if test() { 1 } else { 2 } + + if let Some(a) = b { return } + + if let Some(a) = b { do_something(); return } + + if let Some(a) = b { return } else { continue } + let a = if 1 > 2 { unreachable!() } else { @@ -15,8 +30,9 @@ fn main() { continue } - if width == 50_characters_or_shorter { continue } - if width == 51_characters_long_and_above { return } + if width == is_49_characters____long { continue } + if width == is_50_characters_____long { continue } + if width == is_51_characters______long { continue } if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { return } diff --git a/tests/source/single-line-simple-if.rs b/tests/source/single_line_if/mod.rs similarity index 82% rename from tests/source/single-line-simple-if.rs rename to tests/source/single_line_if/mod.rs index a7ab739d242..ac23fb8006c 100644 --- a/tests/source/single-line-simple-if.rs +++ b/tests/source/single_line_if/mod.rs @@ -1,5 +1,4 @@ -// rustfmt-single_line_simple_if: true -// rustfmt-unstable_features: true +// rustfmt-single_line_if: true fn main() { // if statements may be formatted on a single line if they are "short" @@ -11,8 +10,9 @@ fn main() { } // Default max width is 50 - if width == 50_characters_or_shorter { continue } - if width == 51_characters_long_and_above { return } + if width == is_49_characters____long { continue } + if width == is_50_characters_____long { continue } + if width == is_51_characters______long { continue } if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { return } @@ -56,11 +56,11 @@ fn main() { // New line formatted here as 'loop' != 'return/continue/break' if i == 1 { loop { return } } - + // Works on labelled break/continue - 'gamer: loop { if true{ break 'gamer } } + 'gamer: loop { if true{ break 'gamer } if true { continue 'gamer } } - 'gamer: loop { if true{ break 'gamer; } } + 'gamer: loop { if true{ break 'gamer; } if true { continue 'gamer; } } let result = 'block: { if foo() { break 'block 1 } @@ -68,6 +68,13 @@ fn main() { 3 }; + // Works with `if let` + if let Some(a) = b { return } + + if let Some(a) = b { do_something(); return } + + if let Some(a) = b { return } else { continue } + #[allow(unused)] // Comments after attributes dont mess it up if true { return } @@ -84,7 +91,7 @@ fn main() { if true { continue } if true { if true { continue } } } else if false { - if true { if true { if width == 50_characters_or_shorter { continue } if width == 51_characters_long_and_above { return } } } + if true { if true { if width == is_49_characters____long { continue } if width == is_50_characters_____long { continue } if width == is_51_characters______long { continue } } } } else { if true { return; } } diff --git a/tests/source/single_line_if/true_0.rs b/tests/source/single_line_if/true_0.rs index 4a4f02e7ddd..82a14514112 100644 --- a/tests/source/single_line_if/true_0.rs +++ b/tests/source/single_line_if/true_0.rs @@ -1,8 +1,21 @@ -// rustfmt-single_line_simple_if: true -// rustfmt-unstable_features: true +// rustfmt-single_line_if: true // rustfmt-single_line_if_else_max_width: 0 fn main() { + let x = if true { 1 } else { 2 }; + + funk(if test() { 1 } else { 2 }, arg2); + + if true { 1 } else { 2 } + + if test() { 1 } else { 2 } + + if let Some(a) = b { return } + + if let Some(a) = b { do_something(); return } + + if let Some(a) = b { return } else { continue } + let a = if 1 > 2 { unreachable!() } else { @@ -17,8 +30,10 @@ fn main() { continue } - if width == 50_characters_or_shorter { continue } - if width == 51_characters_long_and_above { return } + + if width == is_49_characters____long { continue } + if width == is_50_characters_____long { continue } + if width == is_51_characters______long { continue } if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { return } diff --git a/tests/source/single_line_if/true_100.rs b/tests/source/single_line_if/true_100.rs index adc4ef56ff3..bda95ba4a40 100644 --- a/tests/source/single_line_if/true_100.rs +++ b/tests/source/single_line_if/true_100.rs @@ -1,8 +1,21 @@ -// rustfmt-single_line_simple_if: true -// rustfmt-unstable_features: true +// rustfmt-single_line_if: true // rustfmt-single_line_if_else_max_width: 100 fn main() { + let x = if true { 1 } else { 2 }; + + funk(if test() { 1 } else { 2 }, arg2); + + if true { 1 } else { 2 } + + if test() { 1 } else { 2 } + + if let Some(a) = b { return } + + if let Some(a) = b { do_something(); return } + + if let Some(a) = b { return } else { continue } + let a = if 1 > 2 { unreachable!() } else { @@ -17,8 +30,9 @@ fn main() { continue } - if width == 50_characters_or_shorter { continue } - if width == 51_characters_long_and_above { return } + if width == is_49_characters____long { continue } + if width == is_50_characters_____long { continue } + if width == is_51_characters______long { continue } if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { return } diff --git a/tests/source/single_line_if/true_50.rs b/tests/source/single_line_if/true_50.rs index 9f44df4e181..7a8e2f16dd7 100644 --- a/tests/source/single_line_if/true_50.rs +++ b/tests/source/single_line_if/true_50.rs @@ -1,8 +1,21 @@ -// rustfmt-single_line_simple_if: true -// rustfmt-unstable_features: true +// rustfmt-single_line_if: true // rustfmt-single_line_if_else_max_width: 50 fn main() { + let x = if true { 1 } else { 2 }; + + funk(if test() { 1 } else { 2 }, arg2); + + if true { 1 } else { 2 } + + if test() { 1 } else { 2 } + + if let Some(a) = b { return } + + if let Some(a) = b { do_something(); return } + + if let Some(a) = b { return } else { continue } + let a = if 1 > 2 { unreachable!() } else { @@ -17,8 +30,9 @@ fn main() { continue } - if width == 50_characters_or_shorter { continue } - if width == 51_characters_long_and_above { return } + if width == is_49_characters____long { continue } + if width == is_50_characters_____long { continue } + if width == is_51_characters______long { continue } if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { return } diff --git a/tests/target/single_line_if/false_0.rs b/tests/target/single_line_if/false_0.rs index 2d4452eecdd..dba12293316 100644 --- a/tests/target/single_line_if/false_0.rs +++ b/tests/target/single_line_if/false_0.rs @@ -1,6 +1,49 @@ +// rustfmt-single_line_if: false // rustfmt-single_line_if_else_max_width: 0 fn main() { + let x = if true { + 1 + } else { + 2 + }; + + funk( + if test() { + 1 + } else { + 2 + }, + arg2, + ); + + if true { + 1 + } else { + 2 + } + + if test() { + 1 + } else { + 2 + } + + if let Some(a) = b { + return; + } + + if let Some(a) = b { + do_something(); + return; + } + + if let Some(a) = b { + return; + } else { + continue; + } + let a = if 1 > 2 { unreachable!() } else { @@ -23,11 +66,14 @@ fn main() { continue; } - if width == 50_characters_or_shorter { + if width == is_49_characters____long { continue; } - if width == 51_characters_long_and_above { - return; + if width == is_50_characters_____long { + continue; + } + if width == is_51_characters______long { + continue; } if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { diff --git a/tests/target/single_line_if/false_100.rs b/tests/target/single_line_if/false_100.rs index 90d90d96212..c17115e7399 100644 --- a/tests/target/single_line_if/false_100.rs +++ b/tests/target/single_line_if/false_100.rs @@ -1,6 +1,38 @@ +// rustfmt-single_line_if: false // rustfmt-single_line_if_else_max_width: 100 fn main() { + let x = if true { 1 } else { 2 }; + + funk(if test() { 1 } else { 2 }, arg2); + + if true { + 1 + } else { + 2 + } + + if test() { + 1 + } else { + 2 + } + + if let Some(a) = b { + return; + } + + if let Some(a) = b { + do_something(); + return; + } + + if let Some(a) = b { + return; + } else { + continue; + } + let a = if 1 > 2 { unreachable!() } else { 10 }; let a = if x { @@ -19,11 +51,14 @@ fn main() { continue; } - if width == 50_characters_or_shorter { + if width == is_49_characters____long { continue; } - if width == 51_characters_long_and_above { - return; + if width == is_50_characters_____long { + continue; + } + if width == is_51_characters______long { + continue; } if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { diff --git a/tests/target/single_line_if/false_50.rs b/tests/target/single_line_if/false_50.rs index dcc898fbcbe..82f725a1ce5 100644 --- a/tests/target/single_line_if/false_50.rs +++ b/tests/target/single_line_if/false_50.rs @@ -1,6 +1,38 @@ +// rustfmt-single_line_if: false // rustfmt-single_line_if_else_max_width: 50 fn main() { + let x = if true { 1 } else { 2 }; + + funk(if test() { 1 } else { 2 }, arg2); + + if true { + 1 + } else { + 2 + } + + if test() { + 1 + } else { + 2 + } + + if let Some(a) = b { + return; + } + + if let Some(a) = b { + do_something(); + return; + } + + if let Some(a) = b { + return; + } else { + continue; + } + let a = if 1 > 2 { unreachable!() } else { 10 }; let a = if x { @@ -19,11 +51,14 @@ fn main() { continue; } - if width == 50_characters_or_shorter { + if width == is_49_characters____long { continue; } - if width == 51_characters_long_and_above { - return; + if width == is_50_characters_____long { + continue; + } + if width == is_51_characters______long { + continue; } if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { diff --git a/tests/target/single-line-simple-if.rs b/tests/target/single_line_if/mod.rs similarity index 85% rename from tests/target/single-line-simple-if.rs rename to tests/target/single_line_if/mod.rs index e0982811e91..47ef453a73b 100644 --- a/tests/target/single-line-simple-if.rs +++ b/tests/target/single_line_if/mod.rs @@ -1,5 +1,4 @@ -// rustfmt-single_line_simple_if: true -// rustfmt-unstable_features: true +// rustfmt-single_line_if: true fn main() { // if statements may be formatted on a single line if they are "short" @@ -9,9 +8,10 @@ fn main() { if true { continue } // Default max width is 50 - if width == 50_characters_or_shorter { continue } - if width == 51_characters_long_and_above { - return; + if width == is_49_characters____long { continue } + if width == is_50_characters_____long { continue } + if width == is_51_characters______long { + continue; } if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { @@ -91,12 +91,16 @@ fn main() { // Works on labelled break/continue 'gamer: loop { if true { break 'gamer } + if true { continue 'gamer } } 'gamer: loop { if true { break 'gamer; } + if true { + continue 'gamer; + } } let result = 'block: { @@ -109,6 +113,20 @@ fn main() { 3 }; + // Works with `if let` + if let Some(a) = b { return } + + if let Some(a) = b { + do_something(); + return; + } + + if let Some(a) = b { + return; + } else { + continue; + } + #[allow(unused)] // Comments after attributes dont mess it up if true { return } @@ -133,9 +151,10 @@ fn main() { } else if false { if true { if true { - if width == 50_characters_or_shorter { continue } - if width == 51_characters_long_and_above { - return; + if width == is_49_characters____long { continue } + if width == is_50_characters_____long { continue } + if width == is_51_characters______long { + continue; } } } diff --git a/tests/target/single_line_if/true_0.rs b/tests/target/single_line_if/true_0.rs index fb03f4415c4..98bafc6c7c3 100644 --- a/tests/target/single_line_if/true_0.rs +++ b/tests/target/single_line_if/true_0.rs @@ -1,8 +1,47 @@ -// rustfmt-single_line_simple_if: true -// rustfmt-unstable_features: true +// rustfmt-single_line_if: true // rustfmt-single_line_if_else_max_width: 0 fn main() { + let x = if true { + 1 + } else { + 2 + }; + + funk( + if test() { + 1 + } else { + 2 + }, + arg2, + ); + + if true { + 1 + } else { + 2 + } + + if test() { + 1 + } else { + 2 + } + + if let Some(a) = b { return } + + if let Some(a) = b { + do_something(); + return; + } + + if let Some(a) = b { + return; + } else { + continue; + } + let a = if 1 > 2 { unreachable!() } else { @@ -21,9 +60,10 @@ fn main() { if true { continue } - if width == 50_characters_or_shorter { continue } - if width == 51_characters_long_and_above { - return; + if width == is_49_characters____long { continue } + if width == is_50_characters_____long { continue } + if width == is_51_characters______long { + continue; } if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { diff --git a/tests/target/single_line_if/true_100.rs b/tests/target/single_line_if/true_100.rs index a73305bfc1a..c25db6ed040 100644 --- a/tests/target/single_line_if/true_100.rs +++ b/tests/target/single_line_if/true_100.rs @@ -1,8 +1,36 @@ -// rustfmt-single_line_simple_if: true -// rustfmt-unstable_features: true +// rustfmt-single_line_if: true // rustfmt-single_line_if_else_max_width: 100 fn main() { + let x = if true { 1 } else { 2 }; + + funk(if test() { 1 } else { 2 }, arg2); + + if true { + 1 + } else { + 2 + } + + if test() { + 1 + } else { + 2 + } + + if let Some(a) = b { return } + + if let Some(a) = b { + do_something(); + return; + } + + if let Some(a) = b { + return; + } else { + continue; + } + let a = if 1 > 2 { unreachable!() } else { 10 }; let a = if x { @@ -17,9 +45,10 @@ fn main() { if true { continue } - if width == 50_characters_or_shorter { continue } - if width == 51_characters_long_and_above { - return; + if width == is_49_characters____long { continue } + if width == is_50_characters_____long { continue } + if width == is_51_characters______long { + continue; } if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { diff --git a/tests/target/single_line_if/true_50.rs b/tests/target/single_line_if/true_50.rs index 2ef738846aa..aca93792679 100644 --- a/tests/target/single_line_if/true_50.rs +++ b/tests/target/single_line_if/true_50.rs @@ -1,8 +1,36 @@ -// rustfmt-single_line_simple_if: true -// rustfmt-unstable_features: true +// rustfmt-single_line_if: true // rustfmt-single_line_if_else_max_width: 50 fn main() { + let x = if true { 1 } else { 2 }; + + funk(if test() { 1 } else { 2 }, arg2); + + if true { + 1 + } else { + 2 + } + + if test() { + 1 + } else { + 2 + } + + if let Some(a) = b { return } + + if let Some(a) = b { + do_something(); + return; + } + + if let Some(a) = b { + return; + } else { + continue; + } + let a = if 1 > 2 { unreachable!() } else { 10 }; let a = if x { @@ -17,9 +45,10 @@ fn main() { if true { continue } - if width == 50_characters_or_shorter { continue } - if width == 51_characters_long_and_above { - return; + if width == is_49_characters____long { continue } + if width == is_50_characters_____long { continue } + if width == is_51_characters______long { + continue; } if name == super_duper_really_really_mega_ultra_giga_long_name_with_a_cherry_on_top { From defb290f78adfae3b5848357e7587b06211a01b6 Mon Sep 17 00:00:00 2001 From: Sjael Date: Thu, 25 Jan 2024 22:32:50 -0800 Subject: [PATCH 6/6] config docs --- Configurations.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Configurations.md b/Configurations.md index 2e054d6f64e..4a09aeb99f2 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2483,12 +2483,13 @@ See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuri ## `single_line_if` -Allows simple single expression if blocks to format on one line. Useful in the case of keeping `let-else` guards format consistent with `if` guards. +Allows if blocks to format on one line only if they contain a single expression of `return`, `continue` or `break`. Useful in the case of keeping `let-else` guards format consistent with `if` guards. Note that line will still break if: -1. The condition length is over the `max_width`/`single_line_simple_if_max_width length` -2. The block contains a trailing semicolon +1. The total length (condition and block) is over the `max_width`/`single_line_simple_if_max_width length` +2. The block ends with a trailing semicolon 3. The block contains a single or multi-lined comment +4. The block contains `return` or `break` and returns a value - **Default value**: `false` - **Possible values**: `true`, `false` @@ -2506,6 +2507,10 @@ fn main() { return; } + if false { + return 5; + } + if width == is_49_characters____long { continue; } @@ -2528,6 +2533,10 @@ fn main() { if false { return } + if false { + return 5; + } + if width == is_49_characters____long { continue } if width == is_50_characters_____long { continue }