diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF055_0.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF055_0.py index 608ea2ef22862..d56da3e484fa7 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF055_0.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF055_0.py @@ -98,3 +98,8 @@ def dashrepl(matchobj): re.sub(r'abc', "", s) re.sub(r"""abc""", "", s) re.sub(r'''abc''', "", s) + +# Empty pattern: re.split("", s) should not be flagged because +# str.split("") raises ValueError while re.split("", s) succeeds +re.split("", s) +re.split(r"", s) diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF055_3.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF055_3.py index 13c93f53de85f..b69ab635b51d3 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF055_3.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF055_3.py @@ -21,4 +21,7 @@ re.match(rb"ab[c]", b_src) re.search(rb"ab[c]", b_src) re.fullmatch(rb"ab[c]", b_src) -re.split(rb"ab[c]", b_src) \ No newline at end of file +re.split(rb"ab[c]", b_src) + +# Empty pattern: re.split(rb"", b_src) should not be flagged +re.split(rb"", b_src) \ No newline at end of file diff --git a/crates/ruff_linter/src/rules/ruff/rules/unnecessary_regular_expression.rs b/crates/ruff_linter/src/rules/ruff/rules/unnecessary_regular_expression.rs index 5ccc516ee3de9..2600371cf3b29 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/unnecessary_regular_expression.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/unnecessary_regular_expression.rs @@ -118,6 +118,12 @@ pub(crate) fn unnecessary_regular_expression(checker: &Checker, call: &ExprCall) return; } + // `str.split("")` raises `ValueError: empty separator` while `re.split("", s)` succeeds, + // so skip the diagnostic for `re.split` with an empty pattern. + if matches!(re_func.kind, ReFuncKind::Split) && literal.is_empty() { + return; + } + // Now we know the pattern is a string literal with no metacharacters, so // we can proceed with the str method replacement. let new_expr = re_func.replacement(); @@ -362,6 +368,15 @@ enum Literal<'a> { Bytes(&'a ExprBytesLiteral), } +impl Literal<'_> { + fn is_empty(&self) -> bool { + match self { + Literal::Str(str_lit) => str_lit.value.is_empty(), + Literal::Bytes(bytes_lit) => bytes_lit.value.is_empty(), + } + } +} + /// Try to resolve `name` to either a string or bytes literal in `semantic`. fn resolve_literal<'a>(name: &'a Expr, semantic: &'a SemanticModel) -> Option> { if let Some(str_lit) = resolve_string_literal(name, semantic) { diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF055_RUF055_0.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF055_RUF055_0.py.snap index c9a75f25c296b..5a20f5c18f779 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF055_RUF055_0.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__preview__RUF055_RUF055_0.py.snap @@ -243,6 +243,7 @@ help: Replace with `s.replace(r'abc', "")` 98 + s.replace(r'abc', "") 99 | re.sub(r"""abc""", "", s) 100 | re.sub(r'''abc''', "", s) +101 | RUF055 [*] Plain string pattern passed to `re` function --> RUF055_0.py:99:1 @@ -260,6 +261,8 @@ help: Replace with `s.replace(r"""abc""", "")` - re.sub(r"""abc""", "", s) 99 + s.replace(r"""abc""", "") 100 | re.sub(r'''abc''', "", s) +101 | +102 | # Empty pattern: re.split("", s) should not be flagged because RUF055 [*] Plain string pattern passed to `re` function --> RUF055_0.py:100:1 @@ -268,6 +271,8 @@ RUF055 [*] Plain string pattern passed to `re` function 99 | re.sub(r"""abc""", "", s) 100 | re.sub(r'''abc''', "", s) | ^^^^^^^^^^^^^^^^^^^^^^^^^ +101 | +102 | # Empty pattern: re.split("", s) should not be flagged because | help: Replace with `s.replace(r'''abc''', "")` 97 | # these double as tests for preserving raw string quoting style @@ -275,3 +280,6 @@ help: Replace with `s.replace(r'''abc''', "")` 99 | re.sub(r"""abc""", "", s) - re.sub(r'''abc''', "", s) 100 + s.replace(r'''abc''', "") +101 | +102 | # Empty pattern: re.split("", s) should not be flagged because +103 | # str.split("") raises ValueError while re.split("", s) succeeds