diff --git a/Configurations.md b/Configurations.md index 6c4e7dc7760..cb2e1707150 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1885,6 +1885,52 @@ fn main() { See also: [`match_block_trailing_comma`](#match_block_trailing_comma). +## `match_reference_style` + +Normalize match reference style + +- **Default value**: `manual` +- **Possible values**: `manual`, `reference`, `dereference`, `auto` +- **Stable**: No + +**Note:** `auto` style requires Rust v1.26.0 or later + +#### `manual` + +don't touch anything + +#### `reference` + +```rust +fn hello(name: &Option<&str>) { + match name { + &Some(name) => println!("Hello {}!", name), + &None => println!("I don't know who you are."), + } +} +``` + +#### `dereference` + +```rust +fn hello(name: &Option<&str>) { + match *name { + Some(name) => println!("Hello {}!", name), + None => println!("I don't know who you are."), + } +} +``` + +#### `auto` + +```rust +fn hello(name: &Option<&str>) { + match name { + Some(name) => println!("Hello {}!", name), + None => println!("I don't know who you are."), + } +} +``` ## `blank_lines_upper_bound` diff --git a/src/config/mod.rs b/src/config/mod.rs index 992bd749b29..a840cbddce4 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -88,6 +88,8 @@ create_config! { 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_reference_style: MatchReferenceStyle, MatchReferenceStyle::Manual, false, + "Normalize match reference style"; force_multiline_blocks: bool, false, false, "Force multiline closure bodies and match arms to be wrapped in a block"; fn_args_density: Density, Density::Tall, false, "Argument density in functions"; diff --git a/src/config/options.rs b/src/config/options.rs index 8f16e57d468..0bcdac6c0f0 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -135,6 +135,25 @@ configuration_option_enum! { IndentStyle: Block, } +configuration_option_enum! { MatchReferenceStyle: + Manual, + // match x { + // &A => ..., + // ... + // } + Reference, + // match *x { + // A => ..., + // ... + // } + Dereference, + // match x { + // A => ..., + // ... + // } + Auto +} + configuration_option_enum! { Density: // Fit as much on one line as possible. Compressed, diff --git a/src/matches.rs b/src/matches.rs index 9551dc36002..32760fc2bf6 100644 --- a/src/matches.rs +++ b/src/matches.rs @@ -18,7 +18,7 @@ use syntax::{ast, ptr}; use codemap::SpanUtils; use comment::combine_strs_with_missing_comments; -use config::{Config, ControlBraceStyle, IndentStyle}; +use config::{Config, ControlBraceStyle, IndentStyle, MatchReferenceStyle}; use expr::{ format_expr, is_empty_block, is_simple_block, is_unsafe_block, prefer_next_line, rewrite_multiple_patterns, ExprType, RhsTactics, ToExpr, @@ -91,7 +91,7 @@ pub fn rewrite_match( IndentStyle::Visual => cond_shape.shrink_left(6)?, IndentStyle::Block => cond_shape.offset_left(6)?, }; - let cond_str = cond.rewrite(context, cond_shape)?; + let mut cond_str = cond.rewrite(context, cond_shape)?; let alt_block_sep = &shape.indent.to_string_with_newline(context.config); let block_sep = match context.config.control_brace_style() { ControlBraceStyle::AlwaysNextLine => alt_block_sep, @@ -138,13 +138,28 @@ pub fn rewrite_match( } } else { let span_after_cond = mk_sp(cond.span.hi(), span.hi()); + let mut arms_str = + rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?; + + rewrite_match_reference_style( + context, + cond, + arms, + shape, + cond_shape, + span_after_cond, + open_brace_pos, + &mut cond_str, + &mut arms_str, + )?; + Some(format!( "match {}{}{{\n{}{}{}\n{}}}", cond_str, block_sep, inner_attrs_str, nested_indent_str, - rewrite_match_arms(context, arms, shape, span_after_cond, open_brace_pos)?, + arms_str, shape.indent.to_string(context.config), )) } @@ -183,6 +198,109 @@ fn collect_beginning_verts( beginning_verts } +fn rewrite_match_reference_style( + context: &RewriteContext, + cond: &ast::Expr, + arms: &[ast::Arm], + shape: Shape, + cond_shape: Shape, + span_after_cond: Span, + open_brace_pos: BytePos, + cond_str: &mut String, + arms_str: &mut String, +) -> Option<()> { + let match_in_reference_style = arms.iter().all(|arm| { + arm.pats.iter().all(|pat| match pat.node { + ast::PatKind::Wild | ast::PatKind::Ref(..) => true, + _ => false, + }) + }); + let match_in_derefrence_style = match cond.node { + ast::ExprKind::Unary(ast::UnOp::Deref, _) => true, + _ => false, + }; + + let prepend_arms_ref = || { + arms.iter() + .map(|arm| { + let pats = arm + .pats + .iter() + .map(|pat| { + ptr::P(ast::Pat { + id: pat.id, + node: ast::PatKind::Ref(pat.clone(), ast::Mutability::Immutable), + span: pat.span, + }) + }) + .collect::>(); + + ast::Arm { + pats, + ..arm.clone() + } + }) + .collect::>() + }; + + let strip_arms_ref = || { + arms.iter() + .map(|arm| { + let pats = arm + .pats + .iter() + .map(|pat| match pat.node { + ast::PatKind::Ref(ref pat, _) => pat, + _ => pat, + }) + .cloned() + .collect::>(); + + ast::Arm { + pats, + ..arm.clone() + } + }) + .collect::>() + }; + + match context.config.match_reference_style() { + MatchReferenceStyle::Reference if match_in_derefrence_style => { + if let ast::ExprKind::Unary(ast::UnOp::Deref, ref cond) = cond.node { + *cond_str = cond.rewrite(context, cond_shape)?; + } + + let arms = prepend_arms_ref(); + + *arms_str = rewrite_match_arms(context, &arms, shape, span_after_cond, open_brace_pos)? + } + MatchReferenceStyle::Dereference if match_in_reference_style => { + let cond = ast::Expr { + node: ast::ExprKind::Unary(ast::UnOp::Deref, ptr::P(cond.clone())), + ..cond.clone() + }; + *cond_str = cond.rewrite(context, cond_shape)?; + + let arms = strip_arms_ref(); + + *arms_str = rewrite_match_arms(context, &arms, shape, span_after_cond, open_brace_pos)? + } + MatchReferenceStyle::Auto if match_in_derefrence_style => { + if let ast::ExprKind::Unary(ast::UnOp::Deref, ref cond) = cond.node { + *cond_str = cond.rewrite(context, cond_shape)?; + } + } + MatchReferenceStyle::Auto if match_in_reference_style => { + let arms = strip_arms_ref(); + + *arms_str = rewrite_match_arms(context, &arms, shape, span_after_cond, open_brace_pos)? + } + _ => {} + } + + Some(()) +} + fn rewrite_match_arms( context: &RewriteContext, arms: &[ast::Arm], diff --git a/tests/source/configs/match_reference_style/auto.rs b/tests/source/configs/match_reference_style/auto.rs new file mode 100644 index 00000000000..466edf3beeb --- /dev/null +++ b/tests/source/configs/match_reference_style/auto.rs @@ -0,0 +1,23 @@ +// rustfmt-match_reference_style: auto +// Normalize match reference style + +fn hello(name: &Option<&str>) { + match name { + &Some(name) => println!("Hello {}!", name), + &None => println!("I don't know who you are."), + } +} + +fn kiss(name: &Option<&str>) { + match *name { + Some(name) => println!("Kiss {}!", name), + None => println!("I don't know who you are."), + } +} + +fn main() { + let name = Some("rustfmt"); + + hello(&name); + kiss(&name); +} diff --git a/tests/source/configs/match_reference_style/derefence.rs b/tests/source/configs/match_reference_style/derefence.rs new file mode 100644 index 00000000000..bd0a65ec606 --- /dev/null +++ b/tests/source/configs/match_reference_style/derefence.rs @@ -0,0 +1,23 @@ +// rustfmt-match_reference_style: dereference +// Normalize match reference style + +fn hello(name: &Option<&str>) { + match name { + &Some(name) => println!("Hello {}!", name), + &None => println!("I don't know who you are."), + } +} + +fn kiss(name: &Option<&str>) { + match *name { + Some(name) => println!("Kiss {}!", name), + None => println!("I don't know who you are."), + } +} + +fn main() { + let name = Some("rustfmt"); + + hello(&name); + kiss(&name); +} diff --git a/tests/source/configs/match_reference_style/reference.rs b/tests/source/configs/match_reference_style/reference.rs new file mode 100644 index 00000000000..0921c20e3ff --- /dev/null +++ b/tests/source/configs/match_reference_style/reference.rs @@ -0,0 +1,23 @@ +// rustfmt-match_reference_style: reference +// Normalize match reference style + +fn hello(name: &Option<&str>) { + match name { + &Some(name) => println!("Hello {}!", name), + &None => println!("I don't know who you are."), + } +} + +fn kiss(name: &Option<&str>) { + match *name { + Some(name) => println!("Kiss {}!", name), + None => println!("I don't know who you are."), + } +} + +fn main() { + let name = Some("rustfmt"); + + hello(&name); + kiss(&name); +} diff --git a/tests/target/configs/match_reference_style/auto.rs b/tests/target/configs/match_reference_style/auto.rs new file mode 100644 index 00000000000..12f827c1374 --- /dev/null +++ b/tests/target/configs/match_reference_style/auto.rs @@ -0,0 +1,23 @@ +// rustfmt-match_reference_style: auto +// Normalize match reference style + +fn hello(name: &Option<&str>) { + match name { + Some(name) => println!("Hello {}!", name), + None => println!("I don't know who you are."), + } +} + +fn kiss(name: &Option<&str>) { + match name { + Some(name) => println!("Kiss {}!", name), + None => println!("I don't know who you are."), + } +} + +fn main() { + let name = Some("rustfmt"); + + hello(&name); + kiss(&name); +} diff --git a/tests/target/configs/match_reference_style/derefence.rs b/tests/target/configs/match_reference_style/derefence.rs new file mode 100644 index 00000000000..db4a0024892 --- /dev/null +++ b/tests/target/configs/match_reference_style/derefence.rs @@ -0,0 +1,23 @@ +// rustfmt-match_reference_style: dereference +// Normalize match reference style + +fn hello(name: &Option<&str>) { + match *name { + Some(name) => println!("Hello {}!", name), + None => println!("I don't know who you are."), + } +} + +fn kiss(name: &Option<&str>) { + match *name { + Some(name) => println!("Kiss {}!", name), + None => println!("I don't know who you are."), + } +} + +fn main() { + let name = Some("rustfmt"); + + hello(&name); + kiss(&name); +} diff --git a/tests/target/configs/match_reference_style/reference.rs b/tests/target/configs/match_reference_style/reference.rs new file mode 100644 index 00000000000..d6494e76622 --- /dev/null +++ b/tests/target/configs/match_reference_style/reference.rs @@ -0,0 +1,23 @@ +// rustfmt-match_reference_style: reference +// Normalize match reference style + +fn hello(name: &Option<&str>) { + match name { + &Some(name) => println!("Hello {}!", name), + &None => println!("I don't know who you are."), + } +} + +fn kiss(name: &Option<&str>) { + match name { + &Some(name) => println!("Kiss {}!", name), + &None => println!("I don't know who you are."), + } +} + +fn main() { + let name = Some("rustfmt"); + + hello(&name); + kiss(&name); +}