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

Code before:
    where
        I: IntoIterator<Item = T>,
        T: Into<Type<'db>>,
    {
        Type::tuple(TupleType::heterogeneous(
            db,
            elements.into_iter().map(Into::into),
        ))
    }

    pub(crate) fn empty_tuple(db: &'db dyn Db) -> Self {
        Type::tuple_instance(TupleType::empty(db))
    }

    /// **Private** helper function to create a `Type::NominalInstance` from a tuple.
    fn tuple_instance(tuple: TupleType<'db>) -> Self {
        Type::NominalInstance(NominalInstanceType(NominalInstanceInner::ExactTuple(tuple)))
    }

    pub(crate) const fn is_nominal_instance(self) -> bool {
        matches!(self, Type::NominalInstance(_))
    }

    pub(crate) const fn is_generic_nominal_instance(self) -> bool {
        matches!(self, Type::NominalInstance(instance_type) if matches!(instance_type.0, NominalInstanceInner::NonTuple(class) if class.is_generic()))
    }

    pub(crate) const fn as_nominal_instance(self) -> Option<NominalInstanceType<'db>> {
        match self {
            Type::NominalInstance(instance_type) => Some(instance_type),
            _ => None,
        }
    }

    /// Return `true` if `self` is a nominal instance of the given known class.
    pub(crate) fn is_instance_of(self, db: &'db dyn Db, known_class: KnownClass) -> bool {
        match self {
            Type::NominalInstance(instance) => instance.class(db).is_known(db, known_class),
            _ => false,
        }
    }

    /// Synthesize a protocol instance type with a given set of read-only property members.
    pub(super) fn protocol_with_readonly_members<'a, M>(db: &'db dyn Db, members: M) -> Self
    where
        M: IntoIterator<Item = (&'a str, Type<'db>)>,
    {
        Self::ProtocolInstance(ProtocolInstanceType::synthesized(
            SynthesizedProtocolType::new(
                db,
                ProtocolInterface::with_property_members(db, members),

Review comment (suggestion):
this feels suspiciously specific ;)

What about doubly specialized `Protocol` instances, doubly specialized `TypedDict` instances, and doubly specialized `tuple` instances? Don't they also deserve to have the "<NAME> is already specialized" part of the diagnostic message?

```py
from typing import Protocol, TypedDict

class Foo[T](Protocol):
    x: T

type Bar = Foo[int]

class Spam[T](TypedDict):
    x: T

type Ham = Spam[int]

b: Bar[str]  # error: "Cannot subscript non-generic type alias"
h: Ham[int]  # error: "Cannot subscript non-generic type alias"

type SpecializedTuple = tuple[int, int]
DoubleSpecialized = SpecializedTuple[str]  # error: "Cannot subscript non-generic type alias"
```

Code after:
    where
        I: IntoIterator<Item = T>,
        T: Into<Type<'db>>,
    {
        Type::tuple(TupleType::heterogeneous(
            db,
            elements.into_iter().map(Into::into),
        ))
    }

    pub(crate) fn empty_tuple(db: &'db dyn Db) -> Self {
        Type::tuple_instance(TupleType::empty(db))
    }

    /// **Private** helper function to create a `Type::NominalInstance` from a tuple.
    fn tuple_instance(tuple: TupleType<'db>) -> Self {
        Type::NominalInstance(NominalInstanceType(NominalInstanceInner::ExactTuple(tuple)))
    }

    pub(crate) const fn is_nominal_instance(self) -> bool {
        matches!(self, Type::NominalInstance(_))
    }

    pub(crate) const fn as_nominal_instance(self) -> Option<NominalInstanceType<'db>> {
        match self {
            Type::NominalInstance(instance_type) => Some(instance_type),
            _ => None,
        }
    }

    /// Return `true` if `self` is a nominal instance of the given known class.
    pub(crate) fn is_instance_of(self, db: &'db dyn Db, known_class: KnownClass) -> bool {
        match self {
            Type::NominalInstance(instance) => instance.class(db).is_known(db, known_class),
            _ => false,
        }
    }

    /// Synthesize a protocol instance type with a given set of read-only property members.
    pub(super) fn protocol_with_readonly_members<'a, M>(db: &'db dyn Db, members: M) -> Self
    where
        M: IntoIterator<Item = (&'a str, Type<'db>)>,
    {
        Self::ProtocolInstance(ProtocolInstanceType::synthesized(
            SynthesizedProtocolType::new(
                db,
                ProtocolInterface::with_property_members(db, members),
                &NormalizedVisitor::default(),
            ),
        ))
    }