[ty] Forbid use of super() in NamedTuple subclasses (#21700)
## Summary The exact behavior around what's allowed vs. disallowed was partly detected through trial and error in the runtime. I was a little confused by [this comment](https://github.com/python/cpython/pull/129352) that says "`NamedTuple` subclasses cannot be inherited from" because in practice that doesn't appear to error at runtime. Closes [#1683](https://github.com/astral-sh/ty/issues/1683).
This commit is contained in:
@@ -19,7 +19,7 @@ use crate::semantic_index::{
|
||||
use crate::types::bound_super::BoundSuperError;
|
||||
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
||||
use crate::types::context::InferContext;
|
||||
use crate::types::diagnostic::INVALID_TYPE_ALIAS_TYPE;
|
||||
use crate::types::diagnostic::{INVALID_TYPE_ALIAS_TYPE, SUPER_CALL_IN_NAMED_TUPLE_METHOD};
|
||||
use crate::types::enums::enum_metadata;
|
||||
use crate::types::function::{DataclassTransformerParams, KnownFunction};
|
||||
use crate::types::generics::{
|
||||
@@ -5546,6 +5546,20 @@ impl KnownClass {
|
||||
return;
|
||||
};
|
||||
|
||||
// Check if the enclosing class is a `NamedTuple`, which forbids the use of `super()`.
|
||||
if CodeGeneratorKind::NamedTuple.matches(db, enclosing_class, None) {
|
||||
if let Some(builder) = context
|
||||
.report_lint(&SUPER_CALL_IN_NAMED_TUPLE_METHOD, call_expression)
|
||||
{
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Cannot use `super()` in a method of NamedTuple class `{}`",
|
||||
enclosing_class.name(db)
|
||||
));
|
||||
}
|
||||
overload.set_return_type(Type::unknown());
|
||||
return;
|
||||
}
|
||||
|
||||
// The type of the first parameter if the given scope is function-like (i.e. function or lambda).
|
||||
// `None` if the scope is not function-like, or has no parameters.
|
||||
let first_param = match scope.node(db) {
|
||||
@@ -5585,6 +5599,22 @@ impl KnownClass {
|
||||
overload.set_return_type(bound_super);
|
||||
}
|
||||
[Some(pivot_class_type), Some(owner_type)] => {
|
||||
// Check if the enclosing class is a `NamedTuple`, which forbids the use of `super()`.
|
||||
if let Some(enclosing_class) = nearest_enclosing_class(db, index, scope) {
|
||||
if CodeGeneratorKind::NamedTuple.matches(db, enclosing_class, None) {
|
||||
if let Some(builder) = context
|
||||
.report_lint(&SUPER_CALL_IN_NAMED_TUPLE_METHOD, call_expression)
|
||||
{
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Cannot use `super()` in a method of NamedTuple class `{}`",
|
||||
enclosing_class.name(db)
|
||||
));
|
||||
}
|
||||
overload.set_return_type(Type::unknown());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let bound_super = BoundSuperType::build(db, *pivot_class_type, *owner_type)
|
||||
.unwrap_or_else(|err| {
|
||||
err.report_diagnostic(context, call_expression.into());
|
||||
|
||||
@@ -121,6 +121,7 @@ pub(crate) fn register_lints(registry: &mut LintRegistryBuilder) {
|
||||
registry.register_lint(&MISSING_TYPED_DICT_KEY);
|
||||
registry.register_lint(&INVALID_METHOD_OVERRIDE);
|
||||
registry.register_lint(&INVALID_EXPLICIT_OVERRIDE);
|
||||
registry.register_lint(&SUPER_CALL_IN_NAMED_TUPLE_METHOD);
|
||||
|
||||
// String annotations
|
||||
registry.register_lint(&BYTE_STRING_TYPE_ANNOTATION);
|
||||
@@ -1760,6 +1761,33 @@ declare_lint! {
|
||||
}
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// ## What it does
|
||||
/// Checks for calls to `super()` inside methods of `NamedTuple` classes.
|
||||
///
|
||||
/// ## Why is this bad?
|
||||
/// Using `super()` in a method of a `NamedTuple` class will raise an exception at runtime.
|
||||
///
|
||||
/// ## Examples
|
||||
/// ```python
|
||||
/// from typing import NamedTuple
|
||||
///
|
||||
/// class F(NamedTuple):
|
||||
/// x: int
|
||||
///
|
||||
/// def method(self):
|
||||
/// super() # error: super() is not supported in methods of NamedTuple classes
|
||||
/// ```
|
||||
///
|
||||
/// ## References
|
||||
/// - [Python documentation: super()](https://docs.python.org/3/library/functions.html#super)
|
||||
pub(crate) static SUPER_CALL_IN_NAMED_TUPLE_METHOD = {
|
||||
summary: "detects `super()` calls in methods of `NamedTuple` classes",
|
||||
status: LintStatus::preview("0.0.1-alpha.30"),
|
||||
default_level: Level::Error,
|
||||
}
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// ## What it does
|
||||
/// Checks for calls to `reveal_type` without importing it.
|
||||
|
||||
Reference in New Issue
Block a user