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 e104189015..71146ea184 100644 --- a/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py +++ b/crates/ruff/resources/test/fixtures/flake8_bugbear/B006_B008.py @@ -68,6 +68,20 @@ def this_is_also_wrong(value={}): ... +class Foo: + @staticmethod + def this_is_also_wrong_and_more_indented(value={}): + pass + + +def multiline_arg_wrong(value={ + +}): + ... + +def single_line_func_wrong(value = {}): ... + + def and_this(value=set()): ... diff --git a/crates/ruff/src/checkers/ast/analyze/parameters.rs b/crates/ruff/src/checkers/ast/analyze/parameters.rs index fe300a8ea1..41d525197b 100644 --- a/crates/ruff/src/checkers/ast/analyze/parameters.rs +++ b/crates/ruff/src/checkers/ast/analyze/parameters.rs @@ -6,9 +6,6 @@ use crate::rules::{flake8_bugbear, flake8_pyi, ruff}; /// Run lint rules over a [`Parameters`] syntax node. pub(crate) fn parameters(parameters: &Parameters, checker: &mut Checker) { - if checker.enabled(Rule::MutableArgumentDefault) { - flake8_bugbear::rules::mutable_argument_default(checker, parameters); - } if checker.enabled(Rule::FunctionCallInDefaultArgument) { flake8_bugbear::rules::function_call_in_argument_default(checker, parameters); } diff --git a/crates/ruff/src/checkers/ast/analyze/statement.rs b/crates/ruff/src/checkers/ast/analyze/statement.rs index db0fa91f56..366f324511 100644 --- a/crates/ruff/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff/src/checkers/ast/analyze/statement.rs @@ -77,7 +77,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { parameters, body, type_params, - range: _, + range, }) => { if checker.enabled(Rule::DjangoNonLeadingReceiverDecorator) { flake8_django::rules::non_leading_receiver_decorator(checker, decorator_list); @@ -204,6 +204,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.enabled(Rule::CachedInstanceMethod) { flake8_bugbear::rules::cached_instance_method(checker, decorator_list); } + if checker.enabled(Rule::MutableArgumentDefault) { + flake8_bugbear::rules::mutable_argument_default(checker, parameters, body, *range); + } if checker.any_enabled(&[ Rule::UnnecessaryReturnNone, Rule::ImplicitReturnValue, 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 e19d7930dc..d6de9e965a 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 @@ -1,10 +1,16 @@ -use ruff_python_ast::{ParameterWithDefault, Parameters, Ranged}; - -use ruff_diagnostics::{Diagnostic, Violation}; +use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation}; use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::docstrings::leading_space; +use ruff_python_ast::{ParameterWithDefault, Parameters, Ranged, Stmt}; +use ruff_python_parser::lexer::lex_starts_at; +use ruff_python_parser::{Mode, Tok}; use ruff_python_semantic::analyze::typing::{is_immutable_annotation, is_mutable_expr}; +use ruff_python_trivia::indentation_at_offset; +use ruff_source_file::Locator; +use ruff_text_size::TextRange; use crate::checkers::ast::Checker; +use crate::registry::AsRule; /// ## What it does /// Checks for uses of mutable objects as function argument defaults. @@ -50,14 +56,26 @@ use crate::checkers::ast::Checker; pub struct MutableArgumentDefault; impl Violation for MutableArgumentDefault { + const AUTOFIX: AutofixKind = AutofixKind::Sometimes; + #[derive_message_formats] fn message(&self) -> String { format!("Do not use mutable data structures for argument defaults") } + fn autofix_title(&self) -> Option { + Some(format!( + "Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None`" + )) + } } /// B006 -pub(crate) fn mutable_argument_default(checker: &mut Checker, parameters: &Parameters) { +pub(crate) fn mutable_argument_default( + checker: &mut Checker, + parameters: &Parameters, + body: &[Stmt], + func_range: TextRange, +) { // Scan in reverse order to right-align zip(). for ParameterWithDefault { parameter, @@ -79,9 +97,53 @@ pub(crate) fn mutable_argument_default(checker: &mut Checker, parameters: &Param .as_ref() .is_some_and(|expr| is_immutable_annotation(expr, checker.semantic())) { - checker - .diagnostics - .push(Diagnostic::new(MutableArgumentDefault, default.range())); + let mut diagnostic = Diagnostic::new(MutableArgumentDefault, default.range()); + + // If the function body is on the same line as the function def, do not fix + if checker.patch(diagnostic.kind.rule()) + && !is_single_line(checker.locator(), func_range, body) + { + // Set the default arg value to None + let arg_edit = Edit::range_replacement("None".to_string(), default.range()); + + // Add conditional check to set the default arg to its original value if still None + let mut check_lines = String::new(); + let indentation = + indentation_at_offset(body[0].start(), checker.locator()).unwrap_or_default(); + let indentation = leading_space(indentation); + // body[0].start() starts at correct indentation so we do need to add indentation + // before pushing the if statement + check_lines.push_str(format!("if {} is None:\n", parameter.name.as_str()).as_str()); + check_lines.push_str(indentation); + check_lines.push_str(checker.stylist().indentation()); + check_lines.push_str( + format!( + "{} = {}", + parameter.name.as_str(), + checker.generator().expr(default), + ) + .as_str(), + ); + check_lines.push_str(&checker.stylist().line_ending()); + check_lines.push_str(indentation); + let check_edit = Edit::insertion(check_lines, body[0].start()); + + diagnostic.set_fix(Fix::manual_edits(arg_edit, [check_edit])); + } + checker.diagnostics.push(diagnostic); } } } + +fn is_single_line(locator: &Locator, func_range: TextRange, body: &[Stmt]) -> bool { + let arg_string = locator.slice(func_range); + for (tok, range) in lex_starts_at(arg_string, Mode::Module, func_range.start()).flatten() { + match tok { + Tok::Colon => { + return !locator.contains_line_break(TextRange::new(range.end(), body[0].start())); + } + _ => continue, + } + } + false +} 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 abac933f98..dd5f00fbec 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 @@ -1,123 +1,376 @@ --- source: crates/ruff/src/rules/flake8_bugbear/mod.rs --- -B006_B008.py:63:25: B006 Do not use mutable data structures for argument defaults +B006_B008.py:63:25: B006 [*] Do not use mutable data structures for argument defaults | 63 | def this_is_wrong(value=[1, 2, 3]): | ^^^^^^^^^ B006 64 | ... | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` -B006_B008.py:67:30: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +60 60 | # Flag mutable literals/comprehensions +61 61 | +62 62 | +63 |-def this_is_wrong(value=[1, 2, 3]): + 63 |+def this_is_wrong(value=None): + 64 |+ if value is None: + 65 |+ value = [1, 2, 3] +64 66 | ... +65 67 | +66 68 | + +B006_B008.py:67:30: B006 [*] Do not use mutable data structures for argument defaults | 67 | def this_is_also_wrong(value={}): | ^^ B006 68 | ... | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` -B006_B008.py:71:20: B006 Do not use mutable data structures for argument defaults - | -71 | def and_this(value=set()): - | ^^^^^ B006 -72 | ... - | +ℹ Possible fix +64 64 | ... +65 65 | +66 66 | +67 |-def this_is_also_wrong(value={}): + 67 |+def this_is_also_wrong(value=None): + 68 |+ if value is None: + 69 |+ value = {} +68 70 | ... +69 71 | +70 72 | -B006_B008.py:75:20: B006 Do not use mutable data structures for argument defaults +B006_B008.py:73:52: B006 [*] Do not use mutable data structures for argument defaults | -75 | def this_too(value=collections.OrderedDict()): - | ^^^^^^^^^^^^^^^^^^^^^^^^^ B006 -76 | ... +71 | class Foo: +72 | @staticmethod +73 | def this_is_also_wrong_and_more_indented(value={}): + | ^^ B006 +74 | pass | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` -B006_B008.py:79:32: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +70 70 | +71 71 | class Foo: +72 72 | @staticmethod +73 |- def this_is_also_wrong_and_more_indented(value={}): + 73 |+ def this_is_also_wrong_and_more_indented(value=None): + 74 |+ if value is None: + 75 |+ value = {} +74 76 | pass +75 77 | +76 78 | + +B006_B008.py:77:31: B006 [*] Do not use mutable data structures for argument defaults + | +77 | def multiline_arg_wrong(value={ + | _______________________________^ +78 | | +79 | | }): + | |_^ B006 +80 | ... + | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` + +ℹ Possible fix +74 74 | pass +75 75 | +76 76 | +77 |-def multiline_arg_wrong(value={ +78 |- +79 |-}): + 77 |+def multiline_arg_wrong(value=None): + 78 |+ if value is None: + 79 |+ value = {} +80 80 | ... +81 81 | +82 82 | def single_line_func_wrong(value = {}): ... + +B006_B008.py:82:36: B006 Do not use mutable data structures for argument defaults | -79 | async def async_this_too(value=collections.defaultdict()): - | ^^^^^^^^^^^^^^^^^^^^^^^^^ B006 80 | ... +81 | +82 | def single_line_func_wrong(value = {}): ... + | ^^ B006 | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` -B006_B008.py:83:26: B006 Do not use mutable data structures for argument defaults +B006_B008.py:85:20: B006 [*] Do not use mutable data structures for argument defaults | -83 | def dont_forget_me(value=collections.deque()): +85 | def and_this(value=set()): + | ^^^^^ B006 +86 | ... + | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` + +ℹ Possible fix +82 82 | def single_line_func_wrong(value = {}): ... +83 83 | +84 84 | +85 |-def and_this(value=set()): + 85 |+def and_this(value=None): + 86 |+ if value is None: + 87 |+ value = set() +86 88 | ... +87 89 | +88 90 | + +B006_B008.py:89:20: B006 [*] Do not use mutable data structures for argument defaults + | +89 | def this_too(value=collections.OrderedDict()): + | ^^^^^^^^^^^^^^^^^^^^^^^^^ B006 +90 | ... + | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` + +ℹ Possible fix +86 86 | ... +87 87 | +88 88 | +89 |-def this_too(value=collections.OrderedDict()): + 89 |+def this_too(value=None): + 90 |+ if value is None: + 91 |+ value = collections.OrderedDict() +90 92 | ... +91 93 | +92 94 | + +B006_B008.py:93:32: B006 [*] Do not use mutable data structures for argument defaults + | +93 | async def async_this_too(value=collections.defaultdict()): + | ^^^^^^^^^^^^^^^^^^^^^^^^^ B006 +94 | ... + | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` + +ℹ Possible fix +90 90 | ... +91 91 | +92 92 | +93 |-async def async_this_too(value=collections.defaultdict()): + 93 |+async def async_this_too(value=None): + 94 |+ if value is None: + 95 |+ value = collections.defaultdict() +94 96 | ... +95 97 | +96 98 | + +B006_B008.py:97:26: B006 [*] Do not use mutable data structures for argument defaults + | +97 | def dont_forget_me(value=collections.deque()): | ^^^^^^^^^^^^^^^^^^^ B006 -84 | ... +98 | ... | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` -B006_B008.py:88:46: B006 Do not use mutable data structures for argument defaults - | -87 | # N.B. we're also flagging the function call in the comprehension -88 | def list_comprehension_also_not_okay(default=[i**2 for i in range(3)]): - | ^^^^^^^^^^^^^^^^^^^^^^^^ B006 -89 | pass - | +ℹ Possible fix +94 94 | ... +95 95 | +96 96 | +97 |-def dont_forget_me(value=collections.deque()): + 97 |+def dont_forget_me(value=None): + 98 |+ if value is None: + 99 |+ value = collections.deque() +98 100 | ... +99 101 | +100 102 | -B006_B008.py:92:46: B006 Do not use mutable data structures for argument defaults - | -92 | def dict_comprehension_also_not_okay(default={i: i**2 for i in range(3)}): - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ B006 -93 | pass - | - -B006_B008.py:96:45: B006 Do not use mutable data structures for argument defaults - | -96 | def set_comprehension_also_not_okay(default={i**2 for i in range(3)}): - | ^^^^^^^^^^^^^^^^^^^^^^^^ B006 -97 | pass - | - -B006_B008.py:100:33: B006 Do not use mutable data structures for argument defaults +B006_B008.py:102:46: B006 [*] Do not use mutable data structures for argument defaults | -100 | def kwonlyargs_mutable(*, value=[]): +101 | # N.B. we're also flagging the function call in the comprehension +102 | def list_comprehension_also_not_okay(default=[i**2 for i in range(3)]): + | ^^^^^^^^^^^^^^^^^^^^^^^^ B006 +103 | pass + | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` + +ℹ Possible fix +99 99 | +100 100 | +101 101 | # N.B. we're also flagging the function call in the comprehension +102 |-def list_comprehension_also_not_okay(default=[i**2 for i in range(3)]): + 102 |+def list_comprehension_also_not_okay(default=None): + 103 |+ if default is None: + 104 |+ default = [i ** 2 for i in range(3)] +103 105 | pass +104 106 | +105 107 | + +B006_B008.py:106:46: B006 [*] Do not use mutable data structures for argument defaults + | +106 | def dict_comprehension_also_not_okay(default={i: i**2 for i in range(3)}): + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ B006 +107 | pass + | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` + +ℹ Possible fix +103 103 | pass +104 104 | +105 105 | +106 |-def dict_comprehension_also_not_okay(default={i: i**2 for i in range(3)}): + 106 |+def dict_comprehension_also_not_okay(default=None): + 107 |+ if default is None: + 108 |+ default = {i: i ** 2 for i in range(3)} +107 109 | pass +108 110 | +109 111 | + +B006_B008.py:110:45: B006 [*] Do not use mutable data structures for argument defaults + | +110 | def set_comprehension_also_not_okay(default={i**2 for i in range(3)}): + | ^^^^^^^^^^^^^^^^^^^^^^^^ B006 +111 | pass + | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` + +ℹ Possible fix +107 107 | pass +108 108 | +109 109 | +110 |-def set_comprehension_also_not_okay(default={i**2 for i in range(3)}): + 110 |+def set_comprehension_also_not_okay(default=None): + 111 |+ if default is None: + 112 |+ default = {i ** 2 for i in range(3)} +111 113 | pass +112 114 | +113 115 | + +B006_B008.py:114:33: B006 [*] Do not use mutable data structures for argument defaults + | +114 | def kwonlyargs_mutable(*, value=[]): | ^^ B006 -101 | ... +115 | ... | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` -B006_B008.py:221:20: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +111 111 | pass +112 112 | +113 113 | +114 |-def kwonlyargs_mutable(*, value=[]): + 114 |+def kwonlyargs_mutable(*, value=None): + 115 |+ if value is None: + 116 |+ value = [] +115 117 | ... +116 118 | +117 119 | + +B006_B008.py:235:20: B006 [*] Do not use mutable data structures for argument defaults | -219 | # B006 and B008 -220 | # We should handle arbitrary nesting of these B008. -221 | def nested_combo(a=[float(3), dt.datetime.now()]): +233 | # B006 and B008 +234 | # We should handle arbitrary nesting of these B008. +235 | def nested_combo(a=[float(3), dt.datetime.now()]): | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B006 -222 | pass +236 | pass | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` -B006_B008.py:258:27: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +232 232 | +233 233 | # B006 and B008 +234 234 | # We should handle arbitrary nesting of these B008. +235 |-def nested_combo(a=[float(3), dt.datetime.now()]): + 235 |+def nested_combo(a=None): + 236 |+ if a is None: + 237 |+ a = [float(3), dt.datetime.now()] +236 238 | pass +237 239 | +238 240 | + +B006_B008.py:272:27: B006 [*] Do not use mutable data structures for argument defaults | -257 | def mutable_annotations( -258 | a: list[int] | None = [], +271 | def mutable_annotations( +272 | a: list[int] | None = [], | ^^ B006 -259 | b: Optional[Dict[int, int]] = {}, -260 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +273 | b: Optional[Dict[int, int]] = {}, +274 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` -B006_B008.py:259:35: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +269 269 | +270 270 | +271 271 | def mutable_annotations( +272 |- a: list[int] | None = [], + 272 |+ a: list[int] | None = None, +273 273 | b: Optional[Dict[int, int]] = {}, +274 274 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +275 275 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +276 276 | ): + 277 |+ if a is None: + 278 |+ a = [] +277 279 | pass + +B006_B008.py:273:35: B006 [*] Do not use mutable data structures for argument defaults | -257 | def mutable_annotations( -258 | a: list[int] | None = [], -259 | b: Optional[Dict[int, int]] = {}, +271 | def mutable_annotations( +272 | a: list[int] | None = [], +273 | b: Optional[Dict[int, int]] = {}, | ^^ B006 -260 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), -261 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +274 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +275 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` -B006_B008.py:260:62: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +270 270 | +271 271 | def mutable_annotations( +272 272 | a: list[int] | None = [], +273 |- b: Optional[Dict[int, int]] = {}, + 273 |+ b: Optional[Dict[int, int]] = None, +274 274 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +275 275 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +276 276 | ): + 277 |+ if b is None: + 278 |+ b = {} +277 279 | pass + +B006_B008.py:274:62: B006 [*] Do not use mutable data structures for argument defaults | -258 | a: list[int] | None = [], -259 | b: Optional[Dict[int, int]] = {}, -260 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +272 | a: list[int] | None = [], +273 | b: Optional[Dict[int, int]] = {}, +274 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), | ^^^^^ B006 -261 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), -262 | ): +275 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +276 | ): | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` -B006_B008.py:261:80: B006 Do not use mutable data structures for argument defaults +ℹ Possible fix +271 271 | def mutable_annotations( +272 272 | a: list[int] | None = [], +273 273 | b: Optional[Dict[int, int]] = {}, +274 |- c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), + 274 |+ c: Annotated[Union[Set[str], abc.Sized], "annotation"] = None, +275 275 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +276 276 | ): + 277 |+ if c is None: + 278 |+ c = set() +277 279 | pass + +B006_B008.py:275:80: B006 [*] Do not use mutable data structures for argument defaults | -259 | b: Optional[Dict[int, int]] = {}, -260 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), -261 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +273 | b: Optional[Dict[int, int]] = {}, +274 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +275 | d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), | ^^^^^ B006 -262 | ): -263 | pass +276 | ): +277 | pass | + = help: Replace mutable data structure with `None` in argument default and replace it with data structure inside the function if still `None` + +ℹ Possible fix +272 272 | a: list[int] | None = [], +273 273 | b: Optional[Dict[int, int]] = {}, +274 274 | c: Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), +275 |- d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = set(), + 275 |+ d: typing_extensions.Annotated[Union[Set[str], abc.Sized], "annotation"] = None, +276 276 | ): + 277 |+ if d is None: + 278 |+ d = set() +277 279 | pass diff --git a/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B008_B006_B008.py.snap b/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B008_B006_B008.py.snap index 36a464813a..dc747bc5b4 100644 --- a/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B008_B006_B008.py.snap +++ b/crates/ruff/src/rules/flake8_bugbear/snapshots/ruff__rules__flake8_bugbear__tests__B008_B006_B008.py.snap @@ -1,83 +1,83 @@ --- source: crates/ruff/src/rules/flake8_bugbear/mod.rs --- -B006_B008.py:88:61: B008 Do not perform function call `range` in argument defaults - | -87 | # N.B. we're also flagging the function call in the comprehension -88 | def list_comprehension_also_not_okay(default=[i**2 for i in range(3)]): - | ^^^^^^^^ B008 -89 | pass - | - -B006_B008.py:92:64: B008 Do not perform function call `range` in argument defaults - | -92 | def dict_comprehension_also_not_okay(default={i: i**2 for i in range(3)}): - | ^^^^^^^^ B008 -93 | pass - | - -B006_B008.py:96:60: B008 Do not perform function call `range` in argument defaults - | -96 | def set_comprehension_also_not_okay(default={i**2 for i in range(3)}): - | ^^^^^^^^ B008 -97 | pass - | - -B006_B008.py:112:39: B008 Do not perform function call `time.time` in argument defaults +B006_B008.py:102:61: B008 Do not perform function call `range` in argument defaults | -110 | # B008 -111 | # Flag function calls as default args (including if they are part of a sub-expression) -112 | def in_fact_all_calls_are_wrong(value=time.time()): +101 | # N.B. we're also flagging the function call in the comprehension +102 | def list_comprehension_also_not_okay(default=[i**2 for i in range(3)]): + | ^^^^^^^^ B008 +103 | pass + | + +B006_B008.py:106:64: B008 Do not perform function call `range` in argument defaults + | +106 | def dict_comprehension_also_not_okay(default={i: i**2 for i in range(3)}): + | ^^^^^^^^ B008 +107 | pass + | + +B006_B008.py:110:60: B008 Do not perform function call `range` in argument defaults + | +110 | def set_comprehension_also_not_okay(default={i**2 for i in range(3)}): + | ^^^^^^^^ B008 +111 | pass + | + +B006_B008.py:126:39: B008 Do not perform function call `time.time` in argument defaults + | +124 | # B008 +125 | # Flag function calls as default args (including if they are part of a sub-expression) +126 | def in_fact_all_calls_are_wrong(value=time.time()): | ^^^^^^^^^^^ B008 -113 | ... +127 | ... | -B006_B008.py:116:12: B008 Do not perform function call `dt.datetime.now` in argument defaults +B006_B008.py:130:12: B008 Do not perform function call `dt.datetime.now` in argument defaults | -116 | def f(when=dt.datetime.now() + dt.timedelta(days=7)): +130 | def f(when=dt.datetime.now() + dt.timedelta(days=7)): | ^^^^^^^^^^^^^^^^^ B008 -117 | pass +131 | pass | -B006_B008.py:120:30: B008 Do not perform function call in argument defaults +B006_B008.py:134:30: B008 Do not perform function call in argument defaults | -120 | def can_even_catch_lambdas(a=(lambda x: x)()): +134 | def can_even_catch_lambdas(a=(lambda x: x)()): | ^^^^^^^^^^^^^^^ B008 -121 | ... +135 | ... | -B006_B008.py:221:31: B008 Do not perform function call `dt.datetime.now` in argument defaults +B006_B008.py:235:31: B008 Do not perform function call `dt.datetime.now` in argument defaults | -219 | # B006 and B008 -220 | # We should handle arbitrary nesting of these B008. -221 | def nested_combo(a=[float(3), dt.datetime.now()]): +233 | # B006 and B008 +234 | # We should handle arbitrary nesting of these B008. +235 | def nested_combo(a=[float(3), dt.datetime.now()]): | ^^^^^^^^^^^^^^^^^ B008 -222 | pass +236 | pass | -B006_B008.py:227:22: B008 Do not perform function call `map` in argument defaults +B006_B008.py:241:22: B008 Do not perform function call `map` in argument defaults | -225 | # Don't flag nested B006 since we can't guarantee that -226 | # it isn't made mutable by the outer operation. -227 | def no_nested_b006(a=map(lambda s: s.upper(), ["a", "b", "c"])): +239 | # Don't flag nested B006 since we can't guarantee that +240 | # it isn't made mutable by the outer operation. +241 | def no_nested_b006(a=map(lambda s: s.upper(), ["a", "b", "c"])): | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B008 -228 | pass +242 | pass | -B006_B008.py:232:19: B008 Do not perform function call `random.randint` in argument defaults +B006_B008.py:246:19: B008 Do not perform function call `random.randint` in argument defaults | -231 | # B008-ception. -232 | def nested_b008(a=random.randint(0, dt.datetime.now().year)): +245 | # B008-ception. +246 | def nested_b008(a=random.randint(0, dt.datetime.now().year)): | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B008 -233 | pass +247 | pass | -B006_B008.py:232:37: B008 Do not perform function call `dt.datetime.now` in argument defaults +B006_B008.py:246:37: B008 Do not perform function call `dt.datetime.now` in argument defaults | -231 | # B008-ception. -232 | def nested_b008(a=random.randint(0, dt.datetime.now().year)): +245 | # B008-ception. +246 | def nested_b008(a=random.randint(0, dt.datetime.now().year)): | ^^^^^^^^^^^^^^^^^ B008 -233 | pass +247 | pass |