diff --git a/resources/test/fixtures/FBT.py b/resources/test/fixtures/FBT.py index a996fd1855..3be547fde9 100644 --- a/resources/test/fixtures/FBT.py +++ b/resources/test/fixtures/FBT.py @@ -38,5 +38,20 @@ def function( def used(do): return do + used("a", True) used(do=True) + + +# Avoid FBT003 for explicitly allowed methods. +""" +FBT003 Boolean positional value on dict +""" +a = {"a": "b"} +a.get("hello", False) +{}.get("hello", False) +{}.setdefault("hello", True) +{}.pop("hello", False) +{}.pop(True, False) +dict.fromkeys(("world",), True) +{}.deploy(True, False) diff --git a/src/check_ast.rs b/src/check_ast.rs index 3898ae2be2..d6d7dc393b 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -1638,7 +1638,7 @@ where // flake8-boolean-trap if self.settings.enabled.contains(&CheckCode::FBT003) { flake8_boolean_trap::plugins::check_boolean_positional_value_in_function_call( - self, args, + self, args, func, ); } if let ExprKind::Name { id, ctx } = &func.node { diff --git a/src/flake8_boolean_trap/plugins.rs b/src/flake8_boolean_trap/plugins.rs index d211738045..9bcd3c8586 100644 --- a/src/flake8_boolean_trap/plugins.rs +++ b/src/flake8_boolean_trap/plugins.rs @@ -5,6 +5,19 @@ use crate::ast::types::Range; use crate::check_ast::Checker; use crate::checks::{Check, CheckKind}; +const FUNC_NAME_ALLOWLIST: &[&str] = &["get", "setdefault", "pop", "fromkeys"]; + +/// Returns `true` if an argument is allowed to use a boolean trap. To return +/// `true`, the function name must be explicitly allowed, and the argument must +/// be either the first or second argument in the call. +fn allow_boolean_trap(func: &Expr) -> bool { + if let ExprKind::Attribute { attr, .. } = &func.node { + FUNC_NAME_ALLOWLIST.contains(&attr.as_ref()) + } else { + false + } +} + fn is_boolean_arg(arg: &Expr) -> bool { matches!( &arg.node, @@ -60,8 +73,15 @@ pub fn check_boolean_default_value_in_function_definition( } } -pub fn check_boolean_positional_value_in_function_call(checker: &mut Checker, args: &[Expr]) { - for arg in args { +pub fn check_boolean_positional_value_in_function_call( + checker: &mut Checker, + args: &[Expr], + func: &Expr, +) { + for (index, arg) in args.iter().enumerate() { + if index < 2 && allow_boolean_trap(func) { + continue; + } add_if_boolean( checker, arg, diff --git a/src/snapshots/ruff__linter__tests__FBT003_FBT.py.snap b/src/snapshots/ruff__linter__tests__FBT003_FBT.py.snap index 58f30d34af..79dd04e9cc 100644 --- a/src/snapshots/ruff__linter__tests__FBT003_FBT.py.snap +++ b/src/snapshots/ruff__linter__tests__FBT003_FBT.py.snap @@ -4,10 +4,26 @@ expression: checks --- - kind: BooleanPositionalValueInFunctionCall location: - row: 41 + row: 42 column: 10 end_location: - row: 41 + row: 42 column: 14 fix: ~ +- kind: BooleanPositionalValueInFunctionCall + location: + row: 57 + column: 10 + end_location: + row: 57 + column: 14 + fix: ~ +- kind: BooleanPositionalValueInFunctionCall + location: + row: 57 + column: 16 + end_location: + row: 57 + column: 21 + fix: ~