Stabilize PLC0207 (#22918)

I made a couple small tweaks to the docs, but this otherwise looked good
to me.

https://docs.astral.sh/ruff/rules/missing-maxsplit-arg/
This commit is contained in:
Brent Westbrook
2026-01-29 12:05:42 -05:00
parent 85ea223f47
commit 8b9ee9f45e
2 changed files with 50 additions and 54 deletions
@@ -11,13 +11,13 @@ use crate::fix;
use crate::{AlwaysFixableViolation, Applicability, Edit, Fix};
/// ## What it does
/// Checks for access to the first or last element of `str.split()` or `str.rsplit()` without
/// `maxsplit=1`
/// Checks for access to the first or last element of `str.split()` or `str.rsplit()` without a
/// `maxsplit=1` argument.
///
/// ## Why is this bad?
/// Calling `str.split()` or `str.rsplit()` without passing `maxsplit=1` splits on every delimiter in the
/// string. When accessing only the first or last element of the result, it
/// would be more efficient to only split once.
/// would be more efficient to split only once.
///
/// ## Example
/// ```python
@@ -38,13 +38,14 @@ use crate::{AlwaysFixableViolation, Applicability, Edit, Fix};
/// ```
///
/// ## Fix Safety
/// This rule's fix is marked as unsafe for `split()`/`rsplit()` calls that contain `*args` or `**kwargs` arguments, as
/// adding a `maxsplit` argument to such a call may lead to duplicated arguments.
/// This rule's fix is marked as unsafe for `split()`/`rsplit()` calls that contain `*args` or
/// `**kwargs` arguments, as adding a `maxsplit` argument to such a call may lead to duplicate
/// arguments.
#[derive(ViolationMetadata)]
#[violation_metadata(preview_since = "0.11.12")]
pub(crate) struct MissingMaxsplitArg {
actual_split_type: String,
suggested_split_type: String,
#[violation_metadata(stable_since = "0.15.0")]
pub(crate) struct MissingMaxsplitArg<'a> {
actual_split_type: &'a str,
suggested_split_type: &'a str,
}
/// Represents the index of the slice used for this rule (which can only be 0 or -1)
@@ -53,15 +54,10 @@ enum SliceBoundary {
Last,
}
impl AlwaysFixableViolation for MissingMaxsplitArg {
impl AlwaysFixableViolation for MissingMaxsplitArg<'_> {
#[derive_message_formats]
fn message(&self) -> String {
let MissingMaxsplitArg {
actual_split_type: _,
suggested_split_type,
} = self;
format!("Replace with `{suggested_split_type}(..., maxsplit=1)`.")
"String is split more times than necessary".to_string()
}
fn fix_title(&self) -> String {
@@ -189,8 +185,8 @@ pub(crate) fn missing_maxsplit_arg(checker: &Checker, value: &Expr, slice: &Expr
let mut diagnostic = checker.report_diagnostic(
MissingMaxsplitArg {
actual_split_type: actual_split_type.to_string(),
suggested_split_type: suggested_split_type.to_string(),
actual_split_type,
suggested_split_type,
},
expr.range(),
);
@@ -1,7 +1,7 @@
---
source: crates/ruff_linter/src/rules/pylint/mod.rs
---
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:14:1
|
12 | # Errors
@@ -21,7 +21,7 @@ help: Pass `maxsplit=1` into `str.split()`
16 | "1,2,3".rsplit(",")[0] # [missing-maxsplit-arg]
17 | "1,2,3".rsplit(",")[-1] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:15:1
|
13 | ## Test split called directly on string literal
@@ -41,7 +41,7 @@ help: Use `str.rsplit()` and pass `maxsplit=1`
17 | "1,2,3".rsplit(",")[-1] # [missing-maxsplit-arg]
18 |
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:16:1
|
14 | "1,2,3".split(",")[0] # [missing-maxsplit-arg]
@@ -60,7 +60,7 @@ help: Use `str.split()` and pass `maxsplit=1`
18 |
19 | ## Test split called on string variable
PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:17:1
|
15 | "1,2,3".split(",")[-1] # [missing-maxsplit-arg]
@@ -80,7 +80,7 @@ help: Pass `maxsplit=1` into `str.rsplit()`
19 | ## Test split called on string variable
20 | SEQ.split(",")[0] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:20:1
|
19 | ## Test split called on string variable
@@ -99,7 +99,7 @@ help: Pass `maxsplit=1` into `str.split()`
22 | SEQ.rsplit(",")[0] # [missing-maxsplit-arg]
23 | SEQ.rsplit(",")[-1] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:21:1
|
19 | ## Test split called on string variable
@@ -119,7 +119,7 @@ help: Use `str.rsplit()` and pass `maxsplit=1`
23 | SEQ.rsplit(",")[-1] # [missing-maxsplit-arg]
24 |
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:22:1
|
20 | SEQ.split(",")[0] # [missing-maxsplit-arg]
@@ -138,7 +138,7 @@ help: Use `str.split()` and pass `maxsplit=1`
24 |
25 | ## Test split called on class attribute
PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:23:1
|
21 | SEQ.split(",")[-1] # [missing-maxsplit-arg]
@@ -158,7 +158,7 @@ help: Pass `maxsplit=1` into `str.rsplit()`
25 | ## Test split called on class attribute
26 | Foo.class_str.split(",")[0] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:26:1
|
25 | ## Test split called on class attribute
@@ -177,7 +177,7 @@ help: Pass `maxsplit=1` into `str.split()`
28 | Foo.class_str.rsplit(",")[0] # [missing-maxsplit-arg]
29 | Foo.class_str.rsplit(",")[-1] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:27:1
|
25 | ## Test split called on class attribute
@@ -197,7 +197,7 @@ help: Use `str.rsplit()` and pass `maxsplit=1`
29 | Foo.class_str.rsplit(",")[-1] # [missing-maxsplit-arg]
30 |
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:28:1
|
26 | Foo.class_str.split(",")[0] # [missing-maxsplit-arg]
@@ -216,7 +216,7 @@ help: Use `str.split()` and pass `maxsplit=1`
30 |
31 | ## Test split called on sliced string
PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:29:1
|
27 | Foo.class_str.split(",")[-1] # [missing-maxsplit-arg]
@@ -236,7 +236,7 @@ help: Pass `maxsplit=1` into `str.rsplit()`
31 | ## Test split called on sliced string
32 | "1,2,3"[::-1].split(",")[0] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:32:1
|
31 | ## Test split called on sliced string
@@ -255,7 +255,7 @@ help: Pass `maxsplit=1` into `str.split()`
34 | SEQ[:3].split(",")[0] # [missing-maxsplit-arg]
35 | Foo.class_str[1:3].split(",")[-1] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:33:1
|
31 | ## Test split called on sliced string
@@ -275,7 +275,7 @@ help: Pass `maxsplit=1` into `str.split()`
35 | Foo.class_str[1:3].split(",")[-1] # [missing-maxsplit-arg]
36 | "1,2,3"[::-1].rsplit(",")[0] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:34:1
|
32 | "1,2,3"[::-1].split(",")[0] # [missing-maxsplit-arg]
@@ -295,7 +295,7 @@ help: Pass `maxsplit=1` into `str.split()`
36 | "1,2,3"[::-1].rsplit(",")[0] # [missing-maxsplit-arg]
37 | SEQ[:3].rsplit(",")[0] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:35:1
|
33 | "1,2,3"[::-1][::-1].split(",")[0] # [missing-maxsplit-arg]
@@ -315,7 +315,7 @@ help: Use `str.rsplit()` and pass `maxsplit=1`
37 | SEQ[:3].rsplit(",")[0] # [missing-maxsplit-arg]
38 | Foo.class_str[1:3].rsplit(",")[-1] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:36:1
|
34 | SEQ[:3].split(",")[0] # [missing-maxsplit-arg]
@@ -335,7 +335,7 @@ help: Use `str.split()` and pass `maxsplit=1`
38 | Foo.class_str[1:3].rsplit(",")[-1] # [missing-maxsplit-arg]
39 |
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:37:1
|
35 | Foo.class_str[1:3].split(",")[-1] # [missing-maxsplit-arg]
@@ -354,7 +354,7 @@ help: Use `str.split()` and pass `maxsplit=1`
39 |
40 | ## Test sep given as named argument
PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:38:1
|
36 | "1,2,3"[::-1].rsplit(",")[0] # [missing-maxsplit-arg]
@@ -374,7 +374,7 @@ help: Pass `maxsplit=1` into `str.rsplit()`
40 | ## Test sep given as named argument
41 | "1,2,3".split(sep=",")[0] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:41:1
|
40 | ## Test sep given as named argument
@@ -393,7 +393,7 @@ help: Pass `maxsplit=1` into `str.split()`
43 | "1,2,3".rsplit(sep=",")[0] # [missing-maxsplit-arg]
44 | "1,2,3".rsplit(sep=",")[-1] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:42:1
|
40 | ## Test sep given as named argument
@@ -413,7 +413,7 @@ help: Use `str.rsplit()` and pass `maxsplit=1`
44 | "1,2,3".rsplit(sep=",")[-1] # [missing-maxsplit-arg]
45 |
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:43:1
|
41 | "1,2,3".split(sep=",")[0] # [missing-maxsplit-arg]
@@ -432,7 +432,7 @@ help: Use `str.split()` and pass `maxsplit=1`
45 |
46 | ## Special cases
PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:44:1
|
42 | "1,2,3".split(sep=",")[-1] # [missing-maxsplit-arg]
@@ -452,7 +452,7 @@ help: Pass `maxsplit=1` into `str.rsplit()`
46 | ## Special cases
47 | "1,2,3".split("\n")[0] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:47:1
|
46 | ## Special cases
@@ -471,7 +471,7 @@ help: Pass `maxsplit=1` into `str.split()`
49 | "1,2,3".rsplit("rsplit")[0] # [missing-maxsplit-arg]
50 |
PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:48:1
|
46 | ## Special cases
@@ -490,7 +490,7 @@ help: Use `str.rsplit()` and pass `maxsplit=1`
50 |
51 | ## Test class attribute named split
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:49:1
|
47 | "1,2,3".split("\n")[0] # [missing-maxsplit-arg]
@@ -510,7 +510,7 @@ help: Use `str.split()` and pass `maxsplit=1`
51 | ## Test class attribute named split
52 | Bar.split.split(",")[0] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:52:1
|
51 | ## Test class attribute named split
@@ -529,7 +529,7 @@ help: Pass `maxsplit=1` into `str.split()`
54 | Bar.split.rsplit(",")[0] # [missing-maxsplit-arg]
55 | Bar.split.rsplit(",")[-1] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:53:1
|
51 | ## Test class attribute named split
@@ -549,7 +549,7 @@ help: Use `str.rsplit()` and pass `maxsplit=1`
55 | Bar.split.rsplit(",")[-1] # [missing-maxsplit-arg]
56 |
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:54:1
|
52 | Bar.split.split(",")[0] # [missing-maxsplit-arg]
@@ -568,7 +568,7 @@ help: Use `str.split()` and pass `maxsplit=1`
56 |
57 | ## Test unpacked dict literal kwargs
PLC0207 [*] Replace with `rsplit(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:55:1
|
53 | Bar.split.split(",")[-1] # [missing-maxsplit-arg]
@@ -588,7 +588,7 @@ help: Pass `maxsplit=1` into `str.rsplit()`
57 | ## Test unpacked dict literal kwargs
58 | "1,2,3".split(**{"sep": ","})[0] # [missing-maxsplit-arg]
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:58:1
|
57 | ## Test unpacked dict literal kwargs
@@ -606,7 +606,7 @@ help: Pass `maxsplit=1` into `str.split()`
61 | # OK
note: This is an unsafe fix and may change runtime behavior
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:179:1
|
177 | # Errors
@@ -627,7 +627,7 @@ help: Pass `maxsplit=1` into `str.split()`
182 | "1,2,3".split(",", **kwargs_with_maxsplit)[0] # TODO: false positive
note: This is an unsafe fix and may change runtime behavior
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:182:1
|
180 | # OK
@@ -648,7 +648,7 @@ help: Pass `maxsplit=1` into `str.split()`
185 |
note: This is an unsafe fix and may change runtime behavior
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:184:1
|
182 | "1,2,3".split(",", **kwargs_with_maxsplit)[0] # TODO: false positive
@@ -667,7 +667,7 @@ help: Pass `maxsplit=1` into `str.split()`
187 | ## Test unpacked list literal args (starred expressions)
note: This is an unsafe fix and may change runtime behavior
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:189:1
|
187 | ## Test unpacked list literal args (starred expressions)
@@ -688,7 +688,7 @@ help: Pass `maxsplit=1` into `str.split()`
192 | # Errors
note: This is an unsafe fix and may change runtime behavior
PLC0207 [*] Replace with `split(..., maxsplit=1)`.
PLC0207 [*] String is split more times than necessary
--> missing_maxsplit_arg.py:194:1
|
192 | # Errors