From 174c619a987671ac7c4964aa921d8560603b879b Mon Sep 17 00:00:00 2001 From: Dan Date: Sat, 1 Nov 2025 15:14:01 -0400 Subject: [PATCH 1/3] fix-21142 --- .../test/fixtures/flake8_bugbear/B006_10.py | 21 +++++ .../src/rules/flake8_bugbear/mod.rs | 2 + .../rules/mutable_argument_default.rs | 21 ++++- ...lake8_bugbear__tests__B006_B006_10.py.snap | 23 +++++ ...bear__tests__preview__B006_B006_10.py.snap | 85 +++++++++++++++++++ 5 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_10.py create mode 100644 crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_10.py.snap create mode 100644 crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_10.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_10.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_10.py new file mode 100644 index 0000000000..e49918308c --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_10.py @@ -0,0 +1,21 @@ +# Module-level mutable "constant" +MY_SET = {"ABC", "DEF"} # plain set +MY_LIST = [1, 2, 3] # plain list +MY_DICT = {"key": "value"} # plain dict + +# NOT triggering B006 (should trigger) +def func_A(s: set[str] = MY_SET): + return s + +# Triggering B006 (correct) +def func_B(s: set[str] = {"ABC", "DEF"}): + return s + +# Should trigger B006 +def func_C(items: list[int] = MY_LIST): + return items + +# Should trigger B006 +def func_D(data: dict[str, str] = MY_DICT): + return data + diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs b/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs index 163d175872..46a3bba26c 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs @@ -48,6 +48,7 @@ mod tests { #[test_case(Rule::MutableArgumentDefault, Path::new("B006_7.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_8.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_9.py"))] + #[test_case(Rule::MutableArgumentDefault, Path::new("B006_10.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_B008.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_1.pyi"))] #[test_case(Rule::NoExplicitStacklevel, Path::new("B028.py"))] @@ -93,6 +94,7 @@ mod tests { #[test_case(Rule::MutableArgumentDefault, Path::new("B006_7.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_8.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_9.py"))] + #[test_case(Rule::MutableArgumentDefault, Path::new("B006_10.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_B008.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_1.pyi"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { 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 94098831b8..578e8c638f 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 @@ -7,7 +7,9 @@ use ruff_python_ast::parenthesize::parenthesized_range; use ruff_python_ast::{self as ast, Expr, ParameterWithDefault}; use ruff_python_semantic::SemanticModel; use ruff_python_semantic::analyze::function_type::is_stub; -use ruff_python_semantic::analyze::typing::{is_immutable_annotation, is_mutable_expr}; +use ruff_python_semantic::analyze::typing::{ + find_binding_value, is_immutable_annotation, is_mutable_expr, +}; use ruff_python_trivia::{indentation_at_offset, textwrap}; use ruff_source_file::LineRanges; use ruff_text_size::Ranged; @@ -142,6 +144,23 @@ fn is_guaranteed_mutable_expr(expr: &Expr, semantic: &SemanticModel) -> bool { elts.iter().any(|e| is_guaranteed_mutable_expr(e, semantic)) } Expr::Named(ast::ExprNamed { value, .. }) => is_guaranteed_mutable_expr(value, semantic), + Expr::Name(name) => { + // Resolve module-level constants that are bound to mutable objects + let Some(binding_id) = semantic.only_binding(name) else { + return false; + }; + let binding = semantic.binding(binding_id); + // Only check assignments (not imports, function parameters, etc.) + if !binding.kind.is_assignment() { + return false; + } + // Get the assigned value and check if it's mutable + if let Some(value) = find_binding_value(binding, semantic) { + is_guaranteed_mutable_expr(value, semantic) + } else { + false + } + } _ => is_mutable_expr(expr, semantic), } } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_10.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_10.py.snap new file mode 100644 index 0000000000..c256bfd8ec --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_10.py.snap @@ -0,0 +1,23 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +--- +B006 [*] Do not use mutable data structures for argument defaults + --> B006_10.py:11:26 + | +10 | # Triggering B006 (correct) +11 | def func_B(s: set[str] = {"ABC", "DEF"}): + | ^^^^^^^^^^^^^^ +12 | return s + | +help: Replace with `None`; initialize within function +8 | return s +9 | +10 | # Triggering B006 (correct) + - def func_B(s: set[str] = {"ABC", "DEF"}): +11 + def func_B(s: set[str] = None): +12 + if s is None: +13 + s = {"ABC", "DEF"} +14 | return s +15 | +16 | # Should trigger B006 +note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_10.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_10.py.snap new file mode 100644 index 0000000000..006f0e328c --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_10.py.snap @@ -0,0 +1,85 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +--- +B006 [*] Do not use mutable data structures for argument defaults + --> B006_10.py:7:26 + | +6 | # NOT triggering B006 (should trigger) +7 | def func_A(s: set[str] = MY_SET): + | ^^^^^^ +8 | return s + | +help: Replace with `None`; initialize within function +4 | MY_DICT = {"key": "value"} # plain dict +5 | +6 | # NOT triggering B006 (should trigger) + - def func_A(s: set[str] = MY_SET): +7 + def func_A(s: set[str] = None): +8 + if s is None: +9 + s = MY_SET +10 | return s +11 | +12 | # Triggering B006 (correct) +note: This is an unsafe fix and may change runtime behavior + +B006 [*] Do not use mutable data structures for argument defaults + --> B006_10.py:11:26 + | +10 | # Triggering B006 (correct) +11 | def func_B(s: set[str] = {"ABC", "DEF"}): + | ^^^^^^^^^^^^^^ +12 | return s + | +help: Replace with `None`; initialize within function +8 | return s +9 | +10 | # Triggering B006 (correct) + - def func_B(s: set[str] = {"ABC", "DEF"}): +11 + def func_B(s: set[str] = None): +12 + if s is None: +13 + s = {"ABC", "DEF"} +14 | return s +15 | +16 | # Should trigger B006 +note: This is an unsafe fix and may change runtime behavior + +B006 [*] Do not use mutable data structures for argument defaults + --> B006_10.py:15:31 + | +14 | # Should trigger B006 +15 | def func_C(items: list[int] = MY_LIST): + | ^^^^^^^ +16 | return items + | +help: Replace with `None`; initialize within function +12 | return s +13 | +14 | # Should trigger B006 + - def func_C(items: list[int] = MY_LIST): +15 + def func_C(items: list[int] = None): +16 + if items is None: +17 + items = MY_LIST +18 | return items +19 | +20 | # Should trigger B006 +note: This is an unsafe fix and may change runtime behavior + +B006 [*] Do not use mutable data structures for argument defaults + --> B006_10.py:19:35 + | +18 | # Should trigger B006 +19 | def func_D(data: dict[str, str] = MY_DICT): + | ^^^^^^^ +20 | return data + | +help: Replace with `None`; initialize within function +16 | return items +17 | +18 | # Should trigger B006 + - def func_D(data: dict[str, str] = MY_DICT): +19 + def func_D(data: dict[str, str] = None): +20 + if data is None: +21 + data = MY_DICT +22 | return data +23 | +note: This is an unsafe fix and may change runtime behavior From 14b55f650436db6a47055f2f1d469929587d581f Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 10 Nov 2025 16:22:02 -0500 Subject: [PATCH 2/3] Exclude `UPPER_CASE` constants --- .../test/fixtures/flake8_bugbear/B006_10.py | 6 +- .../rules/mutable_argument_default.rs | 5 ++ ...lake8_bugbear__tests__B006_B006_10.py.snap | 2 +- ...bear__tests__preview__B006_B006_10.py.snap | 64 +------------------ 4 files changed, 10 insertions(+), 67 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_10.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_10.py index e49918308c..c527182cab 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_10.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_10.py @@ -3,7 +3,7 @@ MY_SET = {"ABC", "DEF"} # plain set MY_LIST = [1, 2, 3] # plain list MY_DICT = {"key": "value"} # plain dict -# NOT triggering B006 (should trigger) +# NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8) def func_A(s: set[str] = MY_SET): return s @@ -11,11 +11,11 @@ def func_A(s: set[str] = MY_SET): def func_B(s: set[str] = {"ABC", "DEF"}): return s -# Should trigger B006 +# NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8) def func_C(items: list[int] = MY_LIST): return items -# Should trigger B006 +# NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8) def func_D(data: dict[str, str] = MY_DICT): return data 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 578e8c638f..01a7fe2314 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 @@ -10,6 +10,7 @@ use ruff_python_semantic::analyze::function_type::is_stub; use ruff_python_semantic::analyze::typing::{ find_binding_value, is_immutable_annotation, is_mutable_expr, }; +use ruff_python_stdlib::str; use ruff_python_trivia::{indentation_at_offset, textwrap}; use ruff_source_file::LineRanges; use ruff_text_size::Ranged; @@ -145,6 +146,10 @@ fn is_guaranteed_mutable_expr(expr: &Expr, semantic: &SemanticModel) -> bool { } Expr::Named(ast::ExprNamed { value, .. }) => is_guaranteed_mutable_expr(value, semantic), Expr::Name(name) => { + // Exclude UPPER_CASE constants (PEP 8 convention - meant to be read-only) + if str::is_cased_uppercase(&name.id) { + return false; + } // Resolve module-level constants that are bound to mutable objects let Some(binding_id) = semantic.only_binding(name) else { return false; diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_10.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_10.py.snap index c256bfd8ec..3282c97ae8 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_10.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_10.py.snap @@ -19,5 +19,5 @@ help: Replace with `None`; initialize within function 13 + s = {"ABC", "DEF"} 14 | return s 15 | -16 | # Should trigger B006 +16 | # NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8) note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_10.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_10.py.snap index 006f0e328c..3282c97ae8 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_10.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_10.py.snap @@ -1,27 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs --- -B006 [*] Do not use mutable data structures for argument defaults - --> B006_10.py:7:26 - | -6 | # NOT triggering B006 (should trigger) -7 | def func_A(s: set[str] = MY_SET): - | ^^^^^^ -8 | return s - | -help: Replace with `None`; initialize within function -4 | MY_DICT = {"key": "value"} # plain dict -5 | -6 | # NOT triggering B006 (should trigger) - - def func_A(s: set[str] = MY_SET): -7 + def func_A(s: set[str] = None): -8 + if s is None: -9 + s = MY_SET -10 | return s -11 | -12 | # Triggering B006 (correct) -note: This is an unsafe fix and may change runtime behavior - B006 [*] Do not use mutable data structures for argument defaults --> B006_10.py:11:26 | @@ -40,46 +19,5 @@ help: Replace with `None`; initialize within function 13 + s = {"ABC", "DEF"} 14 | return s 15 | -16 | # Should trigger B006 -note: This is an unsafe fix and may change runtime behavior - -B006 [*] Do not use mutable data structures for argument defaults - --> B006_10.py:15:31 - | -14 | # Should trigger B006 -15 | def func_C(items: list[int] = MY_LIST): - | ^^^^^^^ -16 | return items - | -help: Replace with `None`; initialize within function -12 | return s -13 | -14 | # Should trigger B006 - - def func_C(items: list[int] = MY_LIST): -15 + def func_C(items: list[int] = None): -16 + if items is None: -17 + items = MY_LIST -18 | return items -19 | -20 | # Should trigger B006 -note: This is an unsafe fix and may change runtime behavior - -B006 [*] Do not use mutable data structures for argument defaults - --> B006_10.py:19:35 - | -18 | # Should trigger B006 -19 | def func_D(data: dict[str, str] = MY_DICT): - | ^^^^^^^ -20 | return data - | -help: Replace with `None`; initialize within function -16 | return items -17 | -18 | # Should trigger B006 - - def func_D(data: dict[str, str] = MY_DICT): -19 + def func_D(data: dict[str, str] = None): -20 + if data is None: -21 + data = MY_DICT -22 | return data -23 | +16 | # NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8) note: This is an unsafe fix and may change runtime behavior From 67a49f2c3203be2738a83509c74ab809adf39c74 Mon Sep 17 00:00:00 2001 From: Dan Date: Sat, 29 Nov 2025 00:08:10 -0500 Subject: [PATCH 3/3] Add tests, clarify docs --- .../test/fixtures/flake8_bugbear/B006_10.py | 6 +-- .../test/fixtures/flake8_bugbear/B006_11.py | 39 ++++++++++++++++ .../src/rules/flake8_bugbear/mod.rs | 2 + .../rules/mutable_argument_default.rs | 5 ++- ...lake8_bugbear__tests__B006_B006_10.py.snap | 2 +- ...lake8_bugbear__tests__B006_B006_11.py.snap | 4 ++ ...bear__tests__preview__B006_B006_10.py.snap | 2 +- ...bear__tests__preview__B006_B006_11.py.snap | 44 +++++++++++++++++++ 8 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_11.py create mode 100644 crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_11.py.snap create mode 100644 crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_11.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_10.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_10.py index c527182cab..88d6247362 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_10.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_10.py @@ -3,7 +3,7 @@ MY_SET = {"ABC", "DEF"} # plain set MY_LIST = [1, 2, 3] # plain list MY_DICT = {"key": "value"} # plain dict -# NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8) +# NOT triggering B006 (correct - PEP 8 specifies that UPPER_CASE variables are considered constants) def func_A(s: set[str] = MY_SET): return s @@ -11,11 +11,11 @@ def func_A(s: set[str] = MY_SET): def func_B(s: set[str] = {"ABC", "DEF"}): return s -# NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8) +# NOT triggering B006 (correct - PEP 8 specifies that UPPER_CASE variables are considered constants) def func_C(items: list[int] = MY_LIST): return items -# NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8) +# NOT triggering B006 (correct - PEP 8 specifies that UPPER_CASE variables are considered constants) def func_D(data: dict[str, str] = MY_DICT): return data diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_11.py b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_11.py new file mode 100644 index 0000000000..dd279030c2 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_bugbear/B006_11.py @@ -0,0 +1,39 @@ +# Test nested function reading value from outer scope +def outer_function(): + my_list = [1, 2, 3] # Assignment in outer function + + # Should trigger B006 - nested function using mutable from outer scope + def inner_function(items=my_list): + return items + + return inner_function + + +# Test that imports don't trigger B006 (assignment restriction) +from some_module import IMPORTED_LIST + +# Should NOT trigger B006 - imported names are not assignments +def func_with_import(items=IMPORTED_LIST): + return items + + +# Test that function parameters don't trigger B006 (assignment restriction) +def func_with_param(param_list): + # Should NOT trigger B006 - function parameters are not assignments + def nested_func(items=param_list): + return items + + return nested_func + + +# Test module-level assignment that should trigger B006 +module_list = [1, 2, 3] # Module-level assignment + +# Should trigger B006 - module-level assignment used as default +def func_with_module_var(items=module_list): + return items + + +# Test that non-assignment bindings don't trigger B006 +# (This would require a more complex setup to actually test, so we'll focus on the key cases above) + diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs b/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs index 46a3bba26c..44af8c2232 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/mod.rs @@ -49,6 +49,7 @@ mod tests { #[test_case(Rule::MutableArgumentDefault, Path::new("B006_8.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_9.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_10.py"))] + #[test_case(Rule::MutableArgumentDefault, Path::new("B006_11.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_B008.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_1.pyi"))] #[test_case(Rule::NoExplicitStacklevel, Path::new("B028.py"))] @@ -95,6 +96,7 @@ mod tests { #[test_case(Rule::MutableArgumentDefault, Path::new("B006_8.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_9.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_10.py"))] + #[test_case(Rule::MutableArgumentDefault, Path::new("B006_11.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_B008.py"))] #[test_case(Rule::MutableArgumentDefault, Path::new("B006_1.pyi"))] fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> { 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 01a7fe2314..fb4be84137 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 @@ -150,12 +150,15 @@ fn is_guaranteed_mutable_expr(expr: &Expr, semantic: &SemanticModel) -> bool { if str::is_cased_uppercase(&name.id) { return false; } - // Resolve module-level constants that are bound to mutable objects + // Resolve the name in the current scope (module-level, function-level, etc.) + // This can resolve constants, non-constants, and any name in the current scope let Some(binding_id) = semantic.only_binding(name) else { return false; }; let binding = semantic.binding(binding_id); // Only check assignments (not imports, function parameters, etc.) + // This restriction ensures we only flag cases where a mutable object is explicitly + // assigned to a name, which is then used as a default argument. if !binding.kind.is_assignment() { return false; } diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_10.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_10.py.snap index 3282c97ae8..1c9554b69b 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_10.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_10.py.snap @@ -19,5 +19,5 @@ help: Replace with `None`; initialize within function 13 + s = {"ABC", "DEF"} 14 | return s 15 | -16 | # NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8) +16 | # NOT triggering B006 (correct - PEP 8 specifies that UPPER_CASE variables are considered constants) note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_11.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_11.py.snap new file mode 100644 index 0000000000..967e60a4f9 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B006_B006_11.py.snap @@ -0,0 +1,4 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +--- + diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_10.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_10.py.snap index 3282c97ae8..1c9554b69b 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_10.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_10.py.snap @@ -19,5 +19,5 @@ help: Replace with `None`; initialize within function 13 + s = {"ABC", "DEF"} 14 | return s 15 | -16 | # NOT triggering B006 (correct - UPPER_CASE constants are excluded per PEP 8) +16 | # NOT triggering B006 (correct - PEP 8 specifies that UPPER_CASE variables are considered constants) note: This is an unsafe fix and may change runtime behavior diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_11.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_11.py.snap new file mode 100644 index 0000000000..2a57e5ad16 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__preview__B006_B006_11.py.snap @@ -0,0 +1,44 @@ +--- +source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs +--- +B006 [*] Do not use mutable data structures for argument defaults + --> B006_11.py:6:30 + | +5 | # Should trigger B006 - nested function using mutable from outer scope +6 | def inner_function(items=my_list): + | ^^^^^^^ +7 | return items + | +help: Replace with `None`; initialize within function +3 | my_list = [1, 2, 3] # Assignment in outer function +4 | +5 | # Should trigger B006 - nested function using mutable from outer scope + - def inner_function(items=my_list): +6 + def inner_function(items=None): +7 + if items is None: +8 + items = my_list +9 | return items +10 | +11 | return inner_function +note: This is an unsafe fix and may change runtime behavior + +B006 [*] Do not use mutable data structures for argument defaults + --> B006_11.py:33:32 + | +32 | # Should trigger B006 - module-level assignment used as default +33 | def func_with_module_var(items=module_list): + | ^^^^^^^^^^^ +34 | return items + | +help: Replace with `None`; initialize within function +30 | module_list = [1, 2, 3] # Module-level assignment +31 | +32 | # Should trigger B006 - module-level assignment used as default + - def func_with_module_var(items=module_list): +33 + def func_with_module_var(items=None): +34 + if items is None: +35 + items = module_list +36 | return items +37 | +38 | +note: This is an unsafe fix and may change runtime behavior