mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 13:24:57 +00:00
[ruff
] Add support for more re
patterns (RUF055
) (#15764)
## Summary Implements some of #14738, by adding support for 6 new patterns: ```py re.search("abc", s) is None # ⇒ "abc" not in s re.search("abc", s) is not None # ⇒ "abc" in s re.match("abc", s) is None # ⇒ not s.startswith("abc") re.match("abc", s) is not None # ⇒ s.startswith("abc") re.fullmatch("abc", s) is None # ⇒ s != "abc" re.fullmatch("abc", s) is not None # ⇒ s == "abc" ``` ## Test Plan ```shell cargo nextest run cargo insta review ``` And ran the fix on my startup's repo. ## Note One minor limitation here: ```py if not re.match('abc', s) is None: pass ``` will get fixed to this (technically correct, just not nice): ```py if not not s.startswith('abc'): pass ``` This seems fine given that Ruff has this covered: the initial code should be caught by [E714](https://docs.astral.sh/ruff/rules/not-is-test/) and the fixed code should be caught by [SIM208](https://docs.astral.sh/ruff/rules/double-negation/).
This commit is contained in:
parent
0f1035b930
commit
6c1e19592e
6 changed files with 345 additions and 47 deletions
|
@ -2,7 +2,7 @@ import re
|
||||||
|
|
||||||
s = "str"
|
s = "str"
|
||||||
|
|
||||||
# this should be replaced with s.replace("abc", "")
|
# this should be replaced with `s.replace("abc", "")`
|
||||||
re.sub("abc", "", s)
|
re.sub("abc", "", s)
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ def dashrepl(matchobj):
|
||||||
|
|
||||||
re.sub("-", dashrepl, "pro----gram-files")
|
re.sub("-", dashrepl, "pro----gram-files")
|
||||||
|
|
||||||
# this one should be replaced with s.startswith("abc") because the Match is
|
# this one should be replaced with `s.startswith("abc")` because the Match is
|
||||||
# used in an if context for its truth value
|
# used in an if context for its truth value
|
||||||
if re.match("abc", s):
|
if re.match("abc", s):
|
||||||
pass
|
pass
|
||||||
|
@ -25,17 +25,17 @@ if m := re.match("abc", s): # this should *not* be replaced
|
||||||
pass
|
pass
|
||||||
re.match("abc", s) # this should not be replaced because match returns a Match
|
re.match("abc", s) # this should not be replaced because match returns a Match
|
||||||
|
|
||||||
# this should be replaced with "abc" in s
|
# this should be replaced with `"abc" in s`
|
||||||
if re.search("abc", s):
|
if re.search("abc", s):
|
||||||
pass
|
pass
|
||||||
re.search("abc", s) # this should not be replaced
|
re.search("abc", s) # this should not be replaced
|
||||||
|
|
||||||
# this should be replaced with "abc" == s
|
# this should be replaced with `"abc" == s`
|
||||||
if re.fullmatch("abc", s):
|
if re.fullmatch("abc", s):
|
||||||
pass
|
pass
|
||||||
re.fullmatch("abc", s) # this should not be replaced
|
re.fullmatch("abc", s) # this should not be replaced
|
||||||
|
|
||||||
# this should be replaced with s.split("abc")
|
# this should be replaced with `s.split("abc")`
|
||||||
re.split("abc", s)
|
re.split("abc", s)
|
||||||
|
|
||||||
# these currently should not be modified because the patterns contain regex
|
# these currently should not be modified because the patterns contain regex
|
||||||
|
|
52
crates/ruff_linter/resources/test/fixtures/ruff/RUF055_2.py
vendored
Normal file
52
crates/ruff_linter/resources/test/fixtures/ruff/RUF055_2.py
vendored
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
"""Patterns that don't just involve the call, but rather the parent expression"""
|
||||||
|
import re
|
||||||
|
|
||||||
|
s = "str"
|
||||||
|
|
||||||
|
# this should be replaced with `"abc" not in s`
|
||||||
|
re.search("abc", s) is None
|
||||||
|
|
||||||
|
|
||||||
|
# this should be replaced with `"abc" in s`
|
||||||
|
re.search("abc", s) is not None
|
||||||
|
|
||||||
|
|
||||||
|
# this should be replaced with `not s.startswith("abc")`
|
||||||
|
re.match("abc", s) is None
|
||||||
|
|
||||||
|
|
||||||
|
# this should be replaced with `s.startswith("abc")`
|
||||||
|
re.match("abc", s) is not None
|
||||||
|
|
||||||
|
|
||||||
|
# this should be replaced with `s != "abc"`
|
||||||
|
re.fullmatch("abc", s) is None
|
||||||
|
|
||||||
|
|
||||||
|
# this should be replaced with `s == "abc"`
|
||||||
|
re.fullmatch("abc", s) is not None
|
||||||
|
|
||||||
|
|
||||||
|
# this should trigger an unsafe fix because of the presence of a comment within the
|
||||||
|
# expression being replaced (which we'd lose)
|
||||||
|
if (
|
||||||
|
re.fullmatch(
|
||||||
|
"a really really really really long string",
|
||||||
|
s,
|
||||||
|
)
|
||||||
|
# with a comment here
|
||||||
|
is None
|
||||||
|
):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# this should trigger a safe fix (comments are preserved given they're outside the
|
||||||
|
# expression)
|
||||||
|
if ( # leading
|
||||||
|
re.fullmatch(
|
||||||
|
"a really really really really long string",
|
||||||
|
s,
|
||||||
|
)
|
||||||
|
is None # trailing
|
||||||
|
):
|
||||||
|
pass
|
|
@ -422,6 +422,7 @@ mod tests {
|
||||||
#[test_case(Rule::UnrawRePattern, Path::new("RUF039_concat.py"))]
|
#[test_case(Rule::UnrawRePattern, Path::new("RUF039_concat.py"))]
|
||||||
#[test_case(Rule::UnnecessaryRegularExpression, Path::new("RUF055_0.py"))]
|
#[test_case(Rule::UnnecessaryRegularExpression, Path::new("RUF055_0.py"))]
|
||||||
#[test_case(Rule::UnnecessaryRegularExpression, Path::new("RUF055_1.py"))]
|
#[test_case(Rule::UnnecessaryRegularExpression, Path::new("RUF055_1.py"))]
|
||||||
|
#[test_case(Rule::UnnecessaryRegularExpression, Path::new("RUF055_2.py"))]
|
||||||
#[test_case(Rule::UnnecessaryCastToInt, Path::new("RUF046.py"))]
|
#[test_case(Rule::UnnecessaryCastToInt, Path::new("RUF046.py"))]
|
||||||
#[test_case(Rule::PytestRaisesAmbiguousPattern, Path::new("RUF043.py"))]
|
#[test_case(Rule::PytestRaisesAmbiguousPattern, Path::new("RUF043.py"))]
|
||||||
#[test_case(Rule::UnnecessaryRound, Path::new("RUF057.py"))]
|
#[test_case(Rule::UnnecessaryRound, Path::new("RUF057.py"))]
|
||||||
|
|
|
@ -3,7 +3,7 @@ use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Vi
|
||||||
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
use ruff_macros::{derive_message_formats, ViolationMetadata};
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
Arguments, CmpOp, Expr, ExprAttribute, ExprCall, ExprCompare, ExprContext, ExprStringLiteral,
|
Arguments, CmpOp, Expr, ExprAttribute, ExprCall, ExprCompare, ExprContext, ExprStringLiteral,
|
||||||
Identifier,
|
ExprUnaryOp, Identifier, UnaryOp,
|
||||||
};
|
};
|
||||||
use ruff_python_semantic::analyze::typing::find_binding_value;
|
use ruff_python_semantic::analyze::typing::find_binding_value;
|
||||||
use ruff_python_semantic::{Modules, SemanticModel};
|
use ruff_python_semantic::{Modules, SemanticModel};
|
||||||
|
@ -111,8 +111,8 @@ pub(crate) fn unnecessary_regular_expression(checker: &mut Checker, call: &ExprC
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Here we know the pattern is a string literal with no metacharacters, so
|
// Now we know the pattern is a string literal with no metacharacters, so
|
||||||
// we can proceed with the str method replacement
|
// we can proceed with the str method replacement.
|
||||||
let new_expr = re_func.replacement();
|
let new_expr = re_func.replacement();
|
||||||
|
|
||||||
let repl = new_expr.map(|expr| checker.generator().expr(&expr));
|
let repl = new_expr.map(|expr| checker.generator().expr(&expr));
|
||||||
|
@ -120,16 +120,13 @@ pub(crate) fn unnecessary_regular_expression(checker: &mut Checker, call: &ExprC
|
||||||
UnnecessaryRegularExpression {
|
UnnecessaryRegularExpression {
|
||||||
replacement: repl.clone(),
|
replacement: repl.clone(),
|
||||||
},
|
},
|
||||||
call.range,
|
re_func.range,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(repl) = repl {
|
if let Some(repl) = repl {
|
||||||
diagnostic.set_fix(Fix::applicable_edit(
|
diagnostic.set_fix(Fix::applicable_edit(
|
||||||
Edit::range_replacement(repl, call.range),
|
Edit::range_replacement(repl, re_func.range),
|
||||||
if checker
|
if checker.comment_ranges().intersects(re_func.range) {
|
||||||
.comment_ranges()
|
|
||||||
.has_comments(call, checker.source())
|
|
||||||
{
|
|
||||||
Applicability::Unsafe
|
Applicability::Unsafe
|
||||||
} else {
|
} else {
|
||||||
Applicability::Safe
|
Applicability::Safe
|
||||||
|
@ -156,6 +153,8 @@ struct ReFunc<'a> {
|
||||||
kind: ReFuncKind<'a>,
|
kind: ReFuncKind<'a>,
|
||||||
pattern: &'a Expr,
|
pattern: &'a Expr,
|
||||||
string: &'a Expr,
|
string: &'a Expr,
|
||||||
|
comparison_to_none: Option<ComparisonToNone>,
|
||||||
|
range: TextRange,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ReFunc<'a> {
|
impl<'a> ReFunc<'a> {
|
||||||
|
@ -165,8 +164,14 @@ impl<'a> ReFunc<'a> {
|
||||||
func_name: &str,
|
func_name: &str,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
// the proposed fixes for match, search, and fullmatch rely on the
|
// the proposed fixes for match, search, and fullmatch rely on the
|
||||||
// return value only being used for its truth value
|
// return value only being used for its truth value or being compared to None
|
||||||
let in_if_context = semantic.in_boolean_test();
|
let comparison_to_none = get_comparison_to_none(semantic);
|
||||||
|
let in_truthy_context = semantic.in_boolean_test() || comparison_to_none.is_some();
|
||||||
|
|
||||||
|
let (comparison_to_none, range) = match comparison_to_none {
|
||||||
|
Some((cmp, range)) => (Some(cmp), range),
|
||||||
|
None => (None, call.range),
|
||||||
|
};
|
||||||
|
|
||||||
match (func_name, call.arguments.len()) {
|
match (func_name, call.arguments.len()) {
|
||||||
// `split` is the safest of these to fix, as long as metacharacters
|
// `split` is the safest of these to fix, as long as metacharacters
|
||||||
|
@ -175,6 +180,8 @@ impl<'a> ReFunc<'a> {
|
||||||
kind: ReFuncKind::Split,
|
kind: ReFuncKind::Split,
|
||||||
pattern: call.arguments.find_argument_value("pattern", 0)?,
|
pattern: call.arguments.find_argument_value("pattern", 0)?,
|
||||||
string: call.arguments.find_argument_value("string", 1)?,
|
string: call.arguments.find_argument_value("string", 1)?,
|
||||||
|
comparison_to_none,
|
||||||
|
range,
|
||||||
}),
|
}),
|
||||||
// `sub` is only safe to fix if `repl` is a string. `re.sub` also
|
// `sub` is only safe to fix if `repl` is a string. `re.sub` also
|
||||||
// allows it to be a function, which will *not* work in the str
|
// allows it to be a function, which will *not* work in the str
|
||||||
|
@ -209,55 +216,91 @@ impl<'a> ReFunc<'a> {
|
||||||
},
|
},
|
||||||
pattern: call.arguments.find_argument_value("pattern", 0)?,
|
pattern: call.arguments.find_argument_value("pattern", 0)?,
|
||||||
string: call.arguments.find_argument_value("string", 2)?,
|
string: call.arguments.find_argument_value("string", 2)?,
|
||||||
|
comparison_to_none,
|
||||||
|
range,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
("match", 2) if in_if_context => Some(ReFunc {
|
("match", 2) if in_truthy_context => Some(ReFunc {
|
||||||
kind: ReFuncKind::Match,
|
kind: ReFuncKind::Match,
|
||||||
pattern: call.arguments.find_argument_value("pattern", 0)?,
|
pattern: call.arguments.find_argument_value("pattern", 0)?,
|
||||||
string: call.arguments.find_argument_value("string", 1)?,
|
string: call.arguments.find_argument_value("string", 1)?,
|
||||||
|
comparison_to_none,
|
||||||
|
range,
|
||||||
}),
|
}),
|
||||||
("search", 2) if in_if_context => Some(ReFunc {
|
("search", 2) if in_truthy_context => Some(ReFunc {
|
||||||
kind: ReFuncKind::Search,
|
kind: ReFuncKind::Search,
|
||||||
pattern: call.arguments.find_argument_value("pattern", 0)?,
|
pattern: call.arguments.find_argument_value("pattern", 0)?,
|
||||||
string: call.arguments.find_argument_value("string", 1)?,
|
string: call.arguments.find_argument_value("string", 1)?,
|
||||||
|
comparison_to_none,
|
||||||
|
range,
|
||||||
}),
|
}),
|
||||||
("fullmatch", 2) if in_if_context => Some(ReFunc {
|
("fullmatch", 2) if in_truthy_context => Some(ReFunc {
|
||||||
kind: ReFuncKind::Fullmatch,
|
kind: ReFuncKind::Fullmatch,
|
||||||
pattern: call.arguments.find_argument_value("pattern", 0)?,
|
pattern: call.arguments.find_argument_value("pattern", 0)?,
|
||||||
string: call.arguments.find_argument_value("string", 1)?,
|
string: call.arguments.find_argument_value("string", 1)?,
|
||||||
|
comparison_to_none,
|
||||||
|
range,
|
||||||
}),
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get replacement for the call or parent expression.
|
||||||
|
///
|
||||||
|
/// Examples:
|
||||||
|
/// `re.search("abc", s) is None` => `"abc" not in s`
|
||||||
|
/// `re.search("abc", s)` => `"abc" in s`
|
||||||
fn replacement(&self) -> Option<Expr> {
|
fn replacement(&self) -> Option<Expr> {
|
||||||
match self.kind {
|
match (&self.kind, &self.comparison_to_none) {
|
||||||
// string.replace(pattern, repl)
|
// string.replace(pattern, repl)
|
||||||
ReFuncKind::Sub { repl } => repl
|
(ReFuncKind::Sub { repl }, _) => repl
|
||||||
.cloned()
|
.cloned()
|
||||||
.map(|repl| self.method_expr("replace", vec![self.pattern.clone(), repl])),
|
.map(|repl| self.method_expr("replace", vec![self.pattern.clone(), repl])),
|
||||||
// string.startswith(pattern)
|
|
||||||
ReFuncKind::Match => Some(self.method_expr("startswith", vec![self.pattern.clone()])),
|
|
||||||
// pattern in string
|
|
||||||
ReFuncKind::Search => Some(self.compare_expr(CmpOp::In)),
|
|
||||||
// string == pattern
|
|
||||||
ReFuncKind::Fullmatch => Some(Expr::Compare(ExprCompare {
|
|
||||||
range: TextRange::default(),
|
|
||||||
left: Box::new(self.string.clone()),
|
|
||||||
ops: Box::new([CmpOp::Eq]),
|
|
||||||
comparators: Box::new([self.pattern.clone()]),
|
|
||||||
})),
|
|
||||||
// string.split(pattern)
|
// string.split(pattern)
|
||||||
ReFuncKind::Split => Some(self.method_expr("split", vec![self.pattern.clone()])),
|
(ReFuncKind::Split, _) => Some(self.method_expr("split", vec![self.pattern.clone()])),
|
||||||
|
// pattern in string
|
||||||
|
(ReFuncKind::Search, None | Some(ComparisonToNone::IsNot)) => {
|
||||||
|
Some(ReFunc::compare_expr(self.pattern, CmpOp::In, self.string))
|
||||||
|
}
|
||||||
|
// pattern not in string
|
||||||
|
(ReFuncKind::Search, Some(ComparisonToNone::Is)) => Some(ReFunc::compare_expr(
|
||||||
|
self.pattern,
|
||||||
|
CmpOp::NotIn,
|
||||||
|
self.string,
|
||||||
|
)),
|
||||||
|
// string.startswith(pattern)
|
||||||
|
(ReFuncKind::Match, None | Some(ComparisonToNone::IsNot)) => {
|
||||||
|
Some(self.method_expr("startswith", vec![self.pattern.clone()]))
|
||||||
|
}
|
||||||
|
// not string.startswith(pattern)
|
||||||
|
(ReFuncKind::Match, Some(ComparisonToNone::Is)) => {
|
||||||
|
let expr = self.method_expr("startswith", vec![self.pattern.clone()]);
|
||||||
|
let negated_expr = Expr::UnaryOp(ExprUnaryOp {
|
||||||
|
op: UnaryOp::Not,
|
||||||
|
operand: Box::new(expr),
|
||||||
|
range: TextRange::default(),
|
||||||
|
});
|
||||||
|
Some(negated_expr)
|
||||||
|
}
|
||||||
|
// string == pattern
|
||||||
|
(ReFuncKind::Fullmatch, None | Some(ComparisonToNone::IsNot)) => {
|
||||||
|
Some(ReFunc::compare_expr(self.string, CmpOp::Eq, self.pattern))
|
||||||
|
}
|
||||||
|
// string != pattern
|
||||||
|
(ReFuncKind::Fullmatch, Some(ComparisonToNone::Is)) => Some(ReFunc::compare_expr(
|
||||||
|
self.string,
|
||||||
|
CmpOp::NotEq,
|
||||||
|
self.pattern,
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a new compare expr of the form `self.pattern op self.string`
|
/// Return a new compare expr of the form `left op right`
|
||||||
fn compare_expr(&self, op: CmpOp) -> Expr {
|
fn compare_expr(left: &Expr, op: CmpOp, right: &Expr) -> Expr {
|
||||||
Expr::Compare(ExprCompare {
|
Expr::Compare(ExprCompare {
|
||||||
left: Box::new(self.pattern.clone()),
|
left: Box::new(left.clone()),
|
||||||
ops: Box::new([op]),
|
ops: Box::new([op]),
|
||||||
comparators: Box::new([self.string.clone()]),
|
comparators: Box::new([right.clone()]),
|
||||||
range: TextRange::default(),
|
range: TextRange::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -302,3 +345,35 @@ fn resolve_string_literal<'a>(
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
enum ComparisonToNone {
|
||||||
|
Is,
|
||||||
|
IsNot,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If the regex call is compared to `None`, return the comparison and its range.
|
||||||
|
/// Example: `re.search("abc", s) is None`
|
||||||
|
fn get_comparison_to_none(semantic: &SemanticModel) -> Option<(ComparisonToNone, TextRange)> {
|
||||||
|
let parent_expr = semantic.current_expression_parent()?;
|
||||||
|
|
||||||
|
let Expr::Compare(ExprCompare {
|
||||||
|
ops,
|
||||||
|
comparators,
|
||||||
|
range,
|
||||||
|
..
|
||||||
|
}) = parent_expr
|
||||||
|
else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(Expr::NoneLiteral(_)) = comparators.first() else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
match ops.as_ref() {
|
||||||
|
[CmpOp::Is] => Some((ComparisonToNone::Is, *range)),
|
||||||
|
[CmpOp::IsNot] => Some((ComparisonToNone::IsNot, *range)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ source: crates/ruff_linter/src/rules/ruff/mod.rs
|
||||||
---
|
---
|
||||||
RUF055_0.py:6:1: RUF055 [*] Plain string pattern passed to `re` function
|
RUF055_0.py:6:1: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
|
|
||||||
5 | # this should be replaced with s.replace("abc", "")
|
5 | # this should be replaced with `s.replace("abc", "")`
|
||||||
6 | re.sub("abc", "", s)
|
6 | re.sub("abc", "", s)
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ RUF055
|
| ^^^^^^^^^^^^^^^^^^^^ RUF055
|
||||||
|
|
|
|
||||||
|
@ -12,7 +12,7 @@ RUF055_0.py:6:1: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
ℹ Safe fix
|
ℹ Safe fix
|
||||||
3 3 | s = "str"
|
3 3 | s = "str"
|
||||||
4 4 |
|
4 4 |
|
||||||
5 5 | # this should be replaced with s.replace("abc", "")
|
5 5 | # this should be replaced with `s.replace("abc", "")`
|
||||||
6 |-re.sub("abc", "", s)
|
6 |-re.sub("abc", "", s)
|
||||||
6 |+s.replace("abc", "")
|
6 |+s.replace("abc", "")
|
||||||
7 7 |
|
7 7 |
|
||||||
|
@ -21,7 +21,7 @@ RUF055_0.py:6:1: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
||||||
RUF055_0.py:22:4: RUF055 [*] Plain string pattern passed to `re` function
|
RUF055_0.py:22:4: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
|
|
||||||
20 | # this one should be replaced with s.startswith("abc") because the Match is
|
20 | # this one should be replaced with `s.startswith("abc")` because the Match is
|
||||||
21 | # used in an if context for its truth value
|
21 | # used in an if context for its truth value
|
||||||
22 | if re.match("abc", s):
|
22 | if re.match("abc", s):
|
||||||
| ^^^^^^^^^^^^^^^^^^ RUF055
|
| ^^^^^^^^^^^^^^^^^^ RUF055
|
||||||
|
@ -32,7 +32,7 @@ RUF055_0.py:22:4: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
||||||
ℹ Safe fix
|
ℹ Safe fix
|
||||||
19 19 |
|
19 19 |
|
||||||
20 20 | # this one should be replaced with s.startswith("abc") because the Match is
|
20 20 | # this one should be replaced with `s.startswith("abc")` because the Match is
|
||||||
21 21 | # used in an if context for its truth value
|
21 21 | # used in an if context for its truth value
|
||||||
22 |-if re.match("abc", s):
|
22 |-if re.match("abc", s):
|
||||||
22 |+if s.startswith("abc"):
|
22 |+if s.startswith("abc"):
|
||||||
|
@ -42,7 +42,7 @@ RUF055_0.py:22:4: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
||||||
RUF055_0.py:29:4: RUF055 [*] Plain string pattern passed to `re` function
|
RUF055_0.py:29:4: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
|
|
||||||
28 | # this should be replaced with "abc" in s
|
28 | # this should be replaced with `"abc" in s`
|
||||||
29 | if re.search("abc", s):
|
29 | if re.search("abc", s):
|
||||||
| ^^^^^^^^^^^^^^^^^^^ RUF055
|
| ^^^^^^^^^^^^^^^^^^^ RUF055
|
||||||
30 | pass
|
30 | pass
|
||||||
|
@ -53,7 +53,7 @@ RUF055_0.py:29:4: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
ℹ Safe fix
|
ℹ Safe fix
|
||||||
26 26 | re.match("abc", s) # this should not be replaced because match returns a Match
|
26 26 | re.match("abc", s) # this should not be replaced because match returns a Match
|
||||||
27 27 |
|
27 27 |
|
||||||
28 28 | # this should be replaced with "abc" in s
|
28 28 | # this should be replaced with `"abc" in s`
|
||||||
29 |-if re.search("abc", s):
|
29 |-if re.search("abc", s):
|
||||||
29 |+if "abc" in s:
|
29 |+if "abc" in s:
|
||||||
30 30 | pass
|
30 30 | pass
|
||||||
|
@ -62,7 +62,7 @@ RUF055_0.py:29:4: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
||||||
RUF055_0.py:34:4: RUF055 [*] Plain string pattern passed to `re` function
|
RUF055_0.py:34:4: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
|
|
||||||
33 | # this should be replaced with "abc" == s
|
33 | # this should be replaced with `"abc" == s`
|
||||||
34 | if re.fullmatch("abc", s):
|
34 | if re.fullmatch("abc", s):
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^ RUF055
|
| ^^^^^^^^^^^^^^^^^^^^^^ RUF055
|
||||||
35 | pass
|
35 | pass
|
||||||
|
@ -73,7 +73,7 @@ RUF055_0.py:34:4: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
ℹ Safe fix
|
ℹ Safe fix
|
||||||
31 31 | re.search("abc", s) # this should not be replaced
|
31 31 | re.search("abc", s) # this should not be replaced
|
||||||
32 32 |
|
32 32 |
|
||||||
33 33 | # this should be replaced with "abc" == s
|
33 33 | # this should be replaced with `"abc" == s`
|
||||||
34 |-if re.fullmatch("abc", s):
|
34 |-if re.fullmatch("abc", s):
|
||||||
34 |+if s == "abc":
|
34 |+if s == "abc":
|
||||||
35 35 | pass
|
35 35 | pass
|
||||||
|
@ -82,7 +82,7 @@ RUF055_0.py:34:4: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
||||||
RUF055_0.py:39:1: RUF055 [*] Plain string pattern passed to `re` function
|
RUF055_0.py:39:1: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
|
|
||||||
38 | # this should be replaced with s.split("abc")
|
38 | # this should be replaced with `s.split("abc")`
|
||||||
39 | re.split("abc", s)
|
39 | re.split("abc", s)
|
||||||
| ^^^^^^^^^^^^^^^^^^ RUF055
|
| ^^^^^^^^^^^^^^^^^^ RUF055
|
||||||
40 |
|
40 |
|
||||||
|
@ -93,7 +93,7 @@ RUF055_0.py:39:1: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
ℹ Safe fix
|
ℹ Safe fix
|
||||||
36 36 | re.fullmatch("abc", s) # this should not be replaced
|
36 36 | re.fullmatch("abc", s) # this should not be replaced
|
||||||
37 37 |
|
37 37 |
|
||||||
38 38 | # this should be replaced with s.split("abc")
|
38 38 | # this should be replaced with `s.split("abc")`
|
||||||
39 |-re.split("abc", s)
|
39 |-re.split("abc", s)
|
||||||
39 |+s.split("abc")
|
39 |+s.split("abc")
|
||||||
40 40 |
|
40 40 |
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/ruff/mod.rs
|
||||||
|
snapshot_kind: text
|
||||||
|
---
|
||||||
|
RUF055_2.py:7:1: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
|
||||||
|
6 | # this should be replaced with `"abc" not in s`
|
||||||
|
7 | re.search("abc", s) is None
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF055
|
||||||
|
|
|
||||||
|
= help: Replace with `"abc" not in s`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
4 4 | s = "str"
|
||||||
|
5 5 |
|
||||||
|
6 6 | # this should be replaced with `"abc" not in s`
|
||||||
|
7 |-re.search("abc", s) is None
|
||||||
|
7 |+"abc" not in s
|
||||||
|
8 8 |
|
||||||
|
9 9 |
|
||||||
|
10 10 | # this should be replaced with `"abc" in s`
|
||||||
|
|
||||||
|
RUF055_2.py:11:1: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
|
||||||
|
10 | # this should be replaced with `"abc" in s`
|
||||||
|
11 | re.search("abc", s) is not None
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF055
|
||||||
|
|
|
||||||
|
= help: Replace with `"abc" in s`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
8 8 |
|
||||||
|
9 9 |
|
||||||
|
10 10 | # this should be replaced with `"abc" in s`
|
||||||
|
11 |-re.search("abc", s) is not None
|
||||||
|
11 |+"abc" in s
|
||||||
|
12 12 |
|
||||||
|
13 13 |
|
||||||
|
14 14 | # this should be replaced with `not s.startswith("abc")`
|
||||||
|
|
||||||
|
RUF055_2.py:15:1: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
|
||||||
|
14 | # this should be replaced with `not s.startswith("abc")`
|
||||||
|
15 | re.match("abc", s) is None
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF055
|
||||||
|
|
|
||||||
|
= help: Replace with `not s.startswith("abc")`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
12 12 |
|
||||||
|
13 13 |
|
||||||
|
14 14 | # this should be replaced with `not s.startswith("abc")`
|
||||||
|
15 |-re.match("abc", s) is None
|
||||||
|
15 |+not s.startswith("abc")
|
||||||
|
16 16 |
|
||||||
|
17 17 |
|
||||||
|
18 18 | # this should be replaced with `s.startswith("abc")`
|
||||||
|
|
||||||
|
RUF055_2.py:19:1: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
|
||||||
|
18 | # this should be replaced with `s.startswith("abc")`
|
||||||
|
19 | re.match("abc", s) is not None
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF055
|
||||||
|
|
|
||||||
|
= help: Replace with `s.startswith("abc")`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
16 16 |
|
||||||
|
17 17 |
|
||||||
|
18 18 | # this should be replaced with `s.startswith("abc")`
|
||||||
|
19 |-re.match("abc", s) is not None
|
||||||
|
19 |+s.startswith("abc")
|
||||||
|
20 20 |
|
||||||
|
21 21 |
|
||||||
|
22 22 | # this should be replaced with `s != "abc"`
|
||||||
|
|
||||||
|
RUF055_2.py:23:1: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
|
||||||
|
22 | # this should be replaced with `s != "abc"`
|
||||||
|
23 | re.fullmatch("abc", s) is None
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF055
|
||||||
|
|
|
||||||
|
= help: Replace with `s != "abc"`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
20 20 |
|
||||||
|
21 21 |
|
||||||
|
22 22 | # this should be replaced with `s != "abc"`
|
||||||
|
23 |-re.fullmatch("abc", s) is None
|
||||||
|
23 |+s != "abc"
|
||||||
|
24 24 |
|
||||||
|
25 25 |
|
||||||
|
26 26 | # this should be replaced with `s == "abc"`
|
||||||
|
|
||||||
|
RUF055_2.py:27:1: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
|
||||||
|
26 | # this should be replaced with `s == "abc"`
|
||||||
|
27 | re.fullmatch("abc", s) is not None
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF055
|
||||||
|
|
|
||||||
|
= help: Replace with `s == "abc"`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
24 24 |
|
||||||
|
25 25 |
|
||||||
|
26 26 | # this should be replaced with `s == "abc"`
|
||||||
|
27 |-re.fullmatch("abc", s) is not None
|
||||||
|
27 |+s == "abc"
|
||||||
|
28 28 |
|
||||||
|
29 29 |
|
||||||
|
30 30 | # this should trigger an unsafe fix because of the presence of a comment within the
|
||||||
|
|
||||||
|
RUF055_2.py:33:5: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
|
||||||
|
31 | # expression being replaced (which we'd lose)
|
||||||
|
32 | if (
|
||||||
|
33 | / re.fullmatch(
|
||||||
|
34 | | "a really really really really long string",
|
||||||
|
35 | | s,
|
||||||
|
36 | | )
|
||||||
|
37 | | # with a comment here
|
||||||
|
38 | | is None
|
||||||
|
| |___________^ RUF055
|
||||||
|
39 | ):
|
||||||
|
40 | pass
|
||||||
|
|
|
||||||
|
= help: Replace with `s != "a really really really really long string"`
|
||||||
|
|
||||||
|
ℹ Unsafe fix
|
||||||
|
30 30 | # this should trigger an unsafe fix because of the presence of a comment within the
|
||||||
|
31 31 | # expression being replaced (which we'd lose)
|
||||||
|
32 32 | if (
|
||||||
|
33 |- re.fullmatch(
|
||||||
|
34 |- "a really really really really long string",
|
||||||
|
35 |- s,
|
||||||
|
36 |- )
|
||||||
|
37 |- # with a comment here
|
||||||
|
38 |- is None
|
||||||
|
33 |+ s != "a really really really really long string"
|
||||||
|
39 34 | ):
|
||||||
|
40 35 | pass
|
||||||
|
41 36 |
|
||||||
|
|
||||||
|
RUF055_2.py:46:5: RUF055 [*] Plain string pattern passed to `re` function
|
||||||
|
|
|
||||||
|
44 | # expression)
|
||||||
|
45 | if ( # leading
|
||||||
|
46 | / re.fullmatch(
|
||||||
|
47 | | "a really really really really long string",
|
||||||
|
48 | | s,
|
||||||
|
49 | | )
|
||||||
|
50 | | is None # trailing
|
||||||
|
| |___________^ RUF055
|
||||||
|
51 | ):
|
||||||
|
52 | pass
|
||||||
|
|
|
||||||
|
= help: Replace with `s != "a really really really really long string"`
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
43 43 | # this should trigger a safe fix (comments are preserved given they're outside the
|
||||||
|
44 44 | # expression)
|
||||||
|
45 45 | if ( # leading
|
||||||
|
46 |- re.fullmatch(
|
||||||
|
47 |- "a really really really really long string",
|
||||||
|
48 |- s,
|
||||||
|
49 |- )
|
||||||
|
50 |- is None # trailing
|
||||||
|
46 |+ s != "a really really really really long string" # trailing
|
||||||
|
51 47 | ):
|
||||||
|
52 48 | pass
|
Loading…
Add table
Add a link
Reference in a new issue