Repository: astral-sh/ruff
File: crates/ty_python_semantic/src/types/overrides.rs (Rust)

Code before:
            ));
        }
    }

    if let Some((superclass, superclass_method)) = overridden_final_method {
        report_overridden_final_method(
            context,
            &member.name,
            *definition,
            member.ty,
            superclass,
            class,
            &superclass_method,
        );
    }
}

/// Check for prohibited `NamedTuple` attribute overrides.
///
/// `NamedTuple` classes have certain synthesized attributes (like `_asdict`, `_make`, etc.)
/// that cannot be overwritten. Attempting to assign to these attributes (without type
/// annotations) or define methods with these names will raise an `AttributeError` at runtime.
fn check_prohibited_namedtuple_attrs<'db>(
    context: &InferContext<'db, '_>,
    own_class_members: &FxHashSet<MemberWithDefinition<'db>>,
) {
    let db = context.db();
    let module = context.module();

    for member in own_class_members {
        if !PROHIBITED_NAMEDTUPLE_ATTRS.contains(&member.member.name.as_str()) {
            continue;
        }

        let Some(definition) = member.definition else {
            continue;
        };

        // Only annotated assignments are allowed (they become fields).
        // Everything else (assignments, method definitions, etc.) is an error.
        if !matches!(definition.kind(db), DefinitionKind::AnnotatedAssignment(_)) {
            if let Some(builder) = context.report_lint(
                &OVERRIDE_OF_PROHIBITED_NAMED_TUPLE_ATTRIBUTE,
                definition.full_range(db, module),
            ) {
                builder.into_diagnostic(format_args!(
                    "Cannot overwrite NamedTuple attribute `{}`",
                    member.member.name
                ));
            }
        }

Review comment (bug):
I think you can simplify this (and optimise slightly), e.g.

```diff
diff --git a/crates/ty_python_semantic/src/types/overrides.rs b/crates/ty_python_semantic/src/types/overrides.rs
index f4bd5fc6cd..46016541ac 100644
--- a/crates/ty_python_semantic/src/types/overrides.rs
+++ b/crates/ty_python_semantic/src/types/overrides.rs
@@ -56,12 +56,6 @@ pub(super) fn check_class<'db>(context: &InferContext<'db, '_>, class: ClassLite
     let own_class_members: FxHashSet<_> =
         all_declarations_and_bindings(db, class.body_scope(db)).collect();
 
-    if configuration.check_prohibited_named_tuple_attrs()
-        && CodeGeneratorKind::NamedTuple.matches(db, class, None)
-    {
-        check_prohibited_namedtuple_attrs(context, &own_class_members);
-    }
-
     for member in own_class_members {
         check_class_declaration(context, configuration, class_specialized, &member);
     }
@@ -152,6 +146,27 @@ fn check_class_declaration<'db>(
     let (literal, specialization) = class.class_literal(db);
     let class_kind = CodeGeneratorKind::from_class(db, literal, specialization);
 
+    // Check for prohibited `NamedTuple` attribute overrides.
+    //
+    // `NamedTuple` classes have certain synthesized attributes (like `_asdict`, `_make`, etc.)
+    // that cannot be overwritten. Attempting to assign to these attributes (without type
+    // annotations) or define methods with these names will raise an `AttributeError` at runtime.
+    if class_kind == Some(CodeGeneratorKind::NamedTuple)
+        && configuration.check_prohibited_named_tuple_attrs()
+        && PROHIBITED_NAMEDTUPLE_ATTRS.contains(&member.name.as_str())
+        && !matches!(definition.kind(db), DefinitionKind::AnnotatedAssignment(_))
+        && let Some(builder) = context.report_lint(
+            &OVERRIDE_OF_PROHIBITED_NAMED_TUPLE_ATTRIBUTE,
+            definition.focus_range(db, context.module()),
+        )
+    {
+        let mut diagnostic = builder.into_diagnostic(format_args!(
+            "Cannot overwrite NamedTuple attribute `{}`",
+            &member.name
+        ));
+        diagnostic.info("This will cause the class creation to fail at runtime");
+    }
+
     let mut subclass_overrides_superclass_declaration = false;
     let mut has_dynamic_superclass = false;
     let mut has_typeddict_in_mro = false;
@@ -361,43 +376,6 @@ fn check_class_declaration<'db>(
     }
 }
 
-/// Check for prohibited `NamedTuple` attribute overrides.
-///
-/// `NamedTuple` classes have certain synthesized attributes (like `_asdict`, `_make`, etc.)
-/// that cannot be overwritten. Attempting to assign to these attributes (without type
-/// annotations) or define methods with these names will raise an `AttributeError` at runtime.
-fn check_prohibited_namedtuple_attrs<'db>(
-    context: &InferContext<'db, '_>,
-    own_class_members: &FxHashSet<MemberWithDefinition<'db>>,
-) {
-    let db = context.db();
-    let module = context.module();
-
-    for member in own_class_members {
-        if !PROHIBITED_NAMEDTUPLE_ATTRS.contains(&member.member.name.as_str()) {
-            continue;
-        }
-
-        let Some(definition) = member.definition else {
-            continue;
-        };
-
-        // Only annotated assignments are allowed (they become fields).
-        // Everything else (assignments, method definitions, etc.) is an error.
-        if !matches!(definition.kind(db), DefinitionKind::AnnotatedAssignment(_)) {
-            if let Some(builder) = context.report_lint(
-                &OVERRIDE_OF_PROHIBITED_NAMED_TUPLE_ATTRIBUTE,
-                definition.full_range(db, module),
-            ) {
-                builder.into_diagnostic(format_args!(
-                    "Cannot overwrite NamedTuple attribute `{}`",
-                    member.member.name
-                ));
-            }
-        }
-    }
-}
-
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
 pub(super) enum MethodKind<'db> {
     Synthesized(CodeGeneratorKind<'db>),
```

Code after:
            function_literal.focus_range(db, context.module()),
        ) {
            let mut diagnostic = builder.into_diagnostic(format_args!(
                "Method `{}` is decorated with `@override` but does not override anything",
                member.name
            ));
            if let Some(decorator_span) =
                function_literal.find_known_decorator_span(db, KnownFunction::Override)
            {
                diagnostic.annotate(Annotation::secondary(decorator_span));
            }
            diagnostic.info(format_args!(
                "No `{member}` definitions were found on any superclasses of `{class}`",
                member = &member.name,
                class = class.name(db)
            ));
        }
    }

    if let Some((superclass, superclass_method)) = overridden_final_method {
        report_overridden_final_method(
            context,
            &member.name,
            *definition,
            member.ty,
            superclass,
            class,
            &superclass_method,
        );
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(super) enum MethodKind<'db> {
    Synthesized(CodeGeneratorKind<'db>),
    #[default]
    NotSynthesized,
}

bitflags! {
    /// Bitflags representing which override-related rules have been enabled.
    #[derive(Default, Debug, Copy, Clone)]
    struct OverrideRulesConfig: u8 {
        const LISKOV_METHODS = 1 << 0;
        const EXPLICIT_OVERRIDE = 1 << 1;
        const FINAL_METHOD_OVERRIDDEN = 1 << 2;
        const PROHIBITED_NAMED_TUPLE_ATTR = 1 << 3;
    }
}

impl From<&InferContext<'_, '_>> for OverrideRulesConfig {