mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-02 18:02:23 +00:00
Generalize special-casing for enums constructed with the functional syntax (#17885)
This commit is contained in:
parent
aa0614509b
commit
457ec4dddd
3 changed files with 54 additions and 57 deletions
|
@ -3890,6 +3890,9 @@ impl<'db> Type<'db> {
|
|||
);
|
||||
Signatures::single(signature)
|
||||
}
|
||||
Some(KnownClass::Enum) => {
|
||||
Signatures::single(CallableSignature::todo("functional `Enum` syntax"))
|
||||
}
|
||||
|
||||
Some(KnownClass::Super) => {
|
||||
// ```py
|
||||
|
@ -4836,14 +4839,6 @@ impl<'db> Type<'db> {
|
|||
Some(KnownClass::NamedTuple) => Ok(todo_type!(
|
||||
"Support for functional `typing.NamedTuple` syntax"
|
||||
)),
|
||||
_ if instance
|
||||
.class()
|
||||
.iter_mro(db)
|
||||
.filter_map(ClassBase::into_class)
|
||||
.any(|class| class.is_known(db, KnownClass::Enum)) =>
|
||||
{
|
||||
Ok(todo_type!("Support for functional `enum` syntax"))
|
||||
}
|
||||
_ => Err(InvalidTypeExpressionError {
|
||||
invalid_expressions: smallvec::smallvec![InvalidTypeExpression::InvalidType(
|
||||
*self
|
||||
|
|
|
@ -4637,46 +4637,42 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
}
|
||||
}
|
||||
|
||||
// It might look odd here that we emit an error for class-literals and generic aliases but not
|
||||
// `type[]` types. But it's deliberate! The typing spec explicitly mandates that `type[]` types
|
||||
// can be called even though class-literals cannot. This is because even though a protocol class
|
||||
// `SomeProtocol` is always an abstract class, `type[SomeProtocol]` can be a concrete subclass of
|
||||
// that protocol -- and indeed, according to the spec, type checkers must disallow abstract
|
||||
// subclasses of the protocol to be passed to parameters that accept `type[SomeProtocol]`.
|
||||
// <https://typing.python.org/en/latest/spec/protocol.html#type-and-class-objects-vs-protocols>.
|
||||
let possible_protocol_class = match callable_type {
|
||||
Type::ClassLiteral(class) => Some(class),
|
||||
Type::GenericAlias(generic) => Some(generic.origin(self.db())),
|
||||
let class = match callable_type {
|
||||
Type::ClassLiteral(class) => Some(ClassType::NonGeneric(class)),
|
||||
Type::GenericAlias(generic) => Some(ClassType::Generic(generic)),
|
||||
Type::SubclassOf(subclass) => subclass.subclass_of().into_class(),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(protocol) =
|
||||
possible_protocol_class.and_then(|class| class.into_protocol_class(self.db()))
|
||||
{
|
||||
report_attempted_protocol_instantiation(&self.context, call_expression, protocol);
|
||||
}
|
||||
if let Some(class) = class {
|
||||
// It might look odd here that we emit an error for class-literals and generic aliases but not
|
||||
// `type[]` types. But it's deliberate! The typing spec explicitly mandates that `type[]` types
|
||||
// can be called even though class-literals cannot. This is because even though a protocol class
|
||||
// `SomeProtocol` is always an abstract class, `type[SomeProtocol]` can be a concrete subclass of
|
||||
// that protocol -- and indeed, according to the spec, type checkers must disallow abstract
|
||||
// subclasses of the protocol to be passed to parameters that accept `type[SomeProtocol]`.
|
||||
// <https://typing.python.org/en/latest/spec/protocol.html#type-and-class-objects-vs-protocols>.
|
||||
if !callable_type.is_subclass_of() {
|
||||
if let Some(protocol) = class
|
||||
.class_literal(self.db())
|
||||
.0
|
||||
.into_protocol_class(self.db())
|
||||
{
|
||||
report_attempted_protocol_instantiation(
|
||||
&self.context,
|
||||
call_expression,
|
||||
protocol,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// For class literals we model the entire class instantiation logic, so it is handled
|
||||
// in a separate function. For some known classes we have manual signatures defined and use
|
||||
// the `try_call` path below.
|
||||
// TODO: it should be possible to move these special cases into the `try_call_constructor`
|
||||
// path instead, or even remove some entirely once we support overloads fully.
|
||||
let (call_constructor, known_class) = match callable_type {
|
||||
Type::ClassLiteral(class) => (true, class.known(self.db())),
|
||||
Type::GenericAlias(generic) => (true, ClassType::Generic(generic).known(self.db())),
|
||||
Type::SubclassOf(subclass) => (
|
||||
true,
|
||||
subclass
|
||||
.subclass_of()
|
||||
.into_class()
|
||||
.and_then(|class| class.known(self.db())),
|
||||
),
|
||||
_ => (false, None),
|
||||
};
|
||||
|
||||
if call_constructor
|
||||
&& !matches!(
|
||||
known_class,
|
||||
// For class literals we model the entire class instantiation logic, so it is handled
|
||||
// in a separate function. For some known classes we have manual signatures defined and use
|
||||
// the `try_call` path below.
|
||||
// TODO: it should be possible to move these special cases into the `try_call_constructor`
|
||||
// path instead, or even remove some entirely once we support overloads fully.
|
||||
if !matches!(
|
||||
class.known(self.db()),
|
||||
Some(
|
||||
KnownClass::Bool
|
||||
| KnownClass::Str
|
||||
|
@ -4687,17 +4683,24 @@ impl<'db> TypeInferenceBuilder<'db> {
|
|||
| KnownClass::TypeVar
|
||||
)
|
||||
)
|
||||
{
|
||||
let argument_forms = vec![Some(ParameterForm::Value); call_arguments.len()];
|
||||
let call_argument_types =
|
||||
self.infer_argument_types(arguments, call_arguments, &argument_forms);
|
||||
// temporary special-casing for all subclasses of `enum.Enum`
|
||||
// until we support the functional syntax for creating enum classes
|
||||
&& KnownClass::Enum
|
||||
.to_class_literal(self.db())
|
||||
.to_class_type(self.db())
|
||||
.is_none_or(|enum_class| !class.is_subclass_of(self.db(), enum_class))
|
||||
{
|
||||
let argument_forms = vec![Some(ParameterForm::Value); call_arguments.len()];
|
||||
let call_argument_types =
|
||||
self.infer_argument_types(arguments, call_arguments, &argument_forms);
|
||||
|
||||
return callable_type
|
||||
.try_call_constructor(self.db(), call_argument_types)
|
||||
.unwrap_or_else(|err| {
|
||||
err.report_diagnostic(&self.context, callable_type, call_expression.into());
|
||||
err.return_type()
|
||||
});
|
||||
return callable_type
|
||||
.try_call_constructor(self.db(), call_argument_types)
|
||||
.unwrap_or_else(|err| {
|
||||
err.report_diagnostic(&self.context, callable_type, call_expression.into());
|
||||
err.return_type()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let signatures = callable_type.signatures(self.db());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue