From d5a8df7fcfd244241f59a43cd7590ec5fe122e19 Mon Sep 17 00:00:00 2001 From: Andrei Matveiakin Date: Sun, 16 Jul 2023 11:10:44 +0400 Subject: [PATCH 1/8] Add option to control trailing zero in floating-point literals --- Configurations.md | 44 ++++++++++ src/config/mod.rs | 4 + src/config/options.rs | 17 ++++ src/expr.rs | 84 +++++++++++++++++-- .../source/float-lit-trailing-zero-always.rs | 17 ++++ .../float-lit-trailing-zero-if-no-postfix.rs | 17 ++++ tests/source/float-lit-trailing-zero-never.rs | 17 ++++ .../target/float-lit-trailing-zero-always.rs | 17 ++++ .../float-lit-trailing-zero-if-no-postfix.rs | 17 ++++ tests/target/float-lit-trailing-zero-never.rs | 17 ++++ 10 files changed, 245 insertions(+), 6 deletions(-) create mode 100644 tests/source/float-lit-trailing-zero-always.rs create mode 100644 tests/source/float-lit-trailing-zero-if-no-postfix.rs create mode 100644 tests/source/float-lit-trailing-zero-never.rs create mode 100644 tests/target/float-lit-trailing-zero-always.rs create mode 100644 tests/target/float-lit-trailing-zero-if-no-postfix.rs create mode 100644 tests/target/float-lit-trailing-zero-never.rs diff --git a/Configurations.md b/Configurations.md index 61b9995e7b9..f5e22394eb2 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1256,6 +1256,50 @@ Control the case of the letters in hexadecimal literal values - **Possible values**: `Preserve`, `Upper`, `Lower` - **Stable**: No (tracking issue: [#5081](https://github.com/rust-lang/rustfmt/issues/5081)) +## `float_literal_trailing_zero` + +Control the presence of trailing zero in floating-point literal values + +- **Default value**: `Preserve` +- **Possible values**: `Preserve`, `Always`, `IfNoPostfix`, `Never` +- **Stable**: No (tracking issue: [#3187](https://github.com/rust-lang/rustfmt/issues/3187)) + +#### `Preserve` (default): + +Leave the literal as-is. + +#### `Always`: + +Add a trailing zero to the literal: + +```rust +fn main() { + let values = [1.0, 2.0e10, 3.0f32]; +} +``` + +#### `IfNoPostfix`: + +Add a trailing zero by default. If the literal contains an exponent or a suffix, the zero +and the preceding period are removed: + +```rust +fn main() { + let values = [1.0, 2e10, 3f32]; +} +``` + +#### `Never`: + +Remove the trailing zero. If the literal contains an exponent or a suffix, the preceding +period is also removed: + +```rust +fn main() { + let values = [1., 2e10, 3f32]; +} +``` + ## `hide_parse_errors` This option is deprecated and has been renamed to `show_parse_errors` to avoid confusion around the double negative default of `hide_parse_errors=false`. diff --git a/src/config/mod.rs b/src/config/mod.rs index 7355adc9f9d..439a5765ff9 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -79,6 +79,8 @@ create_config! { skip_macro_invocations: SkipMacroInvocations, false, "Skip formatting the bodies of macros invoked with the following names."; hex_literal_case: HexLiteralCaseConfig, false, "Format hexadecimal integer literals"; + float_literal_trailing_zero: FloatLiteralTrailingZeroConfig, false, + "Add or remove trailing zero in floating-point literals"; // Single line expressions and items empty_item_single_line: EmptyItemSingleLine, false, @@ -739,6 +741,7 @@ format_macro_matchers = false format_macro_bodies = true skip_macro_invocations = [] hex_literal_case = "Preserve" +float_literal_trailing_zero = "Preserve" empty_item_single_line = true struct_lit_single_line = true fn_single_line = false @@ -829,6 +832,7 @@ format_macro_matchers = false format_macro_bodies = true skip_macro_invocations = [] hex_literal_case = "Preserve" +float_literal_trailing_zero = "Preserve" 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 b9b4713046c..3f86999c30b 100644 --- a/src/config/options.rs +++ b/src/config/options.rs @@ -146,6 +146,21 @@ pub enum HexLiteralCase { Lower, } +/// How to treat trailing zeros in floating-point literals. +#[config_type] +pub enum FloatLiteralTrailingZero { + /// Leave the literal as-is. + Preserve, + /// Add a trailing zero to the literal. + Always, + /// Add a trailing zero by default. If the literal contains an exponent or a suffix, the zero + /// and the preceding period are removed. + IfNoPostfix, + /// Remove the trailing zero. If the literal contains an exponent or a suffix, the preceding + /// period is also removed. + Never, +} + #[config_type] pub enum ReportTactic { Always, @@ -613,6 +628,8 @@ config_option_with_style_edition_default!( FormatMacroBodies, bool, _ => true; SkipMacroInvocations, MacroSelectors, _ => MacroSelectors::default(); HexLiteralCaseConfig, HexLiteralCase, _ => HexLiteralCase::Preserve; + FloatLiteralTrailingZeroConfig, FloatLiteralTrailingZero, _ => + FloatLiteralTrailingZero::Preserve; // Single line expressions and items EmptyItemSingleLine, bool, _ => true; diff --git a/src/expr.rs b/src/expr.rs index 8031ab68290..d6535152b87 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -13,8 +13,8 @@ use crate::comment::{ CharClasses, FindUncommented, combine_strs_with_missing_comments, contains_comment, recover_comment_removed, rewrite_comment, rewrite_missing_comment, }; -use crate::config::lists::*; use crate::config::{Config, ControlBraceStyle, HexLiteralCase, IndentStyle, StyleEdition}; +use crate::config::{FloatLiteralTrailingZero, lists::*}; use crate::lists::{ ListFormatting, Separator, definitive_tactic, itemize_list, shape_for_tactic, struct_lit_formatting, struct_lit_shape, struct_lit_tactic, write_list, @@ -1276,6 +1276,7 @@ pub(crate) fn rewrite_literal( match token_lit.kind { token::LitKind::Str => rewrite_string_lit(context, span, shape), token::LitKind::Integer => rewrite_int_lit(context, token_lit, span, shape), + token::LitKind::Float => rewrite_float_lit(context, token_lit, span, shape), _ => wrap_str( context.snippet(span).to_owned(), context.config.max_width(), @@ -1320,6 +1321,11 @@ fn rewrite_int_lit( shape: Shape, ) -> RewriteResult { let symbol = token_lit.symbol.as_str(); + let suffix = token_lit.suffix.as_ref().map(|s| s.as_str()); + + if suffix == Some("f32") || suffix == Some("f64") { + return rewrite_float_lit(context, token_lit, span, shape); + } if let Some(symbol_stripped) = symbol.strip_prefix("0x") { let hex_lit = match context.config.hex_literal_case() { @@ -1329,11 +1335,7 @@ fn rewrite_int_lit( }; if let Some(hex_lit) = hex_lit { return wrap_str( - format!( - "0x{}{}", - hex_lit, - token_lit.suffix.as_ref().map_or("", |s| s.as_str()) - ), + format!("0x{}{}", hex_lit, suffix.unwrap_or("")), context.config.max_width(), shape, ) @@ -1349,6 +1351,76 @@ fn rewrite_int_lit( .max_width_error(shape.width, span) } +fn rewrite_float_lit( + context: &RewriteContext<'_>, + token_lit: token::Lit, + span: Span, + shape: Shape, +) -> RewriteResult { + // This regex may accept invalid float literals (such as `1`, `_` or `2.e3`). That's ok. + // We only use it to parse literals whose validity has already been established. + let float_literal_regex = static_regex!(r"^([0-9_]+)(?:\.([0-9_]+)?)?([eE][+-]?[0-9_]+)?$"); + let zero_literal_regex = static_regex!(r"^[0_]+$"); + + if matches!( + context.config.float_literal_trailing_zero(), + FloatLiteralTrailingZero::Preserve + ) { + return wrap_str( + context.snippet(span).to_owned(), + context.config.max_width(), + shape, + ) + .max_width_error(shape.width, span); + } + + let symbol = token_lit.symbol.as_str(); + let suffix = token_lit.suffix.as_ref().map(|s| s.as_str()); + + let caps = float_literal_regex.captures(symbol).unwrap(); + let integer_part = caps.get(1).unwrap().as_str(); + let fractional_part = caps.get(2).map(|s| s.as_str()); + let exponent = caps.get(3).map(|s| s.as_str()); + + let has_postfix = exponent.is_some() || suffix.is_some(); + let fractional_part_nonzero = + fractional_part.map_or(false, |s| !zero_literal_regex.is_match(s)); + + let (include_period, include_fractional_part) = + match context.config.float_literal_trailing_zero() { + FloatLiteralTrailingZero::Preserve => unreachable!("handled above"), + FloatLiteralTrailingZero::Always => (true, true), + FloatLiteralTrailingZero::IfNoPostfix => ( + fractional_part_nonzero || !has_postfix, + fractional_part_nonzero || !has_postfix, + ), + FloatLiteralTrailingZero::Never => ( + fractional_part_nonzero || !has_postfix, + fractional_part_nonzero, + ), + }; + + let period = if include_period { "." } else { "" }; + let fractional_part = if include_fractional_part { + fractional_part.unwrap_or("0") + } else { + "" + }; + wrap_str( + format!( + "{}{}{}{}{}", + integer_part, + period, + fractional_part, + exponent.unwrap_or(""), + suffix.unwrap_or(""), + ), + context.config.max_width(), + shape, + ) + .max_width_error(shape.width, span) +} + fn choose_separator_tactic(context: &RewriteContext<'_>, span: Span) -> Option { if context.inside_macro() { if span_ends_with_comma(context, span) { diff --git a/tests/source/float-lit-trailing-zero-always.rs b/tests/source/float-lit-trailing-zero-always.rs new file mode 100644 index 00000000000..ab9305e5cd4 --- /dev/null +++ b/tests/source/float-lit-trailing-zero-always.rs @@ -0,0 +1,17 @@ +// rustfmt-float_literal_trailing_zero: Always + +fn float_literals() { + let a = 0.; + let b = 0.0; + let c = 100.; + let d = 100.0; + let e = 5e3; + let f = 5.0e3; + let g = 7f32; + let h = 7.0f32; + let i = 9e3f32; + let j = 9.0e3f32; + let k = 1000.00; + let l = 1_000_.; + let m = 1_000_.000_000; +} diff --git a/tests/source/float-lit-trailing-zero-if-no-postfix.rs b/tests/source/float-lit-trailing-zero-if-no-postfix.rs new file mode 100644 index 00000000000..43d3d69f67b --- /dev/null +++ b/tests/source/float-lit-trailing-zero-if-no-postfix.rs @@ -0,0 +1,17 @@ +// rustfmt-float_literal_trailing_zero: IfNoPostfix + +fn float_literals() { + let a = 0.; + let b = 0.0; + let c = 100.; + let d = 100.0; + let e = 5e3; + let f = 5.0e3; + let g = 7f32; + let h = 7.0f32; + let i = 9e3f32; + let j = 9.0e3f32; + let k = 1000.00; + let l = 1_000_.; + let m = 1_000_.000_000; +} diff --git a/tests/source/float-lit-trailing-zero-never.rs b/tests/source/float-lit-trailing-zero-never.rs new file mode 100644 index 00000000000..9f455b31f13 --- /dev/null +++ b/tests/source/float-lit-trailing-zero-never.rs @@ -0,0 +1,17 @@ +// rustfmt-float_literal_trailing_zero: Never + +fn float_literals() { + let a = 0.; + let b = 0.0; + let c = 100.; + let d = 100.0; + let e = 5e3; + let f = 5.0e3; + let g = 7f32; + let h = 7.0f32; + let i = 9e3f32; + let j = 9.0e3f32; + let k = 1000.00; + let l = 1_000_.; + let m = 1_000_.000_000; +} diff --git a/tests/target/float-lit-trailing-zero-always.rs b/tests/target/float-lit-trailing-zero-always.rs new file mode 100644 index 00000000000..042433d34ac --- /dev/null +++ b/tests/target/float-lit-trailing-zero-always.rs @@ -0,0 +1,17 @@ +// rustfmt-float_literal_trailing_zero: Always + +fn float_literals() { + let a = 0.0; + let b = 0.0; + let c = 100.0; + let d = 100.0; + let e = 5.0e3; + let f = 5.0e3; + let g = 7.0f32; + let h = 7.0f32; + let i = 9.0e3f32; + let j = 9.0e3f32; + let k = 1000.00; + let l = 1_000_.0; + let m = 1_000_.000_000; +} diff --git a/tests/target/float-lit-trailing-zero-if-no-postfix.rs b/tests/target/float-lit-trailing-zero-if-no-postfix.rs new file mode 100644 index 00000000000..4efec39e382 --- /dev/null +++ b/tests/target/float-lit-trailing-zero-if-no-postfix.rs @@ -0,0 +1,17 @@ +// rustfmt-float_literal_trailing_zero: IfNoPostfix + +fn float_literals() { + let a = 0.0; + let b = 0.0; + let c = 100.0; + let d = 100.0; + let e = 5e3; + let f = 5e3; + let g = 7f32; + let h = 7f32; + let i = 9e3f32; + let j = 9e3f32; + let k = 1000.00; + let l = 1_000_.0; + let m = 1_000_.000_000; +} diff --git a/tests/target/float-lit-trailing-zero-never.rs b/tests/target/float-lit-trailing-zero-never.rs new file mode 100644 index 00000000000..4f28b7ac5c2 --- /dev/null +++ b/tests/target/float-lit-trailing-zero-never.rs @@ -0,0 +1,17 @@ +// rustfmt-float_literal_trailing_zero: Never + +fn float_literals() { + let a = 0.; + let b = 0.; + let c = 100.; + let d = 100.; + let e = 5e3; + let f = 5e3; + let g = 7f32; + let h = 7f32; + let i = 9e3f32; + let j = 9e3f32; + let k = 1000.; + let l = 1_000_.; + let m = 1_000_.; +} From a009d6ada62bda4be5ff8f337d316a26895e912d Mon Sep 17 00:00:00 2001 From: Andrei Matveiakin Date: Sun, 5 Jan 2025 12:28:19 +0000 Subject: [PATCH 2/8] More tests for float_literal_trailing_zero option and other small fixes --- Configurations.md | 12 ++- src/expr.rs | 93 +++++++++++++++---- .../float_literal_trailing_zero/always.rs | 24 +++++ .../if-no-postfix.rs} | 10 ++ .../float_literal_trailing_zero/never.rs} | 11 +++ .../float_literal_trailing_zero/always.rs} | 11 +++ .../if-no-postfix.rs} | 8 ++ .../float_literal_trailing_zero/never.rs} | 7 ++ .../float_literal_trailing_zero/preserve.rs} | 2 +- 9 files changed, 158 insertions(+), 20 deletions(-) create mode 100644 tests/source/configs/float_literal_trailing_zero/always.rs rename tests/source/{float-lit-trailing-zero-if-no-postfix.rs => configs/float_literal_trailing_zero/if-no-postfix.rs} (57%) rename tests/source/{float-lit-trailing-zero-never.rs => configs/float_literal_trailing_zero/never.rs} (55%) rename tests/target/{float-lit-trailing-zero-always.rs => configs/float_literal_trailing_zero/always.rs} (56%) rename tests/target/{float-lit-trailing-zero-if-no-postfix.rs => configs/float_literal_trailing_zero/if-no-postfix.rs} (57%) rename tests/target/{float-lit-trailing-zero-never.rs => configs/float_literal_trailing_zero/never.rs} (59%) rename tests/{source/float-lit-trailing-zero-always.rs => target/configs/float_literal_trailing_zero/preserve.rs} (85%) diff --git a/Configurations.md b/Configurations.md index f5e22394eb2..af4b7bcaf05 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1268,13 +1268,19 @@ Control the presence of trailing zero in floating-point literal values Leave the literal as-is. +```rust +fn main() { + let values = [1.0, 2., 3.0e10, 4f32]; +} +``` + #### `Always`: Add a trailing zero to the literal: ```rust fn main() { - let values = [1.0, 2.0e10, 3.0f32]; + let values = [1.0, 2.0, 3.0e10, 4.0f32]; } ``` @@ -1285,7 +1291,7 @@ and the preceding period are removed: ```rust fn main() { - let values = [1.0, 2e10, 3f32]; + let values = [1.0, 2.0, 3e10, 4f32]; } ``` @@ -1296,7 +1302,7 @@ period is also removed: ```rust fn main() { - let values = [1., 2e10, 3f32]; + let values = [1., 2., 3e10, 4f32]; } ``` diff --git a/src/expr.rs b/src/expr.rs index d6535152b87..2353e77ac62 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1320,13 +1320,13 @@ fn rewrite_int_lit( span: Span, shape: Shape, ) -> RewriteResult { - let symbol = token_lit.symbol.as_str(); - let suffix = token_lit.suffix.as_ref().map(|s| s.as_str()); - - if suffix == Some("f32") || suffix == Some("f64") { + if token_lit.is_semantic_float() { return rewrite_float_lit(context, token_lit, span, shape); } + let symbol = token_lit.symbol.as_str(); + let suffix = token_lit.suffix.as_ref().map(|s| s.as_str()); + if let Some(symbol_stripped) = symbol.strip_prefix("0x") { let hex_lit = match context.config.hex_literal_case() { HexLiteralCase::Preserve => None, @@ -1357,11 +1357,6 @@ fn rewrite_float_lit( span: Span, shape: Shape, ) -> RewriteResult { - // This regex may accept invalid float literals (such as `1`, `_` or `2.e3`). That's ok. - // We only use it to parse literals whose validity has already been established. - let float_literal_regex = static_regex!(r"^([0-9_]+)(?:\.([0-9_]+)?)?([eE][+-]?[0-9_]+)?$"); - let zero_literal_regex = static_regex!(r"^[0_]+$"); - if matches!( context.config.float_literal_trailing_zero(), FloatLiteralTrailingZero::Preserve @@ -1377,14 +1372,14 @@ fn rewrite_float_lit( let symbol = token_lit.symbol.as_str(); let suffix = token_lit.suffix.as_ref().map(|s| s.as_str()); - let caps = float_literal_regex.captures(symbol).unwrap(); - let integer_part = caps.get(1).unwrap().as_str(); - let fractional_part = caps.get(2).map(|s| s.as_str()); - let exponent = caps.get(3).map(|s| s.as_str()); + let FloatSymbolParts { + integer_part, + fractional_part, + exponent, + } = parse_float_symbol(symbol); let has_postfix = exponent.is_some() || suffix.is_some(); - let fractional_part_nonzero = - fractional_part.map_or(false, |s| !zero_literal_regex.is_match(s)); + let fractional_part_nonzero = fractional_part.map_or(false, |s| !is_zero_integer_literal(s)); let (include_period, include_fractional_part) = match context.config.float_literal_trailing_zero() { @@ -2346,9 +2341,34 @@ pub(crate) fn is_method_call(expr: &ast::Expr) -> bool { } } +struct FloatSymbolParts<'a> { + integer_part: &'a str, + fractional_part: Option<&'a str>, + exponent: Option<&'a str>, +} + +// Parses a float literal. The `symbol` must be a valid floating point literal without a type +// suffix. Otherwise the function may panic or return wrong result. +fn parse_float_symbol(symbol: &str) -> FloatSymbolParts<'_> { + // This regex may accept invalid float literals (such as `1`, `_` or `2.e3`). That's ok. + // We only use it to parse literals whose validity has already been established. + let float_literal_regex = static_regex!(r"^([0-9_]+)(?:\.([0-9_]+)?)?([eE][+-]?[0-9_]+)?$"); + let caps = float_literal_regex.captures(symbol).unwrap(); + FloatSymbolParts { + integer_part: caps.get(1).unwrap().as_str(), + fractional_part: caps.get(2).map(|m| m.as_str()), + exponent: caps.get(3).map(|m| m.as_str()), + } +} + +fn is_zero_integer_literal(symbol: &str) -> bool { + let zero_literal_regex = static_regex!(r"^[0_]+$"); + zero_literal_regex.is_match(symbol) +} + #[cfg(test)] mod test { - use super::last_line_offsetted; + use super::*; #[test] fn test_last_line_offsetted() { @@ -2370,4 +2390,45 @@ mod test { let lines = "one\n two three"; assert_eq!(last_line_offsetted(2, lines), false); } + + #[test] + fn test_parse_float_symbol() { + let parts = parse_float_symbol("123.456e789"); + assert_eq!(parts.integer_part, "123"); + assert_eq!(parts.fractional_part, Some("456")); + assert_eq!(parts.exponent, Some("e789")); + + let parts = parse_float_symbol("123e789"); + assert_eq!(parts.integer_part, "123"); + assert_eq!(parts.fractional_part, None); + assert_eq!(parts.exponent, Some("e789")); + + let parts = parse_float_symbol("123."); + assert_eq!(parts.integer_part, "123"); + assert_eq!(parts.fractional_part, None); + assert_eq!(parts.exponent, None); + } + + #[test] + fn test_parse_float_symbol_with_underscores() { + let parts = parse_float_symbol("_123._456e_789"); + assert_eq!(parts.integer_part, "_123"); + assert_eq!(parts.fractional_part, Some("_456")); + assert_eq!(parts.exponent, Some("e_789")); + + let parts = parse_float_symbol("123_.456_e789_"); + assert_eq!(parts.integer_part, "123_"); + assert_eq!(parts.fractional_part, Some("456_")); + assert_eq!(parts.exponent, Some("e789_")); + + let parts = parse_float_symbol("1_23.4_56e7_89"); + assert_eq!(parts.integer_part, "1_23"); + assert_eq!(parts.fractional_part, Some("4_56")); + assert_eq!(parts.exponent, Some("e7_89")); + + let parts = parse_float_symbol("_1_23_._4_56_e_7_89_"); + assert_eq!(parts.integer_part, "_1_23_"); + assert_eq!(parts.fractional_part, Some("_4_56_")); + assert_eq!(parts.exponent, Some("e_7_89_")); + } } diff --git a/tests/source/configs/float_literal_trailing_zero/always.rs b/tests/source/configs/float_literal_trailing_zero/always.rs new file mode 100644 index 00000000000..b63bc1091c6 --- /dev/null +++ b/tests/source/configs/float_literal_trailing_zero/always.rs @@ -0,0 +1,24 @@ +// rustfmt-float_literal_trailing_zero: Always + +fn float_literals() { + let a = 0.; + let b = 0.0; + let c = 100.; + let d = 100.0; + let e = 5e3; + let f = 5.0e3; + let g = 7f32; + let h = 7.0f32; + let i = 9e3f32; + let j = 9.0e3f32; + let k = 1000.00; + let l = 1_000_.; + let m = 1_000_.000_000; +} + +fn line_wrapping() { + let array = [ + 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., + ]; + println!("This is floaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaat {}", 10e3); +} diff --git a/tests/source/float-lit-trailing-zero-if-no-postfix.rs b/tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs similarity index 57% rename from tests/source/float-lit-trailing-zero-if-no-postfix.rs rename to tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs index 43d3d69f67b..d47e336c628 100644 --- a/tests/source/float-lit-trailing-zero-if-no-postfix.rs +++ b/tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs @@ -15,3 +15,13 @@ fn float_literals() { let l = 1_000_.; let m = 1_000_.000_000; } + +fn line_wrapping() { + let array = [ + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11., 12., 13., 14., 15., 16., 17., 18., + ]; + println!( + "This is floaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaat {}", + 10.0e3 + ); +} diff --git a/tests/source/float-lit-trailing-zero-never.rs b/tests/source/configs/float_literal_trailing_zero/never.rs similarity index 55% rename from tests/source/float-lit-trailing-zero-never.rs rename to tests/source/configs/float_literal_trailing_zero/never.rs index 9f455b31f13..22b599b007d 100644 --- a/tests/source/float-lit-trailing-zero-never.rs +++ b/tests/source/configs/float_literal_trailing_zero/never.rs @@ -15,3 +15,14 @@ fn float_literals() { let l = 1_000_.; let m = 1_000_.000_000; } + +fn line_wrapping() { + let array = [ + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, + ]; + println!( + "This is floaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaat {}", + 10.0e3 + ); +} diff --git a/tests/target/float-lit-trailing-zero-always.rs b/tests/target/configs/float_literal_trailing_zero/always.rs similarity index 56% rename from tests/target/float-lit-trailing-zero-always.rs rename to tests/target/configs/float_literal_trailing_zero/always.rs index 042433d34ac..e25f032c717 100644 --- a/tests/target/float-lit-trailing-zero-always.rs +++ b/tests/target/configs/float_literal_trailing_zero/always.rs @@ -15,3 +15,14 @@ fn float_literals() { let l = 1_000_.0; let m = 1_000_.000_000; } + +fn line_wrapping() { + let array = [ + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, + ]; + println!( + "This is floaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaat {}", + 10.0e3 + ); +} diff --git a/tests/target/float-lit-trailing-zero-if-no-postfix.rs b/tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs similarity index 57% rename from tests/target/float-lit-trailing-zero-if-no-postfix.rs rename to tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs index 4efec39e382..caf33ed6353 100644 --- a/tests/target/float-lit-trailing-zero-if-no-postfix.rs +++ b/tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs @@ -15,3 +15,11 @@ fn float_literals() { let l = 1_000_.0; let m = 1_000_.000_000; } + +fn line_wrapping() { + let array = [ + 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, + ]; + println!("This is floaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaat {}", 10e3); +} diff --git a/tests/target/float-lit-trailing-zero-never.rs b/tests/target/configs/float_literal_trailing_zero/never.rs similarity index 59% rename from tests/target/float-lit-trailing-zero-never.rs rename to tests/target/configs/float_literal_trailing_zero/never.rs index 4f28b7ac5c2..085ddc49ba1 100644 --- a/tests/target/float-lit-trailing-zero-never.rs +++ b/tests/target/configs/float_literal_trailing_zero/never.rs @@ -15,3 +15,10 @@ fn float_literals() { let l = 1_000_.; let m = 1_000_.; } + +fn line_wrapping() { + let array = [ + 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., + ]; + println!("This is floaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaat {}", 10e3); +} diff --git a/tests/source/float-lit-trailing-zero-always.rs b/tests/target/configs/float_literal_trailing_zero/preserve.rs similarity index 85% rename from tests/source/float-lit-trailing-zero-always.rs rename to tests/target/configs/float_literal_trailing_zero/preserve.rs index ab9305e5cd4..971443ad1e2 100644 --- a/tests/source/float-lit-trailing-zero-always.rs +++ b/tests/target/configs/float_literal_trailing_zero/preserve.rs @@ -1,4 +1,4 @@ -// rustfmt-float_literal_trailing_zero: Always +// rustfmt-float_literal_trailing_zero: Preserve fn float_literals() { let a = 0.; From a376a11283d700cc164bcfcd38d92513c38ebcca Mon Sep 17 00:00:00 2001 From: Andrei Matveiakin Date: Tue, 27 Feb 2024 19:48:20 +0400 Subject: [PATCH 3/8] More exponent tests for float_literal_trailing_zero option --- src/expr.rs | 15 ++++++++++++++ .../float_literal_trailing_zero/always.rs | 20 ++++++++++++------- .../if-no-postfix.rs | 20 ++++++++++++------- .../float_literal_trailing_zero/never.rs | 20 ++++++++++++------- .../float_literal_trailing_zero/always.rs | 20 ++++++++++++------- .../if-no-postfix.rs | 20 ++++++++++++------- .../float_literal_trailing_zero/never.rs | 20 ++++++++++++------- .../float_literal_trailing_zero/preserve.rs | 20 ++++++++++++------- 8 files changed, 106 insertions(+), 49 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 2353e77ac62..a72b4ec4f01 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -2398,11 +2398,26 @@ mod test { assert_eq!(parts.fractional_part, Some("456")); assert_eq!(parts.exponent, Some("e789")); + let parts = parse_float_symbol("123.456e+789"); + assert_eq!(parts.integer_part, "123"); + assert_eq!(parts.fractional_part, Some("456")); + assert_eq!(parts.exponent, Some("e+789")); + + let parts = parse_float_symbol("123.456e-789"); + assert_eq!(parts.integer_part, "123"); + assert_eq!(parts.fractional_part, Some("456")); + assert_eq!(parts.exponent, Some("e-789")); + let parts = parse_float_symbol("123e789"); assert_eq!(parts.integer_part, "123"); assert_eq!(parts.fractional_part, None); assert_eq!(parts.exponent, Some("e789")); + let parts = parse_float_symbol("123E789"); + assert_eq!(parts.integer_part, "123"); + assert_eq!(parts.fractional_part, None); + assert_eq!(parts.exponent, Some("E789")); + let parts = parse_float_symbol("123."); assert_eq!(parts.integer_part, "123"); assert_eq!(parts.fractional_part, None); diff --git a/tests/source/configs/float_literal_trailing_zero/always.rs b/tests/source/configs/float_literal_trailing_zero/always.rs index b63bc1091c6..aa5f84a9eaa 100644 --- a/tests/source/configs/float_literal_trailing_zero/always.rs +++ b/tests/source/configs/float_literal_trailing_zero/always.rs @@ -7,13 +7,19 @@ fn float_literals() { let d = 100.0; let e = 5e3; let f = 5.0e3; - let g = 7f32; - let h = 7.0f32; - let i = 9e3f32; - let j = 9.0e3f32; - let k = 1000.00; - let l = 1_000_.; - let m = 1_000_.000_000; + let g = 5e+3; + let h = 5.0e+3; + let i = 5e-3; + let j = 5.0e-3; + let k = 5E3; + let l = 5.0E3; + let m = 7f32; + let n = 7.0f32; + let o = 9e3f32; + let p = 9.0e3f32; + let q = 1000.00; + let r = 1_000_.; + let s = 1_000_.000_000; } fn line_wrapping() { diff --git a/tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs b/tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs index d47e336c628..be8af142af8 100644 --- a/tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs +++ b/tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs @@ -7,13 +7,19 @@ fn float_literals() { let d = 100.0; let e = 5e3; let f = 5.0e3; - let g = 7f32; - let h = 7.0f32; - let i = 9e3f32; - let j = 9.0e3f32; - let k = 1000.00; - let l = 1_000_.; - let m = 1_000_.000_000; + let g = 5e+3; + let h = 5.0e+3; + let i = 5e-3; + let j = 5.0e-3; + let k = 5E3; + let l = 5.0E3; + let m = 7f32; + let n = 7.0f32; + let o = 9e3f32; + let p = 9.0e3f32; + let q = 1000.00; + let r = 1_000_.; + let s = 1_000_.000_000; } fn line_wrapping() { diff --git a/tests/source/configs/float_literal_trailing_zero/never.rs b/tests/source/configs/float_literal_trailing_zero/never.rs index 22b599b007d..afd99dd0ac3 100644 --- a/tests/source/configs/float_literal_trailing_zero/never.rs +++ b/tests/source/configs/float_literal_trailing_zero/never.rs @@ -7,13 +7,19 @@ fn float_literals() { let d = 100.0; let e = 5e3; let f = 5.0e3; - let g = 7f32; - let h = 7.0f32; - let i = 9e3f32; - let j = 9.0e3f32; - let k = 1000.00; - let l = 1_000_.; - let m = 1_000_.000_000; + let g = 5e+3; + let h = 5.0e+3; + let i = 5e-3; + let j = 5.0e-3; + let k = 5E3; + let l = 5.0E3; + let m = 7f32; + let n = 7.0f32; + let o = 9e3f32; + let p = 9.0e3f32; + let q = 1000.00; + let r = 1_000_.; + let s = 1_000_.000_000; } fn line_wrapping() { diff --git a/tests/target/configs/float_literal_trailing_zero/always.rs b/tests/target/configs/float_literal_trailing_zero/always.rs index e25f032c717..4205db675a4 100644 --- a/tests/target/configs/float_literal_trailing_zero/always.rs +++ b/tests/target/configs/float_literal_trailing_zero/always.rs @@ -7,13 +7,19 @@ fn float_literals() { let d = 100.0; let e = 5.0e3; let f = 5.0e3; - let g = 7.0f32; - let h = 7.0f32; - let i = 9.0e3f32; - let j = 9.0e3f32; - let k = 1000.00; - let l = 1_000_.0; - let m = 1_000_.000_000; + let g = 5.0e+3; + let h = 5.0e+3; + let i = 5.0e-3; + let j = 5.0e-3; + let k = 5.0E3; + let l = 5.0E3; + let m = 7.0f32; + let n = 7.0f32; + let o = 9.0e3f32; + let p = 9.0e3f32; + let q = 1000.00; + let r = 1_000_.0; + let s = 1_000_.000_000; } fn line_wrapping() { diff --git a/tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs b/tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs index caf33ed6353..b534fa88593 100644 --- a/tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs +++ b/tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs @@ -7,13 +7,19 @@ fn float_literals() { let d = 100.0; let e = 5e3; let f = 5e3; - let g = 7f32; - let h = 7f32; - let i = 9e3f32; - let j = 9e3f32; - let k = 1000.00; - let l = 1_000_.0; - let m = 1_000_.000_000; + let g = 5e+3; + let h = 5e+3; + let i = 5e-3; + let j = 5e-3; + let k = 5E3; + let l = 5E3; + let m = 7f32; + let n = 7f32; + let o = 9e3f32; + let p = 9e3f32; + let q = 1000.00; + let r = 1_000_.0; + let s = 1_000_.000_000; } fn line_wrapping() { diff --git a/tests/target/configs/float_literal_trailing_zero/never.rs b/tests/target/configs/float_literal_trailing_zero/never.rs index 085ddc49ba1..9b211b41d14 100644 --- a/tests/target/configs/float_literal_trailing_zero/never.rs +++ b/tests/target/configs/float_literal_trailing_zero/never.rs @@ -7,13 +7,19 @@ fn float_literals() { let d = 100.; let e = 5e3; let f = 5e3; - let g = 7f32; - let h = 7f32; - let i = 9e3f32; - let j = 9e3f32; - let k = 1000.; - let l = 1_000_.; - let m = 1_000_.; + let g = 5e+3; + let h = 5e+3; + let i = 5e-3; + let j = 5e-3; + let k = 5E3; + let l = 5E3; + let m = 7f32; + let n = 7f32; + let o = 9e3f32; + let p = 9e3f32; + let q = 1000.; + let r = 1_000_.; + let s = 1_000_.; } fn line_wrapping() { diff --git a/tests/target/configs/float_literal_trailing_zero/preserve.rs b/tests/target/configs/float_literal_trailing_zero/preserve.rs index 971443ad1e2..17c3941f342 100644 --- a/tests/target/configs/float_literal_trailing_zero/preserve.rs +++ b/tests/target/configs/float_literal_trailing_zero/preserve.rs @@ -7,11 +7,17 @@ fn float_literals() { let d = 100.0; let e = 5e3; let f = 5.0e3; - let g = 7f32; - let h = 7.0f32; - let i = 9e3f32; - let j = 9.0e3f32; - let k = 1000.00; - let l = 1_000_.; - let m = 1_000_.000_000; + let g = 5e+3; + let h = 5.0e+3; + let i = 5e-3; + let j = 5.0e-3; + let k = 5E3; + let l = 5.0E3; + let m = 7f32; + let n = 7.0f32; + let o = 9e3f32; + let p = 9.0e3f32; + let q = 1000.00; + let r = 1_000_.; + let s = 1_000_.000_000; } From 534d371a3479dd2a9465ddffcdee52b5950367be Mon Sep 17 00:00:00 2001 From: Andrei Matveiakin Date: Sun, 5 Jan 2025 13:37:19 +0000 Subject: [PATCH 4/8] Fix floating-point literals in ranges --- src/chains.rs | 8 +-- src/expr.rs | 54 ++++++++++--------- .../float_literal_trailing_zero/always.rs | 7 +++ .../if-no-postfix.rs | 7 +++ .../float_literal_trailing_zero/never.rs | 7 +++ .../float_literal_trailing_zero/always.rs | 7 +++ .../if-no-postfix.rs | 7 +++ .../float_literal_trailing_zero/never.rs | 7 +++ .../float_literal_trailing_zero/preserve.rs | 7 +++ 9 files changed, 81 insertions(+), 30 deletions(-) diff --git a/src/chains.rs b/src/chains.rs index acd0c2961b1..50a129b695f 100644 --- a/src/chains.rs +++ b/src/chains.rs @@ -264,7 +264,7 @@ impl ChainItemKind { return ( ChainItemKind::Parent { expr: expr.clone(), - parens: is_method_call_receiver && should_add_parens(expr), + parens: is_method_call_receiver && should_add_parens(expr, context), }, expr.span, ); @@ -1049,12 +1049,12 @@ fn trim_tries(s: &str) -> String { /// 1. .method(); /// ``` /// Which all need parenthesis or a space before `.method()`. -fn should_add_parens(expr: &ast::Expr) -> bool { +fn should_add_parens(expr: &ast::Expr, context: &RewriteContext<'_>) -> bool { match expr.kind { - ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit), + ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit, context), ast::ExprKind::Closure(ref cl) => match cl.body.kind { ast::ExprKind::Range(_, _, ast::RangeLimits::HalfOpen) => true, - ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit), + ast::ExprKind::Lit(ref lit) => crate::expr::lit_ends_in_dot(lit, context), _ => false, }, _ => false, diff --git a/src/expr.rs b/src/expr.rs index a72b4ec4f01..0b07d8f2b22 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -54,8 +54,11 @@ pub(crate) enum ExprType { SubExpression, } -pub(crate) fn lit_ends_in_dot(lit: &Lit) -> bool { - matches!(lit, Lit { kind: LitKind::Float, suffix: None, symbol } if symbol.as_str().ends_with('.')) +pub(crate) fn lit_ends_in_dot(lit: &Lit, context: &RewriteContext<'_>) -> bool { + match lit.kind { + LitKind::Float => do_rewrite_float_lit(context, *lit).ends_with('.'), + _ => false, + } } pub(crate) fn format_expr( @@ -297,7 +300,7 @@ pub(crate) fn format_expr( fn needs_space_before_range(context: &RewriteContext<'_>, lhs: &ast::Expr) -> bool { match lhs.kind { - ast::ExprKind::Lit(token_lit) => lit_ends_in_dot(&token_lit), + ast::ExprKind::Lit(token_lit) => lit_ends_in_dot(&token_lit, context), ast::ExprKind::Unary(_, ref expr) => needs_space_before_range(context, expr), ast::ExprKind::Binary(_, _, ref rhs_expr) => { needs_space_before_range(context, rhs_expr) @@ -1351,27 +1354,17 @@ fn rewrite_int_lit( .max_width_error(shape.width, span) } -fn rewrite_float_lit( - context: &RewriteContext<'_>, - token_lit: token::Lit, - span: Span, - shape: Shape, -) -> RewriteResult { +fn do_rewrite_float_lit(context: &RewriteContext<'_>, token_lit: token::Lit) -> String { + let symbol = token_lit.symbol.as_str(); + let suffix = token_lit.suffix.as_ref().map(|s| s.as_str()); + if matches!( context.config.float_literal_trailing_zero(), FloatLiteralTrailingZero::Preserve ) { - return wrap_str( - context.snippet(span).to_owned(), - context.config.max_width(), - shape, - ) - .max_width_error(shape.width, span); + return format!("{}{}", symbol, suffix.unwrap_or("")); } - let symbol = token_lit.symbol.as_str(); - let suffix = token_lit.suffix.as_ref().map(|s| s.as_str()); - let FloatSymbolParts { integer_part, fractional_part, @@ -1401,15 +1394,24 @@ fn rewrite_float_lit( } else { "" }; + format!( + "{}{}{}{}{}", + integer_part, + period, + fractional_part, + exponent.unwrap_or(""), + suffix.unwrap_or(""), + ) +} + +fn rewrite_float_lit( + context: &RewriteContext<'_>, + token_lit: token::Lit, + span: Span, + shape: Shape, +) -> RewriteResult { wrap_str( - format!( - "{}{}{}{}{}", - integer_part, - period, - fractional_part, - exponent.unwrap_or(""), - suffix.unwrap_or(""), - ), + do_rewrite_float_lit(context, token_lit), context.config.max_width(), shape, ) diff --git a/tests/source/configs/float_literal_trailing_zero/always.rs b/tests/source/configs/float_literal_trailing_zero/always.rs index aa5f84a9eaa..d9ae27daa89 100644 --- a/tests/source/configs/float_literal_trailing_zero/always.rs +++ b/tests/source/configs/float_literal_trailing_zero/always.rs @@ -22,6 +22,13 @@ fn float_literals() { let s = 1_000_.000_000; } +fn range_bounds() { + if (1.0..2.0).contains(&1.0) {} + if (1.1..2.2).contains(&1.1) {} + if (1.0e1..2.0e1).contains(&1.0e1) {} + let _binop_range = 3.0 / 2.0..4.0; +} + fn line_wrapping() { let array = [ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., diff --git a/tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs b/tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs index be8af142af8..bd77042d4bb 100644 --- a/tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs +++ b/tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs @@ -22,6 +22,13 @@ fn float_literals() { let s = 1_000_.000_000; } +fn range_bounds() { + if (1.0..2.0).contains(&1.0) {} + if (1.1..2.2).contains(&1.1) {} + if (1.0e1..2.0e1).contains(&1.0e1) {} + let _binop_range = 3.0 / 2.0..4.0; +} + fn line_wrapping() { let array = [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11., 12., 13., 14., 15., 16., 17., 18., diff --git a/tests/source/configs/float_literal_trailing_zero/never.rs b/tests/source/configs/float_literal_trailing_zero/never.rs index afd99dd0ac3..50e6aee8681 100644 --- a/tests/source/configs/float_literal_trailing_zero/never.rs +++ b/tests/source/configs/float_literal_trailing_zero/never.rs @@ -22,6 +22,13 @@ fn float_literals() { let s = 1_000_.000_000; } +fn range_bounds() { + if (1.0..2.0).contains(&1.0) {} + if (1.1..2.2).contains(&1.1) {} + if (1.0e1..2.0e1).contains(&1.0e1) {} + let _binop_range = 3.0 / 2.0..4.0; +} + fn line_wrapping() { let array = [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, diff --git a/tests/target/configs/float_literal_trailing_zero/always.rs b/tests/target/configs/float_literal_trailing_zero/always.rs index 4205db675a4..97ea16e7272 100644 --- a/tests/target/configs/float_literal_trailing_zero/always.rs +++ b/tests/target/configs/float_literal_trailing_zero/always.rs @@ -22,6 +22,13 @@ fn float_literals() { let s = 1_000_.000_000; } +fn range_bounds() { + if (1.0..2.0).contains(&1.0) {} + if (1.1..2.2).contains(&1.1) {} + if (1.0e1..2.0e1).contains(&1.0e1) {} + let _binop_range = 3.0 / 2.0..4.0; +} + fn line_wrapping() { let array = [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, diff --git a/tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs b/tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs index b534fa88593..7667649b680 100644 --- a/tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs +++ b/tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs @@ -22,6 +22,13 @@ fn float_literals() { let s = 1_000_.000_000; } +fn range_bounds() { + if (1.0..2.0).contains(&1.0) {} + if (1.1..2.2).contains(&1.1) {} + if (1e1..2e1).contains(&1e1) {} + let _binop_range = 3.0 / 2.0..4.0; +} + fn line_wrapping() { let array = [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, diff --git a/tests/target/configs/float_literal_trailing_zero/never.rs b/tests/target/configs/float_literal_trailing_zero/never.rs index 9b211b41d14..53da5cb50d9 100644 --- a/tests/target/configs/float_literal_trailing_zero/never.rs +++ b/tests/target/configs/float_literal_trailing_zero/never.rs @@ -22,6 +22,13 @@ fn float_literals() { let s = 1_000_.; } +fn range_bounds() { + if (1. ..2.).contains(&1.) {} + if (1.1..2.2).contains(&1.1) {} + if (1e1..2e1).contains(&1e1) {} + let _binop_range = 3. / 2. ..4.; +} + fn line_wrapping() { let array = [ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., diff --git a/tests/target/configs/float_literal_trailing_zero/preserve.rs b/tests/target/configs/float_literal_trailing_zero/preserve.rs index 17c3941f342..527e606e12e 100644 --- a/tests/target/configs/float_literal_trailing_zero/preserve.rs +++ b/tests/target/configs/float_literal_trailing_zero/preserve.rs @@ -21,3 +21,10 @@ fn float_literals() { let r = 1_000_.; let s = 1_000_.000_000; } + +fn range_bounds() { + if (1.0..2.0).contains(&1.0) {} + if (1.1..2.2).contains(&1.1) {} + if (1.0e1..2.0e1).contains(&1.0e1) {} + let _binop_range = 3.0 / 2.0..4.0; +} From 29e678952033a3c13efcf29bdfa37bb96ee79d6e Mon Sep 17 00:00:00 2001 From: Andrei Matveiakin Date: Sun, 19 Jan 2025 21:16:46 +0000 Subject: [PATCH 5/8] Address ytmimi comments --- src/expr.rs | 66 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 0b07d8f2b22..2b068bbf402 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -56,7 +56,7 @@ pub(crate) enum ExprType { pub(crate) fn lit_ends_in_dot(lit: &Lit, context: &RewriteContext<'_>) -> bool { match lit.kind { - LitKind::Float => do_rewrite_float_lit(context, *lit).ends_with('.'), + LitKind::Float => rewrite_float_lit_inner(context, *lit).ends_with('.'), _ => false, } } @@ -1354,7 +1354,7 @@ fn rewrite_int_lit( .max_width_error(shape.width, span) } -fn do_rewrite_float_lit(context: &RewriteContext<'_>, token_lit: token::Lit) -> String { +fn rewrite_float_lit_inner(context: &RewriteContext<'_>, token_lit: token::Lit) -> String { let symbol = token_lit.symbol.as_str(); let suffix = token_lit.suffix.as_ref().map(|s| s.as_str()); @@ -1365,14 +1365,15 @@ fn do_rewrite_float_lit(context: &RewriteContext<'_>, token_lit: token::Lit) -> return format!("{}{}", symbol, suffix.unwrap_or("")); } + let float_parts = parse_float_symbol(symbol).unwrap(); let FloatSymbolParts { integer_part, fractional_part, exponent, - } = parse_float_symbol(symbol); + } = float_parts; let has_postfix = exponent.is_some() || suffix.is_some(); - let fractional_part_nonzero = fractional_part.map_or(false, |s| !is_zero_integer_literal(s)); + let fractional_part_nonzero = !float_parts.is_fractional_part_zero(); let (include_period, include_fractional_part) = match context.config.float_literal_trailing_zero() { @@ -1411,7 +1412,7 @@ fn rewrite_float_lit( shape: Shape, ) -> RewriteResult { wrap_str( - do_rewrite_float_lit(context, token_lit), + rewrite_float_lit_inner(context, token_lit), context.config.max_width(), shape, ) @@ -2343,29 +2344,40 @@ pub(crate) fn is_method_call(expr: &ast::Expr) -> bool { } } +/// Indicates the parts of a float literal specified as a string. struct FloatSymbolParts<'a> { + /// The integer part, e.g. `123` in `123.456e789`. + /// Always non-empty, because in Rust `.1` is not a valid floating-point literal: + /// integer_part: &'a str, + /// The fractional part excluding the decimal point, e.g. `456` in `123.456e789`. fractional_part: Option<&'a str>, + /// The exponent part including the `e` or `E`, e.g. `e789` in `123.456e789`. exponent: Option<&'a str>, } -// Parses a float literal. The `symbol` must be a valid floating point literal without a type -// suffix. Otherwise the function may panic or return wrong result. -fn parse_float_symbol(symbol: &str) -> FloatSymbolParts<'_> { +impl FloatSymbolParts<'_> { + fn is_fractional_part_zero(&self) -> bool { + let zero_literal_regex = static_regex!(r"^[0_]+$"); + self.fractional_part + .is_none_or(|s| zero_literal_regex.is_match(s)) + } +} + +/// Parses a float literal. The `symbol` must be a valid floating point literal without a type +/// suffix. Otherwise the function may panic or return wrong result. +fn parse_float_symbol(symbol: &str) -> Result, &'static str> { // This regex may accept invalid float literals (such as `1`, `_` or `2.e3`). That's ok. // We only use it to parse literals whose validity has already been established. let float_literal_regex = static_regex!(r"^([0-9_]+)(?:\.([0-9_]+)?)?([eE][+-]?[0-9_]+)?$"); - let caps = float_literal_regex.captures(symbol).unwrap(); - FloatSymbolParts { - integer_part: caps.get(1).unwrap().as_str(), + let caps = float_literal_regex + .captures(symbol) + .ok_or("invalid float literal")?; + Ok(FloatSymbolParts { + integer_part: caps.get(1).ok_or("missing integer part")?.as_str(), fractional_part: caps.get(2).map(|m| m.as_str()), exponent: caps.get(3).map(|m| m.as_str()), - } -} - -fn is_zero_integer_literal(symbol: &str) -> bool { - let zero_literal_regex = static_regex!(r"^[0_]+$"); - zero_literal_regex.is_match(symbol) + }) } #[cfg(test)] @@ -2395,32 +2407,32 @@ mod test { #[test] fn test_parse_float_symbol() { - let parts = parse_float_symbol("123.456e789"); + let parts = parse_float_symbol("123.456e789").unwrap(); assert_eq!(parts.integer_part, "123"); assert_eq!(parts.fractional_part, Some("456")); assert_eq!(parts.exponent, Some("e789")); - let parts = parse_float_symbol("123.456e+789"); + let parts = parse_float_symbol("123.456e+789").unwrap(); assert_eq!(parts.integer_part, "123"); assert_eq!(parts.fractional_part, Some("456")); assert_eq!(parts.exponent, Some("e+789")); - let parts = parse_float_symbol("123.456e-789"); + let parts = parse_float_symbol("123.456e-789").unwrap(); assert_eq!(parts.integer_part, "123"); assert_eq!(parts.fractional_part, Some("456")); assert_eq!(parts.exponent, Some("e-789")); - let parts = parse_float_symbol("123e789"); + let parts = parse_float_symbol("123e789").unwrap(); assert_eq!(parts.integer_part, "123"); assert_eq!(parts.fractional_part, None); assert_eq!(parts.exponent, Some("e789")); - let parts = parse_float_symbol("123E789"); + let parts = parse_float_symbol("123E789").unwrap(); assert_eq!(parts.integer_part, "123"); assert_eq!(parts.fractional_part, None); assert_eq!(parts.exponent, Some("E789")); - let parts = parse_float_symbol("123."); + let parts = parse_float_symbol("123.").unwrap(); assert_eq!(parts.integer_part, "123"); assert_eq!(parts.fractional_part, None); assert_eq!(parts.exponent, None); @@ -2428,22 +2440,22 @@ mod test { #[test] fn test_parse_float_symbol_with_underscores() { - let parts = parse_float_symbol("_123._456e_789"); + let parts = parse_float_symbol("_123._456e_789").unwrap(); assert_eq!(parts.integer_part, "_123"); assert_eq!(parts.fractional_part, Some("_456")); assert_eq!(parts.exponent, Some("e_789")); - let parts = parse_float_symbol("123_.456_e789_"); + let parts = parse_float_symbol("123_.456_e789_").unwrap(); assert_eq!(parts.integer_part, "123_"); assert_eq!(parts.fractional_part, Some("456_")); assert_eq!(parts.exponent, Some("e789_")); - let parts = parse_float_symbol("1_23.4_56e7_89"); + let parts = parse_float_symbol("1_23.4_56e7_89").unwrap(); assert_eq!(parts.integer_part, "1_23"); assert_eq!(parts.fractional_part, Some("4_56")); assert_eq!(parts.exponent, Some("e7_89")); - let parts = parse_float_symbol("_1_23_._4_56_e_7_89_"); + let parts = parse_float_symbol("_1_23_._4_56_e_7_89_").unwrap(); assert_eq!(parts.integer_part, "_1_23_"); assert_eq!(parts.fractional_part, Some("_4_56_")); assert_eq!(parts.exponent, Some("e_7_89_")); From 70df9aee9e49abda35ce0fe2aaaa1619cb39a54e Mon Sep 17 00:00:00 2001 From: Andrei Matveiakin Date: Sun, 9 Feb 2025 21:45:12 +0000 Subject: [PATCH 6/8] Address ytmimi comments - Revert changes to rewrite_int_lit. - Implement float_lit_ends_in_dot independently of rewrite_float_lit. - Remove rewrite_float_lit_inner: it is not needed after the float_lit_ends_in_dot rewrite. --- src/expr.rs | 108 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 83 insertions(+), 25 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 2b068bbf402..0c5752380f6 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -56,11 +56,32 @@ pub(crate) enum ExprType { pub(crate) fn lit_ends_in_dot(lit: &Lit, context: &RewriteContext<'_>) -> bool { match lit.kind { - LitKind::Float => rewrite_float_lit_inner(context, *lit).ends_with('.'), + LitKind::Float => float_lit_ends_in_dot( + lit.symbol.as_str(), + lit.suffix.as_ref().map(|s| s.as_str()), + context.config.float_literal_trailing_zero(), + ), _ => false, } } +pub(crate) fn float_lit_ends_in_dot( + symbol: &str, + suffix: Option<&str>, + float_literal_trailing_zero: FloatLiteralTrailingZero, +) -> bool { + match float_literal_trailing_zero { + FloatLiteralTrailingZero::Preserve => symbol.ends_with('.') && suffix.is_none(), + FloatLiteralTrailingZero::IfNoPostfix | FloatLiteralTrailingZero::Always => false, + FloatLiteralTrailingZero::Never => { + let float_parts = parse_float_symbol(symbol).unwrap(); + let has_postfix = float_parts.exponent.is_some() || suffix.is_some(); + let fractional_part_zero = float_parts.is_fractional_part_zero(); + !has_postfix && fractional_part_zero + } + } +} + pub(crate) fn format_expr( expr: &ast::Expr, expr_type: ExprType, @@ -1328,7 +1349,6 @@ fn rewrite_int_lit( } let symbol = token_lit.symbol.as_str(); - let suffix = token_lit.suffix.as_ref().map(|s| s.as_str()); if let Some(symbol_stripped) = symbol.strip_prefix("0x") { let hex_lit = match context.config.hex_literal_case() { @@ -1338,7 +1358,11 @@ fn rewrite_int_lit( }; if let Some(hex_lit) = hex_lit { return wrap_str( - format!("0x{}{}", hex_lit, suffix.unwrap_or("")), + format!( + "0x{}{}", + hex_lit, + token_lit.suffix.as_ref().map_or("", |s| s.as_str()) + ), context.config.max_width(), shape, ) @@ -1354,17 +1378,27 @@ fn rewrite_int_lit( .max_width_error(shape.width, span) } -fn rewrite_float_lit_inner(context: &RewriteContext<'_>, token_lit: token::Lit) -> String { - let symbol = token_lit.symbol.as_str(); - let suffix = token_lit.suffix.as_ref().map(|s| s.as_str()); - +fn rewrite_float_lit( + context: &RewriteContext<'_>, + token_lit: token::Lit, + span: Span, + shape: Shape, +) -> RewriteResult { if matches!( context.config.float_literal_trailing_zero(), FloatLiteralTrailingZero::Preserve ) { - return format!("{}{}", symbol, suffix.unwrap_or("")); + return wrap_str( + context.snippet(span).to_owned(), + context.config.max_width(), + shape, + ) + .max_width_error(shape.width, span); } + let symbol = token_lit.symbol.as_str(); + let suffix = token_lit.suffix.as_ref().map(|s| s.as_str()); + let float_parts = parse_float_symbol(symbol).unwrap(); let FloatSymbolParts { integer_part, @@ -1395,24 +1429,15 @@ fn rewrite_float_lit_inner(context: &RewriteContext<'_>, token_lit: token::Lit) } else { "" }; - format!( - "{}{}{}{}{}", - integer_part, - period, - fractional_part, - exponent.unwrap_or(""), - suffix.unwrap_or(""), - ) -} - -fn rewrite_float_lit( - context: &RewriteContext<'_>, - token_lit: token::Lit, - span: Span, - shape: Shape, -) -> RewriteResult { wrap_str( - rewrite_float_lit_inner(context, token_lit), + format!( + "{}{}{}{}{}", + integer_part, + period, + fractional_part, + exponent.unwrap_or(""), + suffix.unwrap_or(""), + ), context.config.max_width(), shape, ) @@ -2460,4 +2485,37 @@ mod test { assert_eq!(parts.fractional_part, Some("_4_56_")); assert_eq!(parts.exponent, Some("e_7_89_")); } + + #[test] + fn test_float_lit_ends_in_dot() { + type TZ = FloatLiteralTrailingZero; + + assert!(float_lit_ends_in_dot("1.", None, TZ::Preserve)); + assert!(!float_lit_ends_in_dot("1.0", None, TZ::Preserve)); + assert!(!float_lit_ends_in_dot("1.e2", None, TZ::Preserve)); + assert!(!float_lit_ends_in_dot("1.0e2", None, TZ::Preserve)); + assert!(!float_lit_ends_in_dot("1.", Some("f32"), TZ::Preserve)); + assert!(!float_lit_ends_in_dot("1.0", Some("f32"), TZ::Preserve)); + + assert!(!float_lit_ends_in_dot("1.", None, TZ::Always)); + assert!(!float_lit_ends_in_dot("1.0", None, TZ::Always)); + assert!(!float_lit_ends_in_dot("1.e2", None, TZ::Always)); + assert!(!float_lit_ends_in_dot("1.0e2", None, TZ::Always)); + assert!(!float_lit_ends_in_dot("1.", Some("f32"), TZ::Always)); + assert!(!float_lit_ends_in_dot("1.0", Some("f32"), TZ::Always)); + + assert!(!float_lit_ends_in_dot("1.", None, TZ::IfNoPostfix)); + assert!(!float_lit_ends_in_dot("1.0", None, TZ::IfNoPostfix)); + assert!(!float_lit_ends_in_dot("1.e2", None, TZ::IfNoPostfix)); + assert!(!float_lit_ends_in_dot("1.0e2", None, TZ::IfNoPostfix)); + assert!(!float_lit_ends_in_dot("1.", Some("f32"), TZ::IfNoPostfix)); + assert!(!float_lit_ends_in_dot("1.0", Some("f32"), TZ::IfNoPostfix)); + + assert!(float_lit_ends_in_dot("1.", None, TZ::Never)); + assert!(float_lit_ends_in_dot("1.0", None, TZ::Never)); + assert!(!float_lit_ends_in_dot("1.e2", None, TZ::Never)); + assert!(!float_lit_ends_in_dot("1.0e2", None, TZ::Never)); + assert!(!float_lit_ends_in_dot("1.", Some("f32"), TZ::Never)); + assert!(!float_lit_ends_in_dot("1.0", Some("f32"), TZ::Never)); + } } From e3c99bf1419613b244edc51a8bcf9d0bdc0f8796 Mon Sep 17 00:00:00 2001 From: Andrei Matveiakin Date: Tue, 11 Feb 2025 22:06:01 +0000 Subject: [PATCH 7/8] Add tests for method calls --- .../source/configs/float_literal_trailing_zero/always.rs | 8 ++++++++ .../configs/float_literal_trailing_zero/if-no-postfix.rs | 8 ++++++++ tests/source/configs/float_literal_trailing_zero/never.rs | 8 ++++++++ .../target/configs/float_literal_trailing_zero/always.rs | 8 ++++++++ .../configs/float_literal_trailing_zero/if-no-postfix.rs | 8 ++++++++ tests/target/configs/float_literal_trailing_zero/never.rs | 8 ++++++++ .../configs/float_literal_trailing_zero/preserve.rs | 8 ++++++++ 7 files changed, 56 insertions(+) diff --git a/tests/source/configs/float_literal_trailing_zero/always.rs b/tests/source/configs/float_literal_trailing_zero/always.rs index d9ae27daa89..47b0443137a 100644 --- a/tests/source/configs/float_literal_trailing_zero/always.rs +++ b/tests/source/configs/float_literal_trailing_zero/always.rs @@ -29,6 +29,14 @@ fn range_bounds() { let _binop_range = 3.0 / 2.0..4.0; } +fn method_calls() { + let x = 1.0.neg(); + let y = 2.3.neg(); + let z = (4.).neg(); + let u = 5.0f32.neg(); + let v = -6.0.neg(); +} + fn line_wrapping() { let array = [ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., diff --git a/tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs b/tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs index bd77042d4bb..45e0b87bbae 100644 --- a/tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs +++ b/tests/source/configs/float_literal_trailing_zero/if-no-postfix.rs @@ -29,6 +29,14 @@ fn range_bounds() { let _binop_range = 3.0 / 2.0..4.0; } +fn method_calls() { + let x = 1.0.neg(); + let y = 2.3.neg(); + let z = (4.).neg(); + let u = 5.0f32.neg(); + let v = -6.0.neg(); +} + fn line_wrapping() { let array = [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11., 12., 13., 14., 15., 16., 17., 18., diff --git a/tests/source/configs/float_literal_trailing_zero/never.rs b/tests/source/configs/float_literal_trailing_zero/never.rs index 50e6aee8681..2fe5fe2f438 100644 --- a/tests/source/configs/float_literal_trailing_zero/never.rs +++ b/tests/source/configs/float_literal_trailing_zero/never.rs @@ -29,6 +29,14 @@ fn range_bounds() { let _binop_range = 3.0 / 2.0..4.0; } +fn method_calls() { + let x = 1.0.neg(); + let y = 2.3.neg(); + let z = (4.).neg(); + let u = 5.0f32.neg(); + let v = -6.0.neg(); +} + fn line_wrapping() { let array = [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, diff --git a/tests/target/configs/float_literal_trailing_zero/always.rs b/tests/target/configs/float_literal_trailing_zero/always.rs index 97ea16e7272..e6d643ad43f 100644 --- a/tests/target/configs/float_literal_trailing_zero/always.rs +++ b/tests/target/configs/float_literal_trailing_zero/always.rs @@ -29,6 +29,14 @@ fn range_bounds() { let _binop_range = 3.0 / 2.0..4.0; } +fn method_calls() { + let x = 1.0.neg(); + let y = 2.3.neg(); + let z = (4.0).neg(); + let u = 5.0f32.neg(); + let v = -6.0.neg(); +} + fn line_wrapping() { let array = [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, diff --git a/tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs b/tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs index 7667649b680..d81f5c07ccf 100644 --- a/tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs +++ b/tests/target/configs/float_literal_trailing_zero/if-no-postfix.rs @@ -29,6 +29,14 @@ fn range_bounds() { let _binop_range = 3.0 / 2.0..4.0; } +fn method_calls() { + let x = 1.0.neg(); + let y = 2.3.neg(); + let z = (4.0).neg(); + let u = 5f32.neg(); + let v = -6.0.neg(); +} + fn line_wrapping() { let array = [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, diff --git a/tests/target/configs/float_literal_trailing_zero/never.rs b/tests/target/configs/float_literal_trailing_zero/never.rs index 53da5cb50d9..6391890e535 100644 --- a/tests/target/configs/float_literal_trailing_zero/never.rs +++ b/tests/target/configs/float_literal_trailing_zero/never.rs @@ -29,6 +29,14 @@ fn range_bounds() { let _binop_range = 3. / 2. ..4.; } +fn method_calls() { + let x = (1.).neg(); + let y = 2.3.neg(); + let z = (4.).neg(); + let u = 5f32.neg(); + let v = -(6.).neg(); +} + fn line_wrapping() { let array = [ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., diff --git a/tests/target/configs/float_literal_trailing_zero/preserve.rs b/tests/target/configs/float_literal_trailing_zero/preserve.rs index 527e606e12e..3f0f764796c 100644 --- a/tests/target/configs/float_literal_trailing_zero/preserve.rs +++ b/tests/target/configs/float_literal_trailing_zero/preserve.rs @@ -28,3 +28,11 @@ fn range_bounds() { if (1.0e1..2.0e1).contains(&1.0e1) {} let _binop_range = 3.0 / 2.0..4.0; } + +fn method_calls() { + let x = 1.0.neg(); + let y = 2.3.neg(); + let z = (4.).neg(); + let u = 5.0f32.neg(); + let v = -6.0.neg(); +} From 9d1e92ddbf851ca6f33b30ed0eeef0e0afe7cea1 Mon Sep 17 00:00:00 2001 From: Andrei Matveiakin Date: Sat, 15 Feb 2025 00:15:33 +0000 Subject: [PATCH 8/8] Update tracking issue number --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index af4b7bcaf05..e27c40ff0df 100644 --- a/Configurations.md +++ b/Configurations.md @@ -1262,7 +1262,7 @@ Control the presence of trailing zero in floating-point literal values - **Default value**: `Preserve` - **Possible values**: `Preserve`, `Always`, `IfNoPostfix`, `Never` -- **Stable**: No (tracking issue: [#3187](https://github.com/rust-lang/rustfmt/issues/3187)) +- **Stable**: No (tracking issue: [#6471](https://github.com/rust-lang/rustfmt/issues/6471)) #### `Preserve` (default):