mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:48:32 +00:00
[ty] Report duplicate Protocol
or Generic
base classes with [duplicate-base]
, not [inconsistent-mro]
(#17971)
This commit is contained in:
parent
4d81a41107
commit
9b694ada82
4 changed files with 27 additions and 7 deletions
|
@ -19,6 +19,16 @@ reveal_type(generic_context(SingleTypevar)) # revealed: tuple[T]
|
|||
reveal_type(generic_context(MultipleTypevars)) # revealed: tuple[T, S]
|
||||
```
|
||||
|
||||
Inheriting from `Generic` multiple times yields a `duplicate-base` diagnostic, just like any other
|
||||
class:
|
||||
|
||||
```py
|
||||
class Bad(Generic[T], Generic[T]): ... # error: [duplicate-base]
|
||||
|
||||
# TODO: should emit an error (fails at runtime)
|
||||
class AlsoBad(Generic[T], Generic[S]): ...
|
||||
```
|
||||
|
||||
You cannot use the same typevar more than once.
|
||||
|
||||
```py
|
||||
|
|
|
@ -35,7 +35,7 @@ Just like for any other class base, it is an error for `Protocol` to appear mult
|
|||
class's bases:
|
||||
|
||||
```py
|
||||
class Foo(Protocol, Protocol): ... # error: [inconsistent-mro]
|
||||
class Foo(Protocol, Protocol): ... # error: [duplicate-base]
|
||||
|
||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
||||
```
|
||||
|
|
|
@ -65,6 +65,19 @@ impl<'db> ClassBase<'db> {
|
|||
Display { base: self, db }
|
||||
}
|
||||
|
||||
pub(crate) fn name(self, db: &'db dyn Db) -> &'db str {
|
||||
match self {
|
||||
ClassBase::Class(class) => class.name(db),
|
||||
ClassBase::Dynamic(DynamicType::Any) => "Any",
|
||||
ClassBase::Dynamic(DynamicType::Unknown) => "Unknown",
|
||||
ClassBase::Dynamic(DynamicType::Todo(_)) => "@Todo",
|
||||
ClassBase::Protocol | ClassBase::Dynamic(DynamicType::SubscriptedProtocol) => {
|
||||
"Protocol"
|
||||
}
|
||||
ClassBase::Generic(_) => "Generic",
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a `ClassBase` representing the class `builtins.object`
|
||||
pub(super) fn object(db: &'db dyn Db) -> Self {
|
||||
KnownClass::Object
|
||||
|
|
|
@ -177,16 +177,13 @@ impl<'db> Mro<'db> {
|
|||
continue;
|
||||
}
|
||||
match base {
|
||||
ClassBase::Class(class) => {
|
||||
ClassBase::Class(_) | ClassBase::Generic(_) | ClassBase::Protocol => {
|
||||
errors.push(DuplicateBaseError {
|
||||
duplicate_base: class.class_literal(db).0,
|
||||
duplicate_base: base,
|
||||
first_index: *first_index,
|
||||
later_indices: later_indices.iter().copied().collect(),
|
||||
});
|
||||
}
|
||||
// TODO these should also be reported as duplicate bases
|
||||
// rather than using the less specific `inconsistent-mro` error
|
||||
ClassBase::Generic(_) | ClassBase::Protocol => continue,
|
||||
ClassBase::Dynamic(_) => duplicate_dynamic_bases = true,
|
||||
}
|
||||
}
|
||||
|
@ -385,7 +382,7 @@ impl<'db> MroErrorKind<'db> {
|
|||
#[derive(Debug, PartialEq, Eq, salsa::Update)]
|
||||
pub(super) struct DuplicateBaseError<'db> {
|
||||
/// The base that is duplicated in the class's bases list.
|
||||
pub(super) duplicate_base: ClassLiteral<'db>,
|
||||
pub(super) duplicate_base: ClassBase<'db>,
|
||||
/// The index of the first occurrence of the base in the class's bases list.
|
||||
pub(super) first_index: usize,
|
||||
/// The indices of the base's later occurrences in the class's bases list.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue