Skip to content

Commit 4d8ccb6

Browse files
authored
RUF064: offer a safe fix for multi-digit zeros (#19847)
Fixes #19010 ## Summary See #19010. `0` was not considered a violation, but `000` was. The latter will now be fixed to `0o000`.
1 parent 8230b79 commit 4d8ccb6

File tree

3 files changed

+98
-1
lines changed

3 files changed

+98
-1
lines changed

crates/ruff_linter/resources/test/fixtures/ruff/RUF064.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,11 @@
5151

5252
os.fchmod(0, 256) # 0o400
5353
os.fchmod(0, 493) # 0o755
54+
55+
# https://github.com/astral-sh/ruff/issues/19010
56+
os.chmod("foo", 000) # Error
57+
os.chmod("foo", 0000) # Error
58+
59+
os.chmod("foo", 0b0) # Error
60+
os.chmod("foo", 0x0) # Error
61+
os.chmod("foo", 0) # Ok

crates/ruff_linter/src/rules/ruff/rules/non_octal_permissions.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,17 @@ use crate::{FixAvailability, Violation};
5757
/// original code really intended to use `0o256` (`u=w,g=rx,o=rw`) instead of
5858
/// `256`, this fix should not be accepted.
5959
///
60+
/// As a special case, zero is allowed to omit the `0o` prefix unless it has
61+
/// multiple digits:
62+
///
63+
/// ```python
64+
/// os.chmod("foo", 0) # Ok
65+
/// os.chmod("foo", 0o000) # Ok
66+
/// os.chmod("foo", 000) # Lint emitted and fix suggested
67+
/// ```
68+
///
69+
/// Ruff will suggest a safe fix for multi-digit zeros to add the `0o` prefix.
70+
///
6071
/// ## Fix availability
6172
///
6273
/// A fix is only available if the integer literal matches a set of common modes.
@@ -102,7 +113,14 @@ pub(crate) fn non_octal_permissions(checker: &Checker, call: &ExprCall) {
102113
let mut diagnostic = checker.report_diagnostic(NonOctalPermissions, mode_arg.range());
103114

104115
// Don't suggest a fix for 0x or 0b literals.
105-
if mode_literal.starts_with('0') {
116+
if mode_literal.starts_with("0x") || mode_literal.starts_with("0b") {
117+
return;
118+
}
119+
120+
if mode_literal.chars().all(|c| c == '0') {
121+
// Fix e.g. 000 as 0o000
122+
let edit = Edit::range_replacement(format!("0o{mode_literal}"), mode_arg.range());
123+
diagnostic.set_fix(Fix::safe_edit(edit));
106124
return;
107125
}
108126

crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF064_RUF064.py.snap

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,13 +348,17 @@ help: Replace with octal literal
348348
52 |-os.fchmod(0, 256) # 0o400
349349
52 |+os.fchmod(0, 0o400) # 0o400
350350
53 53 | os.fchmod(0, 493) # 0o755
351+
54 54 |
352+
55 55 | # https://github.com/astral-sh/ruff/issues/19010
351353

352354
RUF064 [*] Non-octal mode
353355
--> RUF064.py:53:14
354356
|
355357
52 | os.fchmod(0, 256) # 0o400
356358
53 | os.fchmod(0, 493) # 0o755
357359
| ^^^
360+
54 |
361+
55 | # https://github.com/astral-sh/ruff/issues/19010
358362
|
359363
help: Replace with octal literal
360364

@@ -364,3 +368,70 @@ help: Replace with octal literal
364368
52 52 | os.fchmod(0, 256) # 0o400
365369
53 |-os.fchmod(0, 493) # 0o755
366370
53 |+os.fchmod(0, 0o755) # 0o755
371+
54 54 |
372+
55 55 | # https://github.com/astral-sh/ruff/issues/19010
373+
56 56 | os.chmod("foo", 000) # Error
374+
375+
RUF064 [*] Non-octal mode
376+
--> RUF064.py:56:17
377+
|
378+
55 | # https://github.com/astral-sh/ruff/issues/19010
379+
56 | os.chmod("foo", 000) # Error
380+
| ^^^
381+
57 | os.chmod("foo", 0000) # Error
382+
|
383+
help: Replace with octal literal
384+
385+
Safe fix
386+
53 53 | os.fchmod(0, 493) # 0o755
387+
54 54 |
388+
55 55 | # https://github.com/astral-sh/ruff/issues/19010
389+
56 |-os.chmod("foo", 000) # Error
390+
56 |+os.chmod("foo", 0o000) # Error
391+
57 57 | os.chmod("foo", 0000) # Error
392+
58 58 |
393+
59 59 | os.chmod("foo", 0b0) # Error
394+
395+
RUF064 [*] Non-octal mode
396+
--> RUF064.py:57:17
397+
|
398+
55 | # https://github.com/astral-sh/ruff/issues/19010
399+
56 | os.chmod("foo", 000) # Error
400+
57 | os.chmod("foo", 0000) # Error
401+
| ^^^^
402+
58 |
403+
59 | os.chmod("foo", 0b0) # Error
404+
|
405+
help: Replace with octal literal
406+
407+
Safe fix
408+
54 54 |
409+
55 55 | # https://github.com/astral-sh/ruff/issues/19010
410+
56 56 | os.chmod("foo", 000) # Error
411+
57 |-os.chmod("foo", 0000) # Error
412+
57 |+os.chmod("foo", 0o0000) # Error
413+
58 58 |
414+
59 59 | os.chmod("foo", 0b0) # Error
415+
60 60 | os.chmod("foo", 0x0) # Error
416+
417+
RUF064 Non-octal mode
418+
--> RUF064.py:59:17
419+
|
420+
57 | os.chmod("foo", 0000) # Error
421+
58 |
422+
59 | os.chmod("foo", 0b0) # Error
423+
| ^^^
424+
60 | os.chmod("foo", 0x0) # Error
425+
61 | os.chmod("foo", 0) # Ok
426+
|
427+
help: Replace with octal literal
428+
429+
RUF064 Non-octal mode
430+
--> RUF064.py:60:17
431+
|
432+
59 | os.chmod("foo", 0b0) # Error
433+
60 | os.chmod("foo", 0x0) # Error
434+
| ^^^
435+
61 | os.chmod("foo", 0) # Ok
436+
|
437+
help: Replace with octal literal

0 commit comments

Comments
 (0)