mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 13:51:16 +00:00
[flake8-bugbear
] Fix mutable-contextvar-default (B039)
to resolve annotated function calls properly (#14532)
## Summary <!-- What's the purpose of the change? What does it do, and why? --> Fix #14525 ## Test Plan <!-- How was it tested? --> New test cases --------- Signed-off-by: harupy <hkawamura0130@gmail.com>
This commit is contained in:
parent
1f303a5eb6
commit
e3d792605f
4 changed files with 104 additions and 77 deletions
|
@ -14,6 +14,8 @@ ContextVar("cv", default=frozenset())
|
||||||
ContextVar("cv", default=MappingProxyType({}))
|
ContextVar("cv", default=MappingProxyType({}))
|
||||||
ContextVar("cv", default=re.compile("foo"))
|
ContextVar("cv", default=re.compile("foo"))
|
||||||
ContextVar("cv", default=float(1))
|
ContextVar("cv", default=float(1))
|
||||||
|
ContextVar("cv", default=frozenset[str]())
|
||||||
|
ContextVar[frozenset[str]]("cv", default=frozenset[str]())
|
||||||
|
|
||||||
# Bad
|
# Bad
|
||||||
ContextVar("cv", default=[])
|
ContextVar("cv", default=[])
|
||||||
|
@ -25,6 +27,8 @@ ContextVar("cv", default=[char for char in "foo"])
|
||||||
ContextVar("cv", default={char for char in "foo"})
|
ContextVar("cv", default={char for char in "foo"})
|
||||||
ContextVar("cv", default={char: idx for idx, char in enumerate("foo")})
|
ContextVar("cv", default={char: idx for idx, char in enumerate("foo")})
|
||||||
ContextVar("cv", default=collections.deque())
|
ContextVar("cv", default=collections.deque())
|
||||||
|
ContextVar("cv", default=set[str]())
|
||||||
|
ContextVar[set[str]]("cv", default=set[str]())
|
||||||
|
|
||||||
def bar() -> list[int]:
|
def bar() -> list[int]:
|
||||||
return [1, 2, 3]
|
return [1, 2, 3]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
use ruff_python_ast::helpers::map_subscript;
|
||||||
use ruff_python_ast::name::QualifiedName;
|
use ruff_python_ast::name::QualifiedName;
|
||||||
use ruff_python_ast::{self as ast, Expr};
|
use ruff_python_ast::{self as ast, Expr};
|
||||||
use ruff_python_semantic::analyze::typing::{is_immutable_func, is_mutable_expr, is_mutable_func};
|
use ruff_python_semantic::analyze::typing::{is_immutable_func, is_mutable_expr, is_mutable_func};
|
||||||
|
@ -96,7 +97,7 @@ pub(crate) fn mutable_contextvar_default(checker: &mut Checker, call: &ast::Expr
|
||||||
&& !is_immutable_func(func, checker.semantic(), &extend_immutable_calls)))
|
&& !is_immutable_func(func, checker.semantic(), &extend_immutable_calls)))
|
||||||
&& checker
|
&& checker
|
||||||
.semantic()
|
.semantic()
|
||||||
.resolve_qualified_name(&call.func)
|
.resolve_qualified_name(map_subscript(&call.func))
|
||||||
.is_some_and(|qualified_name| {
|
.is_some_and(|qualified_name| {
|
||||||
matches!(qualified_name.segments(), ["contextvars", "ContextVar"])
|
matches!(qualified_name.segments(), ["contextvars", "ContextVar"])
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,127 +2,149 @@
|
||||||
source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
|
source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
||||||
B039.py:19:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
|
||||||
|
|
|
||||||
18 | # Bad
|
|
||||||
19 | ContextVar("cv", default=[])
|
|
||||||
| ^^ B039
|
|
||||||
20 | ContextVar("cv", default={})
|
|
||||||
21 | ContextVar("cv", default=list())
|
|
||||||
|
|
|
||||||
= help: Replace with `None`; initialize with `.set()``
|
|
||||||
|
|
||||||
B039.py:20:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
|
||||||
|
|
|
||||||
18 | # Bad
|
|
||||||
19 | ContextVar("cv", default=[])
|
|
||||||
20 | ContextVar("cv", default={})
|
|
||||||
| ^^ B039
|
|
||||||
21 | ContextVar("cv", default=list())
|
|
||||||
22 | ContextVar("cv", default=set())
|
|
||||||
|
|
|
||||||
= help: Replace with `None`; initialize with `.set()``
|
|
||||||
|
|
||||||
B039.py:21:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
B039.py:21:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
||||||
|
|
|
|
||||||
19 | ContextVar("cv", default=[])
|
20 | # Bad
|
||||||
20 | ContextVar("cv", default={})
|
21 | ContextVar("cv", default=[])
|
||||||
21 | ContextVar("cv", default=list())
|
| ^^ B039
|
||||||
| ^^^^^^ B039
|
22 | ContextVar("cv", default={})
|
||||||
22 | ContextVar("cv", default=set())
|
23 | ContextVar("cv", default=list())
|
||||||
23 | ContextVar("cv", default=dict())
|
|
||||||
|
|
|
|
||||||
= help: Replace with `None`; initialize with `.set()``
|
= help: Replace with `None`; initialize with `.set()``
|
||||||
|
|
||||||
B039.py:22:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
B039.py:22:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
||||||
|
|
|
|
||||||
20 | ContextVar("cv", default={})
|
20 | # Bad
|
||||||
21 | ContextVar("cv", default=list())
|
21 | ContextVar("cv", default=[])
|
||||||
22 | ContextVar("cv", default=set())
|
22 | ContextVar("cv", default={})
|
||||||
| ^^^^^ B039
|
| ^^ B039
|
||||||
23 | ContextVar("cv", default=dict())
|
23 | ContextVar("cv", default=list())
|
||||||
24 | ContextVar("cv", default=[char for char in "foo"])
|
24 | ContextVar("cv", default=set())
|
||||||
|
|
|
|
||||||
= help: Replace with `None`; initialize with `.set()``
|
= help: Replace with `None`; initialize with `.set()``
|
||||||
|
|
||||||
B039.py:23:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
B039.py:23:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
||||||
|
|
|
|
||||||
21 | ContextVar("cv", default=list())
|
21 | ContextVar("cv", default=[])
|
||||||
22 | ContextVar("cv", default=set())
|
22 | ContextVar("cv", default={})
|
||||||
23 | ContextVar("cv", default=dict())
|
23 | ContextVar("cv", default=list())
|
||||||
| ^^^^^^ B039
|
| ^^^^^^ B039
|
||||||
24 | ContextVar("cv", default=[char for char in "foo"])
|
24 | ContextVar("cv", default=set())
|
||||||
25 | ContextVar("cv", default={char for char in "foo"})
|
25 | ContextVar("cv", default=dict())
|
||||||
|
|
|
|
||||||
= help: Replace with `None`; initialize with `.set()``
|
= help: Replace with `None`; initialize with `.set()``
|
||||||
|
|
||||||
B039.py:24:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
B039.py:24:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
||||||
|
|
|
|
||||||
22 | ContextVar("cv", default=set())
|
22 | ContextVar("cv", default={})
|
||||||
23 | ContextVar("cv", default=dict())
|
23 | ContextVar("cv", default=list())
|
||||||
24 | ContextVar("cv", default=[char for char in "foo"])
|
24 | ContextVar("cv", default=set())
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ B039
|
| ^^^^^ B039
|
||||||
25 | ContextVar("cv", default={char for char in "foo"})
|
25 | ContextVar("cv", default=dict())
|
||||||
26 | ContextVar("cv", default={char: idx for idx, char in enumerate("foo")})
|
26 | ContextVar("cv", default=[char for char in "foo"])
|
||||||
|
|
|
|
||||||
= help: Replace with `None`; initialize with `.set()``
|
= help: Replace with `None`; initialize with `.set()``
|
||||||
|
|
||||||
B039.py:25:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
B039.py:25:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
||||||
|
|
|
|
||||||
23 | ContextVar("cv", default=dict())
|
23 | ContextVar("cv", default=list())
|
||||||
24 | ContextVar("cv", default=[char for char in "foo"])
|
24 | ContextVar("cv", default=set())
|
||||||
25 | ContextVar("cv", default={char for char in "foo"})
|
25 | ContextVar("cv", default=dict())
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ B039
|
| ^^^^^^ B039
|
||||||
26 | ContextVar("cv", default={char: idx for idx, char in enumerate("foo")})
|
26 | ContextVar("cv", default=[char for char in "foo"])
|
||||||
27 | ContextVar("cv", default=collections.deque())
|
27 | ContextVar("cv", default={char for char in "foo"})
|
||||||
|
|
|
|
||||||
= help: Replace with `None`; initialize with `.set()``
|
= help: Replace with `None`; initialize with `.set()``
|
||||||
|
|
||||||
B039.py:26:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
B039.py:26:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
||||||
|
|
|
|
||||||
24 | ContextVar("cv", default=[char for char in "foo"])
|
24 | ContextVar("cv", default=set())
|
||||||
25 | ContextVar("cv", default={char for char in "foo"})
|
25 | ContextVar("cv", default=dict())
|
||||||
26 | ContextVar("cv", default={char: idx for idx, char in enumerate("foo")})
|
26 | ContextVar("cv", default=[char for char in "foo"])
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B039
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ B039
|
||||||
27 | ContextVar("cv", default=collections.deque())
|
27 | ContextVar("cv", default={char for char in "foo"})
|
||||||
|
28 | ContextVar("cv", default={char: idx for idx, char in enumerate("foo")})
|
||||||
|
|
|
|
||||||
= help: Replace with `None`; initialize with `.set()``
|
= help: Replace with `None`; initialize with `.set()``
|
||||||
|
|
||||||
B039.py:27:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
B039.py:27:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
||||||
|
|
|
|
||||||
25 | ContextVar("cv", default={char for char in "foo"})
|
25 | ContextVar("cv", default=dict())
|
||||||
26 | ContextVar("cv", default={char: idx for idx, char in enumerate("foo")})
|
26 | ContextVar("cv", default=[char for char in "foo"])
|
||||||
27 | ContextVar("cv", default=collections.deque())
|
27 | ContextVar("cv", default={char for char in "foo"})
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ B039
|
||||||
|
28 | ContextVar("cv", default={char: idx for idx, char in enumerate("foo")})
|
||||||
|
29 | ContextVar("cv", default=collections.deque())
|
||||||
|
|
|
||||||
|
= help: Replace with `None`; initialize with `.set()``
|
||||||
|
|
||||||
|
B039.py:28:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
||||||
|
|
|
||||||
|
26 | ContextVar("cv", default=[char for char in "foo"])
|
||||||
|
27 | ContextVar("cv", default={char for char in "foo"})
|
||||||
|
28 | ContextVar("cv", default={char: idx for idx, char in enumerate("foo")})
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ B039
|
||||||
|
29 | ContextVar("cv", default=collections.deque())
|
||||||
|
30 | ContextVar("cv", default=set[str]())
|
||||||
|
|
|
||||||
|
= help: Replace with `None`; initialize with `.set()``
|
||||||
|
|
||||||
|
B039.py:29:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
||||||
|
|
|
||||||
|
27 | ContextVar("cv", default={char for char in "foo"})
|
||||||
|
28 | ContextVar("cv", default={char: idx for idx, char in enumerate("foo")})
|
||||||
|
29 | ContextVar("cv", default=collections.deque())
|
||||||
| ^^^^^^^^^^^^^^^^^^^ B039
|
| ^^^^^^^^^^^^^^^^^^^ B039
|
||||||
28 |
|
30 | ContextVar("cv", default=set[str]())
|
||||||
29 | def bar() -> list[int]:
|
31 | ContextVar[set[str]]("cv", default=set[str]())
|
||||||
|
|
|
|
||||||
= help: Replace with `None`; initialize with `.set()``
|
= help: Replace with `None`; initialize with `.set()``
|
||||||
|
|
||||||
B039.py:32:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
B039.py:30:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
||||||
|
|
|
|
||||||
30 | return [1, 2, 3]
|
28 | ContextVar("cv", default={char: idx for idx, char in enumerate("foo")})
|
||||||
31 |
|
29 | ContextVar("cv", default=collections.deque())
|
||||||
32 | ContextVar("cv", default=bar())
|
30 | ContextVar("cv", default=set[str]())
|
||||||
| ^^^^^ B039
|
| ^^^^^^^^^^ B039
|
||||||
33 | ContextVar("cv", default=time.time())
|
31 | ContextVar[set[str]]("cv", default=set[str]())
|
||||||
|
|
|
|
||||||
= help: Replace with `None`; initialize with `.set()``
|
= help: Replace with `None`; initialize with `.set()``
|
||||||
|
|
||||||
B039.py:33:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
B039.py:31:36: B039 Do not use mutable data structures for `ContextVar` defaults
|
||||||
|
|
|
|
||||||
32 | ContextVar("cv", default=bar())
|
29 | ContextVar("cv", default=collections.deque())
|
||||||
33 | ContextVar("cv", default=time.time())
|
30 | ContextVar("cv", default=set[str]())
|
||||||
| ^^^^^^^^^^^ B039
|
31 | ContextVar[set[str]]("cv", default=set[str]())
|
||||||
34 |
|
| ^^^^^^^^^^ B039
|
||||||
35 | def baz(): ...
|
32 |
|
||||||
|
33 | def bar() -> list[int]:
|
||||||
|
|
|
|
||||||
= help: Replace with `None`; initialize with `.set()``
|
= help: Replace with `None`; initialize with `.set()``
|
||||||
|
|
||||||
B039.py:36:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
B039.py:36:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
||||||
|
|
|
|
||||||
35 | def baz(): ...
|
34 | return [1, 2, 3]
|
||||||
36 | ContextVar("cv", default=baz())
|
35 |
|
||||||
|
36 | ContextVar("cv", default=bar())
|
||||||
|
| ^^^^^ B039
|
||||||
|
37 | ContextVar("cv", default=time.time())
|
||||||
|
|
|
||||||
|
= help: Replace with `None`; initialize with `.set()``
|
||||||
|
|
||||||
|
B039.py:37:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
||||||
|
|
|
||||||
|
36 | ContextVar("cv", default=bar())
|
||||||
|
37 | ContextVar("cv", default=time.time())
|
||||||
|
| ^^^^^^^^^^^ B039
|
||||||
|
38 |
|
||||||
|
39 | def baz(): ...
|
||||||
|
|
|
||||||
|
= help: Replace with `None`; initialize with `.set()``
|
||||||
|
|
||||||
|
B039.py:40:26: B039 Do not use mutable data structures for `ContextVar` defaults
|
||||||
|
|
|
||||||
|
39 | def baz(): ...
|
||||||
|
40 | ContextVar("cv", default=baz())
|
||||||
| ^^^^^ B039
|
| ^^^^^ B039
|
||||||
|
|
|
|
||||||
= help: Replace with `None`; initialize with `.set()``
|
= help: Replace with `None`; initialize with `.set()``
|
||||||
|
|
|
@ -279,7 +279,7 @@ pub fn is_immutable_func(
|
||||||
extend_immutable_calls: &[QualifiedName],
|
extend_immutable_calls: &[QualifiedName],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
semantic
|
semantic
|
||||||
.resolve_qualified_name(func)
|
.resolve_qualified_name(map_subscript(func))
|
||||||
.is_some_and(|qualified_name| {
|
.is_some_and(|qualified_name| {
|
||||||
is_immutable_return_type(qualified_name.segments())
|
is_immutable_return_type(qualified_name.segments())
|
||||||
|| extend_immutable_calls
|
|| extend_immutable_calls
|
||||||
|
@ -306,7 +306,7 @@ pub fn is_mutable_expr(expr: &Expr, semantic: &SemanticModel) -> bool {
|
||||||
| Expr::ListComp(_)
|
| Expr::ListComp(_)
|
||||||
| Expr::DictComp(_)
|
| Expr::DictComp(_)
|
||||||
| Expr::SetComp(_) => true,
|
| Expr::SetComp(_) => true,
|
||||||
Expr::Call(ast::ExprCall { func, .. }) => is_mutable_func(func, semantic),
|
Expr::Call(ast::ExprCall { func, .. }) => is_mutable_func(map_subscript(func), semantic),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue