From 8e417faa3dfad69d9d2ace328950913ee228c3a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Fri, 25 Apr 2025 19:12:24 +0300 Subject: [PATCH 01/16] expr: Escape anchor characters within the core pattern The anchor characters `^` and `$` are not considered special characters by `expr` unless they are used as expected on the start or end of the pattern. --- src/uu/expr/src/syntax_tree.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index ac418cafeee..5a5892597e4 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -150,8 +150,32 @@ impl StringOp { let left = left?.eval_as_string(); let right = right?.eval_as_string(); check_posix_regex_errors(&right)?; - let prefix = if right.starts_with('*') { r"^\" } else { "^" }; - let re_string = format!("{prefix}{right}"); + + // Parse core pattern from between the possible anchor characters + // - Start of string => ^ + // - End of string => $ + let mut core_pattern = right.to_string(); + if let Some(stripped) = right.strip_prefix('^') { + core_pattern = stripped.to_string() + }; + if let Some(stripped) = core_pattern.strip_suffix('$') { + core_pattern = stripped.to_string(); + } + + // Escape anchor characters + core_pattern = core_pattern.replace('^', r"\^").replace('$', r"\$"); + + // Escape asterisk if it is the first character + if core_pattern.starts_with('*') { + core_pattern = format!(r"\{}", core_pattern); + } + + // Add anchor characters around the core pattern. + // All patterns should have the start of string anchor '^'. + let has_end_anchor = right.ends_with('$'); + let re_string = + format!("^{}{}", core_pattern, if has_end_anchor { "$" } else { "" }); + let re = Regex::with_options( &re_string, RegexOptions::REGEX_OPTION_NONE, From 229bc43f06f2834e823f1ce28345bbb10c614372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Fri, 25 Apr 2025 20:02:34 +0300 Subject: [PATCH 02/16] expr: Test that starting caret is not escaped --- src/uu/expr/src/syntax_tree.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index 5a5892597e4..ae46cd77561 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -882,6 +882,27 @@ mod test { assert_eq!(result.eval_as_string(), "5"); } + #[test] + fn starting_caret_is_not_escaped() { + let result = AstNode::parse(&["cats", ":", "^cats"]) + .unwrap() + .eval() + .unwrap(); + assert_eq!(result.eval_as_string(), "4"); + + let result = AstNode::parse(&["^cats", ":", "^^cats"]) + .unwrap() + .eval() + .unwrap(); + assert_eq!(result.eval_as_string(), "5"); + + let result = AstNode::parse(&["^cats", ":", "^cats"]) + .unwrap() + .eval() + .unwrap(); + assert_eq!(result.eval_as_string(), "0"); + } + #[test] fn only_match_in_beginning() { let result = AstNode::parse(&["budget", ":", r"get"]) From fe5a350ac9a234a962ff3cb98f58d44f84ddb55b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Fri, 25 Apr 2025 20:05:14 +0300 Subject: [PATCH 03/16] expr: Test that ending dollar is not escaped --- src/uu/expr/src/syntax_tree.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index ae46cd77561..a0e6054daea 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -903,6 +903,27 @@ mod test { assert_eq!(result.eval_as_string(), "0"); } + #[test] + fn ending_dollar_is_not_escaped() { + let result = AstNode::parse(&["cats", ":", "cats$"]) + .unwrap() + .eval() + .unwrap(); + assert_eq!(result.eval_as_string(), "4"); + + let result = AstNode::parse(&["cats$", ":", "cats$$"]) + .unwrap() + .eval() + .unwrap(); + assert_eq!(result.eval_as_string(), "5"); + + let result = AstNode::parse(&["cats$", ":", "cats$"]) + .unwrap() + .eval() + .unwrap(); + assert_eq!(result.eval_as_string(), "0"); + } + #[test] fn only_match_in_beginning() { let result = AstNode::parse(&["budget", ":", r"get"]) From 7c8bf949c944291b19f41b866d68509eec33f90e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Fri, 25 Apr 2025 20:13:23 +0300 Subject: [PATCH 04/16] expr: Test that achor characters are escaped correctly --- src/uu/expr/src/syntax_tree.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index a0e6054daea..830fca4615a 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -924,6 +924,33 @@ mod test { assert_eq!(result.eval_as_string(), "0"); } + #[test] + fn anchor_characters_are_escaped() { + let result = AstNode::parse(&["^$", ":", "^^$$"]) + .unwrap() + .eval() + .unwrap(); + assert_eq!(result.eval_as_string(), "2"); + + let result = AstNode::parse(&["^cats$", ":", "^^cats$$"]) + .unwrap() + .eval() + .unwrap(); + assert_eq!(result.eval_as_string(), "6"); + + let result = AstNode::parse(&["b^$ic", ":", "b^$ic"]) + .unwrap() + .eval() + .unwrap(); + assert_eq!(result.eval_as_string(), "5"); + + let result = AstNode::parse(&["$^$^", ":", "$^$^"]) + .unwrap() + .eval() + .unwrap(); + assert_eq!(result.eval_as_string(), "4"); + } + #[test] fn only_match_in_beginning() { let result = AstNode::parse(&["budget", ":", r"get"]) From 7d54304c3e9f97da5d6e043c19256d103370bce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Fri, 25 Apr 2025 20:15:30 +0300 Subject: [PATCH 05/16] expr: Add test case from #7663 --- src/uu/expr/src/syntax_tree.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index 830fca4615a..8c001afae14 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -932,6 +932,12 @@ mod test { .unwrap(); assert_eq!(result.eval_as_string(), "2"); + let result = AstNode::parse(&["a^b", ":", "a^b"]) + .unwrap() + .eval() + .unwrap(); + assert_eq!(result.eval_as_string(), "3"); + let result = AstNode::parse(&["^cats$", ":", "^^cats$$"]) .unwrap() .eval() From 4899ee6982e97b9db251cd7e9699dfd973fdee8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Fri, 25 Apr 2025 20:33:40 +0300 Subject: [PATCH 06/16] expr: Do not escape end of string anchor ($) It is not escaped by GNU `expr` either --- src/uu/expr/src/syntax_tree.rs | 47 +++++----------------------------- 1 file changed, 7 insertions(+), 40 deletions(-) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index 8c001afae14..a9e00f9e8fd 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -158,9 +158,6 @@ impl StringOp { if let Some(stripped) = right.strip_prefix('^') { core_pattern = stripped.to_string() }; - if let Some(stripped) = core_pattern.strip_suffix('$') { - core_pattern = stripped.to_string(); - } // Escape anchor characters core_pattern = core_pattern.replace('^', r"\^").replace('$', r"\$"); @@ -170,11 +167,8 @@ impl StringOp { core_pattern = format!(r"\{}", core_pattern); } - // Add anchor characters around the core pattern. - // All patterns should have the start of string anchor '^'. - let has_end_anchor = right.ends_with('$'); - let re_string = - format!("^{}{}", core_pattern, if has_end_anchor { "$" } else { "" }); + // All patterns should have the start of string anchor '^' + let re_string = format!("^{}", core_pattern); let re = Regex::with_options( &re_string, @@ -904,45 +898,18 @@ mod test { } #[test] - fn ending_dollar_is_not_escaped() { - let result = AstNode::parse(&["cats", ":", "cats$"]) - .unwrap() - .eval() - .unwrap(); - assert_eq!(result.eval_as_string(), "4"); - - let result = AstNode::parse(&["cats$", ":", "cats$$"]) - .unwrap() - .eval() - .unwrap(); - assert_eq!(result.eval_as_string(), "5"); - - let result = AstNode::parse(&["cats$", ":", "cats$"]) - .unwrap() - .eval() - .unwrap(); - assert_eq!(result.eval_as_string(), "0"); - } - - #[test] - fn anchor_characters_are_escaped() { - let result = AstNode::parse(&["^$", ":", "^^$$"]) - .unwrap() - .eval() - .unwrap(); - assert_eq!(result.eval_as_string(), "2"); - + fn non_starting_carets_become_escaped() { let result = AstNode::parse(&["a^b", ":", "a^b"]) .unwrap() .eval() .unwrap(); assert_eq!(result.eval_as_string(), "3"); - let result = AstNode::parse(&["^cats$", ":", "^^cats$$"]) + let result = AstNode::parse(&["^cats", ":", "^^cats"]) .unwrap() .eval() .unwrap(); - assert_eq!(result.eval_as_string(), "6"); + assert_eq!(result.eval_as_string(), "5"); let result = AstNode::parse(&["b^$ic", ":", "b^$ic"]) .unwrap() @@ -950,11 +917,11 @@ mod test { .unwrap(); assert_eq!(result.eval_as_string(), "5"); - let result = AstNode::parse(&["$^$^", ":", "$^$^"]) + let result = AstNode::parse(&["^^^^^^^^^", ":", "^^^"]) .unwrap() .eval() .unwrap(); - assert_eq!(result.eval_as_string(), "4"); + assert_eq!(result.eval_as_string(), "2"); } #[test] From 613ab99356cf56da52aee84d1743664a5b42b808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Fri, 25 Apr 2025 20:38:58 +0300 Subject: [PATCH 07/16] expr: Do not escape end of string anchor $ --- src/uu/expr/src/syntax_tree.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index a9e00f9e8fd..8e189d6cd13 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -151,16 +151,12 @@ impl StringOp { let right = right?.eval_as_string(); check_posix_regex_errors(&right)?; - // Parse core pattern from between the possible anchor characters - // - Start of string => ^ - // - End of string => $ + // Escape start of string anchor characters when they are not the first character let mut core_pattern = right.to_string(); if let Some(stripped) = right.strip_prefix('^') { core_pattern = stripped.to_string() }; - - // Escape anchor characters - core_pattern = core_pattern.replace('^', r"\^").replace('$', r"\$"); + core_pattern = core_pattern.replace('^', r"\^"); // Escape asterisk if it is the first character if core_pattern.starts_with('*') { From 0ba5ef51790ef79aa2410e2730f01d2fb665141f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Fri, 25 Apr 2025 20:39:47 +0300 Subject: [PATCH 08/16] expr: Fix test by escaping dollar character --- src/uu/expr/src/syntax_tree.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index 8e189d6cd13..a1f3a226b10 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -907,7 +907,7 @@ mod test { .unwrap(); assert_eq!(result.eval_as_string(), "5"); - let result = AstNode::parse(&["b^$ic", ":", "b^$ic"]) + let result = AstNode::parse(&["b^$ic", ":", r"b^\$ic"]) .unwrap() .eval() .unwrap(); From bdfa0f66c5ab7501efc65b0a9164454cd508a5b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Fri, 25 Apr 2025 20:43:22 +0300 Subject: [PATCH 09/16] expr: Fix clippy warning for missing semicolon --- src/uu/expr/src/syntax_tree.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index a1f3a226b10..7c3e339f1fc 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -154,7 +154,7 @@ impl StringOp { // Escape start of string anchor characters when they are not the first character let mut core_pattern = right.to_string(); if let Some(stripped) = right.strip_prefix('^') { - core_pattern = stripped.to_string() + core_pattern = stripped.to_string(); }; core_pattern = core_pattern.replace('^', r"\^"); From 414f148b8efe86059c42ccea9ffe12bd86658ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Fri, 25 Apr 2025 21:05:32 +0300 Subject: [PATCH 10/16] expr: Escape start anchor (^) if it is not already escaped --- src/uu/expr/src/syntax_tree.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index 7c3e339f1fc..9c8b95892f5 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -156,7 +156,8 @@ impl StringOp { if let Some(stripped) = right.strip_prefix('^') { core_pattern = stripped.to_string(); }; - core_pattern = core_pattern.replace('^', r"\^"); + let unescaped_anchor_re = Regex::new(r"(? Date: Fri, 25 Apr 2025 21:06:14 +0300 Subject: [PATCH 11/16] expr: Remove fixed FixME comment from test --- tests/by-util/test_expr.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/by-util/test_expr.rs b/tests/by-util/test_expr.rs index df2c27c1ce8..4ac15e36dc9 100644 --- a/tests/by-util/test_expr.rs +++ b/tests/by-util/test_expr.rs @@ -274,11 +274,6 @@ fn test_length_mb() { #[test] fn test_regex() { - // FixME: [2022-12-19; rivy] test disabled as it currently fails due to 'oniguruma' bug (see GH:kkos/oniguruma/issues/279) - // new_ucmd!() - // .args(&["a^b", ":", "a^b"]) - // .succeeds() - // .stdout_only("3\n"); new_ucmd!() .args(&["a^b", ":", "a\\^b"]) .succeeds() From 98f1f9f1d69c54d01640f8e76ee8b4698a01deb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Fri, 25 Apr 2025 21:57:23 +0300 Subject: [PATCH 12/16] expr: Move some tests from syntax_tree.rs to test_expr.rs --- src/uu/expr/src/syntax_tree.rs | 48 ---------------------------------- tests/by-util/test_expr.rs | 24 +++++++++++++++++ 2 files changed, 24 insertions(+), 48 deletions(-) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index 9c8b95892f5..0aa9a7c566c 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -873,54 +873,6 @@ mod test { assert_eq!(result.eval_as_string(), "5"); } - #[test] - fn starting_caret_is_not_escaped() { - let result = AstNode::parse(&["cats", ":", "^cats"]) - .unwrap() - .eval() - .unwrap(); - assert_eq!(result.eval_as_string(), "4"); - - let result = AstNode::parse(&["^cats", ":", "^^cats"]) - .unwrap() - .eval() - .unwrap(); - assert_eq!(result.eval_as_string(), "5"); - - let result = AstNode::parse(&["^cats", ":", "^cats"]) - .unwrap() - .eval() - .unwrap(); - assert_eq!(result.eval_as_string(), "0"); - } - - #[test] - fn non_starting_carets_become_escaped() { - let result = AstNode::parse(&["a^b", ":", "a^b"]) - .unwrap() - .eval() - .unwrap(); - assert_eq!(result.eval_as_string(), "3"); - - let result = AstNode::parse(&["^cats", ":", "^^cats"]) - .unwrap() - .eval() - .unwrap(); - assert_eq!(result.eval_as_string(), "5"); - - let result = AstNode::parse(&["b^$ic", ":", r"b^\$ic"]) - .unwrap() - .eval() - .unwrap(); - assert_eq!(result.eval_as_string(), "5"); - - let result = AstNode::parse(&["^^^^^^^^^", ":", "^^^"]) - .unwrap() - .eval() - .unwrap(); - assert_eq!(result.eval_as_string(), "2"); - } - #[test] fn only_match_in_beginning() { let result = AstNode::parse(&["budget", ":", r"get"]) diff --git a/tests/by-util/test_expr.rs b/tests/by-util/test_expr.rs index 4ac15e36dc9..1caa96668bd 100644 --- a/tests/by-util/test_expr.rs +++ b/tests/by-util/test_expr.rs @@ -274,6 +274,10 @@ fn test_length_mb() { #[test] fn test_regex() { + new_ucmd!() + .args(&["a^b", ":", "a^b"]) + .succeeds() + .stdout_only("3\n"); new_ucmd!() .args(&["a^b", ":", "a\\^b"]) .succeeds() @@ -282,6 +286,22 @@ fn test_regex() { .args(&["a$b", ":", "a\\$b"]) .succeeds() .stdout_only("3\n"); + new_ucmd!() + .args(&["abc", ":", "^abc"]) + .succeeds() + .stdout_only("3\n"); + new_ucmd!() + .args(&["^abc", ":", "^^abc"]) + .succeeds() + .stdout_only("4\n"); + new_ucmd!() + .args(&["b^$ic", ":", "b^\\$ic"]) + .succeeds() + .stdout_only("5\n"); + new_ucmd!() + .args(&["^^^^^^^^^", ":", "^^^"]) + .succeeds() + .stdout_only("2\n"); new_ucmd!() .args(&["-5", ":", "-\\{0,1\\}[0-9]*$"]) .succeeds() @@ -290,6 +310,10 @@ fn test_regex() { .args(&["abc", ":", "bc"]) .fails() .stdout_only("0\n"); + new_ucmd!() + .args(&["^abc", ":", "^abc"]) + .fails() + .stdout_only("0\n"); } #[test] From b112ec6cc3cd1025fc3b659a771060fc24b675c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Fri, 25 Apr 2025 22:02:42 +0300 Subject: [PATCH 13/16] expr: Unignore fixed test bre10 The `rust-onig` bug still exists but parsing carets is fixed within uutils' `expr` --- tests/by-util/test_expr.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/by-util/test_expr.rs b/tests/by-util/test_expr.rs index 1caa96668bd..08a4944b42a 100644 --- a/tests/by-util/test_expr.rs +++ b/tests/by-util/test_expr.rs @@ -730,7 +730,6 @@ mod gnu_expr { .stdout_only("\n"); } - #[ignore = "rust-onig bug, see https://github.com/rust-onig/rust-onig/issues/188"] #[test] fn test_bre10() { new_ucmd!() From 19e5f5f0cb3ef414073513e39daa635803cea2fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Sat, 26 Apr 2025 12:40:46 +0300 Subject: [PATCH 14/16] expr: Optimize parsing the user input pattern --- src/uu/expr/src/syntax_tree.rs | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/uu/expr/src/syntax_tree.rs b/src/uu/expr/src/syntax_tree.rs index 0aa9a7c566c..317c86a1a1e 100644 --- a/src/uu/expr/src/syntax_tree.rs +++ b/src/uu/expr/src/syntax_tree.rs @@ -151,22 +151,31 @@ impl StringOp { let right = right?.eval_as_string(); check_posix_regex_errors(&right)?; - // Escape start of string anchor characters when they are not the first character - let mut core_pattern = right.to_string(); - if let Some(stripped) = right.strip_prefix('^') { - core_pattern = stripped.to_string(); + // All patterns are anchored so they begin with a caret (^) + let mut re_string = String::with_capacity(right.len() + 1); + re_string.push('^'); + + // Handle first character from the input pattern + let mut pattern_chars = right.chars(); + let first = pattern_chars.next(); + match first { + Some('^') => {} // Start of string anchor is already added + Some('*') => re_string.push_str(r"\*"), + Some(char) => re_string.push(char), + None => return Ok(0.into()), }; - let unescaped_anchor_re = Regex::new(r"(? re_string.push_str(r"\^"), + char => re_string.push(char), + } + prev = curr; } - // All patterns should have the start of string anchor '^' - let re_string = format!("^{}", core_pattern); - let re = Regex::with_options( &re_string, RegexOptions::REGEX_OPTION_NONE, From 71ea130a61ee1db7f2d27599a89e14e32df447f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Sat, 26 Apr 2025 12:51:23 +0300 Subject: [PATCH 15/16] expr: Test empty pattern for regex --- tests/by-util/test_expr.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/by-util/test_expr.rs b/tests/by-util/test_expr.rs index 08a4944b42a..952c88e9387 100644 --- a/tests/by-util/test_expr.rs +++ b/tests/by-util/test_expr.rs @@ -306,6 +306,14 @@ fn test_regex() { .args(&["-5", ":", "-\\{0,1\\}[0-9]*$"]) .succeeds() .stdout_only("2\n"); + new_ucmd!() + .args(&["", ":", ""]) + .fails() + .stdout_only("0\n"); + new_ucmd!() + .args(&["abc", ":", ""]) + .fails() + .stdout_only("0\n"); new_ucmd!() .args(&["abc", ":", "bc"]) .fails() From fe0109b6c7b0b3ed8799b3413c3504c1ad909fa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Teemu=20P=C3=A4tsi?= Date: Sat, 26 Apr 2025 13:06:10 +0300 Subject: [PATCH 16/16] expr: Autoformat test --- tests/by-util/test_expr.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/by-util/test_expr.rs b/tests/by-util/test_expr.rs index 952c88e9387..b669c2dd7a5 100644 --- a/tests/by-util/test_expr.rs +++ b/tests/by-util/test_expr.rs @@ -306,10 +306,7 @@ fn test_regex() { .args(&["-5", ":", "-\\{0,1\\}[0-9]*$"]) .succeeds() .stdout_only("2\n"); - new_ucmd!() - .args(&["", ":", ""]) - .fails() - .stdout_only("0\n"); + new_ucmd!().args(&["", ":", ""]).fails().stdout_only("0\n"); new_ucmd!() .args(&["abc", ":", ""]) .fails()