mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-17 19:27:11 +00:00
Merge c32615af37 into 0d2cd84df4
This commit is contained in:
commit
5992af4894
7 changed files with 373 additions and 117 deletions
|
|
@ -4581,11 +4581,16 @@ impl<'db> Type<'db> {
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::KnownInstance(KnownInstanceType::TypeVar(typevar))
|
Type::TypeVar(typevar)
|
||||||
if typevar.kind(db).is_paramspec()
|
if typevar.kind(db).is_paramspec()
|
||||||
&& matches!(name.as_str(), "args" | "kwargs") =>
|
&& matches!(name.as_str(), "args" | "kwargs") =>
|
||||||
{
|
{
|
||||||
Place::bound(todo_type!("ParamSpecArgs / ParamSpecKwargs")).into()
|
Place::bound(Type::TypeVar(match name.as_str() {
|
||||||
|
"args" => typevar.with_paramspec_attr(db, ParamSpecAttrKind::Args),
|
||||||
|
"kwargs" => typevar.with_paramspec_attr(db, ParamSpecAttrKind::Kwargs),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}))
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::NominalInstance(instance)
|
Type::NominalInstance(instance)
|
||||||
|
|
@ -6692,6 +6697,17 @@ impl<'db> Type<'db> {
|
||||||
KnownInstanceType::TypeAliasType(alias) => Ok(Type::TypeAlias(*alias)),
|
KnownInstanceType::TypeAliasType(alias) => Ok(Type::TypeAlias(*alias)),
|
||||||
KnownInstanceType::NewType(newtype) => Ok(Type::NewTypeInstance(*newtype)),
|
KnownInstanceType::NewType(newtype) => Ok(Type::NewTypeInstance(*newtype)),
|
||||||
KnownInstanceType::TypeVar(typevar) => {
|
KnownInstanceType::TypeVar(typevar) => {
|
||||||
|
// TODO: A `ParamSpec` type variable cannot be used in type expressions. This
|
||||||
|
// requires storing additional context as it's allowed in some places
|
||||||
|
// (`Concatenate`, `Callable`) but not others.
|
||||||
|
// if typevar.kind(db).is_paramspec() {
|
||||||
|
// return Err(InvalidTypeExpressionError {
|
||||||
|
// invalid_expressions: smallvec::smallvec_inline![
|
||||||
|
// InvalidTypeExpression::InvalidType(*self, scope_id)
|
||||||
|
// ],
|
||||||
|
// fallback_type: Type::unknown(),
|
||||||
|
// });
|
||||||
|
// }
|
||||||
let index = semantic_index(db, scope_id.file(db));
|
let index = semantic_index(db, scope_id.file(db));
|
||||||
Ok(bind_typevar(
|
Ok(bind_typevar(
|
||||||
db,
|
db,
|
||||||
|
|
@ -6907,9 +6923,12 @@ impl<'db> Type<'db> {
|
||||||
Some(KnownClass::TypeVar) => Ok(todo_type!(
|
Some(KnownClass::TypeVar) => Ok(todo_type!(
|
||||||
"Support for `typing.TypeVar` instances in type expressions"
|
"Support for `typing.TypeVar` instances in type expressions"
|
||||||
)),
|
)),
|
||||||
Some(
|
Some(KnownClass::ParamSpecArgs) => {
|
||||||
KnownClass::ParamSpec | KnownClass::ParamSpecArgs | KnownClass::ParamSpecKwargs,
|
Ok(todo_type!("Support for `typing.ParamSpecArgs`"))
|
||||||
) => Ok(todo_type!("Support for `typing.ParamSpec`")),
|
}
|
||||||
|
Some(KnownClass::ParamSpecKwargs) => {
|
||||||
|
Ok(todo_type!("Support for `typing.ParamSpecKwargs`"))
|
||||||
|
}
|
||||||
Some(KnownClass::TypeVarTuple) => Ok(todo_type!(
|
Some(KnownClass::TypeVarTuple) => Ok(todo_type!(
|
||||||
"Support for `typing.TypeVarTuple` instances in type expressions"
|
"Support for `typing.TypeVarTuple` instances in type expressions"
|
||||||
)),
|
)),
|
||||||
|
|
@ -7128,7 +7147,7 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => match type_mapping {
|
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => match type_mapping {
|
||||||
TypeMapping::BindLegacyTypevars(binding_context) => {
|
TypeMapping::BindLegacyTypevars(binding_context) => {
|
||||||
Type::TypeVar(BoundTypeVarInstance::new(db, typevar, *binding_context))
|
Type::TypeVar(BoundTypeVarInstance::new(db, typevar, *binding_context, None))
|
||||||
}
|
}
|
||||||
TypeMapping::Specialization(_) |
|
TypeMapping::Specialization(_) |
|
||||||
TypeMapping::PartialSpecialization(_) |
|
TypeMapping::PartialSpecialization(_) |
|
||||||
|
|
@ -7933,6 +7952,8 @@ pub struct TrackedConstraintSet<'db> {
|
||||||
// The Salsa heap is tracked separately.
|
// The Salsa heap is tracked separately.
|
||||||
impl get_size2::GetSize for TrackedConstraintSet<'_> {}
|
impl get_size2::GetSize for TrackedConstraintSet<'_> {}
|
||||||
|
|
||||||
|
// TODO: The origin is either `TypeVarInstance` or `BoundTypeVarInstance`
|
||||||
|
|
||||||
/// Singleton types that are heavily special-cased by ty. Despite its name,
|
/// Singleton types that are heavily special-cased by ty. Despite its name,
|
||||||
/// quite a different type to [`NominalInstanceType`].
|
/// quite a different type to [`NominalInstanceType`].
|
||||||
///
|
///
|
||||||
|
|
@ -8725,12 +8746,12 @@ fn walk_type_var_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
|
||||||
|
|
||||||
#[salsa::tracked]
|
#[salsa::tracked]
|
||||||
impl<'db> TypeVarInstance<'db> {
|
impl<'db> TypeVarInstance<'db> {
|
||||||
pub(crate) fn with_binding_context(
|
pub(crate) fn as_bound_type_var_instance(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
binding_context: Definition<'db>,
|
binding_context: Definition<'db>,
|
||||||
) -> BoundTypeVarInstance<'db> {
|
) -> BoundTypeVarInstance<'db> {
|
||||||
BoundTypeVarInstance::new(db, self, BindingContext::Definition(binding_context))
|
BoundTypeVarInstance::new(db, self, BindingContext::Definition(binding_context), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn name(self, db: &'db dyn Db) -> &'db ast::name::Name {
|
pub(crate) fn name(self, db: &'db dyn Db) -> &'db ast::name::Name {
|
||||||
|
|
@ -9036,6 +9057,21 @@ impl<'db> BindingContext<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, get_size2::GetSize)]
|
||||||
|
pub enum ParamSpecAttrKind {
|
||||||
|
Args,
|
||||||
|
Kwargs,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ParamSpecAttrKind {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ParamSpecAttrKind::Args => f.write_str("args"),
|
||||||
|
ParamSpecAttrKind::Kwargs => f.write_str("kwargs"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The identity of a bound type variable.
|
/// The identity of a bound type variable.
|
||||||
///
|
///
|
||||||
/// This identifies a specific binding of a typevar to a context (e.g., `T@ClassC` vs `T@FunctionF`),
|
/// This identifies a specific binding of a typevar to a context (e.g., `T@ClassC` vs `T@FunctionF`),
|
||||||
|
|
@ -9048,14 +9084,17 @@ impl<'db> BindingContext<'db> {
|
||||||
pub struct BoundTypeVarIdentity<'db> {
|
pub struct BoundTypeVarIdentity<'db> {
|
||||||
pub(crate) identity: TypeVarIdentity<'db>,
|
pub(crate) identity: TypeVarIdentity<'db>,
|
||||||
pub(crate) binding_context: BindingContext<'db>,
|
pub(crate) binding_context: BindingContext<'db>,
|
||||||
|
paramspec_attr: Option<ParamSpecAttrKind>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type variable that has been bound to a generic context, and which can be specialized to a
|
/// A type variable that has been bound to a generic context, and which can be specialized to a
|
||||||
/// concrete type.
|
/// concrete type.
|
||||||
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
|
||||||
|
#[derive(PartialOrd, Ord)]
|
||||||
pub struct BoundTypeVarInstance<'db> {
|
pub struct BoundTypeVarInstance<'db> {
|
||||||
pub typevar: TypeVarInstance<'db>,
|
pub typevar: TypeVarInstance<'db>,
|
||||||
binding_context: BindingContext<'db>,
|
binding_context: BindingContext<'db>,
|
||||||
|
paramspec_attr: Option<ParamSpecAttrKind>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Salsa heap is tracked separately.
|
// The Salsa heap is tracked separately.
|
||||||
|
|
@ -9070,9 +9109,22 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||||
BoundTypeVarIdentity {
|
BoundTypeVarIdentity {
|
||||||
identity: self.typevar(db).identity(db),
|
identity: self.typevar(db).identity(db),
|
||||||
binding_context: self.binding_context(db),
|
binding_context: self.binding_context(db),
|
||||||
|
paramspec_attr: self.paramspec_attr(db),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn name(self, db: &'db dyn Db) -> &'db ast::name::Name {
|
||||||
|
self.typevar(db).name(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn kind(self, db: &'db dyn Db) -> TypeVarKind {
|
||||||
|
self.typevar(db).kind(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn with_paramspec_attr(self, db: &'db dyn Db, kind: ParamSpecAttrKind) -> Self {
|
||||||
|
Self::new(db, self.typevar(db), self.binding_context(db), Some(kind))
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns whether two bound typevars represent the same logical typevar, regardless of e.g.
|
/// Returns whether two bound typevars represent the same logical typevar, regardless of e.g.
|
||||||
/// differences in their bounds or constraints due to materialization.
|
/// differences in their bounds or constraints due to materialization.
|
||||||
pub(crate) fn is_same_typevar_as(self, db: &'db dyn Db, other: Self) -> bool {
|
pub(crate) fn is_same_typevar_as(self, db: &'db dyn Db, other: Self) -> bool {
|
||||||
|
|
@ -9099,7 +9151,7 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||||
Some(variance),
|
Some(variance),
|
||||||
None, // _default
|
None, // _default
|
||||||
);
|
);
|
||||||
Self::new(db, typevar, BindingContext::Synthetic)
|
Self::new(db, typevar, BindingContext::Synthetic, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new synthetic `Self` type variable with the given upper bound.
|
/// Create a new synthetic `Self` type variable with the given upper bound.
|
||||||
|
|
@ -9121,7 +9173,7 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||||
Some(TypeVarVariance::Invariant),
|
Some(TypeVarVariance::Invariant),
|
||||||
None, // _default
|
None, // _default
|
||||||
);
|
);
|
||||||
Self::new(db, typevar, binding_context)
|
Self::new(db, typevar, binding_context, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn variance_with_polarity(
|
pub(crate) fn variance_with_polarity(
|
||||||
|
|
@ -9197,6 +9249,7 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||||
db,
|
db,
|
||||||
self.typevar(db).normalized_impl(db, visitor),
|
self.typevar(db).normalized_impl(db, visitor),
|
||||||
self.binding_context(db),
|
self.binding_context(db),
|
||||||
|
self.paramspec_attr(db),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -9211,6 +9264,7 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||||
self.typevar(db)
|
self.typevar(db)
|
||||||
.materialize_impl(db, materialization_kind, visitor),
|
.materialize_impl(db, materialization_kind, visitor),
|
||||||
self.binding_context(db),
|
self.binding_context(db),
|
||||||
|
self.paramspec_attr(db),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -9219,6 +9273,7 @@ impl<'db> BoundTypeVarInstance<'db> {
|
||||||
db,
|
db,
|
||||||
self.typevar(db).to_instance(db)?,
|
self.typevar(db).to_instance(db)?,
|
||||||
self.binding_context(db),
|
self.binding_context(db),
|
||||||
|
self.paramspec_attr(db),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,9 @@ use crate::semantic_index::{scope::ScopeKind, semantic_index};
|
||||||
use crate::types::class::{ClassLiteral, ClassType, GenericAlias};
|
use crate::types::class::{ClassLiteral, ClassType, GenericAlias};
|
||||||
use crate::types::function::{FunctionType, OverloadLiteral};
|
use crate::types::function::{FunctionType, OverloadLiteral};
|
||||||
use crate::types::generics::{GenericContext, Specialization};
|
use crate::types::generics::{GenericContext, Specialization};
|
||||||
use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signature};
|
use crate::types::signatures::{
|
||||||
|
CallableSignature, Parameter, Parameters, ParametersKind, Signature,
|
||||||
|
};
|
||||||
use crate::types::tuple::TupleSpec;
|
use crate::types::tuple::TupleSpec;
|
||||||
use crate::types::visitor::TypeVisitor;
|
use crate::types::visitor::TypeVisitor;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
|
|
@ -643,6 +645,9 @@ impl Display for DisplayBoundTypeVarIdentity<'_> {
|
||||||
if let Some(binding_context) = self.bound_typevar_identity.binding_context.name(self.db) {
|
if let Some(binding_context) = self.bound_typevar_identity.binding_context.name(self.db) {
|
||||||
write!(f, "@{binding_context}")?;
|
write!(f, "@{binding_context}")?;
|
||||||
}
|
}
|
||||||
|
if let Some(paramspec_attr) = self.bound_typevar_identity.paramspec_attr {
|
||||||
|
write!(f, ".{paramspec_attr}")?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1116,57 +1121,69 @@ impl DisplaySignature<'_> {
|
||||||
if multiline {
|
if multiline {
|
||||||
writer.write_str("\n ")?;
|
writer.write_str("\n ")?;
|
||||||
}
|
}
|
||||||
if self.parameters.is_gradual() {
|
match self.parameters.kind() {
|
||||||
// We represent gradual form as `...` in the signature, internally the parameters still
|
ParametersKind::Standard => {
|
||||||
// contain `(*args, **kwargs)` parameters.
|
let mut star_added = false;
|
||||||
writer.write_str("...")?;
|
let mut needs_slash = false;
|
||||||
} else {
|
let mut first = true;
|
||||||
let mut star_added = false;
|
let arg_separator = if multiline { ",\n " } else { ", " };
|
||||||
let mut needs_slash = false;
|
|
||||||
let mut first = true;
|
|
||||||
let arg_separator = if multiline { ",\n " } else { ", " };
|
|
||||||
|
|
||||||
for parameter in self.parameters.as_slice() {
|
for parameter in self.parameters.as_slice() {
|
||||||
// Handle special separators
|
// Handle special separators
|
||||||
if !star_added && parameter.is_keyword_only() {
|
if !star_added && parameter.is_keyword_only() {
|
||||||
|
if !first {
|
||||||
|
writer.write_str(arg_separator)?;
|
||||||
|
}
|
||||||
|
writer.write_char('*')?;
|
||||||
|
star_added = true;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
if parameter.is_positional_only() {
|
||||||
|
needs_slash = true;
|
||||||
|
} else if needs_slash {
|
||||||
|
if !first {
|
||||||
|
writer.write_str(arg_separator)?;
|
||||||
|
}
|
||||||
|
writer.write_char('/')?;
|
||||||
|
needs_slash = false;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add comma before parameter if not first
|
||||||
if !first {
|
if !first {
|
||||||
writer.write_str(arg_separator)?;
|
writer.write_str(arg_separator)?;
|
||||||
}
|
}
|
||||||
writer.write_char('*')?;
|
|
||||||
star_added = true;
|
// Write parameter with range tracking
|
||||||
|
let param_name = parameter.display_name();
|
||||||
|
writer.write_parameter(
|
||||||
|
¶meter.display_with(self.db, self.settings.singleline()),
|
||||||
|
param_name.as_deref(),
|
||||||
|
)?;
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
if parameter.is_positional_only() {
|
|
||||||
needs_slash = true;
|
if needs_slash {
|
||||||
} else if needs_slash {
|
|
||||||
if !first {
|
if !first {
|
||||||
writer.write_str(arg_separator)?;
|
writer.write_str(arg_separator)?;
|
||||||
}
|
}
|
||||||
writer.write_char('/')?;
|
writer.write_char('/')?;
|
||||||
needs_slash = false;
|
|
||||||
first = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add comma before parameter if not first
|
|
||||||
if !first {
|
|
||||||
writer.write_str(arg_separator)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write parameter with range tracking
|
|
||||||
let param_name = parameter.display_name();
|
|
||||||
writer.write_parameter(
|
|
||||||
¶meter.display_with(self.db, self.settings.singleline()),
|
|
||||||
param_name.as_deref(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
first = false;
|
|
||||||
}
|
}
|
||||||
|
ParametersKind::Gradual => {
|
||||||
if needs_slash {
|
// We represent gradual form as `...` in the signature, internally the parameters still
|
||||||
if !first {
|
// contain `(*args, **kwargs)` parameters.
|
||||||
writer.write_str(arg_separator)?;
|
writer.write_str("...")?;
|
||||||
|
}
|
||||||
|
ParametersKind::ParamSpec(origin) => {
|
||||||
|
writer.write_str(&format!("**{}", origin.name(self.db)))?;
|
||||||
|
if let Some(name) = origin
|
||||||
|
.binding_context(self.db)
|
||||||
|
.and_then(|binding_context| binding_context.name(self.db))
|
||||||
|
{
|
||||||
|
writer.write_str(&format!("@{name}"))?;
|
||||||
}
|
}
|
||||||
writer.write_char('/')?;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ pub(crate) fn bind_typevar<'db>(
|
||||||
if outer.kind().is_class() {
|
if outer.kind().is_class() {
|
||||||
if let NodeWithScopeKind::Function(function) = inner.node() {
|
if let NodeWithScopeKind::Function(function) = inner.node() {
|
||||||
let definition = index.expect_single_definition(function);
|
let definition = index.expect_single_definition(function);
|
||||||
return Some(typevar.with_binding_context(db, definition));
|
return Some(typevar.as_bound_type_var_instance(db, definition));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +73,7 @@ pub(crate) fn bind_typevar<'db>(
|
||||||
.find_map(|enclosing_context| enclosing_context.binds_typevar(db, typevar))
|
.find_map(|enclosing_context| enclosing_context.binds_typevar(db, typevar))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
typevar_binding_context.map(|typevar_binding_context| {
|
typevar_binding_context.map(|typevar_binding_context| {
|
||||||
typevar.with_binding_context(db, typevar_binding_context)
|
typevar.as_bound_type_var_instance(db, typevar_binding_context)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -318,7 +318,7 @@ impl<'db> GenericContext<'db> {
|
||||||
else {
|
else {
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
Some(typevar.with_binding_context(db, binding_context))
|
Some(typevar.as_bound_type_var_instance(db, binding_context))
|
||||||
}
|
}
|
||||||
// TODO: Support these!
|
// TODO: Support these!
|
||||||
ast::TypeParam::ParamSpec(_) => None,
|
ast::TypeParam::ParamSpec(_) => None,
|
||||||
|
|
|
||||||
|
|
@ -55,16 +55,16 @@ use crate::types::class::{CodeGeneratorKind, FieldKind, MetaclassErrorKind, Meth
|
||||||
use crate::types::context::{InNoTypeCheck, InferContext};
|
use crate::types::context::{InNoTypeCheck, InferContext};
|
||||||
use crate::types::cyclic::CycleDetector;
|
use crate::types::cyclic::CycleDetector;
|
||||||
use crate::types::diagnostic::{
|
use crate::types::diagnostic::{
|
||||||
CALL_NON_CALLABLE, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS, CYCLIC_CLASS_DEFINITION,
|
self, CALL_NON_CALLABLE, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS,
|
||||||
DIVISION_BY_ZERO, DUPLICATE_KW_ONLY, INCONSISTENT_MRO, INVALID_ARGUMENT_TYPE,
|
CYCLIC_CLASS_DEFINITION, DIVISION_BY_ZERO, DUPLICATE_KW_ONLY, INCONSISTENT_MRO,
|
||||||
INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE, INVALID_DECLARATION,
|
INVALID_ARGUMENT_TYPE, INVALID_ASSIGNMENT, INVALID_ATTRIBUTE_ACCESS, INVALID_BASE,
|
||||||
INVALID_GENERIC_CLASS, INVALID_KEY, INVALID_LEGACY_TYPE_VARIABLE, INVALID_METACLASS,
|
INVALID_DECLARATION, INVALID_GENERIC_CLASS, INVALID_KEY, INVALID_LEGACY_TYPE_VARIABLE,
|
||||||
INVALID_NAMED_TUPLE, INVALID_NEWTYPE, INVALID_OVERLOAD, INVALID_PARAMETER_DEFAULT,
|
INVALID_METACLASS, INVALID_NAMED_TUPLE, INVALID_NEWTYPE, INVALID_OVERLOAD,
|
||||||
INVALID_PARAMSPEC, INVALID_PROTOCOL, INVALID_TYPE_FORM, INVALID_TYPE_GUARD_CALL,
|
INVALID_PARAMETER_DEFAULT, INVALID_PARAMSPEC, INVALID_PROTOCOL, INVALID_TYPE_FORM,
|
||||||
INVALID_TYPE_VARIABLE_CONSTRAINTS, IncompatibleBases, NON_SUBSCRIPTABLE,
|
INVALID_TYPE_GUARD_CALL, INVALID_TYPE_VARIABLE_CONSTRAINTS, IncompatibleBases,
|
||||||
POSSIBLY_MISSING_IMPLICIT_CALL, POSSIBLY_MISSING_IMPORT, SUBCLASS_OF_FINAL_CLASS,
|
NON_SUBSCRIPTABLE, POSSIBLY_MISSING_IMPLICIT_CALL, POSSIBLY_MISSING_IMPORT,
|
||||||
UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, UNRESOLVED_GLOBAL, UNRESOLVED_IMPORT,
|
SUBCLASS_OF_FINAL_CLASS, UNDEFINED_REVEAL, UNRESOLVED_ATTRIBUTE, UNRESOLVED_GLOBAL,
|
||||||
UNRESOLVED_REFERENCE, UNSUPPORTED_OPERATOR, USELESS_OVERLOAD_BODY,
|
UNRESOLVED_IMPORT, UNRESOLVED_REFERENCE, UNSUPPORTED_OPERATOR, USELESS_OVERLOAD_BODY,
|
||||||
hint_if_stdlib_attribute_exists_on_other_versions,
|
hint_if_stdlib_attribute_exists_on_other_versions,
|
||||||
hint_if_stdlib_submodule_exists_on_other_versions, report_attempted_protocol_instantiation,
|
hint_if_stdlib_submodule_exists_on_other_versions, report_attempted_protocol_instantiation,
|
||||||
report_bad_dunder_set_call, report_cannot_pop_required_field_on_typed_dict,
|
report_bad_dunder_set_call, report_cannot_pop_required_field_on_typed_dict,
|
||||||
|
|
@ -104,11 +104,11 @@ use crate::types::{
|
||||||
CallDunderError, CallableBinding, CallableType, ClassLiteral, ClassType, DataclassParams,
|
CallDunderError, CallableBinding, CallableType, ClassLiteral, ClassType, DataclassParams,
|
||||||
DynamicType, InferredAs, InternedType, InternedTypes, IntersectionBuilder, IntersectionType,
|
DynamicType, InferredAs, InternedType, InternedTypes, IntersectionBuilder, IntersectionType,
|
||||||
KnownClass, KnownInstanceType, LintDiagnosticGuard, MemberLookupPolicy, MetaclassCandidate,
|
KnownClass, KnownInstanceType, LintDiagnosticGuard, MemberLookupPolicy, MetaclassCandidate,
|
||||||
PEP695TypeAliasType, Parameter, ParameterForm, Parameters, SpecialFormType, SubclassOfType,
|
PEP695TypeAliasType, ParamSpecAttrKind, Parameter, ParameterForm, Parameters, SpecialFormType,
|
||||||
TrackedConstraintSet, Truthiness, Type, TypeAliasType, TypeAndQualifiers, TypeContext,
|
SubclassOfType, TrackedConstraintSet, Truthiness, Type, TypeAliasType, TypeAndQualifiers,
|
||||||
TypeQualifiers, TypeVarBoundOrConstraintsEvaluation, TypeVarDefaultEvaluation, TypeVarIdentity,
|
TypeContext, TypeQualifiers, TypeVarBoundOrConstraintsEvaluation, TypeVarDefaultEvaluation,
|
||||||
TypeVarInstance, TypeVarKind, TypeVarVariance, TypedDictType, UnionBuilder, UnionType,
|
TypeVarIdentity, TypeVarInstance, TypeVarKind, TypeVarVariance, TypedDictType, UnionBuilder,
|
||||||
binding_type, todo_type,
|
UnionType, binding_type, todo_type,
|
||||||
};
|
};
|
||||||
use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic};
|
use crate::types::{ClassBase, add_inferred_python_version_hint_to_diagnostic};
|
||||||
use crate::unpack::{EvaluationMode, UnpackPosition};
|
use crate::unpack::{EvaluationMode, UnpackPosition};
|
||||||
|
|
@ -2540,7 +2540,40 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
todo_type!("PEP 646")
|
todo_type!("PEP 646")
|
||||||
} else {
|
} else {
|
||||||
let annotated_type = self.file_expression_type(annotation);
|
let annotated_type = self.file_expression_type(annotation);
|
||||||
Type::homogeneous_tuple(self.db(), annotated_type)
|
if let Type::TypeVar(typevar) = annotated_type
|
||||||
|
&& typevar.kind(self.db()).is_paramspec()
|
||||||
|
{
|
||||||
|
match typevar.paramspec_attr(self.db()) {
|
||||||
|
// `*args: P.args`
|
||||||
|
Some(ParamSpecAttrKind::Args) => annotated_type,
|
||||||
|
|
||||||
|
// `*args: P.kwargs`
|
||||||
|
Some(ParamSpecAttrKind::Kwargs) => {
|
||||||
|
if let Some(builder) =
|
||||||
|
self.context.report_lint(&INVALID_TYPE_FORM, annotation)
|
||||||
|
{
|
||||||
|
let name = typevar.name(self.db());
|
||||||
|
let mut diag = builder.into_diagnostic(format_args!(
|
||||||
|
"`{name}.kwargs` is valid only in `**kwargs` annotation",
|
||||||
|
));
|
||||||
|
diag.set_primary_message(format_args!(
|
||||||
|
"Did you mean `{name}.args`?"
|
||||||
|
));
|
||||||
|
diagnostic::add_type_expression_reference_link(diag);
|
||||||
|
}
|
||||||
|
// TODO: Should this be `Unknown` instead?
|
||||||
|
Type::homogeneous_tuple(self.db(), Type::unknown())
|
||||||
|
}
|
||||||
|
|
||||||
|
// `*args: P`
|
||||||
|
None => {
|
||||||
|
// TODO: Should this be `Unknown` instead?
|
||||||
|
Type::homogeneous_tuple(self.db(), Type::unknown())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Type::homogeneous_tuple(self.db(), annotated_type)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.add_declaration_with_binding(
|
self.add_declaration_with_binding(
|
||||||
|
|
@ -2623,7 +2656,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
typing_self(db, self.scope(), Some(method_definition), class_literal)
|
typing_self(db, self.scope(), Some(method_definition), class_literal)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set initial declared/inferred types for a `*args` variadic positional parameter.
|
/// Set initial declared/inferred types for a `**kwargs` keyword-variadic parameter.
|
||||||
///
|
///
|
||||||
/// The annotated type is implicitly wrapped in a string-keyed dictionary.
|
/// The annotated type is implicitly wrapped in a string-keyed dictionary.
|
||||||
///
|
///
|
||||||
|
|
@ -2636,11 +2669,47 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
definition: Definition<'db>,
|
definition: Definition<'db>,
|
||||||
) {
|
) {
|
||||||
if let Some(annotation) = parameter.annotation() {
|
if let Some(annotation) = parameter.annotation() {
|
||||||
let annotated_ty = self.file_expression_type(annotation);
|
let annotated_type = self.file_expression_type(annotation);
|
||||||
let ty = KnownClass::Dict.to_specialized_instance(
|
tracing::debug!("annotated_type: {}", annotated_type.display(self.db()));
|
||||||
self.db(),
|
let ty = if let Type::TypeVar(typevar) = annotated_type
|
||||||
[KnownClass::Str.to_instance(self.db()), annotated_ty],
|
&& typevar.kind(self.db()).is_paramspec()
|
||||||
);
|
{
|
||||||
|
match typevar.paramspec_attr(self.db()) {
|
||||||
|
// `**kwargs: P.args`
|
||||||
|
Some(ParamSpecAttrKind::Args) => {
|
||||||
|
if let Some(builder) =
|
||||||
|
self.context.report_lint(&INVALID_TYPE_FORM, annotation)
|
||||||
|
{
|
||||||
|
let name = typevar.name(self.db());
|
||||||
|
let mut diag = builder.into_diagnostic(format_args!(
|
||||||
|
"`{name}.args` is valid only in `*args` annotation",
|
||||||
|
));
|
||||||
|
diag.set_primary_message(format_args!("Did you mean `{name}.kwargs`?"));
|
||||||
|
diagnostic::add_type_expression_reference_link(diag);
|
||||||
|
}
|
||||||
|
// TODO: Should this be `Unknown` instead?
|
||||||
|
KnownClass::Dict.to_specialized_instance(
|
||||||
|
self.db(),
|
||||||
|
[KnownClass::Str.to_instance(self.db()), Type::unknown()],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `**kwargs: P.kwargs`
|
||||||
|
Some(ParamSpecAttrKind::Kwargs) => annotated_type,
|
||||||
|
|
||||||
|
// `**kwargs: P`
|
||||||
|
// TODO: Should this be `Unknown` instead?
|
||||||
|
None => KnownClass::Dict.to_specialized_instance(
|
||||||
|
self.db(),
|
||||||
|
[KnownClass::Str.to_instance(self.db()), Type::unknown()],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
KnownClass::Dict.to_specialized_instance(
|
||||||
|
self.db(),
|
||||||
|
[KnownClass::Str.to_instance(self.db()), annotated_type],
|
||||||
|
)
|
||||||
|
};
|
||||||
self.add_declaration_with_binding(
|
self.add_declaration_with_binding(
|
||||||
parameter.into(),
|
parameter.into(),
|
||||||
definition,
|
definition,
|
||||||
|
|
@ -8925,10 +8994,28 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
fn infer_attribute_load(&mut self, attribute: &ast::ExprAttribute) -> Type<'db> {
|
fn infer_attribute_load(&mut self, attribute: &ast::ExprAttribute) -> Type<'db> {
|
||||||
let ast::ExprAttribute { value, attr, .. } = attribute;
|
let ast::ExprAttribute { value, attr, .. } = attribute;
|
||||||
|
|
||||||
let value_type = self.infer_maybe_standalone_expression(value, TypeContext::default());
|
let mut value_type = self.infer_maybe_standalone_expression(value, TypeContext::default());
|
||||||
let db = self.db();
|
let db = self.db();
|
||||||
let mut constraint_keys = vec![];
|
let mut constraint_keys = vec![];
|
||||||
|
|
||||||
|
tracing::debug!(
|
||||||
|
"value_type for attribute access: {}",
|
||||||
|
value_type.display(db)
|
||||||
|
);
|
||||||
|
if let Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) = value_type
|
||||||
|
&& typevar.kind(db).is_paramspec()
|
||||||
|
&& let Some(bound_typevar) = bind_typevar(
|
||||||
|
db,
|
||||||
|
self.index,
|
||||||
|
self.scope().file_scope_id(db),
|
||||||
|
self.typevar_binding_context,
|
||||||
|
typevar,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
value_type = Type::TypeVar(bound_typevar);
|
||||||
|
tracing::debug!("updated value_type: {}", value_type.display(db));
|
||||||
|
}
|
||||||
|
|
||||||
let mut assigned_type = None;
|
let mut assigned_type = None;
|
||||||
if let Some(place_expr) = PlaceExpr::try_from_expr(attribute) {
|
if let Some(place_expr) = PlaceExpr::try_from_expr(attribute) {
|
||||||
let (resolved, keys) = self.infer_place_load(
|
let (resolved, keys) = self.infer_place_load(
|
||||||
|
|
@ -8940,6 +9027,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
assigned_type = Some(ty);
|
assigned_type = Some(ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tracing::debug!("assigned_type for attribute access: {:?}", assigned_type);
|
||||||
let fallback_place = value_type.member(db, &attr.id);
|
let fallback_place = value_type.member(db, &attr.id);
|
||||||
// Exclude non-definitely-bound places for purposes of reachability
|
// Exclude non-definitely-bound places for purposes of reachability
|
||||||
// analysis. We currently do not perform boundness analysis for implicit
|
// analysis. We currently do not perform boundness analysis for implicit
|
||||||
|
|
@ -9038,6 +9126,11 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
})
|
})
|
||||||
.inner_type();
|
.inner_type();
|
||||||
|
|
||||||
|
tracing::debug!(
|
||||||
|
"resolved_type for attribute access: {}",
|
||||||
|
resolved_type.display(db)
|
||||||
|
);
|
||||||
|
|
||||||
self.check_deprecated(attr, resolved_type);
|
self.check_deprecated(attr, resolved_type);
|
||||||
|
|
||||||
// Even if we can obtain the attribute type based on the assignments, we still perform default type inference
|
// Even if we can obtain the attribute type based on the assignments, we still perform default type inference
|
||||||
|
|
|
||||||
|
|
@ -144,11 +144,16 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
ast::Expr::Attribute(attribute) => match attribute.ctx {
|
ast::Expr::Attribute(attribute) => match attribute.ctx {
|
||||||
ast::ExprContext::Load => infer_name_or_attribute(
|
ast::ExprContext::Load => {
|
||||||
self.infer_attribute_expression(attribute),
|
let attribute_type = self.infer_attribute_expression(attribute);
|
||||||
annotation,
|
if let Type::TypeVar(typevar) = attribute_type
|
||||||
self,
|
&& typevar.paramspec_attr(self.db()).is_some()
|
||||||
),
|
{
|
||||||
|
TypeAndQualifiers::declared(attribute_type)
|
||||||
|
} else {
|
||||||
|
infer_name_or_attribute(attribute_type, annotation, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
ast::ExprContext::Invalid => TypeAndQualifiers::declared(Type::unknown()),
|
ast::ExprContext::Invalid => TypeAndQualifiers::declared(Type::unknown()),
|
||||||
ast::ExprContext::Store | ast::ExprContext::Del => TypeAndQualifiers::declared(
|
ast::ExprContext::Store | ast::ExprContext::Del => TypeAndQualifiers::declared(
|
||||||
todo_type!("Attribute expression annotation in Store/Del context"),
|
todo_type!("Attribute expression annotation in Store/Del context"),
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,15 @@ use itertools::Either;
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
|
|
||||||
use super::{DeferredExpressionState, TypeInferenceBuilder};
|
use super::{DeferredExpressionState, TypeInferenceBuilder};
|
||||||
|
use crate::semantic_index::semantic_index;
|
||||||
use crate::types::diagnostic::{
|
use crate::types::diagnostic::{
|
||||||
self, INVALID_TYPE_FORM, NON_SUBSCRIPTABLE, report_invalid_argument_number_to_special_form,
|
self, INVALID_TYPE_FORM, NON_SUBSCRIPTABLE, report_invalid_argument_number_to_special_form,
|
||||||
report_invalid_arguments_to_callable,
|
report_invalid_arguments_to_callable,
|
||||||
};
|
};
|
||||||
use crate::types::signatures::Signature;
|
use crate::types::generics::bind_typevar;
|
||||||
|
use crate::types::signatures::{ParamSpecOrigin, Signature};
|
||||||
use crate::types::string_annotation::parse_string_annotation;
|
use crate::types::string_annotation::parse_string_annotation;
|
||||||
use crate::types::tuple::{TupleSpecBuilder, TupleType};
|
use crate::types::tuple::{TupleSpecBuilder, TupleType};
|
||||||
use crate::types::visitor::any_over_type;
|
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
CallableType, DynamicType, IntersectionBuilder, KnownClass, KnownInstanceType,
|
CallableType, DynamicType, IntersectionBuilder, KnownClass, KnownInstanceType,
|
||||||
LintDiagnosticGuard, Parameter, Parameters, SpecialFormType, SubclassOfType, Type,
|
LintDiagnosticGuard, Parameter, Parameters, SpecialFormType, SubclassOfType, Type,
|
||||||
|
|
@ -1538,21 +1539,23 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||||
// `Callable[]`.
|
// `Callable[]`.
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
if any_over_type(
|
let name_ty = self.infer_name_load(name);
|
||||||
self.db(),
|
if let Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) = name_ty
|
||||||
self.infer_name_load(name),
|
&& typevar.kind(self.db()).is_paramspec()
|
||||||
&|ty| match ty {
|
{
|
||||||
Type::KnownInstance(known_instance) => {
|
let index = semantic_index(self.db(), self.scope().file(self.db()));
|
||||||
known_instance.class(self.db()) == KnownClass::ParamSpec
|
let origin = bind_typevar(
|
||||||
}
|
self.db(),
|
||||||
Type::NominalInstance(nominal) => {
|
index,
|
||||||
nominal.has_known_class(self.db(), KnownClass::ParamSpec)
|
self.scope().file_scope_id(self.db()),
|
||||||
}
|
self.typevar_binding_context,
|
||||||
_ => false,
|
typevar,
|
||||||
},
|
)
|
||||||
true,
|
.map_or(
|
||||||
) {
|
ParamSpecOrigin::Unbounded(typevar),
|
||||||
return Some(Parameters::todo());
|
ParamSpecOrigin::Bounded,
|
||||||
|
);
|
||||||
|
return Some(Parameters::paramspec(self.db(), origin));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,10 @@ use crate::types::generics::{
|
||||||
};
|
};
|
||||||
use crate::types::infer::nearest_enclosing_class;
|
use crate::types::infer::nearest_enclosing_class;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ApplyTypeMappingVisitor, BoundTypeVarInstance, ClassLiteral, FindLegacyTypeVarsVisitor,
|
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassLiteral,
|
||||||
HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor, KnownClass, MaterializationKind,
|
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor,
|
||||||
NormalizedVisitor, TypeContext, TypeMapping, TypeRelation, VarianceInferable, todo_type,
|
KnownClass, KnownInstanceType, MaterializationKind, NormalizedVisitor, ParamSpecAttrKind,
|
||||||
|
TypeContext, TypeMapping, TypeRelation, TypeVarInstance, VarianceInferable, todo_type,
|
||||||
};
|
};
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
use ruff_python_ast::{self as ast, name::Name};
|
use ruff_python_ast::{self as ast, name::Name};
|
||||||
|
|
@ -1169,10 +1170,56 @@ impl<'db> VarianceInferable<'db> for &Signature<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
#[derive(
|
||||||
pub(crate) struct Parameters<'db> {
|
Copy, Clone, Debug, Eq, Hash, PartialEq, salsa::Update, Ord, PartialOrd, get_size2::GetSize,
|
||||||
// TODO: use SmallVec here once invariance bug is fixed
|
)]
|
||||||
value: Vec<Parameter<'db>>,
|
pub(crate) enum ParamSpecOrigin<'db> {
|
||||||
|
Bounded(BoundTypeVarInstance<'db>),
|
||||||
|
Unbounded(TypeVarInstance<'db>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'db> ParamSpecOrigin<'db> {
|
||||||
|
pub(crate) fn with_paramspec_attr(
|
||||||
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
paramspec_attr: ParamSpecAttrKind,
|
||||||
|
) -> Self {
|
||||||
|
match self {
|
||||||
|
ParamSpecOrigin::Bounded(typevar) => {
|
||||||
|
ParamSpecOrigin::Bounded(typevar.with_paramspec_attr(db, paramspec_attr))
|
||||||
|
}
|
||||||
|
ParamSpecOrigin::Unbounded(_) => self,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn name(&self, db: &'db dyn Db) -> &ast::name::Name {
|
||||||
|
match self {
|
||||||
|
ParamSpecOrigin::Bounded(bound) => bound.typevar(db).name(db),
|
||||||
|
ParamSpecOrigin::Unbounded(unbound) => unbound.name(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn binding_context(&self, db: &'db dyn Db) -> Option<BindingContext<'db>> {
|
||||||
|
match self {
|
||||||
|
ParamSpecOrigin::Bounded(bound) => Some(bound.binding_context(db)),
|
||||||
|
ParamSpecOrigin::Unbounded(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn into_type(self) -> Type<'db> {
|
||||||
|
match self {
|
||||||
|
ParamSpecOrigin::Bounded(bound) => Type::TypeVar(bound),
|
||||||
|
ParamSpecOrigin::Unbounded(unbound) => {
|
||||||
|
Type::KnownInstance(KnownInstanceType::TypeVar(unbound))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||||
|
pub(crate) enum ParametersKind<'db> {
|
||||||
|
#[default]
|
||||||
|
Standard,
|
||||||
|
|
||||||
/// Whether this parameter list represents a gradual form using `...` as the only parameter.
|
/// Whether this parameter list represents a gradual form using `...` as the only parameter.
|
||||||
///
|
///
|
||||||
|
|
@ -1193,27 +1240,41 @@ pub(crate) struct Parameters<'db> {
|
||||||
/// some adjustments to represent that.
|
/// some adjustments to represent that.
|
||||||
///
|
///
|
||||||
/// [the typing specification]: https://typing.python.org/en/latest/spec/callables.html#meaning-of-in-callable
|
/// [the typing specification]: https://typing.python.org/en/latest/spec/callables.html#meaning-of-in-callable
|
||||||
is_gradual: bool,
|
Gradual,
|
||||||
|
|
||||||
|
// TODO: Need to store the name of the paramspec variable for the display implementation.
|
||||||
|
ParamSpec(ParamSpecOrigin<'db>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||||
|
pub(crate) struct Parameters<'db> {
|
||||||
|
// TODO: use SmallVec here once invariance bug is fixed
|
||||||
|
value: Vec<Parameter<'db>>,
|
||||||
|
kind: ParametersKind<'db>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> Parameters<'db> {
|
impl<'db> Parameters<'db> {
|
||||||
pub(crate) fn new(parameters: impl IntoIterator<Item = Parameter<'db>>) -> Self {
|
pub(crate) fn new(parameters: impl IntoIterator<Item = Parameter<'db>>) -> Self {
|
||||||
let value: Vec<Parameter<'db>> = parameters.into_iter().collect();
|
let value: Vec<Parameter<'db>> = parameters.into_iter().collect();
|
||||||
let is_gradual = value.len() == 2
|
let kind = if value.len() == 2
|
||||||
&& value
|
&& value
|
||||||
.iter()
|
.iter()
|
||||||
.any(|p| p.is_variadic() && p.annotated_type().is_none_or(|ty| ty.is_dynamic()))
|
.any(|p| p.is_variadic() && p.annotated_type().is_none_or(|ty| ty.is_dynamic()))
|
||||||
&& value.iter().any(|p| {
|
&& value.iter().any(|p| {
|
||||||
p.is_keyword_variadic() && p.annotated_type().is_none_or(|ty| ty.is_dynamic())
|
p.is_keyword_variadic() && p.annotated_type().is_none_or(|ty| ty.is_dynamic())
|
||||||
});
|
}) {
|
||||||
Self { value, is_gradual }
|
ParametersKind::Gradual
|
||||||
|
} else {
|
||||||
|
ParametersKind::Standard
|
||||||
|
};
|
||||||
|
Self { value, kind }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an empty parameter list.
|
/// Create an empty parameter list.
|
||||||
pub(crate) fn empty() -> Self {
|
pub(crate) fn empty() -> Self {
|
||||||
Self {
|
Self {
|
||||||
value: Vec::new(),
|
value: Vec::new(),
|
||||||
is_gradual: false,
|
kind: ParametersKind::Standard,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1221,8 +1282,12 @@ impl<'db> Parameters<'db> {
|
||||||
self.value.as_slice()
|
self.value.as_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn kind(&self) -> ParametersKind<'db> {
|
||||||
|
self.kind
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) const fn is_gradual(&self) -> bool {
|
pub(crate) const fn is_gradual(&self) -> bool {
|
||||||
self.is_gradual
|
matches!(self.kind, ParametersKind::Gradual)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return todo parameters: (*args: Todo, **kwargs: Todo)
|
/// Return todo parameters: (*args: Todo, **kwargs: Todo)
|
||||||
|
|
@ -1234,7 +1299,7 @@ impl<'db> Parameters<'db> {
|
||||||
Parameter::keyword_variadic(Name::new_static("kwargs"))
|
Parameter::keyword_variadic(Name::new_static("kwargs"))
|
||||||
.with_annotated_type(todo_type!("todo signature **kwargs")),
|
.with_annotated_type(todo_type!("todo signature **kwargs")),
|
||||||
],
|
],
|
||||||
is_gradual: true,
|
kind: ParametersKind::Gradual,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1251,7 +1316,25 @@ impl<'db> Parameters<'db> {
|
||||||
Parameter::keyword_variadic(Name::new_static("kwargs"))
|
Parameter::keyword_variadic(Name::new_static("kwargs"))
|
||||||
.with_annotated_type(Type::Dynamic(DynamicType::Any)),
|
.with_annotated_type(Type::Dynamic(DynamicType::Any)),
|
||||||
],
|
],
|
||||||
is_gradual: true,
|
kind: ParametersKind::Gradual,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn paramspec(db: &'db dyn Db, origin: ParamSpecOrigin<'db>) -> Self {
|
||||||
|
Self {
|
||||||
|
value: vec![
|
||||||
|
Parameter::variadic(Name::new_static("args")).with_annotated_type(
|
||||||
|
origin
|
||||||
|
.with_paramspec_attr(db, ParamSpecAttrKind::Args)
|
||||||
|
.into_type(),
|
||||||
|
),
|
||||||
|
Parameter::keyword_variadic(Name::new_static("kwargs")).with_annotated_type(
|
||||||
|
origin
|
||||||
|
.with_paramspec_attr(db, ParamSpecAttrKind::Kwargs)
|
||||||
|
.into_type(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
kind: ParametersKind::ParamSpec(origin),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1269,7 +1352,7 @@ impl<'db> Parameters<'db> {
|
||||||
Parameter::keyword_variadic(Name::new_static("kwargs"))
|
Parameter::keyword_variadic(Name::new_static("kwargs"))
|
||||||
.with_annotated_type(Type::Dynamic(DynamicType::Unknown)),
|
.with_annotated_type(Type::Dynamic(DynamicType::Unknown)),
|
||||||
],
|
],
|
||||||
is_gradual: true,
|
kind: ParametersKind::Gradual,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1281,7 +1364,7 @@ impl<'db> Parameters<'db> {
|
||||||
Parameter::keyword_variadic(Name::new_static("kwargs"))
|
Parameter::keyword_variadic(Name::new_static("kwargs"))
|
||||||
.with_annotated_type(Type::object()),
|
.with_annotated_type(Type::object()),
|
||||||
],
|
],
|
||||||
is_gradual: false,
|
kind: ParametersKind::Standard,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1471,13 +1554,13 @@ impl<'db> Parameters<'db> {
|
||||||
// Note that we've already flipped the materialization in Signature.apply_type_mapping_impl(),
|
// Note that we've already flipped the materialization in Signature.apply_type_mapping_impl(),
|
||||||
// so the "top" materialization here is the bottom materialization of the whole Signature.
|
// so the "top" materialization here is the bottom materialization of the whole Signature.
|
||||||
// It might make sense to flip the materialization here instead.
|
// It might make sense to flip the materialization here instead.
|
||||||
TypeMapping::Materialize(MaterializationKind::Top) if self.is_gradual => {
|
TypeMapping::Materialize(MaterializationKind::Top) if self.is_gradual() => {
|
||||||
Parameters::object()
|
Parameters::object()
|
||||||
}
|
}
|
||||||
// TODO: This is wrong, the empty Parameters is not a subtype of all materializations.
|
// TODO: This is wrong, the empty Parameters is not a subtype of all materializations.
|
||||||
// The bottom materialization is not currently representable and implementing it
|
// The bottom materialization is not currently representable and implementing it
|
||||||
// properly requires extending the Parameters struct.
|
// properly requires extending the Parameters struct.
|
||||||
TypeMapping::Materialize(MaterializationKind::Bottom) if self.is_gradual => {
|
TypeMapping::Materialize(MaterializationKind::Bottom) if self.is_gradual() => {
|
||||||
Parameters::empty()
|
Parameters::empty()
|
||||||
}
|
}
|
||||||
_ => Self {
|
_ => Self {
|
||||||
|
|
@ -1486,7 +1569,7 @@ impl<'db> Parameters<'db> {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|param| param.apply_type_mapping_impl(db, type_mapping, tcx, visitor))
|
.map(|param| param.apply_type_mapping_impl(db, type_mapping, tcx, visitor))
|
||||||
.collect(),
|
.collect(),
|
||||||
is_gradual: self.is_gradual,
|
kind: self.kind,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue