Remove Type::tuple in favor of TupleType::from_elements (#15218)

## Summary

Remove `Type::tuple` in favor of `TupleType::from_elements`, avoid a few
intermediate `Vec`tors. Resolves an old [review
comment](https://github.com/astral-sh/ruff/pull/14744#discussion_r1867493706).

## Test Plan

New regression test for something I ran into while implementing this.
This commit is contained in:
David Peter 2025-01-02 17:22:32 +01:00 committed by GitHub
parent 11e873eb45
commit 7671a3bbc7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 39 additions and 27 deletions

View file

@ -705,13 +705,6 @@ impl<'db> Type<'db> {
Self::BytesLiteral(BytesLiteralType::new(db, bytes))
}
pub fn tuple<T: Into<Type<'db>>>(
db: &'db dyn Db,
elements: impl IntoIterator<Item = T>,
) -> Self {
TupleType::from_elements(db, elements)
}
#[must_use]
pub fn negate(&self, db: &'db dyn Db) -> Type<'db> {
IntersectionBuilder::new(db).add_negative(*self).build()
@ -2118,15 +2111,16 @@ impl<'db> Type<'db> {
Type::Union(UnionType::new(db, elements))
};
let version_info_elements = &[
Type::IntLiteral(python_version.major.into()),
Type::IntLiteral(python_version.minor.into()),
int_instance_ty,
release_level_ty,
int_instance_ty,
];
Self::tuple(db, version_info_elements)
TupleType::from_elements(
db,
[
Type::IntLiteral(python_version.major.into()),
Type::IntLiteral(python_version.minor.into()),
int_instance_ty,
release_level_ty,
int_instance_ty,
],
)
}
/// Given a type that is assumed to represent an instance of a class,
@ -3435,8 +3429,8 @@ impl<'db> Class<'db> {
/// The member resolves to a member on the class itself or any of its proper superclasses.
pub(crate) fn class_member(self, db: &'db dyn Db, name: &str) -> Symbol<'db> {
if name == "__mro__" {
let tuple_elements: Vec<Type<'db>> = self.iter_mro(db).map(Type::from).collect();
return Type::tuple(db, &tuple_elements).into();
let tuple_elements = self.iter_mro(db).map(Type::from);
return TupleType::from_elements(db, tuple_elements).into();
}
for superclass in self.iter_mro(db) {
@ -3846,7 +3840,7 @@ pub(crate) mod tests {
}
Ty::Tuple(tys) => {
let elements = tys.into_iter().map(|ty| ty.into_type(db));
Type::tuple(db, elements)
TupleType::from_elements(db, elements)
}
Ty::SubclassOfAny => Type::subclass_of_base(ClassBase::Any),
Ty::SubclassOfUnknown => Type::subclass_of_base(ClassBase::Unknown),

View file

@ -2672,10 +2672,12 @@ impl<'db> TypeInferenceBuilder<'db> {
parenthesized: _,
} = tuple;
let element_types: Vec<Type<'db>> =
elts.iter().map(|elt| self.infer_expression(elt)).collect();
// Collecting all elements is necessary to infer all sub-expressions even if some
// element types are `Never` (which leads `from_elements` to return early without
// consuming the whole iterator).
let element_types: Vec<_> = elts.iter().map(|elt| self.infer_expression(elt)).collect();
Type::tuple(self.db(), &element_types)
TupleType::from_elements(self.db(), element_types)
}
fn infer_list_expression(&mut self, list: &ast::ExprList) -> Type<'db> {
@ -4239,8 +4241,7 @@ impl<'db> TypeInferenceBuilder<'db> {
let (start, stop, step) = slice_ty.as_tuple(self.db());
if let Ok(new_elements) = elements.py_slice(start, stop, step) {
let new_elements: Vec<_> = new_elements.copied().collect();
Type::tuple(self.db(), &new_elements)
TupleType::from_elements(self.db(), new_elements)
} else {
report_slice_step_size_zero(&self.context, value_node.into());
Type::Unknown
@ -4842,7 +4843,7 @@ impl<'db> TypeInferenceBuilder<'db> {
let ty = if return_todo {
todo_type!("full tuple[...] support")
} else {
Type::tuple(self.db(), &element_types)
TupleType::from_elements(self.db(), element_types)
};
// Here, we store the type for the inner `int, str` tuple-expression,
@ -4857,7 +4858,7 @@ impl<'db> TypeInferenceBuilder<'db> {
if element_could_alter_type_of_whole_tuple(single_element, single_element_ty) {
todo_type!("full tuple[...] support")
} else {
Type::tuple(self.db(), [single_element_ty])
TupleType::from_elements(self.db(), std::iter::once(single_element_ty))
}
}
}

View file

@ -96,7 +96,7 @@ impl<'db> Unpacker<'db> {
// with each individual character, instead of just an array of
// `LiteralString`, but there would be a cost and it's not clear that
// it's worth it.
Type::tuple(
TupleType::from_elements(
self.db(),
std::iter::repeat(Type::LiteralString)
.take(string_literal_ty.python_len(self.db())),