Skip to content

Commit ae26fa0

Browse files
authored
[flake8-pyi] Preserve inline comment in ellipsis removal (PYI013) (#19399)
## Summary Fixes #19385. Based on [unnecessary-placeholder (PIE790)](https://docs.astral.sh/ruff/rules/unnecessary-placeholder/) behavior, [ellipsis-in-non-empty-class-body (PYI013)](https://docs.astral.sh/ruff/rules/ellipsis-in-non-empty-class-body/) now safely preserve inline comment on ellipsis removal. ## Test Plan A new test class was added: ```python class NonEmptyChildWithInlineComment: value: int ... # preserve me ``` with the following expected fix: ```python class NonEmptyChildWithInlineComment: value: int # preserve me ```
1 parent 88a6799 commit ae26fa0

File tree

5 files changed

+88
-25
lines changed

5 files changed

+88
-25
lines changed

crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI013.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ def __init__():
3939
pass
4040

4141

42+
class NonEmptyChildWithInlineComment:
43+
value: int
44+
... # preserve me
45+
46+
4247
class EmptyClass:
4348
...
4449

crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI013.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ class NonEmptyWithInit:
3838
def __init__():
3939
pass
4040

41+
class NonEmptyChildWithInlineComment:
42+
value: int
43+
... # preserve me
44+
4145
# Not violations
4246

4347
class EmptyClass: ...

crates/ruff_linter/src/rules/flake8_pyi/rules/ellipsis_in_non_empty_class_body.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use ruff_macros::{ViolationMetadata, derive_message_formats};
2+
use ruff_python_ast::whitespace::trailing_comment_start_offset;
23
use ruff_python_ast::{Stmt, StmtExpr};
34
use ruff_text_size::Ranged;
45

56
use crate::checkers::ast::Checker;
67
use crate::fix;
7-
use crate::{Fix, FixAvailability, Violation};
8+
use crate::{Edit, Fix, FixAvailability, Violation};
89

910
/// ## What it does
1011
/// Removes ellipses (`...`) in otherwise non-empty class bodies.
@@ -50,15 +51,21 @@ pub(crate) fn ellipsis_in_non_empty_class_body(checker: &Checker, body: &[Stmt])
5051
}
5152

5253
for stmt in body {
53-
let Stmt::Expr(StmtExpr { value, .. }) = &stmt else {
54+
let Stmt::Expr(StmtExpr { value, .. }) = stmt else {
5455
continue;
5556
};
5657

5758
if value.is_ellipsis_literal_expr() {
5859
let mut diagnostic =
5960
checker.report_diagnostic(EllipsisInNonEmptyClassBody, stmt.range());
60-
let edit =
61-
fix::edits::delete_stmt(stmt, Some(stmt), checker.locator(), checker.indexer());
61+
62+
// Try to preserve trailing comment if it exists
63+
let edit = if let Some(index) = trailing_comment_start_offset(stmt, checker.source()) {
64+
Edit::range_deletion(stmt.range().add_end(index))
65+
} else {
66+
fix::edits::delete_stmt(stmt, Some(stmt), checker.locator(), checker.indexer())
67+
};
68+
6269
diagnostic.set_fix(Fix::safe_edit(edit).isolate(Checker::isolation(
6370
checker.semantic().current_statement_id(),
6471
)));

crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI013_PYI013.py.snap

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,22 @@ PYI013.py:36:5: PYI013 [*] Non-empty class body must not contain `...`
145145
37 36 |
146146
38 37 | def __init__():
147147
39 38 | pass
148+
149+
PYI013.py:44:5: PYI013 [*] Non-empty class body must not contain `...`
150+
|
151+
42 | class NonEmptyChildWithInlineComment:
152+
43 | value: int
153+
44 | ... # preserve me
154+
| ^^^ PYI013
155+
|
156+
= help: Remove unnecessary `...`
157+
158+
Safe fix
159+
41 41 |
160+
42 42 | class NonEmptyChildWithInlineComment:
161+
43 43 | value: int
162+
44 |- ... # preserve me
163+
44 |+ # preserve me
164+
45 45 |
165+
46 46 |
166+
47 47 | class EmptyClass:

crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI013_PYI013.pyi.snap

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ PYI013.pyi:5:5: PYI013 [*] Non-empty class body must not contain `...`
1717
3 3 | class OneAttributeClass:
1818
4 4 | value: int
1919
5 |- ... # Error
20-
6 5 |
21-
7 6 | class OneAttributeClass2:
22-
8 7 | ... # Error
20+
5 |+ # Error
21+
6 6 |
22+
7 7 | class OneAttributeClass2:
23+
8 8 | ... # Error
2324

2425
PYI013.pyi:8:5: PYI013 [*] Non-empty class body must not contain `...`
2526
|
@@ -35,9 +36,10 @@ PYI013.pyi:8:5: PYI013 [*] Non-empty class body must not contain `...`
3536
6 6 |
3637
7 7 | class OneAttributeClass2:
3738
8 |- ... # Error
38-
9 8 | value: int
39-
10 9 |
40-
11 10 | class MyClass:
39+
8 |+ # Error
40+
9 9 | value: int
41+
10 10 |
42+
11 11 | class MyClass:
4143

4244
PYI013.pyi:12:5: PYI013 [*] Non-empty class body must not contain `...`
4345
|
@@ -91,9 +93,10 @@ PYI013.pyi:17:5: PYI013 [*] Non-empty class body must not contain `...`
9193
15 15 | class TwoEllipsesClass:
9294
16 16 | ...
9395
17 |- ... # Error
94-
18 17 |
95-
19 18 | class DocstringClass:
96-
20 19 | """
96+
17 |+ # Error
97+
18 18 |
98+
19 19 | class DocstringClass:
99+
20 20 | """
97100

98101
PYI013.pyi:24:5: PYI013 [*] Non-empty class body must not contain `...`
99102
|
@@ -111,9 +114,10 @@ PYI013.pyi:24:5: PYI013 [*] Non-empty class body must not contain `...`
111114
22 22 | """
112115
23 23 |
113116
24 |- ... # Error
114-
25 24 |
115-
26 25 | class NonEmptyChild(Exception):
116-
27 26 | value: int
117+
24 |+ # Error
118+
25 25 |
119+
26 26 | class NonEmptyChild(Exception):
120+
27 27 | value: int
117121

118122
PYI013.pyi:28:5: PYI013 [*] Non-empty class body must not contain `...`
119123
|
@@ -131,9 +135,10 @@ PYI013.pyi:28:5: PYI013 [*] Non-empty class body must not contain `...`
131135
26 26 | class NonEmptyChild(Exception):
132136
27 27 | value: int
133137
28 |- ... # Error
134-
29 28 |
135-
30 29 | class NonEmptyChild2(Exception):
136-
31 30 | ... # Error
138+
28 |+ # Error
139+
29 29 |
140+
30 30 | class NonEmptyChild2(Exception):
141+
31 31 | ... # Error
137142

138143
PYI013.pyi:31:5: PYI013 [*] Non-empty class body must not contain `...`
139144
|
@@ -149,9 +154,10 @@ PYI013.pyi:31:5: PYI013 [*] Non-empty class body must not contain `...`
149154
29 29 |
150155
30 30 | class NonEmptyChild2(Exception):
151156
31 |- ... # Error
152-
32 31 | value: int
153-
33 32 |
154-
34 33 | class NonEmptyWithInit:
157+
31 |+ # Error
158+
32 32 | value: int
159+
33 33 |
160+
34 34 | class NonEmptyWithInit:
155161

156162
PYI013.pyi:36:5: PYI013 [*] Non-empty class body must not contain `...`
157163
|
@@ -169,6 +175,28 @@ PYI013.pyi:36:5: PYI013 [*] Non-empty class body must not contain `...`
169175
34 34 | class NonEmptyWithInit:
170176
35 35 | value: int
171177
36 |- ... # Error
172-
37 36 |
173-
38 37 | def __init__():
174-
39 38 | pass
178+
36 |+ # Error
179+
37 37 |
180+
38 38 | def __init__():
181+
39 39 | pass
182+
183+
PYI013.pyi:43:5: PYI013 [*] Non-empty class body must not contain `...`
184+
|
185+
41 | class NonEmptyChildWithInlineComment:
186+
42 | value: int
187+
43 | ... # preserve me
188+
| ^^^ PYI013
189+
44 |
190+
45 | # Not violations
191+
|
192+
= help: Remove unnecessary `...`
193+
194+
Safe fix
195+
40 40 |
196+
41 41 | class NonEmptyChildWithInlineComment:
197+
42 42 | value: int
198+
43 |- ... # preserve me
199+
43 |+ # preserve me
200+
44 44 |
201+
45 45 | # Not violations
202+
46 46 |

0 commit comments

Comments
 (0)