PIE794: Detect duplicated declared class fields (#22717)

This commit is contained in:
Micha Reiser
2026-01-19 15:35:58 +01:00
committed by GitHub
parent 7f9b10a193
commit 2bc4756716
3 changed files with 37 additions and 26 deletions

View File

@@ -50,3 +50,8 @@ class Person:
name: str = "Foo"
name: str = name + " Bar"
name: str = "Bar" # PIE794
class TextEdit:
start_line: int
start_line: int # PIE794

View File

@@ -58,17 +58,17 @@ pub(crate) fn duplicate_class_field_definition(checker: &Checker, body: &[Stmt])
let mut seen_targets: FxHashSet<&str> = FxHashSet::default();
for stmt in body {
// Extract the property name from the assignment statement.
let target = match stmt {
Stmt::Assign(ast::StmtAssign { targets, .. }) => {
if let [Expr::Name(id)] = targets.as_slice() {
id
} else {
let (target, value) = match stmt {
Stmt::Assign(ast::StmtAssign { targets, value, .. }) => {
let [Expr::Name(target)] = targets.as_slice() else {
continue;
}
};
(target, Some(&**value))
}
Stmt::AnnAssign(ast::StmtAnnAssign { target, .. }) => {
Stmt::AnnAssign(ast::StmtAnnAssign { target, value, .. }) => {
if let Expr::Name(id) = target.as_ref() {
id
(id, value.as_deref())
} else {
continue;
}
@@ -77,24 +77,12 @@ pub(crate) fn duplicate_class_field_definition(checker: &Checker, body: &[Stmt])
};
// If this is an unrolled augmented assignment (e.g., `x = x + 1`), skip it.
match stmt {
Stmt::Assign(ast::StmtAssign { value, .. }) => {
if any_over_expr(value.as_ref(), &|expr| {
expr.as_name_expr().is_some_and(|name| name.id == target.id)
}) {
continue;
}
}
Stmt::AnnAssign(ast::StmtAnnAssign {
value: Some(value), ..
}) => {
if any_over_expr(value.as_ref(), &|expr| {
expr.as_name_expr().is_some_and(|name| name.id == target.id)
}) {
continue;
}
}
_ => continue,
if let Some(value) = value
&& any_over_expr(value, &|expr| {
expr.as_name_expr().is_some_and(|name| name.id == target.id)
})
{
continue;
}
if !seen_targets.insert(target.id.as_str()) {

View File

@@ -108,4 +108,22 @@ help: Remove duplicate field definition for `name`
50 | name: str = "Foo"
51 | name: str = name + " Bar"
- name: str = "Bar" # PIE794
52 |
53 |
54 | class TextEdit:
note: This is an unsafe fix and may change runtime behavior
PIE794 [*] Class field `start_line` is defined multiple times
--> PIE794.py:57:5
|
55 | class TextEdit:
56 | start_line: int
57 | start_line: int # PIE794
| ^^^^^^^^^^^^^^^
|
help: Remove duplicate field definition for `start_line`
54 |
55 | class TextEdit:
56 | start_line: int
- start_line: int # PIE794
note: This is an unsafe fix and may change runtime behavior