mirror of
https://github.com/astral-sh/ruff
synced 2026-01-21 05:20:49 -05:00
[ty] Emit diagnostics for invalid base classes in type(...) (#22499)
## Summary Tackles a few TODOs from https://github.com/astral-sh/ruff/pull/22291.
This commit is contained in:
@@ -908,12 +908,34 @@ Bad: type[Unrelated] = type("Bad", (Base,), {})
|
||||
## Special base classes
|
||||
|
||||
Some special base classes work with dynamic class creation, but special semantics may not be fully
|
||||
synthesized:
|
||||
synthesized.
|
||||
|
||||
### Invalid special bases
|
||||
|
||||
Dynamic classes cannot directly inherit from `Generic`, `Protocol`, or `TypedDict`. These special
|
||||
forms require class syntax for their semantics to be properly applied:
|
||||
|
||||
```py
|
||||
from typing import Generic, Protocol, TypeVar
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
# error: [invalid-base] "Invalid base for class created via `type()`"
|
||||
GenericClass = type("GenericClass", (Generic[T],), {})
|
||||
|
||||
# error: [unsupported-dynamic-base] "Unsupported base for class created via `type()`"
|
||||
ProtocolClass = type("ProtocolClass", (Protocol,), {})
|
||||
|
||||
# error: [invalid-base] "Invalid base for class created via `type()`"
|
||||
TypedDictClass = type("TypedDictClass", (TypedDict,), {})
|
||||
```
|
||||
|
||||
### Protocol bases
|
||||
|
||||
Inheriting from a class that is itself a protocol is valid:
|
||||
|
||||
```py
|
||||
# Protocol bases work - the class is created as a subclass of the protocol
|
||||
from typing import Protocol
|
||||
from ty_extensions import reveal_mro
|
||||
|
||||
@@ -930,8 +952,9 @@ reveal_type(instance) # revealed: ProtoImpl
|
||||
|
||||
### TypedDict bases
|
||||
|
||||
Inheriting from a class that is itself a TypedDict is valid:
|
||||
|
||||
```py
|
||||
# TypedDict bases work but TypedDict semantics aren't applied to dynamic subclasses
|
||||
from typing_extensions import TypedDict
|
||||
from ty_extensions import reveal_mro
|
||||
|
||||
@@ -964,26 +987,26 @@ reveal_mro(Point3D) # revealed: (<class 'Point3D'>, <class 'Point'>, <class 'tu
|
||||
|
||||
### Enum bases
|
||||
|
||||
Creating a class via `type()` that inherits from any Enum class fails at runtime because `EnumMeta`
|
||||
expects special attributes in the class dict that `type()` doesn't provide:
|
||||
|
||||
```py
|
||||
# Enum subclassing via type() is not supported - EnumMeta requires special dict handling
|
||||
# that type() cannot provide. This applies even to empty enums.
|
||||
from enum import Enum
|
||||
|
||||
class Color(Enum):
|
||||
RED = 1
|
||||
GREEN = 2
|
||||
|
||||
# Enums with members are final and cannot be subclassed
|
||||
# error: [subclass-of-final-class]
|
||||
ExtendedColor = type("ExtendedColor", (Color,), {})
|
||||
|
||||
class EmptyEnum(Enum):
|
||||
pass
|
||||
|
||||
# TODO: We should emit a diagnostic here - type() cannot create Enum subclasses
|
||||
ExtendedColor = type("ExtendedColor", (Color,), {})
|
||||
reveal_type(ExtendedColor) # revealed: <class 'ExtendedColor'>
|
||||
|
||||
# Even empty enums fail - EnumMeta requires special dict handling
|
||||
# TODO: We should emit a diagnostic here too
|
||||
ValidExtension = type("ValidExtension", (EmptyEnum,), {})
|
||||
reveal_type(ValidExtension) # revealed: <class 'ValidExtension'>
|
||||
# Empty enums fail because EnumMeta requires special dict handling
|
||||
# error: [invalid-base] "Invalid base for class created via `type()`"
|
||||
InvalidExtension = type("InvalidExtension", (EmptyEnum,), {})
|
||||
```
|
||||
|
||||
## `__init_subclass__` keyword arguments
|
||||
@@ -1046,3 +1069,27 @@ reveal_type(Dynamic) # revealed: <class 'Dynamic'>
|
||||
# Metaclass attributes are accessible on the class
|
||||
reveal_type(Dynamic.custom_attr) # revealed: str
|
||||
```
|
||||
|
||||
## `final()` on dynamic classes
|
||||
|
||||
Using `final()` as a function (not a decorator) on dynamic classes has no effect. The class is
|
||||
passed through unchanged:
|
||||
|
||||
```py
|
||||
from typing import final
|
||||
|
||||
# TODO: Add a diagnostic for ineffective use of `final()` here.
|
||||
FinalClass = final(type("FinalClass", (), {}))
|
||||
reveal_type(FinalClass) # revealed: <class 'FinalClass'>
|
||||
|
||||
# Subclassing is allowed because `final()` as a function has no effect
|
||||
class Child(FinalClass): ...
|
||||
|
||||
# Same with base classes
|
||||
class Base: ...
|
||||
|
||||
# TODO: Add a diagnostic for ineffective use of `final()` here.
|
||||
FinalDerived = final(type("FinalDerived", (Base,), {}))
|
||||
|
||||
class Child2(FinalDerived): ...
|
||||
```
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
# Unsupported base for dynamic `type()` classes
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
## `@final` class
|
||||
|
||||
Classes decorated with `@final` cannot be subclassed:
|
||||
|
||||
```py
|
||||
from typing import final
|
||||
|
||||
@final
|
||||
class FinalClass:
|
||||
pass
|
||||
|
||||
X = type("X", (FinalClass,), {}) # error: [subclass-of-final-class]
|
||||
```
|
||||
|
||||
## `Generic` base
|
||||
|
||||
Dynamic classes created via `type()` cannot inherit from `Generic`:
|
||||
|
||||
```py
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
X = type("X", (Generic[T],), {}) # error: [invalid-base]
|
||||
```
|
||||
|
||||
## `Protocol` base
|
||||
|
||||
Dynamic classes created via `type()` cannot inherit from `Protocol`:
|
||||
|
||||
```py
|
||||
from typing import Protocol
|
||||
|
||||
X = type("X", (Protocol,), {}) # error: [unsupported-dynamic-base]
|
||||
```
|
||||
|
||||
## `TypedDict` base
|
||||
|
||||
Dynamic classes created via `type()` cannot inherit from `TypedDict` directly. Use
|
||||
`TypedDict("Name", ...)` instead:
|
||||
|
||||
```py
|
||||
from typing_extensions import TypedDict
|
||||
|
||||
X = type("X", (TypedDict,), {}) # error: [invalid-base]
|
||||
```
|
||||
|
||||
## Enum base
|
||||
|
||||
Dynamic classes created via `type()` cannot inherit from Enum classes because `EnumMeta` expects
|
||||
special dict attributes that `type()` doesn't provide:
|
||||
|
||||
```py
|
||||
from enum import Enum
|
||||
|
||||
class MyEnum(Enum):
|
||||
pass
|
||||
|
||||
X = type("X", (MyEnum,), {}) # error: [invalid-base]
|
||||
```
|
||||
|
||||
## Enum with members
|
||||
|
||||
Enums with members are final and cannot be subclassed at all:
|
||||
|
||||
```py
|
||||
from enum import Enum
|
||||
|
||||
class Color(Enum):
|
||||
RED = 1
|
||||
GREEN = 2
|
||||
|
||||
X = type("X", (Color,), {}) # error: [subclass-of-final-class]
|
||||
```
|
||||
@@ -0,0 +1,39 @@
|
||||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
|
||||
---
|
||||
mdtest name: unsupported_base_dynamic_type.md - Unsupported base for dynamic `type()` classes - Enum base
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/unsupported_base_dynamic_type.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | from enum import Enum
|
||||
2 |
|
||||
3 | class MyEnum(Enum):
|
||||
4 | pass
|
||||
5 |
|
||||
6 | X = type("X", (MyEnum,), {}) # error: [invalid-base]
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[invalid-base]: Invalid base for class created via `type()`
|
||||
--> src/mdtest_snippet.py:6:16
|
||||
|
|
||||
4 | pass
|
||||
5 |
|
||||
6 | X = type("X", (MyEnum,), {}) # error: [invalid-base]
|
||||
| ^^^^^^ Has type `<class 'MyEnum'>`
|
||||
|
|
||||
info: Creating an enum class via `type()` is not supported
|
||||
info: Consider using `Enum("X", [])` instead
|
||||
info: rule `invalid-base` is enabled by default
|
||||
|
||||
```
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
|
||||
---
|
||||
mdtest name: unsupported_base_dynamic_type.md - Unsupported base for dynamic `type()` classes - Enum with members
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/unsupported_base_dynamic_type.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | from enum import Enum
|
||||
2 |
|
||||
3 | class Color(Enum):
|
||||
4 | RED = 1
|
||||
5 | GREEN = 2
|
||||
6 |
|
||||
7 | X = type("X", (Color,), {}) # error: [subclass-of-final-class]
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[subclass-of-final-class]: Class `X` cannot inherit from final class `Color`
|
||||
--> src/mdtest_snippet.py:7:16
|
||||
|
|
||||
5 | GREEN = 2
|
||||
6 |
|
||||
7 | X = type("X", (Color,), {}) # error: [subclass-of-final-class]
|
||||
| ^^^^^
|
||||
|
|
||||
info: rule `subclass-of-final-class` is enabled by default
|
||||
|
||||
```
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
|
||||
---
|
||||
mdtest name: unsupported_base_dynamic_type.md - Unsupported base for dynamic `type()` classes - `@final` class
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/unsupported_base_dynamic_type.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | from typing import final
|
||||
2 |
|
||||
3 | @final
|
||||
4 | class FinalClass:
|
||||
5 | pass
|
||||
6 |
|
||||
7 | X = type("X", (FinalClass,), {}) # error: [subclass-of-final-class]
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[subclass-of-final-class]: Class `X` cannot inherit from final class `FinalClass`
|
||||
--> src/mdtest_snippet.py:7:16
|
||||
|
|
||||
5 | pass
|
||||
6 |
|
||||
7 | X = type("X", (FinalClass,), {}) # error: [subclass-of-final-class]
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
info: rule `subclass-of-final-class` is enabled by default
|
||||
|
||||
```
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
|
||||
---
|
||||
mdtest name: unsupported_base_dynamic_type.md - Unsupported base for dynamic `type()` classes - `Generic` base
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/unsupported_base_dynamic_type.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | from typing import Generic, TypeVar
|
||||
2 |
|
||||
3 | T = TypeVar("T")
|
||||
4 |
|
||||
5 | X = type("X", (Generic[T],), {}) # error: [invalid-base]
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[invalid-base]: Invalid base for class created via `type()`
|
||||
--> src/mdtest_snippet.py:5:16
|
||||
|
|
||||
3 | T = TypeVar("T")
|
||||
4 |
|
||||
5 | X = type("X", (Generic[T],), {}) # error: [invalid-base]
|
||||
| ^^^^^^^^^^ Has type `<special-form 'typing.Generic[T]'>`
|
||||
|
|
||||
info: Classes created via `type()` cannot be generic
|
||||
info: Consider using `class X(Generic[...]): ...` instead
|
||||
info: rule `invalid-base` is enabled by default
|
||||
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
|
||||
---
|
||||
mdtest name: unsupported_base_dynamic_type.md - Unsupported base for dynamic `type()` classes - `Protocol` base
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/unsupported_base_dynamic_type.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | from typing import Protocol
|
||||
2 |
|
||||
3 | X = type("X", (Protocol,), {}) # error: [unsupported-dynamic-base]
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
info[unsupported-dynamic-base]: Unsupported base for class created via `type()`
|
||||
--> src/mdtest_snippet.py:3:16
|
||||
|
|
||||
1 | from typing import Protocol
|
||||
2 |
|
||||
3 | X = type("X", (Protocol,), {}) # error: [unsupported-dynamic-base]
|
||||
| ^^^^^^^^ Has type `<special-form 'typing.Protocol'>`
|
||||
|
|
||||
info: Classes created via `type()` cannot be protocols
|
||||
info: Consider using `class X(Protocol): ...` instead
|
||||
info: rule `unsupported-dynamic-base` is enabled by default
|
||||
|
||||
```
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
|
||||
---
|
||||
mdtest name: unsupported_base_dynamic_type.md - Unsupported base for dynamic `type()` classes - `TypedDict` base
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/diagnostics/unsupported_base_dynamic_type.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | from typing_extensions import TypedDict
|
||||
2 |
|
||||
3 | X = type("X", (TypedDict,), {}) # error: [invalid-base]
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[invalid-base]: Invalid base for class created via `type()`
|
||||
--> src/mdtest_snippet.py:3:16
|
||||
|
|
||||
1 | from typing_extensions import TypedDict
|
||||
2 |
|
||||
3 | X = type("X", (TypedDict,), {}) # error: [invalid-base]
|
||||
| ^^^^^^^^^ Has type `<special-form 'typing.TypedDict'>`
|
||||
|
|
||||
info: Classes created via `type()` cannot be TypedDicts
|
||||
info: Consider using `TypedDict("X", {})` instead
|
||||
info: rule `invalid-base` is enabled by default
|
||||
|
||||
```
|
||||
@@ -562,25 +562,16 @@ impl<'db> ClassLiteral<'db> {
|
||||
}
|
||||
|
||||
/// Returns the generic context if this is a generic class.
|
||||
///
|
||||
// TODO: We should emit a diagnostic if a dynamic class (created via `type()`) attempts
|
||||
// to inherit from `Generic[T]`, since dynamic classes can't be generic. See also: `is_protocol`.
|
||||
pub(crate) fn generic_context(self, db: &'db dyn Db) -> Option<GenericContext<'db>> {
|
||||
self.as_static().and_then(|class| class.generic_context(db))
|
||||
}
|
||||
|
||||
/// Returns whether this class is a protocol.
|
||||
///
|
||||
// TODO: We should emit a diagnostic if a dynamic class (created via `type()`) attempts
|
||||
// to inherit from `Protocol`, since dynamic classes can't be protocols. See also: `generic_context`.
|
||||
pub(crate) fn is_protocol(self, db: &'db dyn Db) -> bool {
|
||||
self.as_static().is_some_and(|class| class.is_protocol(db))
|
||||
}
|
||||
|
||||
/// Returns whether this class is a `TypedDict`.
|
||||
// TODO: We should emit a diagnostic if a dynamic class (created via `type()`) attempts
|
||||
// to inherit from `TypedDict`. To create a dynamic TypedDict, you should invoke
|
||||
// `TypedDict` as a function, not `type`. See also: `generic_context`, `is_protocol`.
|
||||
pub fn is_typed_dict(self, db: &'db dyn Db) -> bool {
|
||||
match self {
|
||||
Self::Static(class) => class.is_typed_dict(db),
|
||||
@@ -634,10 +625,12 @@ impl<'db> ClassLiteral<'db> {
|
||||
}
|
||||
|
||||
/// Returns whether this class is final.
|
||||
// TODO: Support `@final` on dynamic classes, e.g. `X = final(type("X", (), {}))`.
|
||||
// We should either recognize and track this, or emit a diagnostic if unsupported.
|
||||
pub(crate) fn is_final(self, db: &'db dyn Db) -> bool {
|
||||
self.as_static().is_some_and(|class| class.is_final(db))
|
||||
match self {
|
||||
Self::Static(class) => class.is_final(db),
|
||||
// Dynamic classes created via `type()` cannot be marked as final.
|
||||
Self::Dynamic(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if this class defines any ordering method (`__lt__`, `__le__`, `__gt__`,
|
||||
|
||||
@@ -67,7 +67,6 @@ pub(crate) fn enum_metadata<'db>(
|
||||
//
|
||||
// MyEnum = type("MyEnum", (BaseEnum,), {"A": 1, "B": 2})
|
||||
// ```
|
||||
// TODO: Add a diagnostic for including an enum in a `type(...)` call.
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6443,26 +6443,129 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, base)| {
|
||||
let diagnostic_node = bases_tuple_elts
|
||||
.and_then(|elts| elts.get(idx))
|
||||
.unwrap_or(bases_node);
|
||||
|
||||
// First try the standard conversion.
|
||||
if let Some(class_base) =
|
||||
ClassBase::try_from_type(db, *base, placeholder_class)
|
||||
{
|
||||
// Collect disjoint bases for instance-layout-conflict checking.
|
||||
if let ClassBase::Class(base_class) = class_base {
|
||||
if let Some(disjoint_base) = base_class.nearest_disjoint_base(db) {
|
||||
disjoint_bases.insert(
|
||||
disjoint_base,
|
||||
idx,
|
||||
base_class.class_literal(db),
|
||||
);
|
||||
// Check for special bases that are not allowed for dynamic classes.
|
||||
// Dynamic classes can't be generic, protocols, TypedDicts, or enums.
|
||||
match class_base {
|
||||
ClassBase::Generic | ClassBase::TypedDict => {
|
||||
if let Some(builder) =
|
||||
self.context.report_lint(&INVALID_BASE, diagnostic_node)
|
||||
{
|
||||
let mut diagnostic = builder.into_diagnostic(
|
||||
"Invalid base for class created via `type()`",
|
||||
);
|
||||
diagnostic.set_primary_message(format_args!(
|
||||
"Has type `{}`",
|
||||
base.display(db)
|
||||
));
|
||||
match class_base {
|
||||
ClassBase::Generic => {
|
||||
diagnostic.info(
|
||||
"Classes created via `type()` cannot be generic",
|
||||
);
|
||||
diagnostic.info(format_args!(
|
||||
"Consider using `class {name}(Generic[...]): ...` instead"
|
||||
));
|
||||
}
|
||||
ClassBase::TypedDict => {
|
||||
diagnostic.info(
|
||||
"Classes created via `type()` cannot be TypedDicts",
|
||||
);
|
||||
diagnostic.info(format_args!(
|
||||
"Consider using `TypedDict(\"{name}\", {{}})` instead"
|
||||
));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
return ClassBase::unknown();
|
||||
}
|
||||
}
|
||||
return class_base;
|
||||
}
|
||||
ClassBase::Protocol => {
|
||||
if let Some(builder) = self
|
||||
.context
|
||||
.report_lint(&UNSUPPORTED_DYNAMIC_BASE, diagnostic_node)
|
||||
{
|
||||
let mut diagnostic = builder.into_diagnostic(
|
||||
"Unsupported base for class created via `type()`",
|
||||
);
|
||||
diagnostic.set_primary_message(format_args!(
|
||||
"Has type `{}`",
|
||||
base.display(db)
|
||||
));
|
||||
diagnostic.info(
|
||||
"Classes created via `type()` cannot be protocols",
|
||||
);
|
||||
diagnostic.info(format_args!(
|
||||
"Consider using `class {name}(Protocol): ...` instead"
|
||||
));
|
||||
}
|
||||
return ClassBase::unknown();
|
||||
}
|
||||
ClassBase::Class(class_type) => {
|
||||
// Check if base is @final (includes enums with members).
|
||||
if class_type.is_final(db) {
|
||||
if let Some(builder) = self
|
||||
.context
|
||||
.report_lint(&SUBCLASS_OF_FINAL_CLASS, diagnostic_node)
|
||||
{
|
||||
builder.into_diagnostic(format_args!(
|
||||
"Class `{name}` cannot inherit from final class `{}`",
|
||||
class_type.name(db)
|
||||
));
|
||||
}
|
||||
return ClassBase::unknown();
|
||||
}
|
||||
|
||||
let diagnostic_node = bases_tuple_elts
|
||||
.and_then(|elts| elts.get(idx))
|
||||
.unwrap_or(bases_node);
|
||||
// Enum subclasses require the EnumMeta metaclass, which
|
||||
// expects special dict attributes that `type()` doesn't provide.
|
||||
if let Some((static_class, _)) =
|
||||
class_type.static_class_literal(db)
|
||||
{
|
||||
if is_enum_class_by_inheritance(db, static_class) {
|
||||
if let Some(builder) = self
|
||||
.context
|
||||
.report_lint(&INVALID_BASE, diagnostic_node)
|
||||
{
|
||||
let mut diagnostic = builder.into_diagnostic(
|
||||
"Invalid base for class created via `type()`",
|
||||
);
|
||||
diagnostic.set_primary_message(format_args!(
|
||||
"Has type `{}`",
|
||||
base.display(db)
|
||||
));
|
||||
diagnostic.info(
|
||||
"Creating an enum class via `type()` is not supported",
|
||||
);
|
||||
diagnostic.info(format_args!(
|
||||
"Consider using `Enum(\"{name}\", [])` instead"
|
||||
));
|
||||
}
|
||||
return ClassBase::unknown();
|
||||
}
|
||||
}
|
||||
|
||||
// Collect disjoint bases for instance-layout-conflict checking.
|
||||
if let Some(disjoint_base) = class_type.nearest_disjoint_base(db)
|
||||
{
|
||||
disjoint_bases.insert(
|
||||
disjoint_base,
|
||||
idx,
|
||||
class_type.class_literal(db),
|
||||
);
|
||||
}
|
||||
|
||||
return class_base;
|
||||
}
|
||||
ClassBase::Dynamic(_) => return class_base,
|
||||
}
|
||||
}
|
||||
|
||||
// If that fails, check if the type is "type-like" (e.g., `type[Base]`).
|
||||
// For type-like bases we emit `unsupported-dynamic-base` and use
|
||||
|
||||
Reference in New Issue
Block a user