mirror of
https://github.com/astral-sh/ruff.git
synced 2025-12-23 09:19:58 +00:00
[ruff] Add extra cases to RUF069
[ruff] Add extra cases to RUF069
Now it handles the following cases:
- __all__: list[str] = ["a", "a"]
- __all__: typing.Any = ("a", "a")
- __all__.extend(["a", "a"])
- __all__ += ["a", "a"]
It still does not track mutable __all__ such as:
__all__ = ["a"]
__all__ += ["a"]
It will be a false negative.
This commit is contained in:
parent
42f1cb523b
commit
c1ea670b81
5 changed files with 278 additions and 118 deletions
|
|
@ -1,3 +1,17 @@
|
|||
import typing
|
||||
|
||||
|
||||
class A: ...
|
||||
|
||||
|
||||
class B: ...
|
||||
|
||||
|
||||
__all__ = "A" + "B"
|
||||
__all__: list[str] = ["A", "B"]
|
||||
__all__: list[str] = ["A", "B", "A"]
|
||||
__all__: typing.Any = ("A", "B")
|
||||
__all__: typing.Any = ("A", "B", "B")
|
||||
__all__ = ["A", "B"]
|
||||
__all__ = ["A", "A", "B"]
|
||||
__all__ = ["A", "B", "A"]
|
||||
|
|
@ -7,8 +21,9 @@ __all__ = [
|
|||
"A",
|
||||
"B",
|
||||
# Comment
|
||||
"B"
|
||||
"B",
|
||||
]
|
||||
|
||||
class A: ...
|
||||
class B: ...
|
||||
__all__ += ["B", "B"]
|
||||
__all__ += ["A", "B"]
|
||||
__all__.extend(["B", "B"])
|
||||
__all__.extend(["A", "B"])
|
||||
|
|
|
|||
|
|
@ -1245,6 +1245,9 @@ pub(crate) fn expression(expr: &Expr, checker: &Checker) {
|
|||
if checker.is_rule_enabled(Rule::UnsortedDunderAll) {
|
||||
ruff::rules::sort_dunder_all_extend_call(checker, call);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::DuplicateEntryInDunderAll) {
|
||||
ruff::rules::duplicate_entry_in_dunder_all_extend_call(checker, call);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::DefaultFactoryKwarg) {
|
||||
ruff::rules::default_factory_kwarg(checker, call);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -966,6 +966,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
|||
if checker.is_rule_enabled(Rule::UnsortedDunderAll) {
|
||||
ruff::rules::sort_dunder_all_aug_assign(checker, aug_assign);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::DuplicateEntryInDunderAll) {
|
||||
ruff::rules::duplicate_entry_in_dunder_all_aug_assign(checker, aug_assign);
|
||||
}
|
||||
}
|
||||
Stmt::If(
|
||||
if_ @ ast::StmtIf {
|
||||
|
|
@ -1435,7 +1438,7 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
|||
ruff::rules::sort_dunder_all_assign(checker, assign);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::DuplicateEntryInDunderAll) {
|
||||
ruff::rules::duplicate_entry_in_dunder_all(checker, assign);
|
||||
ruff::rules::duplicate_entry_in_dunder_all_assign(checker, assign);
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if checker.any_rule_enabled(&[
|
||||
|
|
@ -1528,6 +1531,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
|||
if checker.is_rule_enabled(Rule::UnsortedDunderAll) {
|
||||
ruff::rules::sort_dunder_all_ann_assign(checker, assign_stmt);
|
||||
}
|
||||
if checker.is_rule_enabled(Rule::DuplicateEntryInDunderAll) {
|
||||
ruff::rules::duplicate_entry_in_dunder_all_ann_assign(checker, assign_stmt);
|
||||
}
|
||||
if checker.source_type.is_stub() {
|
||||
if let Some(value) = value {
|
||||
if checker.is_rule_enabled(Rule::AssignmentDefaultInStub) {
|
||||
|
|
|
|||
|
|
@ -50,14 +50,66 @@ impl Violation for DuplicateEntryInDunderAll {
|
|||
}
|
||||
}
|
||||
|
||||
/// RUF069
|
||||
/// This routine checks whether `__all__` contains duplicated entries, and emits
|
||||
/// a violation if it does.
|
||||
pub(crate) fn duplicate_entry_in_dunder_all(
|
||||
/// Apply RUF069 to `StmtAssign` AST node. For example: `__all__ = ["a", "b", "a"]`.
|
||||
pub(crate) fn duplicate_entry_in_dunder_all_assign(
|
||||
checker: &Checker,
|
||||
ast::StmtAssign { value, targets, .. }: &ast::StmtAssign,
|
||||
) {
|
||||
let [target] = targets.as_slice() else { return };
|
||||
if let [expr] = targets.as_slice() {
|
||||
duplicate_entry_in_dunder_all(checker, expr, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply RUF069 to `StmtAugAssign` AST node. For example: `__all__ += ["a", "b", "a"]`.
|
||||
pub(crate) fn duplicate_entry_in_dunder_all_aug_assign(
|
||||
checker: &Checker,
|
||||
node: &ast::StmtAugAssign,
|
||||
) {
|
||||
if node.op.is_add() {
|
||||
duplicate_entry_in_dunder_all(checker, &node.target, &node.value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply RUF069 to `__all__.extend()`.
|
||||
pub(crate) fn duplicate_entry_in_dunder_all_extend_call(
|
||||
checker: &Checker,
|
||||
ast::ExprCall {
|
||||
func,
|
||||
arguments: ast::Arguments { args, keywords, .. },
|
||||
..
|
||||
}: &ast::ExprCall,
|
||||
) {
|
||||
let ([value_passed], []) = (&**args, &**keywords) else {
|
||||
return;
|
||||
};
|
||||
let ast::Expr::Attribute(ast::ExprAttribute {
|
||||
ref value,
|
||||
ref attr,
|
||||
..
|
||||
}) = **func
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if attr == "extend" {
|
||||
duplicate_entry_in_dunder_all(checker, value, value_passed);
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply RUF069 to a `StmtAnnAssign` AST node.
|
||||
/// For example: `__all__: list[str] = ["a", "b", "a"]`.
|
||||
pub(crate) fn duplicate_entry_in_dunder_all_ann_assign(
|
||||
checker: &Checker,
|
||||
node: &ast::StmtAnnAssign,
|
||||
) {
|
||||
if let Some(value) = &node.value {
|
||||
duplicate_entry_in_dunder_all(checker, &node.target, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// RUF069
|
||||
/// This routine checks whether `__all__` contains duplicated entries, and emits
|
||||
/// a violation if it does.
|
||||
fn duplicate_entry_in_dunder_all(checker: &Checker, target: &ast::Expr, value: &ast::Expr) {
|
||||
let ast::Expr::Name(ast::ExprName { id, .. }) = target else {
|
||||
return;
|
||||
};
|
||||
|
|
@ -71,7 +123,7 @@ pub(crate) fn duplicate_entry_in_dunder_all(
|
|||
return;
|
||||
}
|
||||
|
||||
let elts = match value.as_ref() {
|
||||
let elts = match value {
|
||||
ast::Expr::List(ast::ExprList { elts, .. }) => elts,
|
||||
ast::Expr::Tuple(ast::ExprTuple { elts, .. }) => elts,
|
||||
_ => return,
|
||||
|
|
|
|||
|
|
@ -2,115 +2,199 @@
|
|||
source: crates/ruff_linter/src/rules/ruff/mod.rs
|
||||
---
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:2:17
|
||||
|
|
||||
1 | __all__ = ["A", "B"]
|
||||
2 | __all__ = ["A", "A", "B"]
|
||||
| ^^^
|
||||
3 | __all__ = ["A", "B", "A"]
|
||||
4 | __all__ = ["A", "A", "B", "B"]
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
1 | __all__ = ["A", "B"]
|
||||
- __all__ = ["A", "A", "B"]
|
||||
2 + __all__ = ["A", "B"]
|
||||
3 | __all__ = ["A", "B", "A"]
|
||||
4 | __all__ = ["A", "A", "B", "B"]
|
||||
5 | __all__ = [
|
||||
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:3:22
|
||||
|
|
||||
1 | __all__ = ["A", "B"]
|
||||
2 | __all__ = ["A", "A", "B"]
|
||||
3 | __all__ = ["A", "B", "A"]
|
||||
| ^^^
|
||||
4 | __all__ = ["A", "A", "B", "B"]
|
||||
5 | __all__ = [
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
1 | __all__ = ["A", "B"]
|
||||
2 | __all__ = ["A", "A", "B"]
|
||||
- __all__ = ["A", "B", "A"]
|
||||
3 + __all__ = ["A", "B"]
|
||||
4 | __all__ = ["A", "A", "B", "B"]
|
||||
5 | __all__ = [
|
||||
6 | "A",
|
||||
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:4:17
|
||||
|
|
||||
2 | __all__ = ["A", "A", "B"]
|
||||
3 | __all__ = ["A", "B", "A"]
|
||||
4 | __all__ = ["A", "A", "B", "B"]
|
||||
| ^^^
|
||||
5 | __all__ = [
|
||||
6 | "A",
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
1 | __all__ = ["A", "B"]
|
||||
2 | __all__ = ["A", "A", "B"]
|
||||
3 | __all__ = ["A", "B", "A"]
|
||||
- __all__ = ["A", "A", "B", "B"]
|
||||
4 + __all__ = ["A", "B", "B"]
|
||||
5 | __all__ = [
|
||||
6 | "A",
|
||||
7 | "A",
|
||||
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:4:27
|
||||
|
|
||||
2 | __all__ = ["A", "A", "B"]
|
||||
3 | __all__ = ["A", "B", "A"]
|
||||
4 | __all__ = ["A", "A", "B", "B"]
|
||||
| ^^^
|
||||
5 | __all__ = [
|
||||
6 | "A",
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
1 | __all__ = ["A", "B"]
|
||||
2 | __all__ = ["A", "A", "B"]
|
||||
3 | __all__ = ["A", "B", "A"]
|
||||
- __all__ = ["A", "A", "B", "B"]
|
||||
4 + __all__ = ["A", "A", "B"]
|
||||
5 | __all__ = [
|
||||
6 | "A",
|
||||
7 | "A",
|
||||
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:7:5
|
||||
|
|
||||
5 | __all__ = [
|
||||
6 | "A",
|
||||
7 | "A",
|
||||
| ^^^
|
||||
8 | "B",
|
||||
9 | # Comment
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
4 | __all__ = ["A", "A", "B", "B"]
|
||||
5 | __all__ = [
|
||||
6 | "A",
|
||||
- "A",
|
||||
7 | "B",
|
||||
8 | # Comment
|
||||
9 | "B"
|
||||
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:10:5
|
||||
--> RUF069.py:12:33
|
||||
|
|
||||
8 | "B",
|
||||
9 | # Comment
|
||||
10 | "B"
|
||||
10 | __all__ = "A" + "B"
|
||||
11 | __all__: list[str] = ["A", "B"]
|
||||
12 | __all__: list[str] = ["A", "B", "A"]
|
||||
| ^^^
|
||||
13 | __all__: typing.Any = ("A", "B")
|
||||
14 | __all__: typing.Any = ("A", "B", "B")
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
9 |
|
||||
10 | __all__ = "A" + "B"
|
||||
11 | __all__: list[str] = ["A", "B"]
|
||||
- __all__: list[str] = ["A", "B", "A"]
|
||||
12 + __all__: list[str] = ["A", "B"]
|
||||
13 | __all__: typing.Any = ("A", "B")
|
||||
14 | __all__: typing.Any = ("A", "B", "B")
|
||||
15 | __all__ = ["A", "B"]
|
||||
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:14:34
|
||||
|
|
||||
12 | __all__: list[str] = ["A", "B", "A"]
|
||||
13 | __all__: typing.Any = ("A", "B")
|
||||
14 | __all__: typing.Any = ("A", "B", "B")
|
||||
| ^^^
|
||||
15 | __all__ = ["A", "B"]
|
||||
16 | __all__ = ["A", "A", "B"]
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
11 | __all__: list[str] = ["A", "B"]
|
||||
12 | __all__: list[str] = ["A", "B", "A"]
|
||||
13 | __all__: typing.Any = ("A", "B")
|
||||
- __all__: typing.Any = ("A", "B", "B")
|
||||
14 + __all__: typing.Any = ("A", "B")
|
||||
15 | __all__ = ["A", "B"]
|
||||
16 | __all__ = ["A", "A", "B"]
|
||||
17 | __all__ = ["A", "B", "A"]
|
||||
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:16:17
|
||||
|
|
||||
14 | __all__: typing.Any = ("A", "B", "B")
|
||||
15 | __all__ = ["A", "B"]
|
||||
16 | __all__ = ["A", "A", "B"]
|
||||
| ^^^
|
||||
17 | __all__ = ["A", "B", "A"]
|
||||
18 | __all__ = ["A", "A", "B", "B"]
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
13 | __all__: typing.Any = ("A", "B")
|
||||
14 | __all__: typing.Any = ("A", "B", "B")
|
||||
15 | __all__ = ["A", "B"]
|
||||
- __all__ = ["A", "A", "B"]
|
||||
16 + __all__ = ["A", "B"]
|
||||
17 | __all__ = ["A", "B", "A"]
|
||||
18 | __all__ = ["A", "A", "B", "B"]
|
||||
19 | __all__ = [
|
||||
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:17:22
|
||||
|
|
||||
15 | __all__ = ["A", "B"]
|
||||
16 | __all__ = ["A", "A", "B"]
|
||||
17 | __all__ = ["A", "B", "A"]
|
||||
| ^^^
|
||||
18 | __all__ = ["A", "A", "B", "B"]
|
||||
19 | __all__ = [
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
14 | __all__: typing.Any = ("A", "B", "B")
|
||||
15 | __all__ = ["A", "B"]
|
||||
16 | __all__ = ["A", "A", "B"]
|
||||
- __all__ = ["A", "B", "A"]
|
||||
17 + __all__ = ["A", "B"]
|
||||
18 | __all__ = ["A", "A", "B", "B"]
|
||||
19 | __all__ = [
|
||||
20 | "A",
|
||||
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:18:17
|
||||
|
|
||||
16 | __all__ = ["A", "A", "B"]
|
||||
17 | __all__ = ["A", "B", "A"]
|
||||
18 | __all__ = ["A", "A", "B", "B"]
|
||||
| ^^^
|
||||
19 | __all__ = [
|
||||
20 | "A",
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
15 | __all__ = ["A", "B"]
|
||||
16 | __all__ = ["A", "A", "B"]
|
||||
17 | __all__ = ["A", "B", "A"]
|
||||
- __all__ = ["A", "A", "B", "B"]
|
||||
18 + __all__ = ["A", "B", "B"]
|
||||
19 | __all__ = [
|
||||
20 | "A",
|
||||
21 | "A",
|
||||
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:18:27
|
||||
|
|
||||
16 | __all__ = ["A", "A", "B"]
|
||||
17 | __all__ = ["A", "B", "A"]
|
||||
18 | __all__ = ["A", "A", "B", "B"]
|
||||
| ^^^
|
||||
19 | __all__ = [
|
||||
20 | "A",
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
15 | __all__ = ["A", "B"]
|
||||
16 | __all__ = ["A", "A", "B"]
|
||||
17 | __all__ = ["A", "B", "A"]
|
||||
- __all__ = ["A", "A", "B", "B"]
|
||||
18 + __all__ = ["A", "A", "B"]
|
||||
19 | __all__ = [
|
||||
20 | "A",
|
||||
21 | "A",
|
||||
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:21:5
|
||||
|
|
||||
19 | __all__ = [
|
||||
20 | "A",
|
||||
21 | "A",
|
||||
| ^^^
|
||||
11 | ]
|
||||
22 | "B",
|
||||
23 | # Comment
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
7 | "A",
|
||||
8 | "B",
|
||||
9 | # Comment
|
||||
- "B"
|
||||
10 | ]
|
||||
11 |
|
||||
12 | class A: ...
|
||||
18 | __all__ = ["A", "A", "B", "B"]
|
||||
19 | __all__ = [
|
||||
20 | "A",
|
||||
- "A",
|
||||
21 | "B",
|
||||
22 | # Comment
|
||||
23 | "B",
|
||||
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:24:5
|
||||
|
|
||||
22 | "B",
|
||||
23 | # Comment
|
||||
24 | "B",
|
||||
| ^^^
|
||||
25 | ]
|
||||
26 | __all__ += ["B", "B"]
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
20 | "A",
|
||||
21 | "A",
|
||||
22 | "B",
|
||||
- # Comment
|
||||
- "B",
|
||||
23 + # Comment,
|
||||
24 | ]
|
||||
25 | __all__ += ["B", "B"]
|
||||
26 | __all__ += ["A", "B"]
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:26:18
|
||||
|
|
||||
24 | "B",
|
||||
25 | ]
|
||||
26 | __all__ += ["B", "B"]
|
||||
| ^^^
|
||||
27 | __all__ += ["A", "B"]
|
||||
28 | __all__.extend(["B", "B"])
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
23 | # Comment
|
||||
24 | "B",
|
||||
25 | ]
|
||||
- __all__ += ["B", "B"]
|
||||
26 + __all__ += ["B"]
|
||||
27 | __all__ += ["A", "B"]
|
||||
28 | __all__.extend(["B", "B"])
|
||||
29 | __all__.extend(["A", "B"])
|
||||
|
||||
RUF069 [*] `__all__` contains duplicate entries
|
||||
--> RUF069.py:28:22
|
||||
|
|
||||
26 | __all__ += ["B", "B"]
|
||||
27 | __all__ += ["A", "B"]
|
||||
28 | __all__.extend(["B", "B"])
|
||||
| ^^^
|
||||
29 | __all__.extend(["A", "B"])
|
||||
|
|
||||
help: Remove duplicate entries from `__all__`
|
||||
25 | ]
|
||||
26 | __all__ += ["B", "B"]
|
||||
27 | __all__ += ["A", "B"]
|
||||
- __all__.extend(["B", "B"])
|
||||
28 + __all__.extend(["B"])
|
||||
29 | __all__.extend(["A", "B"])
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue