@@ -1981,6 +1981,94 @@ defmodule Module.Types.Descr do
19811981 end )
19821982 end
19831983
1984+ def tuple_values ( descr ) do
1985+ case :maps . take ( :dynamic , descr ) do
1986+ :error ->
1987+ if tuple_only? ( descr ) do
1988+ process_tuples_values ( Map . get ( descr , :tuple , [ ] ) )
1989+ else
1990+ :badtuple
1991+ end
1992+
1993+ { dynamic , static } ->
1994+ if tuple_only? ( static ) and descr_key? ( dynamic , :tuple ) do
1995+ dynamic ( process_tuples_values ( Map . get ( dynamic , :tuple , [ ] ) ) )
1996+ |> union ( process_tuples_values ( Map . get ( static , :tuple , [ ] ) ) )
1997+ else
1998+ :badtuple
1999+ end
2000+ end
2001+ end
2002+
2003+ defp process_tuples_values ( dnf ) do
2004+ Enum . reduce ( dnf , none ( ) , fn { tag , elements , negs } , acc ->
2005+ union ( tuple_values ( tag , elements , negs ) , acc )
2006+ end )
2007+ end
2008+
2009+ defp tuple_values ( tag , elements , [ ] ) do
2010+ cond do
2011+ Enum . any? ( elements , & empty? / 1 ) -> none ( )
2012+ tag == :open -> term ( )
2013+ tag == :closed -> Enum . reduce ( elements , none ( ) , & union / 2 )
2014+ end
2015+ end
2016+
2017+ defp tuple_values ( _tag , _elements , [ { :open , [ ] } | _ ] ) , do: none ( )
2018+
2019+ defp tuple_values ( tag , elements , [ { neg_tag , neg_elements } | negs ] ) do
2020+ n = length ( elements )
2021+ m = length ( neg_elements )
2022+
2023+ if ( tag == :closed and n < m ) or ( neg_tag == :closed and n > m ) do
2024+ tuple_values ( tag , elements , negs )
2025+ else
2026+ # Those two functions eliminate the negations, transforming into
2027+ # a union of tuples to compute their values.
2028+ values_elements ( [ ] , tag , elements , neg_elements , negs )
2029+ |> union ( values_size ( n , m , tag , elements , neg_tag , negs ) )
2030+ end
2031+ end
2032+
2033+ # This means that there are no more neg_elements to subtract -- end the recursion.
2034+ defp values_elements ( _acc , _tag , _elements , [ ] , _ ) , do: none ( )
2035+
2036+ # Eliminates negations according to tuple content.
2037+ # Subtracts each element of a negative tuple to build a new tuple with the difference.
2038+ # Example: {number(), atom()} and not {float(), :foo} contains types {integer(), :foo}
2039+ # as well as {float(), atom() and not :foo}
2040+ # Same process as tuple_elements_empty?
2041+ defp values_elements ( acc , tag , elements , [ neg_type | neg_elements ] , negs ) do
2042+ { ty , elements } = List . pop_at ( elements , 0 , term ( ) )
2043+ diff = difference ( ty , neg_type )
2044+
2045+ if empty? ( diff ) do
2046+ none ( )
2047+ else
2048+ tuple_values ( tag , Enum . reverse ( acc , [ diff | elements ] ) , negs )
2049+ end
2050+ |> union ( values_elements ( [ ty | acc ] , tag , elements , neg_elements , negs ) )
2051+ end
2052+
2053+ # Eliminates negations according to size
2054+ # Example: {integer(), ...} and not {term(), term(), ...} contains {integer()}
2055+ defp values_size ( n , m , tag , elements , neg_tag , negs ) do
2056+ if tag == :closed do
2057+ none ( )
2058+ else
2059+ n .. ( m - 1 ) // 1
2060+ |> Enum . map ( & tuple_values ( :closed , tuple_fill ( elements , & 1 ) , negs ) )
2061+ |> Enum . reduce ( none ( ) , & union / 2 )
2062+ |> union (
2063+ if neg_tag == :open do
2064+ none ( )
2065+ else
2066+ tuple_values ( tag , tuple_fill ( elements , m + 1 ) , negs )
2067+ end
2068+ )
2069+ end
2070+ end
2071+
19842072 defp tuple_pop_index ( tag , elements , index ) do
19852073 case List . pop_at ( elements , index ) do
19862074 { nil , _ } -> { tag_to_type ( tag ) , % { tuple: [ { tag , elements , [ ] } ] } }
0 commit comments