diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF022.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF022.py index e004562f99d02..2c192bad7aef1 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF022.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF022.py @@ -250,6 +250,23 @@ , ) +__all__ = ( # comment about the opening paren + # multiline strange comment 0a + # multiline strange comment 0b + "foo" # inline comment about foo + # multiline strange comment 1a + # multiline strange comment 1b + , # comment about the comma?? + # comment about bar part a + # comment about bar part b + "bar" # inline comment about bar + # strange multiline comment comment 2a + # strange multiline comment 2b + , + # strange multiline comment 3a + # strange multiline comment 3b +) # comment about the closing paren + ################################### # These should all not get flagged: ################################### diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF023.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF023.py index f2c4383f1d32c..c77446056c69f 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF023.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF023.py @@ -188,6 +188,10 @@ class BezierBuilder4: , ) + __slots__ = {"foo", "bar", + "baz", "bingo" + } + ################################### # These should all not get flagged: ################################### diff --git a/crates/ruff_linter/src/rules/ruff/rules/sequence_sorting.rs b/crates/ruff_linter/src/rules/ruff/rules/sequence_sorting.rs index f75fecd730a88..c84b00ce4a124 100644 --- a/crates/ruff_linter/src/rules/ruff/rules/sequence_sorting.rs +++ b/crates/ruff_linter/src/rules/ruff/rules/sequence_sorting.rs @@ -895,6 +895,27 @@ fn multiline_string_sequence_postlude<'a>( }; let postlude = locator.slice(TextRange::new(postlude_start, dunder_all_range_end)); + // If the postlude consists solely of a closing parenthesis + // (not preceded by any whitespace/newlines), + // plus possibly a single trailing comma prior to the parenthesis, + // fixup the postlude so that the parenthesis appears on its own line, + // and so that the final item has a trailing comma. + // This produces formatting more similar + // to that which the formatter would produce. + if postlude.len() <= 2 { + let mut reversed_postlude_chars = postlude.chars().rev(); + if let Some(closing_paren @ (')' | '}' | ']')) = reversed_postlude_chars.next() { + if reversed_postlude_chars.next().map_or(true, |c| c == ',') { + return Cow::Owned(format!(",{newline}{leading_indent}{closing_paren}")); + } + } + } + + let newline_chars = ['\r', '\n']; + if !postlude.starts_with(newline_chars) { + return Cow::Borrowed(postlude); + } + // The rest of this function uses heuristics to // avoid very long indents for the closing paren // that don't match the style for the rest of the @@ -920,10 +941,6 @@ fn multiline_string_sequence_postlude<'a>( // "y", // ] // ``` - let newline_chars = ['\r', '\n']; - if !postlude.starts_with(newline_chars) { - return Cow::Borrowed(postlude); - } if TextSize::of(leading_indentation( postlude.trim_start_matches(newline_chars), )) <= TextSize::of(item_indent) @@ -931,7 +948,7 @@ fn multiline_string_sequence_postlude<'a>( return Cow::Borrowed(postlude); } let trimmed_postlude = postlude.trim_start(); - if trimmed_postlude.starts_with([']', ')']) { + if trimmed_postlude.starts_with([']', ')', '}']) { return Cow::Owned(format!("{newline}{leading_indent}{trimmed_postlude}")); } Cow::Borrowed(postlude) diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF022_RUF022.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF022_RUF022.py.snap index 891f2a80ccef5..c886b91ccf04d 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF022_RUF022.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF022_RUF022.py.snap @@ -386,18 +386,24 @@ RUF022.py:54:11: RUF022 [*] `__all__` is not sorted 76 70 | "SUNDAY", 77 71 | "THURSDAY", 78 72 | "TUESDAY", - 73 |+ "WEDNESDAY", +79 |- "TextCalendar", +80 73 | "WEDNESDAY", 74 |+ "Calendar", 75 |+ "Day", 76 |+ "HTMLCalendar", 77 |+ "IllegalMonthError", 78 |+ "LocaleHTMLCalendar", 79 |+ "Month", -79 80 | "TextCalendar", -80 |- "WEDNESDAY", + 80 |+ "TextCalendar", 81 81 | "calendar", 82 82 | "timegm", 83 83 | "weekday", +84 |- "weekheader"] + 84 |+ "weekheader", + 85 |+] +85 86 | +86 87 | ########################################## +87 88 | # Messier multiline __all__ definitions... RUF022.py:91:11: RUF022 [*] `__all__` is not sorted | @@ -559,10 +565,11 @@ RUF022.py:110:11: RUF022 [*] `__all__` is not sorted 151 |+ "register_error", 152 |+ "replace_errors", 153 |+ "strict_errors", - 154 |+ "xmlcharrefreplace_errors"] -124 155 | -125 156 | __all__: tuple[str, ...] = ( # a comment about the opening paren -126 157 | # multiline comment about "bbb" part 1 + 154 |+ "xmlcharrefreplace_errors", + 155 |+] +124 156 | +125 157 | __all__: tuple[str, ...] = ( # a comment about the opening paren +126 158 | # multiline comment about "bbb" part 1 RUF022.py:125:28: RUF022 [*] `__all__` is not sorted | @@ -918,13 +925,13 @@ RUF022.py:225:11: RUF022 [*] `__all__` is not sorted 223 223 | ############################################################ 224 224 | 225 225 | __all__ = ( -226 |- "loads", -227 |- "dumps",) 226 |+ "dumps", - 227 |+ "loads",) -228 228 | -229 229 | __all__ = [ -230 230 | "loads", +226 227 | "loads", +227 |- "dumps",) + 228 |+) +228 229 | +229 230 | __all__ = [ +230 231 | "loads", RUF022.py:229:11: RUF022 [*] `__all__` is not sorted | @@ -1002,7 +1009,7 @@ RUF022.py:243:11: RUF022 [*] `__all__` is not sorted 251 | | ) | |_^ RUF022 252 | -253 | ################################### +253 | __all__ = ( # comment about the opening paren | = help: Apply an isort-style sorting to `__all__` @@ -1021,4 +1028,53 @@ RUF022.py:243:11: RUF022 [*] `__all__` is not sorted 250 249 | , 251 250 | ) +RUF022.py:253:11: RUF022 [*] `__all__` is not sorted + | +251 | ) +252 | +253 | __all__ = ( # comment about the opening paren + | ___________^ +254 | | # multiline strange comment 0a +255 | | # multiline strange comment 0b +256 | | "foo" # inline comment about foo +257 | | # multiline strange comment 1a +258 | | # multiline strange comment 1b +259 | | , # comment about the comma?? +260 | | # comment about bar part a +261 | | # comment about bar part b +262 | | "bar" # inline comment about bar +263 | | # strange multiline comment comment 2a +264 | | # strange multiline comment 2b +265 | | , +266 | | # strange multiline comment 3a +267 | | # strange multiline comment 3b +268 | | ) # comment about the closing paren + | |_^ RUF022 +269 | +270 | ################################### + | + = help: Apply an isort-style sorting to `__all__` + +ℹ Safe fix +251 251 | ) +252 252 | +253 253 | __all__ = ( # comment about the opening paren +254 |- # multiline strange comment 0a +255 |- # multiline strange comment 0b +256 |- "foo" # inline comment about foo +257 254 | # multiline strange comment 1a +258 255 | # multiline strange comment 1b +259 |- , # comment about the comma?? + 256 |+ # comment about the comma?? +260 257 | # comment about bar part a +261 258 | # comment about bar part b +262 |- "bar" # inline comment about bar + 259 |+ "bar", # inline comment about bar + 260 |+ # multiline strange comment 0a + 261 |+ # multiline strange comment 0b + 262 |+ "foo" # inline comment about foo +263 263 | # strange multiline comment comment 2a +264 264 | # strange multiline comment 2b +265 265 | , + diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF023_RUF023.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF023_RUF023.py.snap index 3b903394795af..2ec896e3081c3 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF023_RUF023.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF023_RUF023.py.snap @@ -564,10 +564,11 @@ RUF023.py:162:17: RUF023 [*] `BezierBuilder.__slots__` is not sorted 162 |+ __slots__ = ( 163 |+ 'canvas', 164 |+ 'xp', - 165 |+ 'yp',) -164 166 | -165 167 | class BezierBuilder2: -166 168 | __slots__ = {'xp', 'yp', + 165 |+ 'yp', + 166 |+ ) +164 167 | +165 168 | class BezierBuilder2: +166 169 | __slots__ = {'xp', 'yp', RUF023.py:166:17: RUF023 [*] `BezierBuilder2.__slots__` is not sorted | @@ -643,7 +644,7 @@ RUF023.py:181:17: RUF023 [*] `BezierBuilder4.__slots__` is not sorted 189 | | ) | |_____^ RUF023 190 | -191 | ################################### +191 | __slots__ = {"foo", "bar", | = help: Apply a natural sort to `BezierBuilder4.__slots__` @@ -662,4 +663,35 @@ RUF023.py:181:17: RUF023 [*] `BezierBuilder4.__slots__` is not sorted 188 187 | , 189 188 | ) +RUF023.py:191:17: RUF023 [*] `BezierBuilder4.__slots__` is not sorted + | +189 | ) +190 | +191 | __slots__ = {"foo", "bar", + | _________________^ +192 | | "baz", "bingo" +193 | | } + | |__________________^ RUF023 +194 | +195 | ################################### + | + = help: Apply a natural sort to `BezierBuilder4.__slots__` + +ℹ Safe fix +188 188 | , +189 189 | ) +190 190 | +191 |- __slots__ = {"foo", "bar", +192 |- "baz", "bingo" +193 |- } + 191 |+ __slots__ = { + 192 |+ "bar", + 193 |+ "baz", + 194 |+ "bingo", + 195 |+ "foo" + 196 |+ } +194 197 | +195 198 | ################################### +196 199 | # These should all not get flagged: +