Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New config options for match arm block wrapping #4924

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
179 changes: 179 additions & 0 deletions Configurations.md
Original file line number Diff line number Diff line change
Expand Up @@ -1604,6 +1604,185 @@ fn foo() {
}
```

## `match_arm_wrapping`

Controls when to block wrap match arm bodies.

- **Default value**: `"Default"`
- **Possible values**: `"Default"`, `"FitFirstLine"`, `"FitEntireBody"`, `"Always"`, `"Preserve`
- **Stable**: No (tracking issue: #4896)

### Example

#### Original code

```rust
#![rustfmt::skip]

fn main() {
match lorem {
1000 => foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x),
2000 => {
println!("{}", sit)
}
3000 => panic!(),
4000 => {
()
}
5000 => this.a_very_long_function_name(foo, bar, bazz, fizz, another_argument, some_more_arguments, which_dont_fit),
}
}
```

#### `"Default"` (default):

The default block wrapping settings, as described in the Style Guide.

```rust
fn main() {
match lorem {
1000 => {
foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x)
}
2000 => {
println!("{}", sit)
}
3000 => panic!(),
4000 => (),
5000 => this.a_very_long_function_name(
foo,
bar,
bazz,
fizz,
another_argument,
some_more_arguments,
which_dont_fit,
),
}
}
```

#### `"FitFirstLine"`:

Same as the default, except don't block wrap match arms when the opening line of its body can't fit on the same line as the `=>`.

```rust
fn main() {
match lorem {
1000 =>
foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x),
2000 => {
println!("{}", sit)
}
3000 => panic!(),
4000 => (),
5000 => this.a_very_long_function_name(
foo,
bar,
bazz,
fizz,
another_argument,
some_more_arguments,
which_dont_fit,
),
}
}
```

#### `"Always"`:

Always block wrap match arm bodies.

```rust
fn main() {
match lorem {
1000 => {
foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x)
}
2000 => {
println!("{}", sit)
}
3000 => {
panic!()
}
4000 => {
()
}
5000 => {
this.a_very_long_function_name(
foo,
bar,
bazz,
fizz,
another_argument,
some_more_arguments,
which_dont_fit,
)
}
}
}
```

#### `"Preserve"`:

Preserve block wrapping on match arm bodies if the developer originally had the body wrapped.

```rust
fn main() {
match lorem {
1000 => {
foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x)
}
2000 => {
println!("{}", sit)
}
3000 => panic!(),
4000 => {
()
}
5000 => this.a_very_long_function_name(
foo,
bar,
bazz,
fizz,
another_argument,
some_more_arguments,
which_dont_fit,
),
}
}
```

#### `"FitEntireBody"`:

Same as default, except block wrap the match arm if the entire body cannot fit on the same line as the `=>`.

```rust
fn main() {
match lorem {
1000 => {
foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo(x)
}
2000 => {
println!("{}", sit)
}
3000 => panic!(),
4000 => (),
5000 => {
this.a_very_long_function_name(
foo,
bar,
bazz,
fizz,
another_argument,
some_more_arguments,
which_dont_fit,
)
}
}
}
```

## `match_block_trailing_comma`

Put a trailing comma after a block based match arm (non-block arms are not affected)
Expand Down
24 changes: 22 additions & 2 deletions src/config/config_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ macro_rules! create_config {
| "chain_width" => self.0.set_heuristics(),
"license_template_path" => self.0.set_license_template(),
"merge_imports" => self.0.set_merge_imports(),
"match_arm_blocks" => self.0.set_match_arm_blocks(),
&_ => (),
}
}
Expand Down Expand Up @@ -166,6 +167,7 @@ macro_rules! create_config {
self.set_license_template();
self.set_ignore(dir);
self.set_merge_imports();
self.set_match_arm_blocks();
self
}

Expand Down Expand Up @@ -249,14 +251,16 @@ macro_rules! create_config {
| "chain_width" => self.set_heuristics(),
"license_template_path" => self.set_license_template(),
"merge_imports" => self.set_merge_imports(),
"match_arm_blocks" => self.set_match_arm_blocks(),
&_ => (),
}
}

#[allow(unreachable_pub)]
pub fn is_hidden_option(name: &str) -> bool {
const HIDE_OPTIONS: [&str; 5] =
["verbose", "verbose_diff", "file_lines", "width_heuristics", "merge_imports"];
const HIDE_OPTIONS: [&str; 6] =
["verbose", "verbose_diff", "file_lines", "width_heuristics", "merge_imports",
"match_arm_blocks"];
HIDE_OPTIONS.contains(&name)
}

Expand Down Expand Up @@ -421,6 +425,22 @@ macro_rules! create_config {
}
}

fn set_match_arm_blocks(&mut self) {
if self.was_set().match_arm_blocks() {
eprintln!(
"Warning: the `match_arm_blocks` option is deprecated. \
Use `match_arm_wrapping` instead"
);
if !self.was_set().match_arm_wrapping() {
self.match_arm_wrapping.2 = if self.match_arm_blocks() {
MatchArmWrapping::Default
} else {
MatchArmWrapping::FitFirstLine
};
}
}
}

#[allow(unreachable_pub)]
/// Returns `true` if the config key was explicitly set and is the default value.
pub fn is_default(&self, key: &str) -> bool {
Expand Down
73 changes: 71 additions & 2 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,9 @@ create_config! {
"Align struct fields if their diffs fits within threshold";
enum_discrim_align_threshold: usize, 0, false,
"Align enum variants discrims, if their diffs fit within threshold";
match_arm_blocks: bool, true, false, "Wrap the body of arms in blocks when it does not fit on \
the same line with the pattern of arms";
match_arm_wrapping: MatchArmWrapping, MatchArmWrapping::Default, false,
"Wrap the body of match arms according to the given options";
match_arm_blocks: bool, true, false, "(deprecated: use match_arm_wrapping instead)";
match_arm_leading_pipes: MatchArmLeadingPipe, MatchArmLeadingPipe::Never, true,
"Determines whether leading pipes are emitted on match arms";
force_multiline_blocks: bool, false, false,
Expand Down Expand Up @@ -426,6 +427,11 @@ mod test {
"Merge imports";
merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)";

// match_arm_blocks deprecation
match_arm_blocks: bool, true, false, "(deprecated: use match_arm_wrapping instead)";
match_arm_wrapping: MatchArmWrapping, MatchArmWrapping::Default, false,
"Wrap the body of match arms according to the given options";

// Width Heuristics
use_small_heuristics: Heuristics, Heuristics::Default, true,
"Whether to use different formatting for items and \
Expand Down Expand Up @@ -590,6 +596,7 @@ combine_control_expr = true
overflow_delimited_expr = false
struct_field_align_threshold = 0
enum_discrim_align_threshold = 0
match_arm_wrapping = "Default"
match_arm_blocks = true
match_arm_leading_pipes = "Never"
force_multiline_blocks = false
Expand Down Expand Up @@ -723,6 +730,68 @@ make_backup = false
}
}

#[cfg(test)]
mod deprecated_option_match_arm_blocks {
use super::*;

#[test]
fn test_old_option_set() {
if !crate::is_nightly_channel!() {
return;
}
// Old option defaults to true - set it to false
let toml = r#"
unstable_features = true
match_arm_blocks = false
"#;
let config = Config::from_toml(toml, Path::new("")).unwrap();
assert_eq!(config.match_arm_wrapping(), MatchArmWrapping::FitFirstLine);
}

#[test]
fn test_both_set() {
if !crate::is_nightly_channel!() {
return;
}
let toml = r#"
unstable_features = true
match_arm_blocks = false
match_arm_wrapping = "Default"
"#;
let config = Config::from_toml(toml, Path::new("")).unwrap();
assert_eq!(config.match_arm_wrapping(), MatchArmWrapping::Default);
}

#[test]
fn test_new_overridden() {
if !crate::is_nightly_channel!() {
return;
}
let toml = r#"
unstable_features = true
merge_imports = false
"#;
let mut config = Config::from_toml(toml, Path::new("")).unwrap();
config.override_value("match_arm_wrapping", "Default");
assert_eq!(config.match_arm_wrapping(), MatchArmWrapping::Default);
}

#[test]
fn test_old_overridden() {
if !crate::is_nightly_channel!() {
return;
}
let toml = r#"
unstable_features = true
match_arm_wrapping = "FitFirstLine"
"#;
let mut config = Config::from_toml(toml, Path::new("")).unwrap();
config.override_value("match_arm_blocks", "false");
// no effect: the new option always takes precedence
assert_eq!(config.match_arm_wrapping(), MatchArmWrapping::FitFirstLine);
}
}

#[cfg(test)]
mod use_small_heuristics {
use super::*;
Expand Down
17 changes: 17 additions & 0 deletions src/config/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,3 +442,20 @@ pub enum MatchArmLeadingPipe {
/// Preserve any existing leading pipes
Preserve,
}

/// Controls wrapping for match arm bodies
#[config_type]
pub enum MatchArmWrapping {
/// Follow the Style Guide Prescription
Default,
/// Same as Default, except don't block wrap match arms when the opening line of its body
/// can't fit on the same line as the `=>`.
FitFirstLine,
/// Always block wrap match arms
Always,
/// Preserve the block wrapping on match arms
Preserve,
/// Same as Default, except wrap the match arm if the entire body cannot fit on the same line
/// as the `=>`.
FitEntireBody,
}
Loading