@@ -1268,6 +1268,91 @@ defmodule Module.Types.DescrTest do
12681268 # assert difference(tuple([number(), term()]), tuple([integer(), atom()]))
12691269 # |> to_quoted_string() ==
12701270 # "{float(), term()} or {number(), term() and not atom()}"
1271+
1272+ assert union ( tuple ( [ integer ( ) , atom ( ) ] ) , tuple ( [ integer ( ) , atom ( ) ] ) ) |> to_quoted_string ( ) ==
1273+ "{integer(), atom()}"
1274+
1275+ assert union ( tuple ( [ integer ( ) , atom ( ) ] ) , tuple ( [ float ( ) , atom ( ) ] ) ) |> to_quoted_string ( ) ==
1276+ "{float() or integer(), atom()}"
1277+
1278+ assert union ( tuple ( [ integer ( ) , atom ( ) ] ) , tuple ( [ float ( ) , atom ( ) ] ) )
1279+ |> union ( tuple ( [ pid ( ) , pid ( ) , port ( ) ] ) )
1280+ |> union ( tuple ( [ pid ( ) , pid ( ) , atom ( ) ] ) )
1281+ |> to_quoted_string ( ) ==
1282+ "{float() or integer(), atom()} or {pid(), pid(), atom() or port()}"
1283+
1284+ assert union ( open_tuple ( [ integer ( ) ] ) , open_tuple ( [ float ( ) ] ) ) |> to_quoted_string ( ) ==
1285+ "{float() or integer(), ...}"
1286+
1287+ # {:ok, {term(), integer()}} or {:ok, {term(), float()}} or {:exit, :kill} or {:exit, :timeout}
1288+ assert tuple ( [ atom ( [ :ok ] ) , tuple ( [ term ( ) , empty_list ( ) ] ) ] )
1289+ |> union ( tuple ( [ atom ( [ :ok ] ) , tuple ( [ term ( ) , open_map ( ) ] ) ] ) )
1290+ |> union ( tuple ( [ atom ( [ :exit ] ) , atom ( [ :kill ] ) ] ) )
1291+ |> union ( tuple ( [ atom ( [ :exit ] ) , atom ( [ :timeout ] ) ] ) )
1292+ |> to_quoted_string ( ) ==
1293+ "{:exit, :kill or :timeout} or {:ok, {term(), %{...} or empty_list()}}"
1294+
1295+ # Detection of duplicates
1296+ assert tuple ( [ atom ( [ :ok ] ) , term ( ) ] )
1297+ |> union ( tuple ( [ atom ( [ :ok ] ) , term ( ) ] ) )
1298+ |> to_quoted_string ( ) == "{:ok, term()}"
1299+
1300+ assert tuple ( [ closed_map ( a: integer ( ) , b: atom ( ) ) , open_map ( ) ] )
1301+ |> union ( tuple ( [ closed_map ( a: integer ( ) , b: atom ( ) ) , open_map ( ) ] ) )
1302+ |> to_quoted_string ( ) ==
1303+ "{%{a: integer(), b: atom()}, %{...}}"
1304+
1305+ # Nested fusion
1306+ assert tuple ( [ closed_map ( a: integer ( ) , b: atom ( ) ) , open_map ( ) ] )
1307+ |> union ( tuple ( [ closed_map ( a: float ( ) , b: atom ( ) ) , open_map ( ) ] ) )
1308+ |> to_quoted_string ( ) ==
1309+ "{%{a: float() or integer(), b: atom()}, %{...}}"
1310+
1311+ # Complex simplification of map/tuple combinations. Initial type is:
1312+ # ```
1313+ # dynamic(
1314+ # :error or
1315+ # ({%Decimal{coef: :inf, exp: integer(), sign: integer()}, binary()} or
1316+ # {%Decimal{coef: :NaN, exp: integer(), sign: integer()}, binary()} or
1317+ # {%Decimal{coef: integer(), exp: integer(), sign: integer()}, term()} or
1318+ # {%Decimal{coef: :inf, exp: integer(), sign: integer()} or
1319+ # %Decimal{coef: :NaN, exp: integer(), sign: integer()} or
1320+ # %Decimal{coef: integer(), exp: integer(), sign: integer()}, term()})
1321+ # )
1322+ # ```
1323+ decimal_inf =
1324+ closed_map (
1325+ __struct__: atom ( [ Decimal ] ) ,
1326+ coef: atom ( [ :inf ] ) ,
1327+ exp: integer ( ) ,
1328+ sign: integer ( )
1329+ )
1330+
1331+ decimal_nan =
1332+ closed_map (
1333+ __struct__: atom ( [ Decimal ] ) ,
1334+ coef: atom ( [ :NaN ] ) ,
1335+ exp: integer ( ) ,
1336+ sign: integer ( )
1337+ )
1338+
1339+ decimal_int =
1340+ closed_map ( __struct__: atom ( [ Decimal ] ) , coef: integer ( ) , exp: integer ( ) , sign: integer ( ) )
1341+
1342+ assert atom ( [ :error ] )
1343+ |> union (
1344+ tuple ( [ decimal_inf , binary ( ) ] )
1345+ |> union (
1346+ tuple ( [ decimal_nan , binary ( ) ] )
1347+ |> union (
1348+ tuple ( [ decimal_int , term ( ) ] )
1349+ |> union ( tuple ( [ union ( decimal_inf , union ( decimal_nan , decimal_int ) ) , term ( ) ] ) )
1350+ )
1351+ )
1352+ )
1353+ |> dynamic ( )
1354+ |> to_quoted_string ( ) ==
1355+ "dynamic(\n :error or\n ({%Decimal{coef: integer() or (:NaN or :inf), exp: integer(), sign: integer()}, term()} or\n {%Decimal{coef: :NaN or :inf, exp: integer(), sign: integer()}, binary()})\n )"
12711356 end
12721357
12731358 test "map" do
@@ -1311,6 +1396,50 @@ defmodule Module.Types.DescrTest do
13111396 assert difference ( open_map ( a: number ( ) , b: atom ( ) ) , open_map ( a: integer ( ) ) )
13121397 |> to_quoted_string ( ) == "%{..., a: float(), b: atom()}"
13131398
1399+ # Basic map fusion
1400+ assert union ( closed_map ( a: integer ( ) ) , closed_map ( a: integer ( ) ) ) |> to_quoted_string ( ) ==
1401+ "%{a: integer()}"
1402+
1403+ assert union ( closed_map ( a: integer ( ) ) , closed_map ( a: float ( ) ) ) |> to_quoted_string ( ) ==
1404+ "%{a: float() or integer()}"
1405+
1406+ # Nested fusion
1407+ assert union ( closed_map ( a: integer ( ) , b: atom ( ) ) , closed_map ( a: float ( ) , b: atom ( ) ) )
1408+ |> union ( closed_map ( x: pid ( ) , y: pid ( ) , z: port ( ) ) )
1409+ |> union ( closed_map ( x: pid ( ) , y: pid ( ) , z: atom ( ) ) )
1410+ |> to_quoted_string ( ) ==
1411+ "%{a: float() or integer(), b: atom()} or %{x: pid(), y: pid(), z: atom() or port()}"
1412+
1413+ # Open map fusion
1414+ assert union ( open_map ( a: integer ( ) ) , open_map ( a: float ( ) ) ) |> to_quoted_string ( ) ==
1415+ "%{..., a: float() or integer()}"
1416+
1417+ # Fusing complex nested maps with unions
1418+ assert closed_map ( status: atom ( [ :ok ] ) , data: closed_map ( value: term ( ) , count: empty_list ( ) ) )
1419+ |> union (
1420+ closed_map ( status: atom ( [ :ok ] ) , data: closed_map ( value: term ( ) , count: open_map ( ) ) )
1421+ )
1422+ |> union ( closed_map ( status: atom ( [ :error ] ) , reason: atom ( [ :timeout ] ) ) )
1423+ |> union ( closed_map ( status: atom ( [ :error ] ) , reason: atom ( [ :crash ] ) ) )
1424+ |> to_quoted_string ( ) ==
1425+ "%{data: %{count: %{...} or empty_list(), value: term()}, status: :ok} or\n %{reason: :crash or :timeout, status: :error}"
1426+
1427+ # Difference and union tests
1428+ assert closed_map ( status: atom ( [ :ok ] ) , value: term ( ) )
1429+ |> difference ( closed_map ( status: atom ( [ :ok ] ) , value: float ( ) ) )
1430+ |> union (
1431+ closed_map ( status: atom ( [ :ok ] ) , value: term ( ) )
1432+ |> difference ( closed_map ( status: atom ( [ :ok ] ) , value: integer ( ) ) )
1433+ )
1434+ |> to_quoted_string ( ) ==
1435+ "%{status: :ok, value: term()}"
1436+
1437+ # Nested map fusion
1438+ assert closed_map ( data: closed_map ( x: integer ( ) , y: atom ( ) ) , meta: open_map ( ) )
1439+ |> union ( closed_map ( data: closed_map ( x: float ( ) , y: atom ( ) ) , meta: open_map ( ) ) )
1440+ |> to_quoted_string ( ) ==
1441+ "%{data: %{x: float() or integer(), y: atom()}, meta: %{...}}"
1442+
13141443 # Test complex combinations
13151444 assert intersection ( open_map ( a: number ( ) , b: atom ( ) ) , open_map ( a: integer ( ) , c: boolean ( ) ) )
13161445 |> union ( difference ( open_map ( x: atom ( ) ) , open_map ( x: boolean ( ) ) ) )
0 commit comments