From fb0d790af52d0b860105762ac1aec2bb047e9ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Tue, 22 Aug 2023 11:37:21 +0200 Subject: [PATCH 01/18] Handle one import --- .../resources/test/fixtures/flake8_bugbear/B006_B008.py | 8 ++++++++ .../flake8_bugbear/rules/mutable_argument_default.rs | 3 +++ 2 files changed, 11 insertions(+) diff --git a/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py b/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py index 4b3492bb4ceaa..f45beb2cb637e 100644 --- a/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py +++ b/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py @@ -304,3 +304,11 @@ def single_line_func_wrong(value: dict[str, str] = { def single_line_func_wrong(value: dict[str, str] = {}) \ : \ """Docstring""" + + +def import_module_wrong(value: dict[str, str] = {}): + import os + + +def from_import_module_wrong(value: dict[str, str] = {}): + from os import path diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs index bee4481a28e80..152626a5f5a36 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs @@ -163,6 +163,9 @@ fn move_initialization( // If the docstring is the only statement, insert _before_ it. Edit::insertion(content, locator.full_line_end(statement.end())) } + } else if statement.is_import_stmt() || statement.is_import_from_stmt() { + // If the first statements in the function are an import, insert _after_ it. + Edit::insertion(content, locator.full_line_end(statement.end())) } else { // Otherwise, insert before the first statement. let at = locator.line_start(statement.start()); From ef25c5c0f1f420b0ab8222a53918f510b5aad410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Tue, 22 Aug 2023 11:44:32 +0200 Subject: [PATCH 02/18] Handle multiple imports --- .../test/fixtures/flake8_bugbear/B006_B008.py | 15 +++++++++++++++ .../rules/mutable_argument_default.rs | 10 +++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py b/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py index f45beb2cb637e..358040aaf733d 100644 --- a/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py +++ b/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py @@ -310,5 +310,20 @@ def import_module_wrong(value: dict[str, str] = {}): import os +def import_modules_wrong(value: dict[str, str] = {}): + import os + import sys + + def from_import_module_wrong(value: dict[str, str] = {}): from os import path + + +def from_imports_module_wrong(value: dict[str, str] = {}): + from os import path + from sys import version_info + + +def from_imports_module_wrong(value: dict[str, str] = {}): + import os + from sys import version_info diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs index 152626a5f5a36..24846e223882a 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs @@ -164,8 +164,16 @@ fn move_initialization( Edit::insertion(content, locator.full_line_end(statement.end())) } } else if statement.is_import_stmt() || statement.is_import_from_stmt() { + let mut pos = statement.end(); + for stmt in function_def.body.iter() { + if stmt.is_import_stmt() || stmt.is_import_from_stmt() { + pos = stmt.end(); + } else { + break + } + } + Edit::insertion(content, locator.full_line_end(pos)) // If the first statements in the function are an import, insert _after_ it. - Edit::insertion(content, locator.full_line_end(statement.end())) } else { // Otherwise, insert before the first statement. let at = locator.line_start(statement.start()); From 7e1f73b40df9863d4a2c9e30f8f9b00156089bf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Wed, 23 Aug 2023 17:05:13 +0200 Subject: [PATCH 03/18] Use initialization_pos --- .../rules/mutable_argument_default.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs index 24846e223882a..256ca719e55ba 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs @@ -150,7 +150,7 @@ fn move_initialization( // Indent the edit to match the body indentation. let content = textwrap::indent(&content, indentation).to_string(); - let initialization_edit = if is_docstring_stmt(statement) { + let initialization_pos = if is_docstring_stmt(statement) { // If the first statement in the function is a docstring, insert _after_ it. if let Some(statement) = body.next() { // If there's a second statement, insert _before_ it, but ensure this isn't a @@ -158,27 +158,26 @@ fn move_initialization( if indexer.preceded_by_multi_statement_line(statement, locator) { return None; } - Edit::insertion(content, locator.line_start(statement.start())) + locator.line_start(statement.start()) } else { // If the docstring is the only statement, insert _before_ it. - Edit::insertion(content, locator.full_line_end(statement.end())) + locator.full_line_end(statement.end()) } } else if statement.is_import_stmt() || statement.is_import_from_stmt() { let mut pos = statement.end(); - for stmt in function_def.body.iter() { + for stmt in body { if stmt.is_import_stmt() || stmt.is_import_from_stmt() { pos = stmt.end(); } else { break } } - Edit::insertion(content, locator.full_line_end(pos)) - // If the first statements in the function are an import, insert _after_ it. + locator.full_line_end(pos) } else { // Otherwise, insert before the first statement. - let at = locator.line_start(statement.start()); - Edit::insertion(content, at) + locator.line_start(statement.start()) }; + let initialization_edit = Edit::insertion(content, initialization_pos); Some(Fix::manual_edits(default_edit, [initialization_edit])) } From 5dbc0503a3981fd122065ffb6812893711688f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Wed, 23 Aug 2023 22:06:24 +0200 Subject: [PATCH 04/18] Find first pos which is not docstring or import --- .../rules/mutable_argument_default.rs | 52 ++++++++----------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs index 256ca719e55ba..da3cc498a214f 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs @@ -122,9 +122,9 @@ fn move_initialization( indexer: &Indexer, generator: Generator, ) -> Option { - let mut body = function_def.body.iter(); + let mut body = function_def.body.iter().peekable(); - let statement = body.next()?; + let statement = body.peek()?; if indexer.preceded_by_multi_statement_line(statement, locator) { return None; } @@ -150,34 +150,28 @@ fn move_initialization( // Indent the edit to match the body indentation. let content = textwrap::indent(&content, indentation).to_string(); - let initialization_pos = if is_docstring_stmt(statement) { - // If the first statement in the function is a docstring, insert _after_ it. - if let Some(statement) = body.next() { - // If there's a second statement, insert _before_ it, but ensure this isn't a - // multi-statement line. - if indexer.preceded_by_multi_statement_line(statement, locator) { - return None; - } - locator.line_start(statement.start()) - } else { - // If the docstring is the only statement, insert _before_ it. - locator.full_line_end(statement.end()) - } - } else if statement.is_import_stmt() || statement.is_import_from_stmt() { - let mut pos = statement.end(); - for stmt in body { - if stmt.is_import_stmt() || stmt.is_import_from_stmt() { - pos = stmt.end(); + // Find the position to insert the initialization after docstring and imports + let mut pos = locator.line_start(statement.start()); + while let Some(stmt) = body.next() { + if is_docstring_stmt(stmt) { + // // If the statement in the function is a docstring, insert _after_ it. + if let Some(stmt) = body.peek() { + if indexer.preceded_by_multi_statement_line(stmt, locator) { + return None; + } + pos = locator.line_start(stmt.start()); } else { - break + // If the docstring is the only statement, insert _before_ it. + pos = locator.full_line_end(stmt.end()); } - } - locator.full_line_end(pos) - } else { - // Otherwise, insert before the first statement. - locator.line_start(statement.start()) - }; - - let initialization_edit = Edit::insertion(content, initialization_pos); + } else if stmt.is_import_stmt() || stmt.is_import_from_stmt() { + // If the statement in the function is an import, insert _after_ it. + pos = locator.full_line_end(stmt.end()); + } else { + // Otherwise, insert before the first statement. + break + }; + } + let initialization_edit = Edit::insertion(content, pos); Some(Fix::manual_edits(default_edit, [initialization_edit])) } From 4fbf6352bf292d189fb15239be8a797ed61001b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Wed, 23 Aug 2023 22:15:35 +0200 Subject: [PATCH 05/18] Rename back to statement --- .../rules/mutable_argument_default.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs index da3cc498a214f..ece26cc554e8d 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs @@ -152,21 +152,21 @@ fn move_initialization( // Find the position to insert the initialization after docstring and imports let mut pos = locator.line_start(statement.start()); - while let Some(stmt) = body.next() { - if is_docstring_stmt(stmt) { + while let Some(statement) = body.next() { + if is_docstring_stmt(statement) { // // If the statement in the function is a docstring, insert _after_ it. - if let Some(stmt) = body.peek() { - if indexer.preceded_by_multi_statement_line(stmt, locator) { + if let Some(statement) = body.peek() { + if indexer.preceded_by_multi_statement_line(statement, locator) { return None; } - pos = locator.line_start(stmt.start()); + pos = locator.line_start(statement.start()); } else { // If the docstring is the only statement, insert _before_ it. - pos = locator.full_line_end(stmt.end()); + pos = locator.full_line_end(statement.end()); } - } else if stmt.is_import_stmt() || stmt.is_import_from_stmt() { + } else if statement.is_import_stmt() || statement.is_import_from_stmt() { // If the statement in the function is an import, insert _after_ it. - pos = locator.full_line_end(stmt.end()); + pos = locator.full_line_end(statement.end()); } else { // Otherwise, insert before the first statement. break From 923f86adf8e5aa395424682d78dd21db8bb8d448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Wed, 23 Aug 2023 22:20:13 +0200 Subject: [PATCH 06/18] Add more tests --- .../test/fixtures/flake8_bugbear/B006_B008.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py b/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py index 358040aaf733d..4d9b8d978353b 100644 --- a/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py +++ b/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py @@ -310,9 +310,15 @@ def import_module_wrong(value: dict[str, str] = {}): import os +def import_module_with_values_wrong(value: dict[str, str] = {}): + import os + return 2 + + def import_modules_wrong(value: dict[str, str] = {}): import os import sys + import itertools def from_import_module_wrong(value: dict[str, str] = {}): @@ -324,6 +330,11 @@ def from_imports_module_wrong(value: dict[str, str] = {}): from sys import version_info -def from_imports_module_wrong(value: dict[str, str] = {}): +def import_and_from_imports_module_wrong(value: dict[str, str] = {}): import os from sys import version_info + + +def import_docstring_module_wrong(value: dict[str, str] = {}): + """Docstring""" + import os From b245d128201d96f2a774c400300fb1f8a149a897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Wed, 23 Aug 2023 22:34:02 +0200 Subject: [PATCH 07/18] Update snapshot --- ...ke8_bugbear__tests__B006_B006_B008.py.snap | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_B008.py.snap b/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_B008.py.snap index 038b21f9a2303..49c1099018f0f 100644 --- a/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_B008.py.snap +++ b/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_B008.py.snap @@ -476,4 +476,158 @@ B006_B008.py:304:52: B006 Do not use mutable data structures for argument defaul | = help: Replace with `None`; initialize within function +B006_B008.py:309:49: B006 [*] Do not use mutable data structures for argument defaults + | +309 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +310 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +306 306 | """Docstring""" +307 307 | +308 308 | +309 |-def import_module_wrong(value: dict[str, str] = {}): + 309 |+def import_module_wrong(value: dict[str, str] = None): +310 310 | import os + 311 |+ if value is None: + 312 |+ value = {} +311 313 | +312 314 | +313 315 | def import_module_with_values_wrong(value: dict[str, str] = {}): + +B006_B008.py:313:61: B006 [*] Do not use mutable data structures for argument defaults + | +313 | def import_module_with_values_wrong(value: dict[str, str] = {}): + | ^^ B006 +314 | import os +315 | return 2 + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +310 310 | import os +311 311 | +312 312 | +313 |-def import_module_with_values_wrong(value: dict[str, str] = {}): + 313 |+def import_module_with_values_wrong(value: dict[str, str] = None): +314 314 | import os + 315 |+ if value is None: + 316 |+ value = {} +315 317 | return 2 +316 318 | +317 319 | + +B006_B008.py:318:50: B006 [*] Do not use mutable data structures for argument defaults + | +318 | def import_modules_wrong(value: dict[str, str] = {}): + | ^^ B006 +319 | import os +320 | import sys + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +315 315 | return 2 +316 316 | +317 317 | +318 |-def import_modules_wrong(value: dict[str, str] = {}): + 318 |+def import_modules_wrong(value: dict[str, str] = None): +319 319 | import os +320 320 | import sys +321 321 | import itertools + 322 |+ if value is None: + 323 |+ value = {} +322 324 | +323 325 | +324 326 | def from_import_module_wrong(value: dict[str, str] = {}): + +B006_B008.py:324:54: B006 [*] Do not use mutable data structures for argument defaults + | +324 | def from_import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +325 | from os import path + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +321 321 | import itertools +322 322 | +323 323 | +324 |-def from_import_module_wrong(value: dict[str, str] = {}): + 324 |+def from_import_module_wrong(value: dict[str, str] = None): +325 325 | from os import path + 326 |+ if value is None: + 327 |+ value = {} +326 328 | +327 329 | +328 330 | def from_imports_module_wrong(value: dict[str, str] = {}): + +B006_B008.py:328:55: B006 [*] Do not use mutable data structures for argument defaults + | +328 | def from_imports_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +329 | from os import path +330 | from sys import version_info + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +325 325 | from os import path +326 326 | +327 327 | +328 |-def from_imports_module_wrong(value: dict[str, str] = {}): + 328 |+def from_imports_module_wrong(value: dict[str, str] = None): +329 329 | from os import path +330 330 | from sys import version_info + 331 |+ if value is None: + 332 |+ value = {} +331 333 | +332 334 | +333 335 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + +B006_B008.py:333:66: B006 [*] Do not use mutable data structures for argument defaults + | +333 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +334 | import os +335 | from sys import version_info + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +330 330 | from sys import version_info +331 331 | +332 332 | +333 |-def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + 333 |+def import_and_from_imports_module_wrong(value: dict[str, str] = None): +334 334 | import os +335 335 | from sys import version_info + 336 |+ if value is None: + 337 |+ value = {} +336 338 | +337 339 | +338 340 | def import_docstring_module_wrong(value: dict[str, str] = {}): + +B006_B008.py:338:59: B006 [*] Do not use mutable data structures for argument defaults + | +338 | def import_docstring_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +339 | """Docstring""" +340 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +335 335 | from sys import version_info +336 336 | +337 337 | +338 |-def import_docstring_module_wrong(value: dict[str, str] = {}): + 338 |+def import_docstring_module_wrong(value: dict[str, str] = None): +339 339 | """Docstring""" +340 340 | import os + 341 |+ if value is None: + 342 |+ value = {} + From 135684971603cd04120d85f110e8be98903e9f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Wed, 23 Aug 2023 22:49:59 +0200 Subject: [PATCH 08/18] Fix lint --- .../src/rules/flake8_bugbear/rules/mutable_argument_default.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs index ece26cc554e8d..a824ecbc14d61 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs @@ -169,7 +169,7 @@ fn move_initialization( pos = locator.full_line_end(statement.end()); } else { // Otherwise, insert before the first statement. - break + break; }; } let initialization_edit = Edit::insertion(content, pos); From a52e6d6617a27f953cee33a13d146f17347c8a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Sun, 3 Sep 2023 13:16:00 +0200 Subject: [PATCH 09/18] Update snap --- ...ke8_bugbear__tests__B006_B006_B008.py.snap | 198 +++++++++--------- 1 file changed, 99 insertions(+), 99 deletions(-) diff --git a/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_B008.py.snap b/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_B008.py.snap index 117b2ee1d9287..3ef25f51305bd 100644 --- a/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_B008.py.snap +++ b/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_B008.py.snap @@ -476,158 +476,158 @@ B006_B008.py:308:52: B006 Do not use mutable data structures for argument defaul | = help: Replace with `None`; initialize within function -B006_B008.py:309:49: B006 [*] Do not use mutable data structures for argument defaults +B006_B008.py:313:49: B006 [*] Do not use mutable data structures for argument defaults | -309 | def import_module_wrong(value: dict[str, str] = {}): +313 | def import_module_wrong(value: dict[str, str] = {}): | ^^ B006 -310 | import os - | - = help: Replace with `None`; initialize within function - -ℹ Possible fix -306 306 | """Docstring""" -307 307 | -308 308 | -309 |-def import_module_wrong(value: dict[str, str] = {}): - 309 |+def import_module_wrong(value: dict[str, str] = None): -310 310 | import os - 311 |+ if value is None: - 312 |+ value = {} -311 313 | -312 314 | -313 315 | def import_module_with_values_wrong(value: dict[str, str] = {}): - -B006_B008.py:313:61: B006 [*] Do not use mutable data structures for argument defaults - | -313 | def import_module_with_values_wrong(value: dict[str, str] = {}): - | ^^ B006 314 | import os -315 | return 2 | = help: Replace with `None`; initialize within function ℹ Possible fix -310 310 | import os +310 310 | """Docstring""" 311 311 | 312 312 | -313 |-def import_module_with_values_wrong(value: dict[str, str] = {}): - 313 |+def import_module_with_values_wrong(value: dict[str, str] = None): +313 |-def import_module_wrong(value: dict[str, str] = {}): + 313 |+def import_module_wrong(value: dict[str, str] = None): 314 314 | import os 315 |+ if value is None: 316 |+ value = {} -315 317 | return 2 +315 317 | 316 318 | -317 319 | +317 319 | def import_module_with_values_wrong(value: dict[str, str] = {}): -B006_B008.py:318:50: B006 [*] Do not use mutable data structures for argument defaults +B006_B008.py:317:61: B006 [*] Do not use mutable data structures for argument defaults | -318 | def import_modules_wrong(value: dict[str, str] = {}): - | ^^ B006 -319 | import os -320 | import sys +317 | def import_module_with_values_wrong(value: dict[str, str] = {}): + | ^^ B006 +318 | import os +319 | return 2 | = help: Replace with `None`; initialize within function ℹ Possible fix -315 315 | return 2 +314 314 | import os +315 315 | 316 316 | -317 317 | -318 |-def import_modules_wrong(value: dict[str, str] = {}): - 318 |+def import_modules_wrong(value: dict[str, str] = None): -319 319 | import os -320 320 | import sys -321 321 | import itertools - 322 |+ if value is None: - 323 |+ value = {} -322 324 | -323 325 | -324 326 | def from_import_module_wrong(value: dict[str, str] = {}): - -B006_B008.py:324:54: B006 [*] Do not use mutable data structures for argument defaults - | -324 | def from_import_module_wrong(value: dict[str, str] = {}): - | ^^ B006 -325 | from os import path +317 |-def import_module_with_values_wrong(value: dict[str, str] = {}): + 317 |+def import_module_with_values_wrong(value: dict[str, str] = None): +318 318 | import os + 319 |+ if value is None: + 320 |+ value = {} +319 321 | return 2 +320 322 | +321 323 | + +B006_B008.py:322:50: B006 [*] Do not use mutable data structures for argument defaults + | +322 | def import_modules_wrong(value: dict[str, str] = {}): + | ^^ B006 +323 | import os +324 | import sys | = help: Replace with `None`; initialize within function ℹ Possible fix -321 321 | import itertools -322 322 | -323 323 | -324 |-def from_import_module_wrong(value: dict[str, str] = {}): - 324 |+def from_import_module_wrong(value: dict[str, str] = None): -325 325 | from os import path +319 319 | return 2 +320 320 | +321 321 | +322 |-def import_modules_wrong(value: dict[str, str] = {}): + 322 |+def import_modules_wrong(value: dict[str, str] = None): +323 323 | import os +324 324 | import sys +325 325 | import itertools 326 |+ if value is None: 327 |+ value = {} 326 328 | 327 329 | -328 330 | def from_imports_module_wrong(value: dict[str, str] = {}): +328 330 | def from_import_module_wrong(value: dict[str, str] = {}): -B006_B008.py:328:55: B006 [*] Do not use mutable data structures for argument defaults +B006_B008.py:328:54: B006 [*] Do not use mutable data structures for argument defaults | -328 | def from_imports_module_wrong(value: dict[str, str] = {}): - | ^^ B006 +328 | def from_import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 329 | from os import path -330 | from sys import version_info | = help: Replace with `None`; initialize within function ℹ Possible fix -325 325 | from os import path +325 325 | import itertools 326 326 | 327 327 | -328 |-def from_imports_module_wrong(value: dict[str, str] = {}): - 328 |+def from_imports_module_wrong(value: dict[str, str] = None): +328 |-def from_import_module_wrong(value: dict[str, str] = {}): + 328 |+def from_import_module_wrong(value: dict[str, str] = None): 329 329 | from os import path -330 330 | from sys import version_info - 331 |+ if value is None: - 332 |+ value = {} + 330 |+ if value is None: + 331 |+ value = {} +330 332 | 331 333 | -332 334 | -333 335 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): +332 334 | def from_imports_module_wrong(value: dict[str, str] = {}): -B006_B008.py:333:66: B006 [*] Do not use mutable data structures for argument defaults +B006_B008.py:332:55: B006 [*] Do not use mutable data structures for argument defaults | -333 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): - | ^^ B006 -334 | import os -335 | from sys import version_info +332 | def from_imports_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +333 | from os import path +334 | from sys import version_info | = help: Replace with `None`; initialize within function ℹ Possible fix -330 330 | from sys import version_info +329 329 | from os import path +330 330 | 331 331 | -332 332 | -333 |-def import_and_from_imports_module_wrong(value: dict[str, str] = {}): - 333 |+def import_and_from_imports_module_wrong(value: dict[str, str] = None): -334 334 | import os -335 335 | from sys import version_info - 336 |+ if value is None: - 337 |+ value = {} +332 |-def from_imports_module_wrong(value: dict[str, str] = {}): + 332 |+def from_imports_module_wrong(value: dict[str, str] = None): +333 333 | from os import path +334 334 | from sys import version_info + 335 |+ if value is None: + 336 |+ value = {} +335 337 | 336 338 | -337 339 | -338 340 | def import_docstring_module_wrong(value: dict[str, str] = {}): +337 339 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): -B006_B008.py:338:59: B006 [*] Do not use mutable data structures for argument defaults +B006_B008.py:337:66: B006 [*] Do not use mutable data structures for argument defaults | -338 | def import_docstring_module_wrong(value: dict[str, str] = {}): - | ^^ B006 -339 | """Docstring""" -340 | import os +337 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +338 | import os +339 | from sys import version_info | = help: Replace with `None`; initialize within function ℹ Possible fix -335 335 | from sys import version_info +334 334 | from sys import version_info +335 335 | 336 336 | -337 337 | -338 |-def import_docstring_module_wrong(value: dict[str, str] = {}): - 338 |+def import_docstring_module_wrong(value: dict[str, str] = None): -339 339 | """Docstring""" -340 340 | import os - 341 |+ if value is None: - 342 |+ value = {} +337 |-def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + 337 |+def import_and_from_imports_module_wrong(value: dict[str, str] = None): +338 338 | import os +339 339 | from sys import version_info + 340 |+ if value is None: + 341 |+ value = {} +340 342 | +341 343 | +342 344 | def import_docstring_module_wrong(value: dict[str, str] = {}): + +B006_B008.py:342:59: B006 [*] Do not use mutable data structures for argument defaults + | +342 | def import_docstring_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +343 | """Docstring""" +344 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +339 339 | from sys import version_info +340 340 | +341 341 | +342 |-def import_docstring_module_wrong(value: dict[str, str] = {}): + 342 |+def import_docstring_module_wrong(value: dict[str, str] = None): +343 343 | """Docstring""" +344 344 | import os + 345 |+ if value is None: + 346 |+ value = {} From 0fdb1a4280dabd24cee445b9011b27b29428a2e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Sat, 16 Sep 2023 15:35:00 +0200 Subject: [PATCH 10/18] Update to work with existing tests --- .../rules/mutable_argument_default.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs index 802e5d4f7227f..a35ad1074689f 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/mutable_argument_default.rs @@ -165,27 +165,34 @@ fn move_initialization( content.push_str(stylist.line_ending().as_str()); // Indent the edit to match the body indentation. - let content = textwrap::indent(&content, indentation).to_string(); + let mut content = textwrap::indent(&content, indentation).to_string(); // Find the position to insert the initialization after docstring and imports let mut pos = locator.line_start(statement.start()); while let Some(statement) = body.next() { if is_docstring_stmt(statement) { - // // If the statement in the function is a docstring, insert _after_ it. + // If the statement in the function is a docstring, insert _after_ it. if let Some(statement) = body.peek() { + // If there's a second statement, insert _before_ it, but ensure this isn't a + // multi-statement line. if indexer.in_multi_statement_line(statement, locator) { return None; } pos = locator.line_start(statement.start()); + } else if locator.full_line_end(statement.end()) == locator.text_len() { + // If the statement is at the end of the file, without a trailing newline, insert + // _after_ it with an extra newline. + content = format!("{}{}", stylist.line_ending().as_str(), content); + pos = locator.full_line_end(statement.end()); + break; } else { - // If the docstring is the only statement, insert _before_ it. + // If the docstring is the only statement, insert _after_ it. pos = locator.full_line_end(statement.end()); } } else if statement.is_import_stmt() || statement.is_import_from_stmt() { // If the statement in the function is an import, insert _after_ it. pos = locator.full_line_end(statement.end()); } else { - // Otherwise, insert before the first statement. break; }; } From 996b65080e67cd00bb54769d546470323292f44e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Sat, 16 Sep 2023 15:40:36 +0200 Subject: [PATCH 11/18] Add B006 import tests --- .../fixtures/flake8_bugbear/B006_imports.py | 68 ++++ crates/ruff/src/rules/flake8_bugbear/mod.rs | 1 + ..._bugbear__tests__B006_B006_imports.py.snap | 310 ++++++++++++++++++ 3 files changed, 379 insertions(+) create mode 100644 crates/ruff/resources/test/fixtures/flake8_bugbear/B006_imports.py create mode 100644 crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_imports.py.snap diff --git a/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_imports.py b/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_imports.py new file mode 100644 index 0000000000000..dbd857f3bc222 --- /dev/null +++ b/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_imports.py @@ -0,0 +1,68 @@ +def import_module_wrong(value: dict[str, str] = {}): + import os + + +def import_module_with_values_wrong(value: dict[str, str] = {}): + import os + + return 2 + + +def import_modules_wrong(value: dict[str, str] = {}): + import os + import sys + import itertools + + +def from_import_module_wrong(value: dict[str, str] = {}): + from os import path + + +def from_imports_module_wrong(value: dict[str, str] = {}): + from os import path + from sys import version_info + + +def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + import os + from sys import version_info + + +def import_docstring_module_wrong(value: dict[str, str] = {}): + """Docstring""" + import os + + +def import_module_wrong(value: dict[str, str] = {}): + import os + + +def import_module_with_values_wrong(value: dict[str, str] = {}): + import os + + return 2 + + +def import_modules_wrong(value: dict[str, str] = {}): + import os + import sys + import itertools + + +def from_import_module_wrong(value: dict[str, str] = {}): + from os import path + + +def from_imports_module_wrong(value: dict[str, str] = {}): + from os import path + from sys import version_info + + +def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + import os + from sys import version_info + + +def import_docstring_module_wrong(value: dict[str, str] = {}): + """Docstring""" + import os diff --git a/crates/ruff/src/rules/flake8_bugbear/mod.rs b/crates/ruff/src/rules/flake8_bugbear/mod.rs index 71ec3b130a648..39ff4c0b079c8 100644 --- a/crates/ruff/src/rules/flake8_bugbear/mod.rs +++ b/crates/ruff/src/rules/flake8_bugbear/mod.rs @@ -36,6 +36,7 @@ mod tests { #[test_case(Rule::MutableArgumentDefault, Path::new("B006_1.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_2.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_3.py"))] + #[test_case(Rule::MutableArgumentDefault, Path::new("B006_imports.py"))] #[test_case(Rule::NoExplicitStacklevel, Path::new("B028.py"))] #[test_case(Rule::RaiseLiteral, Path::new("B016.py"))] #[test_case(Rule::RaiseWithoutFromInsideExcept, Path::new("B904.py"))] diff --git a/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_imports.py.snap b/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_imports.py.snap new file mode 100644 index 0000000000000..c22f7a3e1d6d0 --- /dev/null +++ b/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_imports.py.snap @@ -0,0 +1,310 @@ +--- +source: crates/ruff/src/rules/flake8_bugbear/mod.rs +--- +B006_imports.py:1:49: B006 [*] Do not use mutable data structures for argument defaults + | +1 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +2 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +1 |-def import_module_wrong(value: dict[str, str] = {}): + 1 |+def import_module_wrong(value: dict[str, str] = None): +2 2 | import os + 3 |+ if value is None: + 4 |+ value = {} +3 5 | +4 6 | +5 7 | def import_module_with_values_wrong(value: dict[str, str] = {}): + +B006_imports.py:5:61: B006 [*] Do not use mutable data structures for argument defaults + | +5 | def import_module_with_values_wrong(value: dict[str, str] = {}): + | ^^ B006 +6 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +2 2 | import os +3 3 | +4 4 | +5 |-def import_module_with_values_wrong(value: dict[str, str] = {}): + 5 |+def import_module_with_values_wrong(value: dict[str, str] = None): +6 6 | import os + 7 |+ if value is None: + 8 |+ value = {} +7 9 | +8 10 | return 2 +9 11 | + +B006_imports.py:11:50: B006 [*] Do not use mutable data structures for argument defaults + | +11 | def import_modules_wrong(value: dict[str, str] = {}): + | ^^ B006 +12 | import os +13 | import sys + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +8 8 | return 2 +9 9 | +10 10 | +11 |-def import_modules_wrong(value: dict[str, str] = {}): + 11 |+def import_modules_wrong(value: dict[str, str] = None): +12 12 | import os +13 13 | import sys +14 14 | import itertools + 15 |+ if value is None: + 16 |+ value = {} +15 17 | +16 18 | +17 19 | def from_import_module_wrong(value: dict[str, str] = {}): + +B006_imports.py:17:54: B006 [*] Do not use mutable data structures for argument defaults + | +17 | def from_import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +18 | from os import path + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +14 14 | import itertools +15 15 | +16 16 | +17 |-def from_import_module_wrong(value: dict[str, str] = {}): + 17 |+def from_import_module_wrong(value: dict[str, str] = None): +18 18 | from os import path + 19 |+ if value is None: + 20 |+ value = {} +19 21 | +20 22 | +21 23 | def from_imports_module_wrong(value: dict[str, str] = {}): + +B006_imports.py:21:55: B006 [*] Do not use mutable data structures for argument defaults + | +21 | def from_imports_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +22 | from os import path +23 | from sys import version_info + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +18 18 | from os import path +19 19 | +20 20 | +21 |-def from_imports_module_wrong(value: dict[str, str] = {}): + 21 |+def from_imports_module_wrong(value: dict[str, str] = None): +22 22 | from os import path +23 23 | from sys import version_info + 24 |+ if value is None: + 25 |+ value = {} +24 26 | +25 27 | +26 28 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + +B006_imports.py:26:66: B006 [*] Do not use mutable data structures for argument defaults + | +26 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +27 | import os +28 | from sys import version_info + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +23 23 | from sys import version_info +24 24 | +25 25 | +26 |-def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + 26 |+def import_and_from_imports_module_wrong(value: dict[str, str] = None): +27 27 | import os +28 28 | from sys import version_info + 29 |+ if value is None: + 30 |+ value = {} +29 31 | +30 32 | +31 33 | def import_docstring_module_wrong(value: dict[str, str] = {}): + +B006_imports.py:31:59: B006 [*] Do not use mutable data structures for argument defaults + | +31 | def import_docstring_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +32 | """Docstring""" +33 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +28 28 | from sys import version_info +29 29 | +30 30 | +31 |-def import_docstring_module_wrong(value: dict[str, str] = {}): + 31 |+def import_docstring_module_wrong(value: dict[str, str] = None): +32 32 | """Docstring""" +33 33 | import os + 34 |+ if value is None: + 35 |+ value = {} +34 36 | +35 37 | +36 38 | def import_module_wrong(value: dict[str, str] = {}): + +B006_imports.py:36:49: B006 [*] Do not use mutable data structures for argument defaults + | +36 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +37 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +33 33 | import os +34 34 | +35 35 | +36 |-def import_module_wrong(value: dict[str, str] = {}): + 36 |+def import_module_wrong(value: dict[str, str] = None): +37 37 | import os + 38 |+ if value is None: + 39 |+ value = {} +38 40 | +39 41 | +40 42 | def import_module_with_values_wrong(value: dict[str, str] = {}): + +B006_imports.py:40:61: B006 [*] Do not use mutable data structures for argument defaults + | +40 | def import_module_with_values_wrong(value: dict[str, str] = {}): + | ^^ B006 +41 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +37 37 | import os +38 38 | +39 39 | +40 |-def import_module_with_values_wrong(value: dict[str, str] = {}): + 40 |+def import_module_with_values_wrong(value: dict[str, str] = None): +41 41 | import os + 42 |+ if value is None: + 43 |+ value = {} +42 44 | +43 45 | return 2 +44 46 | + +B006_imports.py:46:50: B006 [*] Do not use mutable data structures for argument defaults + | +46 | def import_modules_wrong(value: dict[str, str] = {}): + | ^^ B006 +47 | import os +48 | import sys + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +43 43 | return 2 +44 44 | +45 45 | +46 |-def import_modules_wrong(value: dict[str, str] = {}): + 46 |+def import_modules_wrong(value: dict[str, str] = None): +47 47 | import os +48 48 | import sys +49 49 | import itertools + 50 |+ if value is None: + 51 |+ value = {} +50 52 | +51 53 | +52 54 | def from_import_module_wrong(value: dict[str, str] = {}): + +B006_imports.py:52:54: B006 [*] Do not use mutable data structures for argument defaults + | +52 | def from_import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +53 | from os import path + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +49 49 | import itertools +50 50 | +51 51 | +52 |-def from_import_module_wrong(value: dict[str, str] = {}): + 52 |+def from_import_module_wrong(value: dict[str, str] = None): +53 53 | from os import path + 54 |+ if value is None: + 55 |+ value = {} +54 56 | +55 57 | +56 58 | def from_imports_module_wrong(value: dict[str, str] = {}): + +B006_imports.py:56:55: B006 [*] Do not use mutable data structures for argument defaults + | +56 | def from_imports_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +57 | from os import path +58 | from sys import version_info + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +53 53 | from os import path +54 54 | +55 55 | +56 |-def from_imports_module_wrong(value: dict[str, str] = {}): + 56 |+def from_imports_module_wrong(value: dict[str, str] = None): +57 57 | from os import path +58 58 | from sys import version_info + 59 |+ if value is None: + 60 |+ value = {} +59 61 | +60 62 | +61 63 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + +B006_imports.py:61:66: B006 [*] Do not use mutable data structures for argument defaults + | +61 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +62 | import os +63 | from sys import version_info + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +58 58 | from sys import version_info +59 59 | +60 60 | +61 |-def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + 61 |+def import_and_from_imports_module_wrong(value: dict[str, str] = None): +62 62 | import os +63 63 | from sys import version_info + 64 |+ if value is None: + 65 |+ value = {} +64 66 | +65 67 | +66 68 | def import_docstring_module_wrong(value: dict[str, str] = {}): + +B006_imports.py:66:59: B006 [*] Do not use mutable data structures for argument defaults + | +66 | def import_docstring_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +67 | """Docstring""" +68 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +63 63 | from sys import version_info +64 64 | +65 65 | +66 |-def import_docstring_module_wrong(value: dict[str, str] = {}): + 66 |+def import_docstring_module_wrong(value: dict[str, str] = None): +67 67 | """Docstring""" +68 68 | import os + 69 |+ if value is None: + 70 |+ value = {} + + From 432658eb02e743d7d281450d760f41028751b1c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Fri, 22 Sep 2023 17:14:14 +0200 Subject: [PATCH 12/18] Remove old snap --- ...er__rules__flake8_bugbear__tests__B006_B006_imports.py.snap} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename crates/ruff_linter/src/rules/flake8_bugbear/snapshots/{ruff__rules__flake8_bugbear__tests__B006_B006_imports.py.snap => ruff_linter__rules__flake8_bugbear__tests__B006_B006_imports.py.snap} (99%) diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_imports.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_imports.py.snap similarity index 99% rename from crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_imports.py.snap rename to crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_imports.py.snap index c22f7a3e1d6d0..db4554d620ebd 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B006_B006_imports.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_imports.py.snap @@ -1,5 +1,5 @@ --- -source: crates/ruff/src/rules/flake8_bugbear/mod.rs +source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs --- B006_imports.py:1:49: B006 [*] Do not use mutable data structures for argument defaults | From efb68c336e39cd95435d2bba258fd4772f215d6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Sun, 24 Sep 2023 13:33:15 +0200 Subject: [PATCH 13/18] Rename test file --- .../{B006_imports.py => B006_5.py} | 0 .../src/rules/flake8_bugbear/mod.rs | 2 +- ...lake8_bugbear__tests__B006_B006_5.py.snap} | 28 +++++++++---------- 3 files changed, 15 insertions(+), 15 deletions(-) rename crates/ruff_linter/resources/test/fixtures/flake8_bugbear/{B006_imports.py => B006_5.py} (100%) rename crates/ruff_linter/src/rules/flake8_bugbear/snapshots/{ruff_linter__rules__flake8_bugbear__tests__B006_B006_imports.py.snap => ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap} (87%) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_imports.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py similarity index 100% rename from crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_imports.py rename to crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs b/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs index 104eb08e8e28d..aa242ff78d5da 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs @@ -37,7 +37,7 @@ mod tests { #[test_case(Rule::MutableArgumentDefault, Path::new("B006_2.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_3.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_4.py"))] - #[test_case(Rule::MutableArgumentDefault, Path::new("B006_imports.py"))] + #[test_case(Rule::MutableArgumentDefault, Path::new("B006_5.py"))] #[test_case(Rule::NoExplicitStacklevel, Path::new("B028.py"))] #[test_case(Rule::RaiseLiteral, Path::new("B016.py"))] #[test_case(Rule::RaiseWithoutFromInsideExcept, Path::new("B904.py"))] diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_imports.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap similarity index 87% rename from crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_imports.py.snap rename to crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap index db4554d620ebd..6dd27408d9cd5 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_imports.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap @@ -1,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs --- -B006_imports.py:1:49: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:1:49: B006 [*] Do not use mutable data structures for argument defaults | 1 | def import_module_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -19,7 +19,7 @@ B006_imports.py:1:49: B006 [*] Do not use mutable data structures for argument d 4 6 | 5 7 | def import_module_with_values_wrong(value: dict[str, str] = {}): -B006_imports.py:5:61: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:5:61: B006 [*] Do not use mutable data structures for argument defaults | 5 | def import_module_with_values_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -40,7 +40,7 @@ B006_imports.py:5:61: B006 [*] Do not use mutable data structures for argument d 8 10 | return 2 9 11 | -B006_imports.py:11:50: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:11:50: B006 [*] Do not use mutable data structures for argument defaults | 11 | def import_modules_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -64,7 +64,7 @@ B006_imports.py:11:50: B006 [*] Do not use mutable data structures for argument 16 18 | 17 19 | def from_import_module_wrong(value: dict[str, str] = {}): -B006_imports.py:17:54: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:17:54: B006 [*] Do not use mutable data structures for argument defaults | 17 | def from_import_module_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -85,7 +85,7 @@ B006_imports.py:17:54: B006 [*] Do not use mutable data structures for argument 20 22 | 21 23 | def from_imports_module_wrong(value: dict[str, str] = {}): -B006_imports.py:21:55: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:21:55: B006 [*] Do not use mutable data structures for argument defaults | 21 | def from_imports_module_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -108,7 +108,7 @@ B006_imports.py:21:55: B006 [*] Do not use mutable data structures for argument 25 27 | 26 28 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): -B006_imports.py:26:66: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:26:66: B006 [*] Do not use mutable data structures for argument defaults | 26 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -131,7 +131,7 @@ B006_imports.py:26:66: B006 [*] Do not use mutable data structures for argument 30 32 | 31 33 | def import_docstring_module_wrong(value: dict[str, str] = {}): -B006_imports.py:31:59: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:31:59: B006 [*] Do not use mutable data structures for argument defaults | 31 | def import_docstring_module_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -154,7 +154,7 @@ B006_imports.py:31:59: B006 [*] Do not use mutable data structures for argument 35 37 | 36 38 | def import_module_wrong(value: dict[str, str] = {}): -B006_imports.py:36:49: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:36:49: B006 [*] Do not use mutable data structures for argument defaults | 36 | def import_module_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -175,7 +175,7 @@ B006_imports.py:36:49: B006 [*] Do not use mutable data structures for argument 39 41 | 40 42 | def import_module_with_values_wrong(value: dict[str, str] = {}): -B006_imports.py:40:61: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:40:61: B006 [*] Do not use mutable data structures for argument defaults | 40 | def import_module_with_values_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -196,7 +196,7 @@ B006_imports.py:40:61: B006 [*] Do not use mutable data structures for argument 43 45 | return 2 44 46 | -B006_imports.py:46:50: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:46:50: B006 [*] Do not use mutable data structures for argument defaults | 46 | def import_modules_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -220,7 +220,7 @@ B006_imports.py:46:50: B006 [*] Do not use mutable data structures for argument 51 53 | 52 54 | def from_import_module_wrong(value: dict[str, str] = {}): -B006_imports.py:52:54: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:52:54: B006 [*] Do not use mutable data structures for argument defaults | 52 | def from_import_module_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -241,7 +241,7 @@ B006_imports.py:52:54: B006 [*] Do not use mutable data structures for argument 55 57 | 56 58 | def from_imports_module_wrong(value: dict[str, str] = {}): -B006_imports.py:56:55: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:56:55: B006 [*] Do not use mutable data structures for argument defaults | 56 | def from_imports_module_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -264,7 +264,7 @@ B006_imports.py:56:55: B006 [*] Do not use mutable data structures for argument 60 62 | 61 63 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): -B006_imports.py:61:66: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:61:66: B006 [*] Do not use mutable data structures for argument defaults | 61 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -287,7 +287,7 @@ B006_imports.py:61:66: B006 [*] Do not use mutable data structures for argument 65 67 | 66 68 | def import_docstring_module_wrong(value: dict[str, str] = {}): -B006_imports.py:66:59: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:66:59: B006 [*] Do not use mutable data structures for argument defaults | 66 | def import_docstring_module_wrong(value: dict[str, str] = {}): | ^^ B006 From c83dbcb9c3e2cb606fb72fe735ddfc1a0e9b82e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Sun, 24 Sep 2023 13:34:09 +0200 Subject: [PATCH 14/18] Add small info at top of test file --- .../test/fixtures/flake8_bugbear/B006_5.py | 4 + ...flake8_bugbear__tests__B006_B006_5.py.snap | 363 +++++++++--------- 2 files changed, 187 insertions(+), 180 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py index dbd857f3bc222..0e702e34045df 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py @@ -1,3 +1,7 @@ +# Move mutable arguments below imports and docstrings +# https://github.com/astral-sh/ruff/issues/7616 + + def import_module_wrong(value: dict[str, str] = {}): import os diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap index 6dd27408d9cd5..181a2ae263648 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap @@ -1,184 +1,166 @@ --- source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs --- -B006_5.py:1:49: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:5:49: B006 [*] Do not use mutable data structures for argument defaults | -1 | def import_module_wrong(value: dict[str, str] = {}): +5 | def import_module_wrong(value: dict[str, str] = {}): | ^^ B006 -2 | import os - | - = help: Replace with `None`; initialize within function - -ℹ Possible fix -1 |-def import_module_wrong(value: dict[str, str] = {}): - 1 |+def import_module_wrong(value: dict[str, str] = None): -2 2 | import os - 3 |+ if value is None: - 4 |+ value = {} -3 5 | -4 6 | -5 7 | def import_module_with_values_wrong(value: dict[str, str] = {}): - -B006_5.py:5:61: B006 [*] Do not use mutable data structures for argument defaults - | -5 | def import_module_with_values_wrong(value: dict[str, str] = {}): - | ^^ B006 6 | import os | = help: Replace with `None`; initialize within function ℹ Possible fix -2 2 | import os +2 2 | # https://github.com/astral-sh/ruff/issues/7616 3 3 | 4 4 | -5 |-def import_module_with_values_wrong(value: dict[str, str] = {}): - 5 |+def import_module_with_values_wrong(value: dict[str, str] = None): +5 |-def import_module_wrong(value: dict[str, str] = {}): + 5 |+def import_module_wrong(value: dict[str, str] = None): 6 6 | import os 7 |+ if value is None: 8 |+ value = {} 7 9 | -8 10 | return 2 -9 11 | +8 10 | +9 11 | def import_module_with_values_wrong(value: dict[str, str] = {}): -B006_5.py:11:50: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:9:61: B006 [*] Do not use mutable data structures for argument defaults | -11 | def import_modules_wrong(value: dict[str, str] = {}): - | ^^ B006 -12 | import os -13 | import sys + 9 | def import_module_with_values_wrong(value: dict[str, str] = {}): + | ^^ B006 +10 | import os | = help: Replace with `None`; initialize within function ℹ Possible fix -8 8 | return 2 -9 9 | -10 10 | -11 |-def import_modules_wrong(value: dict[str, str] = {}): - 11 |+def import_modules_wrong(value: dict[str, str] = None): -12 12 | import os -13 13 | import sys -14 14 | import itertools - 15 |+ if value is None: - 16 |+ value = {} -15 17 | -16 18 | -17 19 | def from_import_module_wrong(value: dict[str, str] = {}): +6 6 | import os +7 7 | +8 8 | +9 |-def import_module_with_values_wrong(value: dict[str, str] = {}): + 9 |+def import_module_with_values_wrong(value: dict[str, str] = None): +10 10 | import os + 11 |+ if value is None: + 12 |+ value = {} +11 13 | +12 14 | return 2 +13 15 | -B006_5.py:17:54: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:15:50: B006 [*] Do not use mutable data structures for argument defaults | -17 | def from_import_module_wrong(value: dict[str, str] = {}): - | ^^ B006 -18 | from os import path +15 | def import_modules_wrong(value: dict[str, str] = {}): + | ^^ B006 +16 | import os +17 | import sys | = help: Replace with `None`; initialize within function ℹ Possible fix -14 14 | import itertools -15 15 | -16 16 | -17 |-def from_import_module_wrong(value: dict[str, str] = {}): - 17 |+def from_import_module_wrong(value: dict[str, str] = None): -18 18 | from os import path +12 12 | return 2 +13 13 | +14 14 | +15 |-def import_modules_wrong(value: dict[str, str] = {}): + 15 |+def import_modules_wrong(value: dict[str, str] = None): +16 16 | import os +17 17 | import sys +18 18 | import itertools 19 |+ if value is None: 20 |+ value = {} 19 21 | 20 22 | -21 23 | def from_imports_module_wrong(value: dict[str, str] = {}): +21 23 | def from_import_module_wrong(value: dict[str, str] = {}): -B006_5.py:21:55: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:21:54: B006 [*] Do not use mutable data structures for argument defaults | -21 | def from_imports_module_wrong(value: dict[str, str] = {}): - | ^^ B006 +21 | def from_import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 22 | from os import path -23 | from sys import version_info | = help: Replace with `None`; initialize within function ℹ Possible fix -18 18 | from os import path +18 18 | import itertools 19 19 | 20 20 | -21 |-def from_imports_module_wrong(value: dict[str, str] = {}): - 21 |+def from_imports_module_wrong(value: dict[str, str] = None): +21 |-def from_import_module_wrong(value: dict[str, str] = {}): + 21 |+def from_import_module_wrong(value: dict[str, str] = None): 22 22 | from os import path -23 23 | from sys import version_info - 24 |+ if value is None: - 25 |+ value = {} + 23 |+ if value is None: + 24 |+ value = {} +23 25 | 24 26 | -25 27 | -26 28 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): +25 27 | def from_imports_module_wrong(value: dict[str, str] = {}): -B006_5.py:26:66: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:25:55: B006 [*] Do not use mutable data structures for argument defaults | -26 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): - | ^^ B006 -27 | import os -28 | from sys import version_info +25 | def from_imports_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +26 | from os import path +27 | from sys import version_info | = help: Replace with `None`; initialize within function ℹ Possible fix -23 23 | from sys import version_info +22 22 | from os import path +23 23 | 24 24 | -25 25 | -26 |-def import_and_from_imports_module_wrong(value: dict[str, str] = {}): - 26 |+def import_and_from_imports_module_wrong(value: dict[str, str] = None): -27 27 | import os -28 28 | from sys import version_info - 29 |+ if value is None: - 30 |+ value = {} +25 |-def from_imports_module_wrong(value: dict[str, str] = {}): + 25 |+def from_imports_module_wrong(value: dict[str, str] = None): +26 26 | from os import path +27 27 | from sys import version_info + 28 |+ if value is None: + 29 |+ value = {} +28 30 | 29 31 | -30 32 | -31 33 | def import_docstring_module_wrong(value: dict[str, str] = {}): +30 32 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): -B006_5.py:31:59: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:30:66: B006 [*] Do not use mutable data structures for argument defaults | -31 | def import_docstring_module_wrong(value: dict[str, str] = {}): - | ^^ B006 -32 | """Docstring""" -33 | import os +30 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +31 | import os +32 | from sys import version_info | = help: Replace with `None`; initialize within function ℹ Possible fix -28 28 | from sys import version_info +27 27 | from sys import version_info +28 28 | 29 29 | -30 30 | -31 |-def import_docstring_module_wrong(value: dict[str, str] = {}): - 31 |+def import_docstring_module_wrong(value: dict[str, str] = None): -32 32 | """Docstring""" -33 33 | import os - 34 |+ if value is None: - 35 |+ value = {} +30 |-def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + 30 |+def import_and_from_imports_module_wrong(value: dict[str, str] = None): +31 31 | import os +32 32 | from sys import version_info + 33 |+ if value is None: + 34 |+ value = {} +33 35 | 34 36 | -35 37 | -36 38 | def import_module_wrong(value: dict[str, str] = {}): +35 37 | def import_docstring_module_wrong(value: dict[str, str] = {}): -B006_5.py:36:49: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:35:59: B006 [*] Do not use mutable data structures for argument defaults | -36 | def import_module_wrong(value: dict[str, str] = {}): - | ^^ B006 +35 | def import_docstring_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +36 | """Docstring""" 37 | import os | = help: Replace with `None`; initialize within function ℹ Possible fix -33 33 | import os +32 32 | from sys import version_info +33 33 | 34 34 | -35 35 | -36 |-def import_module_wrong(value: dict[str, str] = {}): - 36 |+def import_module_wrong(value: dict[str, str] = None): +35 |-def import_docstring_module_wrong(value: dict[str, str] = {}): + 35 |+def import_docstring_module_wrong(value: dict[str, str] = None): +36 36 | """Docstring""" 37 37 | import os 38 |+ if value is None: 39 |+ value = {} 38 40 | 39 41 | -40 42 | def import_module_with_values_wrong(value: dict[str, str] = {}): +40 42 | def import_module_wrong(value: dict[str, str] = {}): -B006_5.py:40:61: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:40:49: B006 [*] Do not use mutable data structures for argument defaults | -40 | def import_module_with_values_wrong(value: dict[str, str] = {}): - | ^^ B006 +40 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 41 | import os | = help: Replace with `None`; initialize within function @@ -187,124 +169,145 @@ B006_5.py:40:61: B006 [*] Do not use mutable data structures for argument defaul 37 37 | import os 38 38 | 39 39 | -40 |-def import_module_with_values_wrong(value: dict[str, str] = {}): - 40 |+def import_module_with_values_wrong(value: dict[str, str] = None): +40 |-def import_module_wrong(value: dict[str, str] = {}): + 40 |+def import_module_wrong(value: dict[str, str] = None): 41 41 | import os 42 |+ if value is None: 43 |+ value = {} 42 44 | -43 45 | return 2 -44 46 | +43 45 | +44 46 | def import_module_with_values_wrong(value: dict[str, str] = {}): -B006_5.py:46:50: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:44:61: B006 [*] Do not use mutable data structures for argument defaults | -46 | def import_modules_wrong(value: dict[str, str] = {}): - | ^^ B006 -47 | import os -48 | import sys +44 | def import_module_with_values_wrong(value: dict[str, str] = {}): + | ^^ B006 +45 | import os | = help: Replace with `None`; initialize within function ℹ Possible fix -43 43 | return 2 -44 44 | -45 45 | -46 |-def import_modules_wrong(value: dict[str, str] = {}): - 46 |+def import_modules_wrong(value: dict[str, str] = None): -47 47 | import os -48 48 | import sys -49 49 | import itertools - 50 |+ if value is None: - 51 |+ value = {} -50 52 | -51 53 | -52 54 | def from_import_module_wrong(value: dict[str, str] = {}): +41 41 | import os +42 42 | +43 43 | +44 |-def import_module_with_values_wrong(value: dict[str, str] = {}): + 44 |+def import_module_with_values_wrong(value: dict[str, str] = None): +45 45 | import os + 46 |+ if value is None: + 47 |+ value = {} +46 48 | +47 49 | return 2 +48 50 | -B006_5.py:52:54: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:50:50: B006 [*] Do not use mutable data structures for argument defaults | -52 | def from_import_module_wrong(value: dict[str, str] = {}): - | ^^ B006 -53 | from os import path +50 | def import_modules_wrong(value: dict[str, str] = {}): + | ^^ B006 +51 | import os +52 | import sys | = help: Replace with `None`; initialize within function ℹ Possible fix -49 49 | import itertools -50 50 | -51 51 | -52 |-def from_import_module_wrong(value: dict[str, str] = {}): - 52 |+def from_import_module_wrong(value: dict[str, str] = None): -53 53 | from os import path +47 47 | return 2 +48 48 | +49 49 | +50 |-def import_modules_wrong(value: dict[str, str] = {}): + 50 |+def import_modules_wrong(value: dict[str, str] = None): +51 51 | import os +52 52 | import sys +53 53 | import itertools 54 |+ if value is None: 55 |+ value = {} 54 56 | 55 57 | -56 58 | def from_imports_module_wrong(value: dict[str, str] = {}): +56 58 | def from_import_module_wrong(value: dict[str, str] = {}): -B006_5.py:56:55: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:56:54: B006 [*] Do not use mutable data structures for argument defaults | -56 | def from_imports_module_wrong(value: dict[str, str] = {}): - | ^^ B006 +56 | def from_import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 57 | from os import path -58 | from sys import version_info | = help: Replace with `None`; initialize within function ℹ Possible fix -53 53 | from os import path +53 53 | import itertools 54 54 | 55 55 | -56 |-def from_imports_module_wrong(value: dict[str, str] = {}): - 56 |+def from_imports_module_wrong(value: dict[str, str] = None): +56 |-def from_import_module_wrong(value: dict[str, str] = {}): + 56 |+def from_import_module_wrong(value: dict[str, str] = None): 57 57 | from os import path -58 58 | from sys import version_info - 59 |+ if value is None: - 60 |+ value = {} + 58 |+ if value is None: + 59 |+ value = {} +58 60 | 59 61 | -60 62 | -61 63 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): +60 62 | def from_imports_module_wrong(value: dict[str, str] = {}): -B006_5.py:61:66: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:60:55: B006 [*] Do not use mutable data structures for argument defaults | -61 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): - | ^^ B006 -62 | import os -63 | from sys import version_info +60 | def from_imports_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +61 | from os import path +62 | from sys import version_info | = help: Replace with `None`; initialize within function ℹ Possible fix -58 58 | from sys import version_info +57 57 | from os import path +58 58 | 59 59 | -60 60 | -61 |-def import_and_from_imports_module_wrong(value: dict[str, str] = {}): - 61 |+def import_and_from_imports_module_wrong(value: dict[str, str] = None): -62 62 | import os -63 63 | from sys import version_info - 64 |+ if value is None: - 65 |+ value = {} +60 |-def from_imports_module_wrong(value: dict[str, str] = {}): + 60 |+def from_imports_module_wrong(value: dict[str, str] = None): +61 61 | from os import path +62 62 | from sys import version_info + 63 |+ if value is None: + 64 |+ value = {} +63 65 | 64 66 | -65 67 | -66 68 | def import_docstring_module_wrong(value: dict[str, str] = {}): +65 67 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): -B006_5.py:66:59: B006 [*] Do not use mutable data structures for argument defaults +B006_5.py:65:66: B006 [*] Do not use mutable data structures for argument defaults | -66 | def import_docstring_module_wrong(value: dict[str, str] = {}): - | ^^ B006 -67 | """Docstring""" -68 | import os +65 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +66 | import os +67 | from sys import version_info | = help: Replace with `None`; initialize within function ℹ Possible fix -63 63 | from sys import version_info +62 62 | from sys import version_info +63 63 | 64 64 | -65 65 | -66 |-def import_docstring_module_wrong(value: dict[str, str] = {}): - 66 |+def import_docstring_module_wrong(value: dict[str, str] = None): -67 67 | """Docstring""" -68 68 | import os - 69 |+ if value is None: - 70 |+ value = {} +65 |-def import_and_from_imports_module_wrong(value: dict[str, str] = {}): + 65 |+def import_and_from_imports_module_wrong(value: dict[str, str] = None): +66 66 | import os +67 67 | from sys import version_info + 68 |+ if value is None: + 69 |+ value = {} +68 70 | +69 71 | +70 72 | def import_docstring_module_wrong(value: dict[str, str] = {}): + +B006_5.py:70:59: B006 [*] Do not use mutable data structures for argument defaults + | +70 | def import_docstring_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +71 | """Docstring""" +72 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +67 67 | from sys import version_info +68 68 | +69 69 | +70 |-def import_docstring_module_wrong(value: dict[str, str] = {}): + 70 |+def import_docstring_module_wrong(value: dict[str, str] = None): +71 71 | """Docstring""" +72 72 | import os + 73 |+ if value is None: + 74 |+ value = {} From cd17b8c539673c5ea2ce7c4246bbc0cbc0a5baa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Sun, 24 Sep 2023 19:41:13 +0200 Subject: [PATCH 15/18] Fix duplicate tests --- .../test/fixtures/flake8_bugbear/B006_5.py | 35 ---- ...flake8_bugbear__tests__B006_B006_5.py.snap | 156 ------------------ 2 files changed, 191 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py index 0e702e34045df..998fe9479de11 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py @@ -2,41 +2,6 @@ # https://github.com/astral-sh/ruff/issues/7616 -def import_module_wrong(value: dict[str, str] = {}): - import os - - -def import_module_with_values_wrong(value: dict[str, str] = {}): - import os - - return 2 - - -def import_modules_wrong(value: dict[str, str] = {}): - import os - import sys - import itertools - - -def from_import_module_wrong(value: dict[str, str] = {}): - from os import path - - -def from_imports_module_wrong(value: dict[str, str] = {}): - from os import path - from sys import version_info - - -def import_and_from_imports_module_wrong(value: dict[str, str] = {}): - import os - from sys import version_info - - -def import_docstring_module_wrong(value: dict[str, str] = {}): - """Docstring""" - import os - - def import_module_wrong(value: dict[str, str] = {}): import os diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap index 181a2ae263648..9f42816507799 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap @@ -153,161 +153,5 @@ B006_5.py:35:59: B006 [*] Do not use mutable data structures for argument defaul 37 37 | import os 38 |+ if value is None: 39 |+ value = {} -38 40 | -39 41 | -40 42 | def import_module_wrong(value: dict[str, str] = {}): - -B006_5.py:40:49: B006 [*] Do not use mutable data structures for argument defaults - | -40 | def import_module_wrong(value: dict[str, str] = {}): - | ^^ B006 -41 | import os - | - = help: Replace with `None`; initialize within function - -ℹ Possible fix -37 37 | import os -38 38 | -39 39 | -40 |-def import_module_wrong(value: dict[str, str] = {}): - 40 |+def import_module_wrong(value: dict[str, str] = None): -41 41 | import os - 42 |+ if value is None: - 43 |+ value = {} -42 44 | -43 45 | -44 46 | def import_module_with_values_wrong(value: dict[str, str] = {}): - -B006_5.py:44:61: B006 [*] Do not use mutable data structures for argument defaults - | -44 | def import_module_with_values_wrong(value: dict[str, str] = {}): - | ^^ B006 -45 | import os - | - = help: Replace with `None`; initialize within function - -ℹ Possible fix -41 41 | import os -42 42 | -43 43 | -44 |-def import_module_with_values_wrong(value: dict[str, str] = {}): - 44 |+def import_module_with_values_wrong(value: dict[str, str] = None): -45 45 | import os - 46 |+ if value is None: - 47 |+ value = {} -46 48 | -47 49 | return 2 -48 50 | - -B006_5.py:50:50: B006 [*] Do not use mutable data structures for argument defaults - | -50 | def import_modules_wrong(value: dict[str, str] = {}): - | ^^ B006 -51 | import os -52 | import sys - | - = help: Replace with `None`; initialize within function - -ℹ Possible fix -47 47 | return 2 -48 48 | -49 49 | -50 |-def import_modules_wrong(value: dict[str, str] = {}): - 50 |+def import_modules_wrong(value: dict[str, str] = None): -51 51 | import os -52 52 | import sys -53 53 | import itertools - 54 |+ if value is None: - 55 |+ value = {} -54 56 | -55 57 | -56 58 | def from_import_module_wrong(value: dict[str, str] = {}): - -B006_5.py:56:54: B006 [*] Do not use mutable data structures for argument defaults - | -56 | def from_import_module_wrong(value: dict[str, str] = {}): - | ^^ B006 -57 | from os import path - | - = help: Replace with `None`; initialize within function - -ℹ Possible fix -53 53 | import itertools -54 54 | -55 55 | -56 |-def from_import_module_wrong(value: dict[str, str] = {}): - 56 |+def from_import_module_wrong(value: dict[str, str] = None): -57 57 | from os import path - 58 |+ if value is None: - 59 |+ value = {} -58 60 | -59 61 | -60 62 | def from_imports_module_wrong(value: dict[str, str] = {}): - -B006_5.py:60:55: B006 [*] Do not use mutable data structures for argument defaults - | -60 | def from_imports_module_wrong(value: dict[str, str] = {}): - | ^^ B006 -61 | from os import path -62 | from sys import version_info - | - = help: Replace with `None`; initialize within function - -ℹ Possible fix -57 57 | from os import path -58 58 | -59 59 | -60 |-def from_imports_module_wrong(value: dict[str, str] = {}): - 60 |+def from_imports_module_wrong(value: dict[str, str] = None): -61 61 | from os import path -62 62 | from sys import version_info - 63 |+ if value is None: - 64 |+ value = {} -63 65 | -64 66 | -65 67 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): - -B006_5.py:65:66: B006 [*] Do not use mutable data structures for argument defaults - | -65 | def import_and_from_imports_module_wrong(value: dict[str, str] = {}): - | ^^ B006 -66 | import os -67 | from sys import version_info - | - = help: Replace with `None`; initialize within function - -ℹ Possible fix -62 62 | from sys import version_info -63 63 | -64 64 | -65 |-def import_and_from_imports_module_wrong(value: dict[str, str] = {}): - 65 |+def import_and_from_imports_module_wrong(value: dict[str, str] = None): -66 66 | import os -67 67 | from sys import version_info - 68 |+ if value is None: - 69 |+ value = {} -68 70 | -69 71 | -70 72 | def import_docstring_module_wrong(value: dict[str, str] = {}): - -B006_5.py:70:59: B006 [*] Do not use mutable data structures for argument defaults - | -70 | def import_docstring_module_wrong(value: dict[str, str] = {}): - | ^^ B006 -71 | """Docstring""" -72 | import os - | - = help: Replace with `None`; initialize within function - -ℹ Possible fix -67 67 | from sys import version_info -68 68 | -69 69 | -70 |-def import_docstring_module_wrong(value: dict[str, str] = {}): - 70 |+def import_docstring_module_wrong(value: dict[str, str] = None): -71 71 | """Docstring""" -72 72 | import os - 73 |+ if value is None: - 74 |+ value = {} From d8df26e34bb5143748464337aa2c12a61ead8afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Mon, 25 Sep 2023 19:58:20 +0200 Subject: [PATCH 16/18] Handle when pos is equal to line length --- .../test/fixtures/flake8_bugbear/B006_6.py | 5 ++++ .../test/fixtures/flake8_bugbear/B006_7.py | 5 ++++ .../src/rules/flake8_bugbear/mod.rs | 2 ++ .../rules/mutable_argument_default.rs | 6 +++++ ...flake8_bugbear__tests__B006_B006_5.py.snap | 5 ++-- ...flake8_bugbear__tests__B006_B006_6.py.snap | 25 +++++++++++++++++++ ...flake8_bugbear__tests__B006_B006_7.py.snap | 23 +++++++++++++++++ 7 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_6.py create mode 100644 crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_7.py create mode 100644 crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_6.py.snap create mode 100644 crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_7.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_6.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_6.py new file mode 100644 index 0000000000000..5cf88370a46dd --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_6.py @@ -0,0 +1,5 @@ +# Docstring followed by whitespace with no newline +# Regression test for https://github.com/astral-sh/ruff/issues/7155 + +def foobar(foor, bar={}): + import os \ No newline at end of file diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_7.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_7.py new file mode 100644 index 0000000000000..dbe6cf7e17794 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_7.py @@ -0,0 +1,5 @@ +# Docstring with no newline + + +def foobar(foor, bar={}): + import os \ No newline at end of file diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs b/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs index aa242ff78d5da..e31681ca43a5b 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs @@ -38,6 +38,8 @@ mod tests { #[test_case(Rule::MutableArgumentDefault, Path::new("B006_3.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_4.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_5.py"))] + #[test_case(Rule::MutableArgumentDefault, Path::new("B006_6.py"))] + #[test_case(Rule::MutableArgumentDefault, Path::new("B006_7.py"))] #[test_case(Rule::NoExplicitStacklevel, Path::new("B028.py"))] #[test_case(Rule::RaiseLiteral, Path::new("B016.py"))] #[test_case(Rule::RaiseWithoutFromInsideExcept, Path::new("B904.py"))] diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs index a35ad1074689f..261a8f68b36e4 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs @@ -192,6 +192,12 @@ fn move_initialization( } else if statement.is_import_stmt() || statement.is_import_from_stmt() { // If the statement in the function is an import, insert _after_ it. pos = locator.full_line_end(statement.end()); + if pos == locator.text_len() { + // If the statement is at the end of the file, without a trailing newline, insert + // _after_ it with an extra newline. + content = format!("{}{}", stylist.line_ending().as_str(), content); + break; + } } else { break; }; diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap index 9f42816507799..1624f70c6648d 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap @@ -151,7 +151,8 @@ B006_5.py:35:59: B006 [*] Do not use mutable data structures for argument defaul 35 |+def import_docstring_module_wrong(value: dict[str, str] = None): 36 36 | """Docstring""" 37 37 | import os - 38 |+ if value is None: - 39 |+ value = {} + 38 |+ + 39 |+ if value is None: + 40 |+ value = {} diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_6.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_6.py.snap new file mode 100644 index 0000000000000..e3fd873bc7082 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_6.py.snap @@ -0,0 +1,25 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +--- +B006_6.py:4:22: B006 [*] Do not use mutable data structures for argument defaults + | +2 | # Regression test for https://github.com/astral-sh/ruff/issues/7155 +3 | +4 | def foobar(foor, bar={}): + | ^^ B006 +5 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +1 1 | # Docstring followed by whitespace with no newline +2 2 | # Regression test for https://github.com/astral-sh/ruff/issues/7155 +3 3 | +4 |-def foobar(foor, bar={}): +5 |- import os + 4 |+def foobar(foor, bar=None): + 5 |+ import os + 6 |+ if bar is None: + 7 |+ bar = {} + + diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_7.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_7.py.snap new file mode 100644 index 0000000000000..484e6987d91b1 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_7.py.snap @@ -0,0 +1,23 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +--- +B006_7.py:4:22: B006 [*] Do not use mutable data structures for argument defaults + | +4 | def foobar(foor, bar={}): + | ^^ B006 +5 | import os + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +1 1 | # Docstring with no newline +2 2 | +3 3 | +4 |-def foobar(foor, bar={}): +5 |- import os + 4 |+def foobar(foor, bar=None): + 5 |+ import os + 6 |+ if bar is None: + 7 |+ bar = {} + + From 6c86f28772b0e09c57eebe18c800443b06177a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20H=C3=B8xbro=20Hansen?= Date: Mon, 25 Sep 2023 20:57:28 +0200 Subject: [PATCH 17/18] Update info for new tests --- .../resources/test/fixtures/flake8_bugbear/B006_6.py | 4 ++-- .../resources/test/fixtures/flake8_bugbear/B006_7.py | 4 ++-- ...inter__rules__flake8_bugbear__tests__B006_B006_6.py.snap | 6 +++--- ...inter__rules__flake8_bugbear__tests__B006_B006_7.py.snap | 6 ++++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_6.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_6.py index 5cf88370a46dd..b651380f0def1 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_6.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_6.py @@ -1,5 +1,5 @@ -# Docstring followed by whitespace with no newline -# Regression test for https://github.com/astral-sh/ruff/issues/7155 +# Import followed by whitespace with no newline +# Same as B006_2.py, but import instead of docstring def foobar(foor, bar={}): import os \ No newline at end of file diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_7.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_7.py index dbe6cf7e17794..0b382b5fbfcf9 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_7.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_7.py @@ -1,5 +1,5 @@ -# Docstring with no newline - +# Import with no newline +# Same as B006_3.py, but import instead of docstring def foobar(foor, bar={}): import os \ No newline at end of file diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_6.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_6.py.snap index e3fd873bc7082..f1af067d82f64 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_6.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_6.py.snap @@ -3,7 +3,7 @@ source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs --- B006_6.py:4:22: B006 [*] Do not use mutable data structures for argument defaults | -2 | # Regression test for https://github.com/astral-sh/ruff/issues/7155 +2 | # Same as B006_2.py, but import instead of docstring 3 | 4 | def foobar(foor, bar={}): | ^^ B006 @@ -12,8 +12,8 @@ B006_6.py:4:22: B006 [*] Do not use mutable data structures for argument default = help: Replace with `None`; initialize within function ℹ Possible fix -1 1 | # Docstring followed by whitespace with no newline -2 2 | # Regression test for https://github.com/astral-sh/ruff/issues/7155 +1 1 | # Import followed by whitespace with no newline +2 2 | # Same as B006_2.py, but import instead of docstring 3 3 | 4 |-def foobar(foor, bar={}): 5 |- import os diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_7.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_7.py.snap index 484e6987d91b1..fbe42a7285e85 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_7.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_7.py.snap @@ -3,6 +3,8 @@ source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs --- B006_7.py:4:22: B006 [*] Do not use mutable data structures for argument defaults | +2 | # Same as B006_3.py, but import instead of docstring +3 | 4 | def foobar(foor, bar={}): | ^^ B006 5 | import os @@ -10,8 +12,8 @@ B006_7.py:4:22: B006 [*] Do not use mutable data structures for argument default = help: Replace with `None`; initialize within function ℹ Possible fix -1 1 | # Docstring with no newline -2 2 | +1 1 | # Import with no newline +2 2 | # Same as B006_3.py, but import instead of docstring 3 3 | 4 |-def foobar(foor, bar={}): 5 |- import os From 7d2716010a2f48b572e60c2a978e974905c29039 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Tue, 26 Sep 2023 21:10:54 -0400 Subject: [PATCH 18/18] Collapse cases --- .../test/fixtures/flake8_bugbear/B006_5.py | 37 ++++ .../rules/mutable_argument_default.rs | 33 ++-- ...flake8_bugbear__tests__B006_B006_5.py.snap | 167 +++++++++++++++++- ...ke8_bugbear__tests__B006_B006_B008.py.snap | 28 ++- 4 files changed, 239 insertions(+), 26 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py index 998fe9479de11..b2bde5afa118d 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_5.py @@ -35,3 +35,40 @@ def import_and_from_imports_module_wrong(value: dict[str, str] = {}): def import_docstring_module_wrong(value: dict[str, str] = {}): """Docstring""" import os + + +def import_module_wrong(value: dict[str, str] = {}): + """Docstring""" + import os; import sys + + +def import_module_wrong(value: dict[str, str] = {}): + """Docstring""" + import os; import sys; x = 1 + + +def import_module_wrong(value: dict[str, str] = {}): + """Docstring""" + import os; import sys + + +def import_module_wrong(value: dict[str, str] = {}): + import os; import sys + + +def import_module_wrong(value: dict[str, str] = {}): + import os; import sys; x = 1 + + +def import_module_wrong(value: dict[str, str] = {}): + import os; import sys + + +def import_module_wrong(value: dict[str, str] = {}): import os + + +def import_module_wrong(value: dict[str, str] = {}): import os; import sys + + +def import_module_wrong(value: dict[str, str] = {}): \ + import os diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs index 261a8f68b36e4..3d664a4da0727 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/mutable_argument_default.rs @@ -141,14 +141,12 @@ fn move_initialization( ) -> Option { let mut body = function_def.body.iter().peekable(); + // Avoid attempting to fix single-line functions. let statement = body.peek()?; - if indexer.in_multi_statement_line(statement, locator) { + if indexer.preceded_by_multi_statement_line(statement, locator) { return None; } - // Determine the indentation depth of the function body. - let indentation = indentation_at_offset(statement.start(), locator)?; - // Set the default argument value to `None`. let default_edit = Edit::range_replacement("None".to_string(), default.range()); @@ -164,21 +162,27 @@ fn move_initialization( )); content.push_str(stylist.line_ending().as_str()); + // Determine the indentation depth of the function body. + let indentation = indentation_at_offset(statement.start(), locator)?; + // Indent the edit to match the body indentation. let mut content = textwrap::indent(&content, indentation).to_string(); // Find the position to insert the initialization after docstring and imports let mut pos = locator.line_start(statement.start()); while let Some(statement) = body.next() { - if is_docstring_stmt(statement) { - // If the statement in the function is a docstring, insert _after_ it. - if let Some(statement) = body.peek() { + // If the statement is a docstring or an import, insert _after_ it. + if is_docstring_stmt(statement) + || statement.is_import_stmt() + || statement.is_import_from_stmt() + { + if let Some(next) = body.peek() { // If there's a second statement, insert _before_ it, but ensure this isn't a // multi-statement line. if indexer.in_multi_statement_line(statement, locator) { - return None; + continue; } - pos = locator.line_start(statement.start()); + pos = locator.line_start(next.start()); } else if locator.full_line_end(statement.end()) == locator.text_len() { // If the statement is at the end of the file, without a trailing newline, insert // _after_ it with an extra newline. @@ -186,22 +190,15 @@ fn move_initialization( pos = locator.full_line_end(statement.end()); break; } else { - // If the docstring is the only statement, insert _after_ it. + // If this is the only statement, insert _after_ it. pos = locator.full_line_end(statement.end()); - } - } else if statement.is_import_stmt() || statement.is_import_from_stmt() { - // If the statement in the function is an import, insert _after_ it. - pos = locator.full_line_end(statement.end()); - if pos == locator.text_len() { - // If the statement is at the end of the file, without a trailing newline, insert - // _after_ it with an extra newline. - content = format!("{}{}", stylist.line_ending().as_str(), content); break; } } else { break; }; } + let initialization_edit = Edit::insertion(content, pos); Some(Fix::manual_edits(default_edit, [initialization_edit])) } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap index 1624f70c6648d..d741dc3258eba 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_5.py.snap @@ -37,11 +37,12 @@ B006_5.py:9:61: B006 [*] Do not use mutable data structures for argument default 9 |-def import_module_with_values_wrong(value: dict[str, str] = {}): 9 |+def import_module_with_values_wrong(value: dict[str, str] = None): 10 10 | import os - 11 |+ if value is None: - 12 |+ value = {} -11 13 | +11 11 | + 12 |+ if value is None: + 13 |+ value = {} 12 14 | return 2 13 15 | +14 16 | B006_5.py:15:50: B006 [*] Do not use mutable data structures for argument defaults | @@ -151,8 +152,162 @@ B006_5.py:35:59: B006 [*] Do not use mutable data structures for argument defaul 35 |+def import_docstring_module_wrong(value: dict[str, str] = None): 36 36 | """Docstring""" 37 37 | import os - 38 |+ - 39 |+ if value is None: - 40 |+ value = {} + 38 |+ if value is None: + 39 |+ value = {} +38 40 | +39 41 | +40 42 | def import_module_wrong(value: dict[str, str] = {}): + +B006_5.py:40:49: B006 [*] Do not use mutable data structures for argument defaults + | +40 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +41 | """Docstring""" +42 | import os; import sys + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +37 37 | import os +38 38 | +39 39 | +40 |-def import_module_wrong(value: dict[str, str] = {}): + 40 |+def import_module_wrong(value: dict[str, str] = None): +41 41 | """Docstring""" +42 42 | import os; import sys + 43 |+ if value is None: + 44 |+ value = {} +43 45 | +44 46 | +45 47 | def import_module_wrong(value: dict[str, str] = {}): + +B006_5.py:45:49: B006 [*] Do not use mutable data structures for argument defaults + | +45 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +46 | """Docstring""" +47 | import os; import sys; x = 1 + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +42 42 | import os; import sys +43 43 | +44 44 | +45 |-def import_module_wrong(value: dict[str, str] = {}): + 45 |+def import_module_wrong(value: dict[str, str] = None): +46 46 | """Docstring""" + 47 |+ if value is None: + 48 |+ value = {} +47 49 | import os; import sys; x = 1 +48 50 | +49 51 | + +B006_5.py:50:49: B006 [*] Do not use mutable data structures for argument defaults + | +50 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +51 | """Docstring""" +52 | import os; import sys + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +47 47 | import os; import sys; x = 1 +48 48 | +49 49 | +50 |-def import_module_wrong(value: dict[str, str] = {}): + 50 |+def import_module_wrong(value: dict[str, str] = None): +51 51 | """Docstring""" +52 52 | import os; import sys + 53 |+ if value is None: + 54 |+ value = {} +53 55 | +54 56 | +55 57 | def import_module_wrong(value: dict[str, str] = {}): + +B006_5.py:55:49: B006 [*] Do not use mutable data structures for argument defaults + | +55 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +56 | import os; import sys + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +52 52 | import os; import sys +53 53 | +54 54 | +55 |-def import_module_wrong(value: dict[str, str] = {}): + 55 |+def import_module_wrong(value: dict[str, str] = None): +56 56 | import os; import sys + 57 |+ if value is None: + 58 |+ value = {} +57 59 | +58 60 | +59 61 | def import_module_wrong(value: dict[str, str] = {}): + +B006_5.py:59:49: B006 [*] Do not use mutable data structures for argument defaults + | +59 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +60 | import os; import sys; x = 1 + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +56 56 | import os; import sys +57 57 | +58 58 | +59 |-def import_module_wrong(value: dict[str, str] = {}): + 59 |+def import_module_wrong(value: dict[str, str] = None): + 60 |+ if value is None: + 61 |+ value = {} +60 62 | import os; import sys; x = 1 +61 63 | +62 64 | + +B006_5.py:63:49: B006 [*] Do not use mutable data structures for argument defaults + | +63 | def import_module_wrong(value: dict[str, str] = {}): + | ^^ B006 +64 | import os; import sys + | + = help: Replace with `None`; initialize within function + +ℹ Possible fix +60 60 | import os; import sys; x = 1 +61 61 | +62 62 | +63 |-def import_module_wrong(value: dict[str, str] = {}): + 63 |+def import_module_wrong(value: dict[str, str] = None): +64 64 | import os; import sys + 65 |+ if value is None: + 66 |+ value = {} +65 67 | +66 68 | +67 69 | def import_module_wrong(value: dict[str, str] = {}): import os + +B006_5.py:67:49: B006 Do not use mutable data structures for argument defaults + | +67 | def import_module_wrong(value: dict[str, str] = {}): import os + | ^^ B006 + | + = help: Replace with `None`; initialize within function + +B006_5.py:70:49: B006 Do not use mutable data structures for argument defaults + | +70 | def import_module_wrong(value: dict[str, str] = {}): import os; import sys + | ^^ B006 + | + = help: Replace with `None`; initialize within function + +B006_5.py:73:49: B006 Do not use mutable data structures for argument defaults + | +73 | def import_module_wrong(value: dict[str, str] = {}): \ + | ^^ B006 +74 | import os + | + = help: Replace with `None`; initialize within function diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_B008.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_B008.py.snap index 8272eb6aa6a09..6f868ac491c64 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_B008.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_B008.py.snap @@ -424,7 +424,7 @@ B006_B008.py:288:52: B006 [*] Do not use mutable data structures for argument de 291 293 | 292 294 | -B006_B008.py:293:52: B006 Do not use mutable data structures for argument defaults +B006_B008.py:293:52: B006 [*] Do not use mutable data structures for argument defaults | 293 | def single_line_func_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -432,7 +432,19 @@ B006_B008.py:293:52: B006 Do not use mutable data structures for argument defaul | = help: Replace with `None`; initialize within function -B006_B008.py:297:52: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +290 290 | ... +291 291 | +292 292 | +293 |-def single_line_func_wrong(value: dict[str, str] = {}): + 293 |+def single_line_func_wrong(value: dict[str, str] = None): + 294 |+ if value is None: + 295 |+ value = {} +294 296 | """Docstring"""; ... +295 297 | +296 298 | + +B006_B008.py:297:52: B006 [*] Do not use mutable data structures for argument defaults | 297 | def single_line_func_wrong(value: dict[str, str] = {}): | ^^ B006 @@ -441,6 +453,18 @@ B006_B008.py:297:52: B006 Do not use mutable data structures for argument defaul | = help: Replace with `None`; initialize within function +ℹ Possible fix +294 294 | """Docstring"""; ... +295 295 | +296 296 | +297 |-def single_line_func_wrong(value: dict[str, str] = {}): + 297 |+def single_line_func_wrong(value: dict[str, str] = None): + 298 |+ if value is None: + 299 |+ value = {} +298 300 | """Docstring"""; \ +299 301 | ... +300 302 | + B006_B008.py:302:52: B006 [*] Do not use mutable data structures for argument defaults | 302 | def single_line_func_wrong(value: dict[str, str] = {