Skip to content

Commit 3898d73

Browse files
[pyupgrade] Show violations without auto-fix for UP031 (#11229)
Co-authored-by: Micha Reiser <micha@reiser.io>
1 parent c487149 commit 3898d73

6 files changed

Lines changed: 1387 additions & 27 deletions

File tree

crates/ruff_linter/resources/test/fixtures/pyupgrade/UP031_0.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,33 @@
139139

140140
print("%2X" % 1)
141141
print("%02X" % 1)
142+
143+
# UP031 (no longer false negatives, but offer no fix because of more complex syntax)
144+
145+
"%d.%d" % (a, b)
146+
147+
"%*s" % (5, "hi")
148+
149+
"%d" % (flt,)
150+
151+
"%c" % (some_string,)
152+
153+
"%.2r" % (1.25)
154+
155+
"%.*s" % (5, "hi")
156+
157+
"%i" % (flt,)
158+
159+
"%()s" % {"": "empty"}
160+
161+
"%s" % {"k": "v"}
162+
163+
"%()s" % {"": "bar"}
164+
165+
"%(1)s" % {"1": "bar"}
166+
167+
"%(a)s" % {"a": 1, "a": 2}
168+
169+
"%(1)s" % {1: 2, "1": 2}
170+
171+
"%(and)s" % {"and": 2}
Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,8 @@
11
# OK
22
b"%s" % (b"bytestring",)
33

4-
"%*s" % (5, "hi")
5-
6-
"%d" % (flt,)
7-
8-
"%c" % (some_string,)
9-
104
"%4%" % ()
115

12-
"%.2r" % (1.25)
13-
146
i % 3
157

16-
"%.*s" % (5, "hi")
17-
18-
"%i" % (flt,)
19-
20-
"%()s" % {"": "empty"}
21-
22-
"%s" % {"k": "v"}
23-
24-
"%(1)s" % {"1": "bar"}
25-
26-
"%(a)s" % {"a": 1, "a": 2}
27-
288
pytest.param('"%8s" % (None,)', id="unsafe width-string conversion"),
29-
30-
"%()s" % {"": "bar"}
31-
32-
"%(1)s" % {1: 2, "1": 2}
33-
34-
"%(and)s" % {"and": 2}

crates/ruff_linter/src/rules/pyupgrade/mod.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ mod tests {
1414

1515
use crate::registry::Rule;
1616
use crate::rules::pyupgrade;
17-
use crate::settings::types::PythonVersion;
17+
use crate::settings::types::{PreviewMode, PythonVersion};
1818
use crate::test::test_path;
1919
use crate::{assert_messages, settings};
2020

@@ -100,6 +100,19 @@ mod tests {
100100
Ok(())
101101
}
102102

103+
#[test_case(Rule::PrintfStringFormatting, Path::new("UP031_0.py"))]
104+
fn preview(rule_code: Rule, path: &Path) -> Result<()> {
105+
let diagnostics = test_path(
106+
Path::new("pyupgrade").join(path),
107+
&settings::LinterSettings {
108+
preview: PreviewMode::Enabled,
109+
..settings::LinterSettings::for_rule(rule_code)
110+
},
111+
)?;
112+
assert_messages!(diagnostics);
113+
Ok(())
114+
}
115+
103116
#[test]
104117
fn async_timeout_error_alias_not_applied_py310() -> Result<()> {
105118
let diagnostics = test_path(

crates/ruff_linter/src/rules/pyupgrade/rules/printf_string_formatting.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,29 @@ use crate::rules::pyupgrade::helpers::curly_escape;
2828
/// formatting.
2929
///
3030
/// ## Example
31+
///
3132
/// ```python
3233
/// "%s, %s" % ("Hello", "World") # "Hello, World"
3334
/// ```
3435
///
3536
/// Use instead:
37+
///
3638
/// ```python
3739
/// "{}, {}".format("Hello", "World") # "Hello, World"
3840
/// ```
3941
///
42+
/// ```python
43+
/// f"{'Hello'}, {'World'}" # "Hello, World"
44+
/// ```
45+
///
4046
/// ## Fix safety
47+
///
4148
/// In cases where the format string contains a single generic format specifier
4249
/// (e.g. `%s`), and the right-hand side is an ambiguous expression,
4350
/// we cannot offer a safe fix.
4451
///
4552
/// For example, given:
53+
///
4654
/// ```python
4755
/// "%s" % val
4856
/// ```
@@ -379,6 +387,11 @@ pub(crate) fn printf_string_formatting(
379387
return;
380388
};
381389
if !convertible(&format_string, right) {
390+
if checker.settings.preview.is_enabled() {
391+
checker
392+
.diagnostics
393+
.push(Diagnostic::new(PrintfStringFormatting, string_expr.range()));
394+
}
382395
return;
383396
}
384397

@@ -437,6 +450,11 @@ pub(crate) fn printf_string_formatting(
437450
let Some(params_string) =
438451
clean_params_dictionary(right, checker.locator(), checker.stylist())
439452
else {
453+
if checker.settings.preview.is_enabled() {
454+
checker
455+
.diagnostics
456+
.push(Diagnostic::new(PrintfStringFormatting, string_expr.range()));
457+
}
440458
return;
441459
};
442460
Cow::Owned(params_string)

crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP031_0.py.snap

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,12 +1142,16 @@ UP031_0.py:140:7: UP031 [*] Use format specifiers instead of percent format
11421142
140 |-print("%2X" % 1)
11431143
140 |+print("{:2X}".format(1))
11441144
141 141 | print("%02X" % 1)
1145+
142 142 |
1146+
143 143 | # UP031 (no longer false negatives, but offer no fix because of more complex syntax)
11451147

11461148
UP031_0.py:141:7: UP031 [*] Use format specifiers instead of percent format
11471149
|
11481150
140 | print("%2X" % 1)
11491151
141 | print("%02X" % 1)
11501152
| ^^^^^^^^^^ UP031
1153+
142 |
1154+
143 | # UP031 (no longer false negatives, but offer no fix because of more complex syntax)
11511155
|
11521156
= help: Replace with format specifiers
11531157

@@ -1157,3 +1161,6 @@ UP031_0.py:141:7: UP031 [*] Use format specifiers instead of percent format
11571161
140 140 | print("%2X" % 1)
11581162
141 |-print("%02X" % 1)
11591163
141 |+print("{:02X}".format(1))
1164+
142 142 |
1165+
143 143 | # UP031 (no longer false negatives, but offer no fix because of more complex syntax)
1166+
144 144 |

0 commit comments

Comments
 (0)