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"
|
||||
|
||||
# this should be replaced with s.replace("abc", "")
|
||||
# this should be replaced with `s.replace("abc", "")`
|
||||
re.sub("abc", "", s)
|
||||
|
||||
|
||||
|
@ -17,7 +17,7 @@ def dashrepl(matchobj):
|
|||
|
||||
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
|
||||
if re.match("abc", s):
|
||||
pass
|
||||
|
@ -25,17 +25,17 @@ if m := re.match("abc", s): # this should *not* be replaced
|
|||
pass
|
||||
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):
|
||||
pass
|
||||
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):
|
||||
pass
|
||||
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)
|
||||
|
||||
# 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::UnnecessaryRegularExpression, Path::new("RUF055_0.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::PytestRaisesAmbiguousPattern, Path::new("RUF043.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_python_ast::{
|
||||
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::{Modules, SemanticModel};
|
||||
|
@ -111,8 +111,8 @@ pub(crate) fn unnecessary_regular_expression(checker: &mut Checker, call: &ExprC
|
|||
return;
|
||||
}
|
||||
|
||||
// Here we know the pattern is a string literal with no metacharacters, so
|
||||
// we can proceed with the str method replacement
|
||||
// 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();
|
||||
|
||||
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 {
|
||||
replacement: repl.clone(),
|
||||
},
|
||||
call.range,
|
||||
re_func.range,
|
||||
);
|
||||
|
||||
if let Some(repl) = repl {
|
||||
diagnostic.set_fix(Fix::applicable_edit(
|
||||
Edit::range_replacement(repl, call.range),
|
||||
if checker
|
||||
.comment_ranges()
|
||||
.has_comments(call, checker.source())
|
||||
{
|
||||
Edit::range_replacement(repl, re_func.range),
|
||||
if checker.comment_ranges().intersects(re_func.range) {
|
||||
Applicability::Unsafe
|
||||
} else {
|
||||
Applicability::Safe
|
||||
|
@ -156,6 +153,8 @@ struct ReFunc<'a> {
|
|||
kind: ReFuncKind<'a>,
|
||||
pattern: &'a Expr,
|
||||
string: &'a Expr,
|
||||
comparison_to_none: Option<ComparisonToNone>,
|
||||
range: TextRange,
|
||||
}
|
||||
|
||||
impl<'a> ReFunc<'a> {
|
||||
|
@ -165,8 +164,14 @@ impl<'a> ReFunc<'a> {
|
|||
func_name: &str,
|
||||
) -> Option<Self> {
|
||||
// the proposed fixes for match, search, and fullmatch rely on the
|
||||
// return value only being used for its truth value
|
||||
let in_if_context = semantic.in_boolean_test();
|
||||
// return value only being used for its truth value or being compared to None
|
||||
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()) {
|
||||
// `split` is the safest of these to fix, as long as metacharacters
|
||||
|
@ -175,6 +180,8 @@ impl<'a> ReFunc<'a> {
|
|||
kind: ReFuncKind::Split,
|
||||
pattern: call.arguments.find_argument_value("pattern", 0)?,
|
||||
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
|
||||
// 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)?,
|
||||
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,
|
||||
pattern: call.arguments.find_argument_value("pattern", 0)?,
|
||||
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,
|
||||
pattern: call.arguments.find_argument_value("pattern", 0)?,
|
||||
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,
|
||||
pattern: call.arguments.find_argument_value("pattern", 0)?,
|
||||
string: call.arguments.find_argument_value("string", 1)?,
|
||||
comparison_to_none,
|
||||
range,
|
||||
}),
|
||||
_ => 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> {
|
||||
match self.kind {
|
||||
match (&self.kind, &self.comparison_to_none) {
|
||||
// string.replace(pattern, repl)
|
||||
ReFuncKind::Sub { repl } => repl
|
||||
(ReFuncKind::Sub { repl }, _) => repl
|
||||
.cloned()
|
||||
.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)
|
||||
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`
|
||||
fn compare_expr(&self, op: CmpOp) -> Expr {
|
||||
/// Return a new compare expr of the form `left op right`
|
||||
fn compare_expr(left: &Expr, op: CmpOp, right: &Expr) -> Expr {
|
||||
Expr::Compare(ExprCompare {
|
||||
left: Box::new(self.pattern.clone()),
|
||||
left: Box::new(left.clone()),
|
||||
ops: Box::new([op]),
|
||||
comparators: Box::new([self.string.clone()]),
|
||||
comparators: Box::new([right.clone()]),
|
||||
range: TextRange::default(),
|
||||
})
|
||||
}
|
||||
|
@ -302,3 +345,35 @@ fn resolve_string_literal<'a>(
|
|||
|
||||
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
|
||||
|
|
||||
5 | # this should be replaced with s.replace("abc", "")
|
||||
5 | # this should be replaced with `s.replace("abc", "")`
|
||||
6 | re.sub("abc", "", s)
|
||||
| ^^^^^^^^^^^^^^^^^^^^ RUF055
|
||||
|
|
||||
|
@ -12,7 +12,7 @@ RUF055_0.py:6:1: RUF055 [*] Plain string pattern passed to `re` function
|
|||
ℹ Safe fix
|
||||
3 3 | s = "str"
|
||||
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 |+s.replace("abc", "")
|
||||
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
|
||||
|
|
||||
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
|
||||
22 | if re.match("abc", s):
|
||||
| ^^^^^^^^^^^^^^^^^^ RUF055
|
||||
|
@ -32,7 +32,7 @@ RUF055_0.py:22:4: RUF055 [*] Plain string pattern passed to `re` function
|
|||
|
||||
ℹ Safe fix
|
||||
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
|
||||
22 |-if re.match("abc", s):
|
||||
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
|
||||
|
|
||||
28 | # this should be replaced with "abc" in s
|
||||
28 | # this should be replaced with `"abc" in s`
|
||||
29 | if re.search("abc", s):
|
||||
| ^^^^^^^^^^^^^^^^^^^ RUF055
|
||||
30 | pass
|
||||
|
@ -53,7 +53,7 @@ RUF055_0.py:29:4: RUF055 [*] Plain string pattern passed to `re` function
|
|||
ℹ Safe fix
|
||||
26 26 | re.match("abc", s) # this should not be replaced because match returns a Match
|
||||
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 "abc" in s:
|
||||
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
|
||||
|
|
||||
33 | # this should be replaced with "abc" == s
|
||||
33 | # this should be replaced with `"abc" == s`
|
||||
34 | if re.fullmatch("abc", s):
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ RUF055
|
||||
35 | pass
|
||||
|
@ -73,7 +73,7 @@ RUF055_0.py:34:4: RUF055 [*] Plain string pattern passed to `re` function
|
|||
ℹ Safe fix
|
||||
31 31 | re.search("abc", s) # this should not be replaced
|
||||
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 s == "abc":
|
||||
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
|
||||
|
|
||||
38 | # this should be replaced with s.split("abc")
|
||||
38 | # this should be replaced with `s.split("abc")`
|
||||
39 | re.split("abc", s)
|
||||
| ^^^^^^^^^^^^^^^^^^ RUF055
|
||||
40 |
|
||||
|
@ -93,7 +93,7 @@ RUF055_0.py:39:1: RUF055 [*] Plain string pattern passed to `re` function
|
|||
ℹ Safe fix
|
||||
36 36 | re.fullmatch("abc", s) # this should not be replaced
|
||||
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 |+s.split("abc")
|
||||
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