diff --git a/crates/ruff_linter/resources/test/fixtures/pep8_naming/N815.py b/crates/ruff_linter/resources/test/fixtures/pep8_naming/N815.py index d3578d3cfbd7d..41a9f46fd8f96 100644 --- a/crates/ruff_linter/resources/test/fixtures/pep8_naming/N815.py +++ b/crates/ruff_linter/resources/test/fixtures/pep8_naming/N815.py @@ -21,3 +21,10 @@ class D(TypedDict): mixedCase: bool _mixedCase: list mixed_Case: set + +class E(D): + lower: int + CONSTANT: str + mixedCase: bool + _mixedCase: list + mixed_Case: set diff --git a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs index 9e85625e87eba..f7fb21373d889 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs @@ -223,14 +223,10 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { } } if checker.enabled(Rule::MixedCaseVariableInClassScope) { - if let ScopeKind::Class(ast::StmtClassDef { arguments, .. }) = - &checker.semantic.current_scope().kind + if let ScopeKind::Class(class_def) = &checker.semantic.current_scope().kind { pep8_naming::rules::mixed_case_variable_in_class_scope( - checker, - expr, - id, - arguments.as_deref(), + checker, expr, id, class_def, ); } } diff --git a/crates/ruff_linter/src/rules/pep8_naming/helpers.rs b/crates/ruff_linter/src/rules/pep8_naming/helpers.rs index 068c4b143b95e..e94ace527c638 100644 --- a/crates/ruff_linter/src/rules/pep8_naming/helpers.rs +++ b/crates/ruff_linter/src/rules/pep8_naming/helpers.rs @@ -1,8 +1,8 @@ use itertools::Itertools; use ruff_python_ast::name::UnqualifiedName; -use ruff_python_ast::{self as ast, Arguments, Expr, Stmt}; -use ruff_python_semantic::SemanticModel; +use ruff_python_ast::{self as ast, Expr, Stmt}; +use ruff_python_semantic::{analyze, SemanticModel}; use ruff_python_stdlib::str::{is_cased_lowercase, is_cased_uppercase}; pub(super) fn is_camelcase(name: &str) -> bool { @@ -86,16 +86,13 @@ pub(super) fn is_type_alias_assignment(stmt: &Stmt, semantic: &SemanticModel) -> } /// Returns `true` if the statement is an assignment to a `TypedDict`. -pub(super) fn is_typed_dict_class(arguments: Option<&Arguments>, semantic: &SemanticModel) -> bool { +pub(super) fn is_typed_dict_class(class_def: &ast::StmtClassDef, semantic: &SemanticModel) -> bool { if !semantic.seen_typing() { return false; } - arguments.is_some_and(|arguments| { - arguments - .args - .iter() - .any(|base| semantic.match_typing_expr(base, "TypedDict")) + analyze::class::any_qualified_name(class_def, semantic, &|qualified_name| { + semantic.match_typing_qualified_name(&qualified_name, "TypedDict") }) } diff --git a/crates/ruff_linter/src/rules/pep8_naming/rules/mixed_case_variable_in_class_scope.rs b/crates/ruff_linter/src/rules/pep8_naming/rules/mixed_case_variable_in_class_scope.rs index 6ebed08650a61..15b11ac894bd1 100644 --- a/crates/ruff_linter/src/rules/pep8_naming/rules/mixed_case_variable_in_class_scope.rs +++ b/crates/ruff_linter/src/rules/pep8_naming/rules/mixed_case_variable_in_class_scope.rs @@ -1,7 +1,6 @@ -use ruff_python_ast::{Arguments, Expr}; - use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::{self as ast, Expr}; use ruff_text_size::Ranged; use crate::checkers::ast::Checker; @@ -55,7 +54,7 @@ pub(crate) fn mixed_case_variable_in_class_scope( checker: &mut Checker, expr: &Expr, name: &str, - arguments: Option<&Arguments>, + class_def: &ast::StmtClassDef, ) { if !helpers::is_mixed_case(name) { return; @@ -64,7 +63,7 @@ pub(crate) fn mixed_case_variable_in_class_scope( let parent = checker.semantic().current_statement(); if helpers::is_named_tuple_assignment(parent, checker.semantic()) - || helpers::is_typed_dict_class(arguments, checker.semantic()) + || helpers::is_typed_dict_class(class_def, checker.semantic()) { return; }