Compare commits

...

2 Commits

Author SHA1 Message Date
Douglas Creager
9964f4eee6 use tuplespec for variadic param annotation 2025-08-01 16:22:46 -04:00
Douglas Creager
46c936cc72 move annotated_type into paramkind 2025-08-01 16:22:46 -04:00
6 changed files with 364 additions and 186 deletions

View File

@@ -1438,7 +1438,7 @@ impl<'db> CallableBinding<'db> {
// TODO: For an unannotated `self` / `cls` parameter, the type should be
// `typing.Self` / `type[typing.Self]`
let current_parameter_type = overload.signature.parameters()[*parameter_index]
.annotated_type()
.annotated_type(db)
.unwrap_or(Type::unknown());
if let Some(first_parameter_type) = first_parameter_type {
if !first_parameter_type.is_equivalent_to(db, current_parameter_type) {
@@ -1483,7 +1483,7 @@ impl<'db> CallableBinding<'db> {
// TODO: For an unannotated `self` / `cls` parameter, the type should be
// `typing.Self` / `type[typing.Self]`
let parameter_type = overload.signature.parameters()[*parameter_index]
.annotated_type()
.annotated_type(db)
.unwrap_or(Type::unknown());
current_parameter_types.push(parameter_type);
}
@@ -2056,7 +2056,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
{
for parameter_index in &self.argument_matches[argument_index].parameters {
let parameter = &parameters[*parameter_index];
let Some(expected_type) = parameter.annotated_type() else {
let Some(expected_type) = parameter.annotated_type(self.db) else {
continue;
};
if let Err(error) = builder.infer(expected_type, argument_type) {
@@ -2087,7 +2087,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
) {
let parameters = self.signature.parameters();
let parameter = &parameters[parameter_index];
if let Some(mut expected_ty) = parameter.annotated_type() {
if let Some(mut expected_ty) = parameter.annotated_type(self.db) {
if let Some(specialization) = self.specialization {
argument_type = argument_type.apply_specialization(self.db, specialization);
expected_ty = expected_ty.apply_specialization(self.db, specialization);

View File

@@ -1852,7 +1852,9 @@ impl<'db> ClassLiteral<'db> {
overload.signature.parameters().get_positional(2)
{
value_types = value_types.add(
value_param.annotated_type().unwrap_or_else(Type::unknown),
value_param
.annotated_type(db)
.unwrap_or_else(Type::unknown),
);
} else if overload.signature.parameters().is_gradual() {
value_types = value_types.add(Type::unknown());

View File

@@ -753,18 +753,19 @@ impl Display for DisplayParameter<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if let Some(name) = self.param.display_name() {
f.write_str(&name)?;
if let Some(annotated_type) = self.param.annotated_type() {
if let Some(annotated_type) = self.param.annotated_type(self.db) {
// TODO: display starred annotation
write!(f, ": {}", annotated_type.display(self.db))?;
}
// Default value can only be specified if `name` is given.
if let Some(default_ty) = self.param.default_type() {
if self.param.annotated_type().is_some() {
if self.param.annotated_type(self.db).is_some() {
write!(f, " = {}", default_ty.display(self.db))?;
} else {
write!(f, "={}", default_ty.display(self.db))?;
}
}
} else if let Some(ty) = self.param.annotated_type() {
} else if let Some(ty) = self.param.annotated_type(self.db) {
// This case is specifically for the `Callable` signature where name and default value
// cannot be provided.
ty.display(self.db).fmt(f)?;

View File

@@ -133,7 +133,7 @@ impl<'db> GenericContext<'db> {
// Find all of the legacy typevars mentioned in the function signature.
let mut variables = FxOrderSet::default();
for param in parameters {
if let Some(ty) = param.annotated_type() {
if let Some(ty) = param.annotated_type(db) {
ty.find_legacy_typevars(db, &mut variables);
}
if let Some(ty) = param.default_type() {

View File

@@ -18,6 +18,7 @@ use smallvec::{SmallVec, smallvec_inline};
use super::{DynamicType, Type, TypeTransformer, TypeVarVariance, definition_expression_type};
use crate::semantic_index::definition::Definition;
use crate::types::generics::{GenericContext, walk_generic_context};
use crate::types::tuple::TupleSpec;
use crate::types::{KnownClass, TypeMapping, TypeRelation, TypeVarInstance, todo_type};
use crate::{Db, FxOrderSet};
use ruff_python_ast::{self as ast, name::Name};
@@ -255,7 +256,7 @@ pub(super) fn walk_signature<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
// By default we usually don't visit the type of the default value,
// as it isn't relevant to most things
for parameter in &signature.parameters {
if let Some(ty) = parameter.annotated_type() {
if let Some(ty) = parameter.annotated_type(db) {
visitor.visit_type(db, ty);
}
}
@@ -431,7 +432,7 @@ impl<'db> Signature<'db> {
typevars: &mut FxOrderSet<TypeVarInstance<'db>>,
) {
for param in &self.parameters {
if let Some(ty) = param.annotated_type() {
if let Some(ty) = param.annotated_type(db) {
ty.find_legacy_typevars(db, typevars);
}
if let Some(ty) = param.default_type() {
@@ -502,10 +503,12 @@ impl<'db> Signature<'db> {
ParameterKind::PositionalOrKeyword {
name: self_name,
default_type: self_default,
..
},
ParameterKind::PositionalOrKeyword {
name: other_name,
default_type: other_default,
..
},
) if self_default.is_some() == other_default.is_some()
&& self_name == other_name => {}
@@ -516,10 +519,12 @@ impl<'db> Signature<'db> {
ParameterKind::KeywordOnly {
name: self_name,
default_type: self_default,
..
},
ParameterKind::KeywordOnly {
name: other_name,
default_type: other_default,
..
},
) if self_default.is_some() == other_default.is_some()
&& self_name == other_name => {}
@@ -530,8 +535,8 @@ impl<'db> Signature<'db> {
}
if !check_types(
self_parameter.annotated_type(),
other_parameter.annotated_type(),
self_parameter.annotated_type(db),
other_parameter.annotated_type(db),
) {
return false;
}
@@ -624,14 +629,15 @@ impl<'db> Signature<'db> {
// A gradual parameter list is a supertype of the "bottom" parameter list (*args: object,
// **kwargs: object).
if other.parameters.is_gradual()
&& self
.parameters
.variadic()
.is_some_and(|(_, param)| param.annotated_type().is_some_and(|ty| ty.is_object(db)))
&& self.parameters.variadic().is_some_and(|(_, param)| {
param.annotated_type(db).is_some_and(|ty| ty.is_object(db))
})
&& self
.parameters
.keyword_variadic()
.is_some_and(|(_, param)| param.annotated_type().is_some_and(|ty| ty.is_object(db)))
.is_some_and(|(_, param)| {
param.annotated_type(db).is_some_and(|ty| ty.is_object(db))
})
{
return true;
}
@@ -696,14 +702,17 @@ impl<'db> Signature<'db> {
match (self_parameter.kind(), other_parameter.kind()) {
(
ParameterKind::PositionalOnly {
annotated_type: self_annotated,
default_type: self_default,
..
}
| ParameterKind::PositionalOrKeyword {
annotated_type: self_annotated,
default_type: self_default,
..
},
ParameterKind::PositionalOnly {
annotated_type: other_annotated,
default_type: other_default,
..
},
@@ -711,10 +720,7 @@ impl<'db> Signature<'db> {
if self_default.is_none() && other_default.is_some() {
return false;
}
if !check_types(
other_parameter.annotated_type(),
self_parameter.annotated_type(),
) {
if !check_types(*other_annotated, *self_annotated) {
return false;
}
}
@@ -722,11 +728,15 @@ impl<'db> Signature<'db> {
(
ParameterKind::PositionalOrKeyword {
name: self_name,
annotated_type: self_annotated,
default_type: self_default,
..
},
ParameterKind::PositionalOrKeyword {
name: other_name,
annotated_type: other_annotated,
default_type: other_default,
..
},
) => {
if self_name != other_name {
@@ -736,23 +746,24 @@ impl<'db> Signature<'db> {
if self_default.is_none() && other_default.is_some() {
return false;
}
if !check_types(
other_parameter.annotated_type(),
self_parameter.annotated_type(),
) {
if !check_types(*other_annotated, *self_annotated) {
return false;
}
}
(
ParameterKind::Variadic { .. },
ParameterKind::PositionalOnly { .. }
| ParameterKind::PositionalOrKeyword { .. },
ParameterKind::PositionalOnly {
annotated_type: other_annotated,
..
}
| ParameterKind::PositionalOrKeyword {
annotated_type: other_annotated,
..
},
) => {
if !check_types(
other_parameter.annotated_type(),
self_parameter.annotated_type(),
) {
let self_annotated = self_parameter.annotated_type(db);
if !check_types(*other_annotated, self_annotated) {
return false;
}
@@ -789,10 +800,8 @@ impl<'db> Signature<'db> {
break;
}
}
if !check_types(
other_parameter.annotated_type(),
self_parameter.annotated_type(),
) {
if !check_types(other_parameter.annotated_type(db), self_annotated)
{
return false;
}
parameters.next_other();
@@ -801,8 +810,8 @@ impl<'db> Signature<'db> {
(ParameterKind::Variadic { .. }, ParameterKind::Variadic { .. }) => {
if !check_types(
other_parameter.annotated_type(),
self_parameter.annotated_type(),
other_parameter.annotated_type(db),
self_parameter.annotated_type(db),
) {
return false;
}
@@ -845,8 +854,8 @@ impl<'db> Signature<'db> {
| ParameterKind::PositionalOrKeyword { name, .. } => {
self_keywords.insert(name.clone(), self_parameter);
}
ParameterKind::KeywordVariadic { .. } => {
self_keyword_variadic = Some(self_parameter.annotated_type());
ParameterKind::KeywordVariadic { annotated_type, .. } => {
self_keyword_variadic = Some(*annotated_type);
}
ParameterKind::PositionalOnly { .. } => {
// These are the unmatched positional-only parameters in `self` from the
@@ -863,29 +872,32 @@ impl<'db> Signature<'db> {
match other_parameter.kind() {
ParameterKind::KeywordOnly {
name: other_name,
annotated_type: other_annotated,
default_type: other_default,
..
}
| ParameterKind::PositionalOrKeyword {
name: other_name,
annotated_type: other_annotated,
default_type: other_default,
..
} => {
if let Some(self_parameter) = self_keywords.remove(other_name) {
match self_parameter.kind() {
ParameterKind::PositionalOrKeyword {
annotated_type: self_annotated,
default_type: self_default,
..
}
| ParameterKind::KeywordOnly {
annotated_type: self_annotated,
default_type: self_default,
..
} => {
if self_default.is_none() && other_default.is_some() {
return false;
}
if !check_types(
other_parameter.annotated_type(),
self_parameter.annotated_type(),
) {
if !check_types(*other_annotated, *self_annotated) {
return false;
}
}
@@ -894,23 +906,23 @@ impl<'db> Signature<'db> {
),
}
} else if let Some(self_keyword_variadic_type) = self_keyword_variadic {
if !check_types(
other_parameter.annotated_type(),
self_keyword_variadic_type,
) {
if !check_types(*other_annotated, self_keyword_variadic_type) {
return false;
}
} else {
return false;
}
}
ParameterKind::KeywordVariadic { .. } => {
ParameterKind::KeywordVariadic {
annotated_type: other_annotated,
..
} => {
let Some(self_keyword_variadic_type) = self_keyword_variadic else {
// For a `self <: other` relationship, if `other` has a keyword variadic
// parameter, `self` must also have a keyword variadic parameter.
return false;
};
if !check_types(other_parameter.annotated_type(), self_keyword_variadic_type) {
if !check_types(*other_annotated, self_keyword_variadic_type) {
return false;
}
}
@@ -991,11 +1003,18 @@ impl<'db> Parameters<'db> {
pub(crate) fn new(parameters: impl IntoIterator<Item = Parameter<'db>>) -> Self {
let value: Vec<Parameter<'db>> = parameters.into_iter().collect();
let is_gradual = value.len() == 2
&& value
.iter()
.any(|p| p.is_variadic() && p.annotated_type().is_none_or(|ty| ty.is_dynamic()))
&& value.iter().any(|p| {
p.is_keyword_variadic() && p.annotated_type().is_none_or(|ty| ty.is_dynamic())
&& value.iter().any(|p| match p.kind() {
ParameterKind::Variadic { annotated_type, .. } => annotated_type
.as_ref()
.and_then(|tuple| tuple.to_homogeneous().copied())
.is_none_or(|ty| ty.is_dynamic()),
_ => false,
})
&& value.iter().any(|p| match p.kind() {
ParameterKind::KeywordVariadic { annotated_type, .. } => {
annotated_type.is_none_or(|ty| ty.is_dynamic())
}
_ => false,
});
Self { value, is_gradual }
}
@@ -1101,63 +1120,53 @@ impl<'db> Parameters<'db> {
range: _,
node_index: _,
} = parameters;
let annotated_type = |param: &ast::Parameter| {
param
.annotation()
.map(|annotation| definition_expression_type(db, definition, annotation))
};
let default_type = |param: &ast::ParameterWithDefault| {
param
.default()
.map(|default| definition_expression_type(db, definition, default))
};
let positional_only = posonlyargs.iter().map(|arg| {
Parameter::from_node_and_kind(
db,
definition,
&arg.parameter,
ParameterKind::PositionalOnly {
name: Some(arg.parameter.name.id.clone()),
default_type: default_type(arg),
},
)
let positional_only = posonlyargs.iter().map(|arg| Parameter {
kind: ParameterKind::PositionalOnly {
name: Some(arg.parameter.name.id.clone()),
annotated_type: annotated_type(&arg.parameter),
default_type: default_type(arg),
},
form: ParameterForm::Value,
});
let positional_or_keyword = args.iter().map(|arg| {
Parameter::from_node_and_kind(
db,
definition,
&arg.parameter,
ParameterKind::PositionalOrKeyword {
name: arg.parameter.name.id.clone(),
default_type: default_type(arg),
},
)
let positional_or_keyword = args.iter().map(|arg| Parameter {
kind: ParameterKind::PositionalOrKeyword {
name: arg.parameter.name.id.clone(),
annotated_type: annotated_type(&arg.parameter),
default_type: default_type(arg),
},
form: ParameterForm::Value,
});
let variadic = vararg.as_ref().map(|arg| {
Parameter::from_node_and_kind(
db,
definition,
arg,
ParameterKind::Variadic {
name: arg.name.id.clone(),
},
)
let variadic = vararg.as_ref().map(|arg| Parameter {
kind: ParameterKind::Variadic {
name: arg.name.id.clone(),
annotated_type: annotated_type(arg).map(TupleSpec::homogeneous),
},
form: ParameterForm::Value,
});
let keyword_only = kwonlyargs.iter().map(|arg| {
Parameter::from_node_and_kind(
db,
definition,
&arg.parameter,
ParameterKind::KeywordOnly {
name: arg.parameter.name.id.clone(),
default_type: default_type(arg),
},
)
let keyword_only = kwonlyargs.iter().map(|arg| Parameter {
kind: ParameterKind::KeywordOnly {
name: arg.parameter.name.id.clone(),
annotated_type: annotated_type(&arg.parameter),
default_type: default_type(arg),
},
form: ParameterForm::Value,
});
let keywords = kwarg.as_ref().map(|arg| {
Parameter::from_node_and_kind(
db,
definition,
arg,
ParameterKind::KeywordVariadic {
name: arg.name.id.clone(),
},
)
let keywords = kwarg.as_ref().map(|arg| Parameter {
kind: ParameterKind::KeywordVariadic {
name: arg.name.id.clone(),
annotated_type: annotated_type(arg),
},
form: ParameterForm::Value,
});
Self::new(
positional_only
@@ -1261,9 +1270,6 @@ impl<'db> std::ops::Index<usize> for Parameters<'db> {
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
pub(crate) struct Parameter<'db> {
/// Annotated type of the parameter.
annotated_type: Option<Type<'db>>,
kind: ParameterKind<'db>,
pub(crate) form: ParameterForm,
}
@@ -1271,9 +1277,9 @@ pub(crate) struct Parameter<'db> {
impl<'db> Parameter<'db> {
pub(crate) fn positional_only(name: Option<Name>) -> Self {
Self {
annotated_type: None,
kind: ParameterKind::PositionalOnly {
name,
annotated_type: None,
default_type: None,
},
form: ParameterForm::Value,
@@ -1282,9 +1288,9 @@ impl<'db> Parameter<'db> {
pub(crate) fn positional_or_keyword(name: Name) -> Self {
Self {
annotated_type: None,
kind: ParameterKind::PositionalOrKeyword {
name,
annotated_type: None,
default_type: None,
},
form: ParameterForm::Value,
@@ -1293,17 +1299,19 @@ impl<'db> Parameter<'db> {
pub(crate) fn variadic(name: Name) -> Self {
Self {
annotated_type: None,
kind: ParameterKind::Variadic { name },
kind: ParameterKind::Variadic {
name,
annotated_type: None,
},
form: ParameterForm::Value,
}
}
pub(crate) fn keyword_only(name: Name) -> Self {
Self {
annotated_type: None,
kind: ParameterKind::KeywordOnly {
name,
annotated_type: None,
default_type: None,
},
form: ParameterForm::Value,
@@ -1312,14 +1320,26 @@ impl<'db> Parameter<'db> {
pub(crate) fn keyword_variadic(name: Name) -> Self {
Self {
annotated_type: None,
kind: ParameterKind::KeywordVariadic { name },
kind: ParameterKind::KeywordVariadic {
name,
annotated_type: None,
},
form: ParameterForm::Value,
}
}
pub(crate) fn with_annotated_type(mut self, annotated_type: Type<'db>) -> Self {
self.annotated_type = Some(annotated_type);
pub(crate) fn with_annotated_type(mut self, annotated: Type<'db>) -> Self {
match &mut self.kind {
ParameterKind::PositionalOnly { annotated_type, .. }
| ParameterKind::PositionalOrKeyword { annotated_type, .. }
| ParameterKind::KeywordOnly { annotated_type, .. }
| ParameterKind::KeywordVariadic { annotated_type, .. } => {
*annotated_type = Some(annotated);
}
ParameterKind::Variadic { annotated_type, .. } => {
*annotated_type = Some(TupleSpec::homogeneous(annotated));
}
}
self
}
@@ -1342,21 +1362,13 @@ impl<'db> Parameter<'db> {
fn materialize(&self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
Self {
annotated_type: Some(
self.annotated_type
.unwrap_or(Type::unknown())
.materialize(db, variance),
),
kind: self.kind.clone(),
kind: self.kind.materialize(db, variance),
form: self.form,
}
}
fn apply_type_mapping<'a>(&self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self {
Self {
annotated_type: self
.annotated_type
.map(|ty| ty.apply_type_mapping(db, type_mapping)),
kind: self.kind.apply_type_mapping(db, type_mapping),
form: self.form,
}
@@ -1371,69 +1383,81 @@ impl<'db> Parameter<'db> {
db: &'db dyn Db,
visitor: &mut TypeTransformer<'db>,
) -> Self {
let Parameter {
annotated_type,
kind,
form,
} = self;
let Parameter { kind, form } = self;
// Ensure unions and intersections are ordered in the annotated type (if there is one).
// Ensure that a parameter without an annotation is treated equivalently to a parameter
// with a dynamic type as its annotation. (We must use `Any` here as all dynamic types
// normalize to `Any`.)
let annotated_type = annotated_type
.map(|ty| ty.normalized_impl(db, visitor))
.unwrap_or_else(Type::any);
// Ensure that parameter names are stripped from positional-only, variadic and keyword-variadic parameters.
// Ensure that we only record whether a parameter *has* a default
// (strip the precise *type* of the default from the parameter, replacing it with `Never`).
let kind = match kind {
ParameterKind::PositionalOnly {
name: _,
annotated_type,
default_type,
} => ParameterKind::PositionalOnly {
name: None,
annotated_type: Some(
annotated_type
.map(|ty| ty.normalized_impl(db, visitor))
.unwrap_or_else(Type::any),
),
default_type: default_type.map(|_| Type::Never),
},
ParameterKind::PositionalOrKeyword { name, default_type } => {
ParameterKind::PositionalOrKeyword {
name: name.clone(),
default_type: default_type.map(|_| Type::Never),
}
}
ParameterKind::KeywordOnly { name, default_type } => ParameterKind::KeywordOnly {
ParameterKind::PositionalOrKeyword {
name,
annotated_type,
default_type,
} => ParameterKind::PositionalOrKeyword {
name: name.clone(),
annotated_type: Some(
annotated_type
.map(|ty| ty.normalized_impl(db, visitor))
.unwrap_or_else(Type::any),
),
default_type: default_type.map(|_| Type::Never),
},
ParameterKind::Variadic { name: _ } => ParameterKind::Variadic {
name: Name::new_static("args"),
ParameterKind::KeywordOnly {
name,
annotated_type,
default_type,
} => ParameterKind::KeywordOnly {
name: name.clone(),
annotated_type: Some(
annotated_type
.map(|ty| ty.normalized_impl(db, visitor))
.unwrap_or_else(Type::any),
),
default_type: default_type.map(|_| Type::Never),
},
ParameterKind::KeywordVariadic { name: _ } => ParameterKind::KeywordVariadic {
ParameterKind::Variadic {
name: _,
annotated_type,
} => ParameterKind::Variadic {
name: Name::new_static("args"),
annotated_type: Some(
annotated_type
.as_ref()
.map(|tuple| tuple.normalized_impl(db, visitor))
.unwrap_or_else(|| TupleSpec::homogeneous(Type::any())),
),
},
ParameterKind::KeywordVariadic {
name: _,
annotated_type,
} => ParameterKind::KeywordVariadic {
name: Name::new_static("kwargs"),
annotated_type: Some(
annotated_type
.map(|ty| ty.normalized_impl(db, visitor))
.unwrap_or_else(Type::any),
),
},
};
Self {
annotated_type: Some(annotated_type),
kind,
form: *form,
}
}
fn from_node_and_kind(
db: &'db dyn Db,
definition: Definition<'db>,
parameter: &ast::Parameter,
kind: ParameterKind<'db>,
) -> Self {
Self {
annotated_type: parameter
.annotation()
.map(|annotation| definition_expression_type(db, definition, annotation)),
kind,
form: ParameterForm::Value,
}
Self { kind, form: *form }
}
/// Returns `true` if this is a keyword-only parameter.
@@ -1478,8 +1502,16 @@ impl<'db> Parameter<'db> {
}
/// Annotated type of the parameter, if annotated.
pub(crate) fn annotated_type(&self) -> Option<Type<'db>> {
self.annotated_type
pub(crate) fn annotated_type(&self, db: &'db dyn Db) -> Option<Type<'db>> {
match &self.kind {
ParameterKind::PositionalOnly { annotated_type, .. }
| ParameterKind::PositionalOrKeyword { annotated_type, .. }
| ParameterKind::KeywordOnly { annotated_type, .. }
| ParameterKind::KeywordVariadic { annotated_type, .. } => *annotated_type,
ParameterKind::Variadic { annotated_type, .. } => annotated_type
.as_ref()
.map(|tuple| tuple.homogeneous_element_type(db)),
}
}
/// Kind of the parameter.
@@ -1492,9 +1524,9 @@ impl<'db> Parameter<'db> {
match &self.kind {
ParameterKind::PositionalOnly { name, .. } => name.as_ref(),
ParameterKind::PositionalOrKeyword { name, .. } => Some(name),
ParameterKind::Variadic { name } => Some(name),
ParameterKind::Variadic { name, .. } => Some(name),
ParameterKind::KeywordOnly { name, .. } => Some(name),
ParameterKind::KeywordVariadic { name } => Some(name),
ParameterKind::KeywordVariadic { name, .. } => Some(name),
}
}
@@ -1527,6 +1559,7 @@ pub(crate) enum ParameterKind<'db> {
/// It is possible for signatures to be defined in ways that leave positional-only parameters
/// nameless (e.g. via `Callable` annotations).
name: Option<Name>,
annotated_type: Option<Type<'db>>,
default_type: Option<Type<'db>>,
},
@@ -1534,6 +1567,7 @@ pub(crate) enum ParameterKind<'db> {
PositionalOrKeyword {
/// Parameter name.
name: Name,
annotated_type: Option<Type<'db>>,
default_type: Option<Type<'db>>,
},
@@ -1541,12 +1575,14 @@ pub(crate) enum ParameterKind<'db> {
Variadic {
/// Parameter name.
name: Name,
annotated_type: Option<TupleSpec<'db>>,
},
/// Keyword-only parameter, e.g. `def f(*, x): ...`
KeywordOnly {
/// Parameter name.
name: Name,
annotated_type: Option<Type<'db>>,
default_type: Option<Type<'db>>,
},
@@ -1554,31 +1590,137 @@ pub(crate) enum ParameterKind<'db> {
KeywordVariadic {
/// Parameter name.
name: Name,
annotated_type: Option<Type<'db>>,
},
}
impl<'db> ParameterKind<'db> {
fn materialize(&self, db: &'db dyn Db, variance: TypeVarVariance) -> Self {
match self {
Self::PositionalOnly {
annotated_type,
default_type,
name,
} => Self::PositionalOnly {
annotated_type: Some(
annotated_type
.unwrap_or(Type::unknown())
.materialize(db, variance),
),
default_type: *default_type,
name: name.clone(),
},
Self::PositionalOrKeyword {
annotated_type,
default_type,
name,
} => Self::PositionalOrKeyword {
annotated_type: Some(
annotated_type
.unwrap_or(Type::unknown())
.materialize(db, variance),
),
default_type: *default_type,
name: name.clone(),
},
Self::KeywordOnly {
annotated_type,
default_type,
name,
} => Self::KeywordOnly {
annotated_type: Some(
annotated_type
.unwrap_or(Type::unknown())
.materialize(db, variance),
),
default_type: *default_type,
name: name.clone(),
},
Self::Variadic {
annotated_type,
name,
} => Self::Variadic {
annotated_type: Some(
annotated_type
.as_ref()
.map(|tuple| tuple.materialize(db, variance))
.unwrap_or_else(|| TupleSpec::homogeneous(Type::unknown())),
),
name: name.clone(),
},
Self::KeywordVariadic {
annotated_type,
name,
} => Self::KeywordVariadic {
annotated_type: Some(
annotated_type
.unwrap_or(Type::unknown())
.materialize(db, variance),
),
name: name.clone(),
},
}
}
fn apply_type_mapping<'a>(&self, db: &'db dyn Db, type_mapping: &TypeMapping<'a, 'db>) -> Self {
match self {
Self::PositionalOnly { default_type, name } => Self::PositionalOnly {
Self::PositionalOnly {
annotated_type,
default_type,
name,
} => Self::PositionalOnly {
annotated_type: annotated_type
.as_ref()
.map(|ty| ty.apply_type_mapping(db, type_mapping)),
default_type: default_type
.as_ref()
.map(|ty| ty.apply_type_mapping(db, type_mapping)),
name: name.clone(),
},
Self::PositionalOrKeyword { default_type, name } => Self::PositionalOrKeyword {
Self::PositionalOrKeyword {
annotated_type,
default_type,
name,
} => Self::PositionalOrKeyword {
annotated_type: annotated_type
.as_ref()
.map(|ty| ty.apply_type_mapping(db, type_mapping)),
default_type: default_type
.as_ref()
.map(|ty| ty.apply_type_mapping(db, type_mapping)),
name: name.clone(),
},
Self::KeywordOnly { default_type, name } => Self::KeywordOnly {
Self::KeywordOnly {
annotated_type,
default_type,
name,
} => Self::KeywordOnly {
annotated_type: annotated_type
.as_ref()
.map(|ty| ty.apply_type_mapping(db, type_mapping)),
default_type: default_type
.as_ref()
.map(|ty| ty.apply_type_mapping(db, type_mapping)),
name: name.clone(),
},
Self::Variadic { .. } | Self::KeywordVariadic { .. } => self.clone(),
Self::Variadic {
annotated_type,
name,
} => Self::Variadic {
annotated_type: annotated_type
.as_ref()
.map(|ty| ty.apply_type_mapping(db, type_mapping)),
name: name.clone(),
},
Self::KeywordVariadic {
annotated_type,
name,
} => Self::KeywordVariadic {
annotated_type: annotated_type
.as_ref()
.map(|ty| ty.apply_type_mapping(db, type_mapping)),
name: name.clone(),
},
}
}
}
@@ -1702,8 +1844,12 @@ mod tests {
let [
Parameter {
annotated_type,
kind: ParameterKind::PositionalOrKeyword { name, .. },
kind:
ParameterKind::PositionalOrKeyword {
name,
annotated_type,
..
},
..
},
] = &sig.parameters.value[..]
@@ -1740,8 +1886,12 @@ mod tests {
let [
Parameter {
annotated_type,
kind: ParameterKind::PositionalOrKeyword { name, .. },
kind:
ParameterKind::PositionalOrKeyword {
name,
annotated_type,
..
},
..
},
] = &sig.parameters.value[..]
@@ -1778,13 +1928,21 @@ mod tests {
let [
Parameter {
annotated_type: a_annotated_ty,
kind: ParameterKind::PositionalOrKeyword { name: a_name, .. },
kind:
ParameterKind::PositionalOrKeyword {
name: a_name,
annotated_type: a_annotated_ty,
..
},
..
},
Parameter {
annotated_type: b_annotated_ty,
kind: ParameterKind::PositionalOrKeyword { name: b_name, .. },
kind:
ParameterKind::PositionalOrKeyword {
name: b_name,
annotated_type: b_annotated_ty,
..
},
..
},
] = &sig.parameters.value[..]
@@ -1826,13 +1984,21 @@ mod tests {
let [
Parameter {
annotated_type: a_annotated_ty,
kind: ParameterKind::PositionalOrKeyword { name: a_name, .. },
kind:
ParameterKind::PositionalOrKeyword {
name: a_name,
annotated_type: a_annotated_ty,
..
},
..
},
Parameter {
annotated_type: b_annotated_ty,
kind: ParameterKind::PositionalOrKeyword { name: b_name, .. },
kind:
ParameterKind::PositionalOrKeyword {
name: b_name,
annotated_type: b_annotated_ty,
..
},
..
},
] = &sig.parameters.value[..]

View File

@@ -300,7 +300,7 @@ pub(crate) type TupleSpec<'db> = Tuple<Type<'db>>;
///
/// Our tuple representation can hold instances of any Rust type. For tuples containing Python
/// types, use [`TupleSpec`], which defines some additional type-specific methods.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
pub struct FixedLengthTuple<T>(Vec<T>);
impl<T> FixedLengthTuple<T> {
@@ -517,7 +517,7 @@ impl<'db> PySlice<'db> for FixedLengthTuple<Type<'db>> {
///
/// Our tuple representation can hold instances of any Rust type. For tuples containing Python
/// types, use [`TupleSpec`], which defines some additional type-specific methods.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
pub struct VariableLengthTuple<T> {
pub(crate) prefix: Vec<T>,
pub(crate) variable: T,
@@ -959,7 +959,7 @@ impl<'db> PyIndex<'db> for &VariableLengthTuple<Type<'db>> {
///
/// Our tuple representation can hold instances of any Rust type. For tuples containing Python
/// types, use [`TupleSpec`], which defines some additional type-specific methods.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, PartialEq, get_size2::GetSize)]
pub enum Tuple<T> {
Fixed(FixedLengthTuple<T>),
Variable(VariableLengthTuple<T>),
@@ -986,6 +986,15 @@ impl<T> Tuple<T> {
}
}
pub(crate) fn to_homogeneous(&self) -> Option<&T> {
match self {
Tuple::Variable(tuple) if tuple.prefix.is_empty() && tuple.suffix.is_empty() => {
Some(&tuple.variable)
}
Tuple::Variable(_) | Tuple::Fixed(_) => None,
}
}
/// Returns an iterator of all of the element types of this tuple. Does not deduplicate the
/// elements, and does not distinguish between fixed- and variable-length elements.
pub(crate) fn all_elements(&self) -> impl Iterator<Item = &T> + '_ {