Compare commits

...

3 Commits

Author SHA1 Message Date
Alex Waygood
9ea95e9f24 rework check_call more 2025-06-26 18:58:25 +01:00
Alex Waygood
040bb0f932 consolidate how special-cased bindings for KnownClasses are determined 2025-06-26 17:33:45 +01:00
Alex Waygood
0150ac964d move special-cased bindings to class.rs 2025-06-26 17:33:45 +01:00
4 changed files with 420 additions and 440 deletions

View File

@@ -3900,353 +3900,25 @@ impl<'db> Type<'db> {
.into(),
},
Type::ClassLiteral(class) => match class.known(db) {
// TODO: Ideally we'd use `try_call_constructor` for all constructor calls.
// Currently we don't for a few special known types, either because their
// constructors are defined with overloads, or because we want to special case
// their return type beyond what typeshed provides (though this support could
// likely be moved into the `try_call_constructor` path). Once we support
// overloads, re-evaluate the need for these arms.
Some(KnownClass::Bool) => {
// ```py
// class bool(int):
// def __new__(cls, o: object = ..., /) -> Self: ...
// ```
Type::ClassLiteral(class) => class
.known(db)
.and_then(|known_class| known_class.bindings(db, self))
.unwrap_or_else(|| {
// Most class literal constructor calls are handled by `try_call_constructor` and
// not via getting the signature here. This signature can still be used in some
// cases (e.g. evaluating callable subtyping). TODO improve this definition
// (intersection of `__new__` and `__init__` signatures? and respect metaclass
// `__call__`).
Binding::single(
self,
Signature::new(
Parameters::new([Parameter::positional_only(Some(Name::new_static(
"o",
)))
.with_annotated_type(Type::any())
.with_default_type(Type::BooleanLiteral(false))]),
Some(KnownClass::Bool.to_instance(db)),
Signature::new_generic(
class.generic_context(db),
Parameters::gradual_form(),
self.to_instance(db),
),
)
.into()
}
Some(KnownClass::Str) => {
// ```py
// class str(Sequence[str]):
// @overload
// def __new__(cls, object: object = ...) -> Self: ...
// @overload
// def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ...
// ```
CallableBinding::from_overloads(
self,
[
Signature::new(
Parameters::new([Parameter::positional_or_keyword(
Name::new_static("object"),
)
.with_annotated_type(Type::object(db))
.with_default_type(Type::string_literal(db, ""))]),
Some(KnownClass::Str.to_instance(db)),
),
Signature::new(
Parameters::new([
Parameter::positional_or_keyword(Name::new_static("object"))
// TODO: Should be `ReadableBuffer` instead of this union type:
.with_annotated_type(UnionType::from_elements(
db,
[
KnownClass::Bytes.to_instance(db),
KnownClass::Bytearray.to_instance(db),
],
))
.with_default_type(Type::bytes_literal(db, b"")),
Parameter::positional_or_keyword(Name::new_static("encoding"))
.with_annotated_type(KnownClass::Str.to_instance(db))
.with_default_type(Type::string_literal(db, "utf-8")),
Parameter::positional_or_keyword(Name::new_static("errors"))
.with_annotated_type(KnownClass::Str.to_instance(db))
.with_default_type(Type::string_literal(db, "strict")),
]),
Some(KnownClass::Str.to_instance(db)),
),
],
)
.into()
}
Some(KnownClass::Type) => {
let str_instance = KnownClass::Str.to_instance(db);
let type_instance = KnownClass::Type.to_instance(db);
// ```py
// class type:
// @overload
// def __init__(self, o: object, /) -> None: ...
// @overload
// def __init__(self, name: str, bases: tuple[type, ...], dict: dict[str, Any], /, **kwds: Any) -> None: ...
// ```
CallableBinding::from_overloads(
self,
[
Signature::new(
Parameters::new([Parameter::positional_only(Some(
Name::new_static("o"),
))
.with_annotated_type(Type::any())]),
Some(type_instance),
),
Signature::new(
Parameters::new([
Parameter::positional_only(Some(Name::new_static("name")))
.with_annotated_type(str_instance),
Parameter::positional_only(Some(Name::new_static("bases")))
.with_annotated_type(TupleType::homogeneous(
db,
type_instance,
)),
Parameter::positional_only(Some(Name::new_static("dict")))
.with_annotated_type(
KnownClass::Dict.to_specialized_instance(
db,
[str_instance, Type::any()],
),
),
]),
Some(type_instance),
),
],
)
.into()
}
Some(KnownClass::NamedTuple) => {
Binding::single(self, Signature::todo("functional `NamedTuple` syntax")).into()
}
Some(KnownClass::Object) => {
// ```py
// class object:
// def __init__(self) -> None: ...
// def __new__(cls) -> Self: ...
// ```
Binding::single(
self,
Signature::new(
Parameters::empty(),
Some(KnownClass::Object.to_instance(db)),
),
)
.into()
}
Some(KnownClass::Enum) => {
Binding::single(self, Signature::todo("functional `Enum` syntax")).into()
}
Some(KnownClass::Super) => {
// ```py
// class super:
// @overload
// def __init__(self, t: Any, obj: Any, /) -> None: ...
// @overload
// def __init__(self, t: Any, /) -> None: ...
// @overload
// def __init__(self) -> None: ...
// ```
CallableBinding::from_overloads(
self,
[
Signature::new(
Parameters::new([
Parameter::positional_only(Some(Name::new_static("t")))
.with_annotated_type(Type::any()),
Parameter::positional_only(Some(Name::new_static("obj")))
.with_annotated_type(Type::any()),
]),
Some(KnownClass::Super.to_instance(db)),
),
Signature::new(
Parameters::new([Parameter::positional_only(Some(
Name::new_static("t"),
))
.with_annotated_type(Type::any())]),
Some(KnownClass::Super.to_instance(db)),
),
Signature::new(
Parameters::empty(),
Some(KnownClass::Super.to_instance(db)),
),
],
)
.into()
}
Some(KnownClass::TypeVar) => {
// ```py
// class TypeVar:
// def __new__(
// cls,
// name: str,
// *constraints: Any,
// bound: Any | None = None,
// contravariant: bool = False,
// covariant: bool = False,
// infer_variance: bool = False,
// default: Any = ...,
// ) -> Self: ...
// ```
Binding::single(
self,
Signature::new(
Parameters::new([
Parameter::positional_or_keyword(Name::new_static("name"))
.with_annotated_type(Type::LiteralString),
Parameter::variadic(Name::new_static("constraints"))
.type_form()
.with_annotated_type(Type::any()),
Parameter::keyword_only(Name::new_static("bound"))
.type_form()
.with_annotated_type(UnionType::from_elements(
db,
[Type::any(), Type::none(db)],
))
.with_default_type(Type::none(db)),
Parameter::keyword_only(Name::new_static("default"))
.type_form()
.with_annotated_type(Type::any())
.with_default_type(KnownClass::NoneType.to_instance(db)),
Parameter::keyword_only(Name::new_static("contravariant"))
.with_annotated_type(KnownClass::Bool.to_instance(db))
.with_default_type(Type::BooleanLiteral(false)),
Parameter::keyword_only(Name::new_static("covariant"))
.with_annotated_type(KnownClass::Bool.to_instance(db))
.with_default_type(Type::BooleanLiteral(false)),
Parameter::keyword_only(Name::new_static("infer_variance"))
.with_annotated_type(KnownClass::Bool.to_instance(db))
.with_default_type(Type::BooleanLiteral(false)),
]),
Some(KnownClass::TypeVar.to_instance(db)),
),
)
.into()
}
Some(KnownClass::TypeAliasType) => {
// ```py
// def __new__(
// cls,
// name: str,
// value: Any,
// *,
// type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = ()
// ) -> Self: ...
// ```
Binding::single(
self,
Signature::new(
Parameters::new([
Parameter::positional_or_keyword(Name::new_static("name"))
.with_annotated_type(KnownClass::Str.to_instance(db)),
Parameter::positional_or_keyword(Name::new_static("value"))
.with_annotated_type(Type::any())
.type_form(),
Parameter::keyword_only(Name::new_static("type_params"))
.with_annotated_type(TupleType::homogeneous(
db,
UnionType::from_elements(
db,
[
KnownClass::TypeVar.to_instance(db),
KnownClass::ParamSpec.to_instance(db),
KnownClass::TypeVarTuple.to_instance(db),
],
),
))
.with_default_type(TupleType::empty(db)),
]),
None,
),
)
.into()
}
Some(KnownClass::Property) => {
let getter_signature = Signature::new(
Parameters::new([
Parameter::positional_only(None).with_annotated_type(Type::any())
]),
Some(Type::any()),
);
let setter_signature = Signature::new(
Parameters::new([
Parameter::positional_only(None).with_annotated_type(Type::any()),
Parameter::positional_only(None).with_annotated_type(Type::any()),
]),
Some(Type::none(db)),
);
let deleter_signature = Signature::new(
Parameters::new([
Parameter::positional_only(None).with_annotated_type(Type::any())
]),
Some(Type::any()),
);
Binding::single(
self,
Signature::new(
Parameters::new([
Parameter::positional_or_keyword(Name::new_static("fget"))
.with_annotated_type(UnionType::from_elements(
db,
[
CallableType::single(db, getter_signature),
Type::none(db),
],
))
.with_default_type(Type::none(db)),
Parameter::positional_or_keyword(Name::new_static("fset"))
.with_annotated_type(UnionType::from_elements(
db,
[
CallableType::single(db, setter_signature),
Type::none(db),
],
))
.with_default_type(Type::none(db)),
Parameter::positional_or_keyword(Name::new_static("fdel"))
.with_annotated_type(UnionType::from_elements(
db,
[
CallableType::single(db, deleter_signature),
Type::none(db),
],
))
.with_default_type(Type::none(db)),
Parameter::positional_or_keyword(Name::new_static("doc"))
.with_annotated_type(UnionType::from_elements(
db,
[KnownClass::Str.to_instance(db), Type::none(db)],
))
.with_default_type(Type::none(db)),
]),
None,
),
)
.into()
}
// Most class literal constructor calls are handled by `try_call_constructor` and
// not via getting the signature here. This signature can still be used in some
// cases (e.g. evaluating callable subtyping). TODO improve this definition
// (intersection of `__new__` and `__init__` signatures? and respect metaclass
// `__call__`).
_ => Binding::single(
self,
Signature::new_generic(
class.generic_context(db),
Parameters::gradual_form(),
self.to_instance(db),
),
)
.into(),
},
}),
Type::SpecialForm(SpecialFormType::TypedDict) => {
Binding::single(

View File

@@ -26,7 +26,7 @@ use crate::types::function::{
DataclassTransformerParams, FunctionDecorators, FunctionType, KnownFunction, OverloadLiteral,
};
use crate::types::generics::{Specialization, SpecializationBuilder, SpecializationError};
use crate::types::signatures::{Parameter, ParameterForm};
use crate::types::signatures::{Parameter, ParameterForm, Parameters};
use crate::types::tuple::TupleType;
use crate::types::{
BoundMethodType, ClassLiteral, DataclassParams, KnownClass, KnownInstanceType,
@@ -56,6 +56,24 @@ pub(crate) struct Bindings<'db> {
}
impl<'db> Bindings<'db> {
pub(crate) fn single(
callee: Type<'db>,
parameters: Parameters<'db>,
return_type: Option<Type<'db>>,
) -> Self {
Self::from(Binding::single(
callee,
Signature::new(parameters, return_type),
))
}
pub(crate) fn from_overloads(
callee: Type<'db>,
overloads: impl IntoIterator<Item = Signature<'db>>,
) -> Self {
Self::from(CallableBinding::from_overloads(callee, overloads))
}
/// Creates a new `Bindings` from an iterator of [`Bindings`]s. Panics if the iterator is
/// empty.
pub(crate) fn from_union<I>(callable_type: Type<'db>, elements: I) -> Self

View File

@@ -20,9 +20,9 @@ use crate::types::infer::nearest_enclosing_class;
use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signature};
use crate::types::tuple::TupleType;
use crate::types::{
BareTypeAliasType, Binding, BoundSuperError, BoundSuperType, CallableType, DataclassParams,
KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation, TypeVarBoundOrConstraints,
TypeVarInstance, TypeVarKind, infer_definition_types,
BareTypeAliasType, Binding, Bindings, BoundSuperError, BoundSuperType, CallableType,
DataclassParams, KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation,
TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, infer_definition_types,
};
use crate::{
Db, FxOrderSet, KnownModule, Program,
@@ -3184,16 +3184,16 @@ impl KnownClass {
}
}
/// Evaluate a call to this known class, and emit any diagnostics that are necessary
/// as a result of the call.
/// Evaluate a call to this known class, emit any diagnostics that are necessary
/// as a result of the call, and return the type that results from the call.
pub(super) fn check_call<'db>(
self,
context: &InferContext<'db, '_>,
index: &SemanticIndex<'db>,
overload_binding: &mut Binding<'db>,
overload_binding: &Binding<'db>,
call_argument_types: &CallArgumentTypes<'_, 'db>,
call_expression: &ast::ExprCall,
) {
) -> Option<Type<'db>> {
let db = context.db();
let scope = context.scope();
let module = context.module();
@@ -3209,10 +3209,9 @@ impl KnownClass {
let Some(enclosing_class) =
nearest_enclosing_class(db, index, scope, module)
else {
overload_binding.set_return_type(Type::unknown());
BoundSuperError::UnavailableImplicitArguments
.report_diagnostic(context, call_expression.into());
return;
return Some(Type::unknown());
};
// The type of the first parameter if the given scope is function-like (i.e. function or lambda).
@@ -3232,10 +3231,9 @@ impl KnownClass {
};
let Some(first_param) = first_param else {
overload_binding.set_return_type(Type::unknown());
BoundSuperError::UnavailableImplicitArguments
.report_diagnostic(context, call_expression.into());
return;
return Some(Type::unknown());
};
let definition = index.expect_single_definition(first_param);
@@ -3252,7 +3250,7 @@ impl KnownClass {
Type::unknown()
});
overload_binding.set_return_type(bound_super);
Some(bound_super)
}
[Some(pivot_class_type), Some(owner_type)] => {
let bound_super = BoundSuperType::build(db, *pivot_class_type, *owner_type)
@@ -3261,9 +3259,9 @@ impl KnownClass {
Type::unknown()
});
overload_binding.set_return_type(bound_super);
Some(bound_super)
}
_ => {}
_ => None,
}
}
@@ -3278,14 +3276,12 @@ impl KnownClass {
_ => None,
}
}) else {
if let Some(builder) =
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
{
builder.into_diagnostic(
"A legacy `typing.TypeVar` must be immediately assigned to a variable",
);
}
return;
let builder =
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)?;
builder.into_diagnostic(
"A legacy `typing.TypeVar` must be immediately assigned to a variable",
);
return None;
};
let [
@@ -3298,7 +3294,7 @@ impl KnownClass {
_infer_variance,
] = overload_binding.parameter_types()
else {
return;
return None;
};
let covariant = covariant
@@ -3311,39 +3307,30 @@ impl KnownClass {
let variance = match (contravariant, covariant) {
(Truthiness::Ambiguous, _) => {
let Some(builder) =
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
else {
return;
};
let builder =
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)?;
builder.into_diagnostic(
"The `contravariant` parameter of a legacy `typing.TypeVar` \
cannot have an ambiguous value",
);
return;
return None;
}
(_, Truthiness::Ambiguous) => {
let Some(builder) =
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
else {
return;
};
let builder =
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)?;
builder.into_diagnostic(
"The `covariant` parameter of a legacy `typing.TypeVar` \
cannot have an ambiguous value",
);
return;
return None;
}
(Truthiness::AlwaysTrue, Truthiness::AlwaysTrue) => {
let Some(builder) =
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
else {
return;
};
let builder =
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)?;
builder.into_diagnostic(
"A legacy `typing.TypeVar` cannot be both covariant and contravariant",
);
return;
return None;
}
(Truthiness::AlwaysTrue, Truthiness::AlwaysFalse) => {
TypeVarVariance::Contravariant
@@ -3357,11 +3344,8 @@ impl KnownClass {
let name_param = name_param.into_string_literal().map(|name| name.value(db));
if name_param.is_none_or(|name_param| name_param != target.id) {
let Some(builder) =
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)
else {
return;
};
let builder =
context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, call_expression)?;
builder.into_diagnostic(format_args!(
"The name of a legacy `typing.TypeVar`{} must match \
the name of the variable it is assigned to (`{}`)",
@@ -3372,7 +3356,7 @@ impl KnownClass {
},
target.id,
));
return;
return None;
}
let bound_or_constraint = match (bound, constraints) {
@@ -3397,13 +3381,13 @@ impl KnownClass {
// TODO: Emit a diagnostic that TypeVar cannot be both bounded and
// constrained
(Some(_), Some(_)) => return,
(Some(_), Some(_)) => return None,
(None, None) => None,
};
let containing_assignment = index.expect_single_definition(target);
overload_binding.set_return_type(Type::KnownInstance(KnownInstanceType::TypeVar(
Some(Type::KnownInstance(KnownInstanceType::TypeVar(
TypeVarInstance::new(
db,
target.id.clone(),
@@ -3413,7 +3397,7 @@ impl KnownClass {
*default,
TypeVarKind::Legacy,
),
)));
)))
}
KnownClass::TypeAliasType => {
@@ -3429,32 +3413,336 @@ impl KnownClass {
});
let [Some(name), Some(value), ..] = overload_binding.parameter_types() else {
return;
return None;
};
if let Some(name) = name.into_string_literal() {
overload_binding.set_return_type(Type::KnownInstance(
KnownInstanceType::TypeAliasType(TypeAliasType::Bare(
name.into_string_literal()
.map(|name| {
Type::KnownInstance(KnownInstanceType::TypeAliasType(TypeAliasType::Bare(
BareTypeAliasType::new(
db,
ast::name::Name::new(name.value(db)),
containing_assignment,
value,
),
)),
));
} else if let Some(builder) =
context.report_lint(&INVALID_TYPE_ALIAS_TYPE, call_expression)
{
builder.into_diagnostic(
"The name of a `typing.TypeAlias` must be a string literal",
);
}
)))
})
.or_else(|| {
let builder =
context.report_lint(&INVALID_TYPE_ALIAS_TYPE, call_expression)?;
builder.into_diagnostic(
"The name of a `typing.TypeAlias` must be a string literal",
);
None
})
}
_ => {}
_ => None,
}
}
/// Return the bindings we should use when inferring a constructor call for this special-cased class.
/// Return `None` if this class doesn't require special handling for calls to its constructor.
pub(super) fn bindings<'db>(self, db: &'db dyn Db, callee: Type<'db>) -> Option<Bindings<'db>> {
let bindings = match self {
// ```py
// class bool(int):
// def __new__(cls, o: object = ..., /) -> Self: ...
// ```
KnownClass::Bool => Bindings::single(
callee,
Parameters::new([Parameter::positional_only(Some(Name::new_static("o")))
.with_annotated_type(Type::any())
.with_default_type(Type::BooleanLiteral(false))]),
Some(KnownClass::Bool.to_instance(db)),
),
// ```py
// class object:
// def __init__(self) -> None: ...
// def __new__(cls) -> Self: ...
// ```
KnownClass::Object => Bindings::single(
callee,
Parameters::empty(),
Some(KnownClass::Object.to_instance(db)),
),
KnownClass::Enum => Bindings::from(Binding::single(
callee,
Signature::todo("functional `Enum` syntax"),
)),
// ```py
// class str(Sequence[str]):
// @overload
// def __new__(cls, object: object = ...) -> Self: ...
// @overload
// def __new__(cls, object: ReadableBuffer, encoding: str = ..., errors: str = ...) -> Self: ...
// ```
KnownClass::Str => {
let str_instance = KnownClass::Str.to_instance(db);
Bindings::from_overloads(
callee,
[
Signature::new(
Parameters::new([Parameter::positional_or_keyword(Name::new_static(
"object",
))
.with_annotated_type(Type::object(db))
.with_default_type(Type::string_literal(db, ""))]),
Some(str_instance),
),
Signature::new(
Parameters::new([
Parameter::positional_or_keyword(Name::new_static("object"))
// TODO: Should be `ReadableBuffer` instead of this union type:
.with_annotated_type(UnionType::from_elements(
db,
[
KnownClass::Bytes.to_instance(db),
KnownClass::Bytearray.to_instance(db),
],
))
.with_default_type(Type::bytes_literal(db, b"")),
Parameter::positional_or_keyword(Name::new_static("encoding"))
.with_annotated_type(str_instance)
.with_default_type(Type::string_literal(db, "utf-8")),
Parameter::positional_or_keyword(Name::new_static("errors"))
.with_annotated_type(str_instance)
.with_default_type(Type::string_literal(db, "strict")),
]),
Some(str_instance),
),
],
)
}
KnownClass::Type => {
let str_instance = KnownClass::Str.to_instance(db);
let type_instance = KnownClass::Type.to_instance(db);
// ```py
// class type:
// @overload
// def __init__(self, o: object, /) -> None: ...
// @overload
// def __init__(self, name: str, bases: tuple[type, ...], dict: dict[str, Any], /, **kwds: Any) -> None: ...
// ```
Bindings::from_overloads(
callee,
[
Signature::new(
Parameters::new([Parameter::positional_only(Some(Name::new_static(
"o",
)))
.with_annotated_type(Type::any())]),
Some(type_instance),
),
Signature::new(
Parameters::new([
Parameter::positional_only(Some(Name::new_static("name")))
.with_annotated_type(str_instance),
Parameter::positional_only(Some(Name::new_static("bases")))
.with_annotated_type(TupleType::homogeneous(db, type_instance)),
Parameter::positional_only(Some(Name::new_static("dict")))
.with_annotated_type(
KnownClass::Dict.to_specialized_instance(
db,
[str_instance, Type::any()],
),
),
]),
Some(type_instance),
),
],
)
}
KnownClass::NamedTuple => Bindings::from(Binding::single(
callee,
Signature::todo("functional `NamedTuple` syntax"),
)),
// ```py
// class super:
// @overload
// def __init__(self, t: Any, obj: Any, /) -> None: ...
// @overload
// def __init__(self, t: Any, /) -> None: ...
// @overload
// def __init__(self) -> None: ...
// ```
KnownClass::Super => {
let super_instance = KnownClass::Super.to_instance(db);
Bindings::from_overloads(
callee,
[
Signature::new(
Parameters::new([
Parameter::positional_only(Some(Name::new_static("t")))
.with_annotated_type(Type::any()),
Parameter::positional_only(Some(Name::new_static("obj")))
.with_annotated_type(Type::any()),
]),
Some(super_instance),
),
Signature::new(
Parameters::new([Parameter::positional_only(Some(Name::new_static(
"t",
)))
.with_annotated_type(Type::any())]),
Some(super_instance),
),
Signature::new(Parameters::empty(), Some(super_instance)),
],
)
}
// ```py
// class TypeVar:
// def __new__(
// cls,
// name: str,
// *constraints: Any,
// bound: Any | None = None,
// contravariant: bool = False,
// covariant: bool = False,
// infer_variance: bool = False,
// default: Any = ...,
// ) -> Self: ...
// ```
KnownClass::TypeVar => {
let none_instance = Type::none(db);
let bool_instance = KnownClass::Bool.to_instance(db);
Bindings::single(
callee,
Parameters::new([
Parameter::positional_or_keyword(Name::new_static("name"))
.with_annotated_type(Type::LiteralString),
Parameter::variadic(Name::new_static("constraints"))
.type_form()
.with_annotated_type(Type::any()),
Parameter::keyword_only(Name::new_static("bound"))
.type_form()
.with_annotated_type(UnionType::from_elements(
db,
[Type::any(), none_instance],
))
.with_default_type(none_instance),
Parameter::keyword_only(Name::new_static("default"))
.type_form()
.with_annotated_type(Type::any())
.with_default_type(none_instance),
Parameter::keyword_only(Name::new_static("contravariant"))
.with_annotated_type(bool_instance)
.with_default_type(Type::BooleanLiteral(false)),
Parameter::keyword_only(Name::new_static("covariant"))
.with_annotated_type(bool_instance)
.with_default_type(Type::BooleanLiteral(false)),
Parameter::keyword_only(Name::new_static("infer_variance"))
.with_annotated_type(bool_instance)
.with_default_type(Type::BooleanLiteral(false)),
]),
Some(KnownClass::TypeVar.to_instance(db)),
)
}
// ```py
// def __new__(
// cls,
// name: str,
// value: Any,
// *,
// type_params: tuple[TypeVar | ParamSpec | TypeVarTuple, ...] = ()
// ) -> Self: ...
// ```
KnownClass::TypeAliasType => Bindings::single(
callee,
Parameters::new([
Parameter::positional_or_keyword(Name::new_static("name"))
.with_annotated_type(KnownClass::Str.to_instance(db)),
Parameter::positional_or_keyword(Name::new_static("value"))
.with_annotated_type(Type::any())
.type_form(),
Parameter::keyword_only(Name::new_static("type_params"))
.with_annotated_type(TupleType::homogeneous(
db,
UnionType::from_elements(
db,
[
KnownClass::TypeVar.to_instance(db),
KnownClass::ParamSpec.to_instance(db),
KnownClass::TypeVarTuple.to_instance(db),
],
),
))
.with_default_type(TupleType::empty(db)),
]),
None,
),
KnownClass::Property => {
let getter_signature = Signature::new(
Parameters::new([
Parameter::positional_only(None).with_annotated_type(Type::any())
]),
Some(Type::any()),
);
let setter_signature = Signature::new(
Parameters::new([
Parameter::positional_only(None).with_annotated_type(Type::any()),
Parameter::positional_only(None).with_annotated_type(Type::any()),
]),
Some(Type::none(db)),
);
let deleter_signature = Signature::new(
Parameters::new([
Parameter::positional_only(None).with_annotated_type(Type::any())
]),
Some(Type::any()),
);
let none_instance = Type::none(db);
Bindings::single(
callee,
Parameters::new([
Parameter::positional_or_keyword(Name::new_static("fget"))
.with_annotated_type(UnionType::from_elements(
db,
[CallableType::single(db, getter_signature), none_instance],
))
.with_default_type(none_instance),
Parameter::positional_or_keyword(Name::new_static("fset"))
.with_annotated_type(UnionType::from_elements(
db,
[CallableType::single(db, setter_signature), none_instance],
))
.with_default_type(none_instance),
Parameter::positional_or_keyword(Name::new_static("fdel"))
.with_annotated_type(UnionType::from_elements(
db,
[CallableType::single(db, deleter_signature), none_instance],
))
.with_default_type(none_instance),
Parameter::positional_or_keyword(Name::new_static("doc"))
.with_annotated_type(UnionType::from_elements(
db,
[KnownClass::Str.to_instance(db), none_instance],
))
.with_default_type(none_instance),
]),
None,
)
}
_ => return None,
};
Some(bindings)
}
}
/// Enumeration of ways in which looking up a [`KnownClass`] in typeshed could fail.

View File

@@ -5309,6 +5309,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
}
}
let mut special_cased_bindings = None;
let class = match callable_type {
Type::ClassLiteral(class) => Some(ClassType::NonGeneric(class)),
Type::GenericAlias(generic) => Some(ClassType::Generic(generic)),
@@ -5338,48 +5340,45 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
}
}
// 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
| KnownClass::Type
| KnownClass::Object
| KnownClass::Property
| KnownClass::Super
| KnownClass::TypeVar
| KnownClass::NamedTuple
| KnownClass::TypeAliasType
)
)
// temporary special-casing for all subclasses of `enum.Enum`
// until we support the functional syntax for creating enum classes
&& KnownClass::Enum
if 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);
if let Some(known_class_bindings) = class
.known(self.db())
.and_then(|class| class.bindings(self.db(), callable_type))
{
special_cased_bindings = Some(known_class_bindings);
} else {
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 bindings = callable_type
.bindings(self.db())
.match_parameters(&call_arguments);
let bindings = special_cased_bindings
.map(|bindings| bindings.match_parameters(&call_arguments))
.unwrap_or_else(|| {
callable_type
.bindings(self.db())
.match_parameters(&call_arguments)
});
let call_argument_types =
self.infer_argument_types(arguments, call_arguments, &bindings.argument_forms);
@@ -5405,13 +5404,16 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let Some(known_class) = class.known(self.db()) else {
continue;
};
known_class.check_call(
let overridden_return = known_class.check_call(
&self.context,
self.index,
overload,
&call_argument_types,
call_expression,
);
if let Some(overridden_return) = overridden_return {
overload.set_return_type(overridden_return);
}
}
_ => {}
}