diff --git a/Configurations.md b/Configurations.md index 78545c6187a..4a09aeb99f2 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2481,6 +2481,85 @@ fn main() { See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) +## `single_line_if` + +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 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` +- **Stable**: No + +#### `false` (default): + +```rust +fn main() { + if true { + break; + } + + if false { + return; + } + + if false { + return 5; + } + + 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 false { + return 5; + } + + 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 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) +- **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 f7cff1a1729..8991731979a 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 9484b2e5829..37736c635b6 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_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_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 3c5c713a33a..7943bd61af2 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 7808f891336..c14636cdffe 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1102,8 +1102,18 @@ 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 result = if self.keyword == "if" { + format_if( + context, + shape, + self.block, + &cond_str, + used_width, + 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 +1168,72 @@ 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; + } + if block.stmts.len() == 0 { + return true; + } + if block.stmts.len() == 1 { + return is_simple_control_flow_stmt(&block.stmts[0]); + } + false +} + +fn is_simple_control_flow_stmt(stmt: &ast::Stmt) -> bool { + match stmt.kind { + ast::StmtKind::Expr(ref expr) => match expr.kind { + 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 + } + } + _ => 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/single_line_if/false_0.rs b/tests/source/single_line_if/false_0.rs new file mode 100644 index 00000000000..26f6da9bff4 --- /dev/null +++ b/tests/source/single_line_if/false_0.rs @@ -0,0 +1,59 @@ +// 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 { + 10 + }; + + let a = if x { 1 } else if y { 2 } else { 3 }; + + if true { continue } + + if true { + continue + } + + 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 } + + 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..bbca4ac6576 --- /dev/null +++ b/tests/source/single_line_if/false_100.rs @@ -0,0 +1,59 @@ +// 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 { 1 } else if y { 2 } else { 3 }; + + if true { continue } + + if true { + continue + } + + 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 } + + 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..e345eca8d5d --- /dev/null +++ b/tests/source/single_line_if/false_50.rs @@ -0,0 +1,59 @@ +// 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 { 1 } else if y { 2 } else { 3 }; + + if true { continue } + + if true { + continue + } + + 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 } + + 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/mod.rs b/tests/source/single_line_if/mod.rs new file mode 100644 index 00000000000..ac23fb8006c --- /dev/null +++ b/tests/source/single_line_if/mod.rs @@ -0,0 +1,116 @@ +// rustfmt-single_line_if: 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 == 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 } + + // 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 } if true { continue 'gamer } } + + 'gamer: loop { if true{ break 'gamer; } if true { continue 'gamer; } } + + let result = 'block: { + if foo() { break 'block 1 } + if bar() { break 'block 2; } + 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 } + #[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 == is_49_characters____long { continue } if width == is_50_characters_____long { continue } if width == is_51_characters______long { continue } } } + } 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/true_0.rs b/tests/source/single_line_if/true_0.rs new file mode 100644 index 00000000000..82a14514112 --- /dev/null +++ b/tests/source/single_line_if/true_0.rs @@ -0,0 +1,60 @@ +// 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 { + 10 + }; + + let a = if x { 1 } else if y { 2 } else { 3 }; + + if true { continue } + + if true { + continue + } + + + 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 } + + 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..bda95ba4a40 --- /dev/null +++ b/tests/source/single_line_if/true_100.rs @@ -0,0 +1,59 @@ +// 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 { 1 } else if y { 2 } else { 3 }; + + if true { continue } + + if true { + continue + } + + 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 } + + 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..7a8e2f16dd7 --- /dev/null +++ b/tests/source/single_line_if/true_50.rs @@ -0,0 +1,59 @@ +// 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 { 1 } else if y { 2 } else { 3 }; + + if true { continue } + + if true { + continue + } + + 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 } + + 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_0.rs b/tests/target/single_line_if/false_0.rs new file mode 100644 index 00000000000..dba12293316 --- /dev/null +++ b/tests/target/single_line_if/false_0.rs @@ -0,0 +1,115 @@ +// 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 { + 10 + }; + + let a = if x { + 1 + } else if y { + 2 + } else { + 3 + }; + + if true { + continue; + } + + if true { + continue; + } + + 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; + } + + 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..c17115e7399 --- /dev/null +++ b/tests/target/single_line_if/false_100.rs @@ -0,0 +1,96 @@ +// 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 { + 1 + } else if y { + 2 + } else { + 3 + }; + + if true { + continue; + } + + if true { + continue; + } + + 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; + } + + 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..82f725a1ce5 --- /dev/null +++ b/tests/target/single_line_if/false_50.rs @@ -0,0 +1,96 @@ +// 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 { + 1 + } else if y { + 2 + } else { + 3 + }; + + if true { + continue; + } + + if true { + continue; + } + + 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; + } + + 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/mod.rs b/tests/target/single_line_if/mod.rs new file mode 100644 index 00000000000..47ef453a73b --- /dev/null +++ b/tests/target/single_line_if/mod.rs @@ -0,0 +1,217 @@ +// rustfmt-single_line_if: 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 == 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; + } + + // 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 } + if true { continue 'gamer } + } + + 'gamer: loop { + if true { + break 'gamer; + } + if true { + continue 'gamer; + } + } + + let result = 'block: { + if foo() { + break 'block 1; + } + if bar() { + break 'block 2; + } + 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 } + #[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 == is_49_characters____long { continue } + if width == is_50_characters_____long { continue } + if width == is_51_characters______long { + continue; + } + } + } + } 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/true_0.rs b/tests/target/single_line_if/true_0.rs new file mode 100644 index 00000000000..98bafc6c7c3 --- /dev/null +++ b/tests/target/single_line_if/true_0.rs @@ -0,0 +1,105 @@ +// 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 { + 10 + }; + + let a = if x { + 1 + } else if y { + 2 + } else { + 3 + }; + + if true { continue } + + if true { continue } + + 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; + } + + 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..c25db6ed040 --- /dev/null +++ b/tests/target/single_line_if/true_100.rs @@ -0,0 +1,86 @@ +// 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 { + 1 + } else if y { + 2 + } else { + 3 + }; + + if true { continue } + + if true { continue } + + 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; + } + + 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..aca93792679 --- /dev/null +++ b/tests/target/single_line_if/true_50.rs @@ -0,0 +1,86 @@ +// 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 { + 1 + } else if y { + 2 + } else { + 3 + }; + + if true { continue } + + if true { continue } + + 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; + } + + 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(); + } +}