@@ -56,7 +56,7 @@ use crate::types::infer::infer_unpack_types;
5656use crate :: types:: mro:: { Mro , MroError , MroIterator } ;
5757pub ( crate ) use crate :: types:: narrow:: infer_narrowing_constraint;
5858use crate :: types:: signatures:: { Parameter , ParameterForm , Parameters , walk_signature} ;
59- use crate :: types:: tuple:: TupleType ;
59+ use crate :: types:: tuple:: { TupleSpec , TupleType } ;
6060pub use crate :: util:: diagnostics:: add_inferred_python_version_hint_to_diagnostic;
6161use crate :: { Db , FxOrderSet , Module , Program } ;
6262pub ( crate ) use class:: { ClassLiteral , ClassType , GenericAlias , KnownClass } ;
@@ -4610,35 +4610,43 @@ impl<'db> Type<'db> {
46104610 }
46114611 }
46124612
4613- /// Returns the element type when iterating over `self`.
4613+ /// Returns a tuple spec describing the elements that are produced when iterating over `self`.
46144614 ///
46154615 /// This method should only be used outside of type checking because it omits any errors.
46164616 /// For type checking, use [`try_iterate`](Self::try_iterate) instead.
4617- fn iterate ( self , db : & ' db dyn Db ) -> Type < ' db > {
4617+ fn iterate ( self , db : & ' db dyn Db ) -> TupleSpec < ' db > {
46184618 self . try_iterate ( db)
4619- . unwrap_or_else ( |err| err. fallback_element_type ( db) )
4619+ . unwrap_or_else ( |err| TupleSpec :: homogeneous ( err. fallback_element_type ( db) ) )
46204620 }
46214621
46224622 /// Given the type of an object that is iterated over in some way,
4623- /// return the type of objects that are yielded by that iteration.
4623+ /// return a tuple spec describing the type of objects that are yielded by that iteration.
46244624 ///
4625- /// E.g., for the following loop, given the type of `x`, infer the type of `y`:
4625+ /// E.g., for the following call, given the type of `x`, infer the types of the values that are
4626+ /// splatted into `y`'s positional arguments:
4627+ /// of `y`:
46264628 /// ```python
4627- /// for y in x:
4628- /// pass
4629+ /// y(*x)
46294630 /// ```
4630- fn try_iterate ( self , db : & ' db dyn Db ) -> Result < Type < ' db > , IterationError < ' db > > {
4631- if let Type :: Tuple ( tuple_type) = self {
4632- return Ok ( UnionType :: from_elements (
4633- db,
4634- tuple_type. tuple ( db) . all_elements ( ) ,
4635- ) ) ;
4636- }
4637-
4638- if let Type :: GenericAlias ( alias) = self {
4639- if alias. origin ( db) . is_known ( db, KnownClass :: Tuple ) {
4640- return Ok ( todo_type ! ( "*tuple[] annotations" ) ) ;
4641- }
4631+ fn try_iterate ( self , db : & ' db dyn Db ) -> Result < TupleSpec < ' db > , IterationError < ' db > > {
4632+ match self {
4633+ // XXX: Cow?
4634+ Type :: Tuple ( tuple_type) => return Ok ( tuple_type. tuple ( db) . clone ( ) ) ,
4635+ Type :: GenericAlias ( alias) if alias. origin ( db) . is_known ( db, KnownClass :: Tuple ) => {
4636+ return Ok ( TupleSpec :: homogeneous ( todo_type ! ( "*tuple[] annotations" ) ) ) ;
4637+ }
4638+ Type :: StringLiteral ( string_literal_ty) => {
4639+ // We could go further and deconstruct to an array of `StringLiteral`
4640+ // with each individual character, instead of just an array of
4641+ // `LiteralString`, but there would be a cost and it's not clear that
4642+ // it's worth it.
4643+ return Ok ( TupleSpec :: from_elements ( std:: iter:: repeat_n (
4644+ Type :: LiteralString ,
4645+ string_literal_ty. python_len ( db) ,
4646+ ) ) ) ;
4647+ }
4648+ Type :: LiteralString => return Ok ( TupleSpec :: homogeneous ( Type :: LiteralString ) ) ,
4649+ _ => { }
46424650 }
46434651
46444652 let try_call_dunder_getitem = || {
@@ -4664,12 +4672,14 @@ impl<'db> Type<'db> {
46644672 Ok ( iterator) => {
46654673 // `__iter__` is definitely bound and calling it succeeds.
46664674 // See what calling `__next__` on the object returned by `__iter__` gives us...
4667- try_call_dunder_next_on_iterator ( iterator) . map_err ( |dunder_next_error| {
4668- IterationError :: IterReturnsInvalidIterator {
4669- iterator,
4670- dunder_next_error,
4671- }
4672- } )
4675+ try_call_dunder_next_on_iterator ( iterator)
4676+ . map ( TupleSpec :: homogeneous)
4677+ . map_err (
4678+ |dunder_next_error| IterationError :: IterReturnsInvalidIterator {
4679+ iterator,
4680+ dunder_next_error,
4681+ } ,
4682+ )
46734683 }
46744684
46754685 // `__iter__` is possibly unbound...
@@ -4687,10 +4697,10 @@ impl<'db> Type<'db> {
46874697 // and the type returned by the `__getitem__` method.
46884698 //
46894699 // No diagnostic is emitted; iteration will always succeed!
4690- UnionType :: from_elements (
4700+ TupleSpec :: homogeneous ( UnionType :: from_elements (
46914701 db,
46924702 [ dunder_next_return, dunder_getitem_return_type] ,
4693- )
4703+ ) )
46944704 } )
46954705 . map_err ( |dunder_getitem_error| {
46964706 IterationError :: PossiblyUnboundIterAndGetitemError {
@@ -4713,13 +4723,13 @@ impl<'db> Type<'db> {
47134723 }
47144724
47154725 // There's no `__iter__` method. Try `__getitem__` instead...
4716- Err ( CallDunderError :: MethodNotAvailable ) => {
4717- try_call_dunder_getitem ( ) . map_err ( |dunder_getitem_error| {
4718- IterationError :: UnboundIterAndGetitemError {
4726+ Err ( CallDunderError :: MethodNotAvailable ) => try_call_dunder_getitem ( )
4727+ . map ( TupleSpec :: homogeneous)
4728+ . map_err (
4729+ |dunder_getitem_error| IterationError :: UnboundIterAndGetitemError {
47194730 dunder_getitem_error,
4720- }
4721- } )
4722- }
4731+ } ,
4732+ ) ,
47234733 }
47244734 }
47254735
0 commit comments