diff --git a/crates/ty/docs/rules.md b/crates/ty/docs/rules.md
index 6923aeef6f..78f757a6bb 100644
--- a/crates/ty/docs/rules.md
+++ b/crates/ty/docs/rules.md
@@ -39,7 +39,7 @@ def test(): -> "int":
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -63,7 +63,7 @@ Calling a non-callable object will raise a `TypeError` at runtime.
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -95,7 +95,7 @@ f(int) # error
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -126,7 +126,7 @@ a = 1
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -158,7 +158,7 @@ class C(A, B): ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -190,7 +190,7 @@ class B(A): ...
Default level: error ·
Preview (since 1.0.0) ·
Related issues ·
-View source
+View source
@@ -218,7 +218,7 @@ type B = A
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -245,7 +245,7 @@ class B(A, A): ...
Default level: error ·
Added in 0.0.1-alpha.12 ·
Related issues ·
-View source
+View source
@@ -357,7 +357,7 @@ def test(): -> "Literal[5]":
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -387,7 +387,7 @@ class C(A, B): ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -413,7 +413,7 @@ t[3] # IndexError: tuple index out of range
Default level: error ·
Added in 0.0.1-alpha.12 ·
Related issues ·
-View source
+View source
@@ -502,7 +502,7 @@ an atypical memory layout.
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -529,7 +529,7 @@ func("foo") # error: [invalid-argument-type]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -557,7 +557,7 @@ a: int = ''
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -591,7 +591,7 @@ C.instance_var = 3 # error: Cannot assign to instance variable
Default level: error ·
Added in 0.0.1-alpha.19 ·
Related issues ·
-View source
+View source
@@ -627,7 +627,7 @@ asyncio.run(main())
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -651,7 +651,7 @@ class A(42): ... # error: [invalid-base]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -678,7 +678,7 @@ with 1:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -707,7 +707,7 @@ a: str
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -751,7 +751,7 @@ except ZeroDivisionError:
Default level: error ·
Added in 0.0.1-alpha.28 ·
Related issues ·
-View source
+View source
@@ -793,7 +793,7 @@ class D(A):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -826,7 +826,7 @@ class C[U](Generic[T]): ...
Default level: error ·
Added in 0.0.1-alpha.17 ·
Related issues ·
-View source
+View source
@@ -865,7 +865,7 @@ carol = Person(name="Carol", age=25) # typo!
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -900,7 +900,7 @@ def f(t: TypeVar("U")): ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -934,7 +934,7 @@ class B(metaclass=f): ...
Default level: error ·
Added in 0.0.1-alpha.20 ·
Related issues ·
-View source
+View source
@@ -1041,7 +1041,7 @@ Correct use of `@override` is enforced by ty's `invalid-explicit-override` rule.
Default level: error ·
Added in 0.0.1-alpha.19 ·
Related issues ·
-View source
+View source
@@ -1073,7 +1073,7 @@ TypeError: can only inherit from a NamedTuple type and Generic
Default level: error ·
Preview (since 1.0.0) ·
Related issues ·
-View source
+View source
@@ -1103,7 +1103,7 @@ Baz = NewType("Baz", int | str) # error: invalid base for `typing.NewType`
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1153,7 +1153,7 @@ def foo(x: int) -> int: ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1179,7 +1179,7 @@ def f(a: int = ''): ...
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1210,7 +1210,7 @@ P2 = ParamSpec("S2") # error: ParamSpec name must match the variable it's assig
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1244,7 +1244,7 @@ TypeError: Protocols can only inherit from other protocols, got
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1293,7 +1293,7 @@ def g():
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1318,7 +1318,7 @@ def func() -> int:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1376,7 +1376,7 @@ TODO #14889
Default level: error ·
Added in 0.0.1-alpha.6 ·
Related issues ·
-View source
+View source
@@ -1403,7 +1403,7 @@ NewAlias = TypeAliasType(get_name(), int) # error: TypeAliasType name mus
Default level: error ·
Added in 0.0.1-alpha.29 ·
Related issues ·
-View source
+View source
@@ -1450,7 +1450,7 @@ Bar[int] # error: too few arguments
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1480,7 +1480,7 @@ TYPE_CHECKING = ''
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1510,7 +1510,7 @@ b: Annotated[int] # `Annotated` expects at least two arguments
Default level: error ·
Added in 0.0.1-alpha.11 ·
Related issues ·
-View source
+View source
@@ -1544,7 +1544,7 @@ f(10) # Error
Default level: error ·
Added in 0.0.1-alpha.11 ·
Related issues ·
-View source
+View source
@@ -1578,7 +1578,7 @@ class C:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1613,7 +1613,7 @@ T = TypeVar('T', bound=str) # valid bound TypeVar
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1638,7 +1638,7 @@ func() # TypeError: func() missing 1 required positional argument: 'x'
Default level: error ·
Added in 0.0.1-alpha.20 ·
Related issues ·
-View source
+View source
@@ -1671,7 +1671,7 @@ alice["age"] # KeyError
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1700,7 +1700,7 @@ func("string") # error: [no-matching-overload]
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1724,7 +1724,7 @@ Subscripting an object that does not support it will raise a `TypeError` at runt
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1750,7 +1750,7 @@ for i in 34: # TypeError: 'int' object is not iterable
Default level: error ·
Added in 0.0.1-alpha.29 ·
Related issues ·
-View source
+View source
@@ -1783,7 +1783,7 @@ class B(A):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1810,7 +1810,7 @@ f(1, x=2) # Error raised here
Default level: error ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -1868,7 +1868,7 @@ def test(): -> "int":
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1898,7 +1898,7 @@ static_assert(int(2.0 * 3.0) == 6) # error: does not have a statically known tr
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1921,13 +1921,47 @@ class A: ...
class B(A): ... # Error raised here
```
+## `super-call-in-named-tuple-method`
+
+
+Default level: error ·
+Preview (since 0.0.1-alpha.30) ·
+Related issues ·
+View source
+
+
+
+**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)
+
## `too-many-positional-arguments`
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1954,7 +1988,7 @@ f("foo") # Error raised here
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -1982,7 +2016,7 @@ def _(x: int):
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2028,7 +2062,7 @@ class A:
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2055,7 +2089,7 @@ f(x=1, y=2) # Error raised here
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2083,7 +2117,7 @@ A().foo # AttributeError: 'A' object has no attribute 'foo'
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2108,7 +2142,7 @@ import foo # ModuleNotFoundError: No module named 'foo'
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2133,7 +2167,7 @@ print(x) # NameError: name 'x' is not defined
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2170,7 +2204,7 @@ b1 < b2 < b1 # exception raised here
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2198,7 +2232,7 @@ A() + A() # TypeError: unsupported operand type(s) for +: 'A' and 'A'
Default level: error ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2223,7 +2257,7 @@ l[1:10:0] # ValueError: slice step cannot be zero
Default level: warn ·
Added in 0.0.1-alpha.20 ·
Related issues ·
-View source
+View source
@@ -2264,7 +2298,7 @@ class SubProto(BaseProto, Protocol):
Default level: warn ·
Added in 0.0.1-alpha.16 ·
Related issues ·
-View source
+View source
@@ -2352,7 +2386,7 @@ a = 20 / 0 # type: ignore
Default level: warn ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -2380,7 +2414,7 @@ A.c # AttributeError: type object 'A' has no attribute 'c'
Default level: warn ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -2412,7 +2446,7 @@ A()[0] # TypeError: 'A' object is not subscriptable
Default level: warn ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -2444,7 +2478,7 @@ from module import a # ImportError: cannot import name 'a' from 'module'
Default level: warn ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2471,7 +2505,7 @@ cast(int, f()) # Redundant
Default level: warn ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
@@ -2495,7 +2529,7 @@ reveal_type(1) # NameError: name 'reveal_type' is not defined
Default level: warn ·
Added in 0.0.1-alpha.15 ·
Related issues ·
-View source
+View source
@@ -2553,7 +2587,7 @@ def g():
Default level: warn ·
Added in 0.0.1-alpha.7 ·
Related issues ·
-View source
+View source
@@ -2592,7 +2626,7 @@ class D(C): ... # error: [unsupported-base]
Default level: warn ·
Added in 0.0.1-alpha.22 ·
Related issues ·
-View source
+View source
@@ -2655,7 +2689,7 @@ def foo(x: int | str) -> int | str:
Default level: ignore ·
Preview (since 0.0.1-alpha.1) ·
Related issues ·
-View source
+View source
@@ -2679,7 +2713,7 @@ Dividing by zero raises a `ZeroDivisionError` at runtime.
Default level: ignore ·
Added in 0.0.1-alpha.1 ·
Related issues ·
-View source
+View source
diff --git a/crates/ty_python_semantic/resources/mdtest/named_tuple.md b/crates/ty_python_semantic/resources/mdtest/named_tuple.md
index c49cf1708c..439cfff06b 100644
--- a/crates/ty_python_semantic/resources/mdtest/named_tuple.md
+++ b/crates/ty_python_semantic/resources/mdtest/named_tuple.md
@@ -408,3 +408,77 @@ class Vec2(NamedTuple):
Vec2(0.0, 0.0)
```
+
+## `super()` is not supported in NamedTuple methods
+
+Using `super()` in a method of a `NamedTuple` class will raise an exception at runtime. In Python
+3.14+, a `TypeError` is raised; in earlier versions, a confusing `RuntimeError` about
+`__classcell__` is raised.
+
+```py
+from typing import NamedTuple
+
+class F(NamedTuple):
+ x: int
+
+ def method(self):
+ # error: [super-call-in-named-tuple-method] "Cannot use `super()` in a method of NamedTuple class `F`"
+ super()
+
+ def method_with_args(self):
+ # error: [super-call-in-named-tuple-method] "Cannot use `super()` in a method of NamedTuple class `F`"
+ super(F, self)
+
+ def method_with_different_pivot(self):
+ # Even passing a different pivot class fails.
+ # error: [super-call-in-named-tuple-method] "Cannot use `super()` in a method of NamedTuple class `F`"
+ super(tuple, self)
+
+ @classmethod
+ def class_method(cls):
+ # error: [super-call-in-named-tuple-method] "Cannot use `super()` in a method of NamedTuple class `F`"
+ super()
+
+ @staticmethod
+ def static_method():
+ # error: [super-call-in-named-tuple-method] "Cannot use `super()` in a method of NamedTuple class `F`"
+ super()
+
+ @property
+ def prop(self):
+ # error: [super-call-in-named-tuple-method] "Cannot use `super()` in a method of NamedTuple class `F`"
+ return super()
+```
+
+However, classes that **inherit from** a `NamedTuple` class (but don't directly inherit from
+`NamedTuple`) can use `super()` normally:
+
+```py
+from typing import NamedTuple
+
+class Base(NamedTuple):
+ x: int
+
+class Child(Base):
+ def method(self):
+ super()
+```
+
+And regular classes that don't inherit from `NamedTuple` at all can use `super()` as normal:
+
+```py
+class Regular:
+ def method(self):
+ super() # fine
+```
+
+Using `super()` on a `NamedTuple` class also works fine if it occurs outside the class:
+
+```py
+from typing import NamedTuple
+
+class F(NamedTuple):
+ x: int
+
+super(F, F(42)) # fine
+```
diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs
index 1f613fa568..3c38f6bdfc 100644
--- a/crates/ty_python_semantic/src/types/class.rs
+++ b/crates/ty_python_semantic/src/types/class.rs
@@ -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());
diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs
index 96aa876b50..8e92c495cf 100644
--- a/crates/ty_python_semantic/src/types/diagnostic.rs
+++ b/crates/ty_python_semantic/src/types/diagnostic.rs
@@ -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.
diff --git a/crates/ty_server/tests/e2e/snapshots/e2e__commands__debug_command.snap b/crates/ty_server/tests/e2e/snapshots/e2e__commands__debug_command.snap
index 5505fbd5f2..0daa6c768a 100644
--- a/crates/ty_server/tests/e2e/snapshots/e2e__commands__debug_command.snap
+++ b/crates/ty_server/tests/e2e/snapshots/e2e__commands__debug_command.snap
@@ -93,6 +93,7 @@ Settings: Settings {
"redundant-cast": Warning (Default),
"static-assert-error": Error (Default),
"subclass-of-final-class": Error (Default),
+ "super-call-in-named-tuple-method": Error (Default),
"too-many-positional-arguments": Error (Default),
"type-assertion-failure": Error (Default),
"unavailable-implicit-super-arguments": Error (Default),
diff --git a/ty.schema.json b/ty.schema.json
index 38d5fd1326..e2325d6ac4 100644
--- a/ty.schema.json
+++ b/ty.schema.json
@@ -973,6 +973,16 @@
}
]
},
+ "super-call-in-named-tuple-method": {
+ "title": "detects `super()` calls in methods of `NamedTuple` classes",
+ "description": "## What it does\nChecks for calls to `super()` inside methods of `NamedTuple` classes.\n\n## Why is this bad?\nUsing `super()` in a method of a `NamedTuple` class will raise an exception at runtime.\n\n## Examples\n```python\nfrom typing import NamedTuple\n\nclass F(NamedTuple):\n x: int\n\n def method(self):\n super() # error: super() is not supported in methods of NamedTuple classes\n```\n\n## References\n- [Python documentation: super()](https://docs.python.org/3/library/functions.html#super)",
+ "default": "error",
+ "oneOf": [
+ {
+ "$ref": "#/definitions/Level"
+ }
+ ]
+ },
"too-many-positional-arguments": {
"title": "detects calls passing too many positional arguments",
"description": "## What it does\nChecks for calls that pass more positional arguments than the callable can accept.\n\n## Why is this bad?\nPassing too many positional arguments will raise `TypeError` at runtime.\n\n## Example\n\n```python\ndef f(): ...\n\nf(\"foo\") # Error raised here\n```",