@@ -19,7 +19,7 @@ defmodule Kernel.Typespec do
19
19
20
20
Most of the built-in types provided in Erlang (for example, `pid()`) are
21
21
expressed the same way: `pid()` or simply `pid`. Parametrized types are also
22
- supported (`list(integer`) and so are remote types (`Enum.t`).
22
+ supported (`list(integer) `) and so are remote types (`Enum.t`).
23
23
24
24
Integers and atom literals are allowed as types (ex. `1`, `:atom` or
25
25
`false`). All other types are built of unions of predefined types. Certain
@@ -353,19 +353,21 @@ defmodule Kernel.Typespec do
353
353
Converts a spec clause back to Elixir AST.
354
354
"""
355
355
def spec_to_ast ( name , { :type , line , :fun , [ { :type , _ , :product , args } , result ] } ) do
356
- ast_args = lc arg inlist args , do: typespec_to_ast ( arg )
357
- ast = { ::: , [ line: line ] , [ { name , [ line: line ] , ast_args } , typespec_to_ast ( result ) ] }
356
+ meta = [ line: line ]
357
+ body = { name , meta , Enum . map ( args , & typespec_to_ast / 1 ) }
358
358
359
359
vars = args ++ [ result ]
360
360
|> Enum . flat_map ( & collect_vars / 1 )
361
361
|> Enum . uniq
362
- |> Enum . map ( & { & 1 , { :var , [ line: line ] , nil } } )
362
+ |> Enum . map ( & { & 1 , { :var , meta , nil } } )
363
363
364
- unless vars == [ ] do
365
- ast = { :when , [ line: line ] , [ ast , vars ] }
364
+ result = if vars == [ ] do
365
+ typespec_to_ast ( result )
366
+ else
367
+ { :when , meta , [ typespec_to_ast ( result ) , vars ] }
366
368
end
367
369
368
- ast
370
+ { ::: , meta , [ body , result ] }
369
371
end
370
372
371
373
def spec_to_ast ( name , { :type , line , :fun , [ ] } ) do
@@ -378,16 +380,20 @@ defmodule Kernel.Typespec do
378
380
{ var , typespec_to_ast ( type ) }
379
381
end
380
382
381
- ast_args = lc arg inlist args , do: typespec_to_ast ( arg )
383
+ meta = [ line: line ]
382
384
383
385
vars = args ++ [ result ]
384
386
|> Enum . flat_map ( & collect_vars / 1 )
385
387
|> Enum . uniq
388
+ |> Kernel . -- ( Keyword . keys ( guards ) )
389
+ |> Enum . map ( & { & 1 , { :var , meta , nil } } )
386
390
387
- vars = vars -- Keyword . keys ( guards )
388
- |> Enum . map ( & { & 1 , { :var , [ line: line ] , nil } } )
391
+ args = lc arg inlist args , do: typespec_to_ast ( arg )
389
392
390
- { :when , [ line: line ] , [ { ::: , [ line: line ] , [ { name , [ line: line ] , ast_args } , typespec_to_ast ( result ) ] } , guards ++ vars ] }
393
+ { ::: , meta , [
394
+ { name , [ line: line ] , args } ,
395
+ { :when , meta , [ typespec_to_ast ( result ) , guards ++ vars ] }
396
+ ] }
391
397
end
392
398
393
399
@ doc """
@@ -473,7 +479,7 @@ defmodule Kernel.Typespec do
473
479
The result is returned as a list of tuples where the first
474
480
element is spec name and arity and the second is the spec.
475
481
476
- The module must have a corresponding beam file
482
+ The module must have a corresponding beam file
477
483
which can be located by the runtime system.
478
484
"""
479
485
@ spec beam_callbacks ( module | binary ) :: [ tuple ] | nil
@@ -524,7 +530,7 @@ defmodule Kernel.Typespec do
524
530
525
531
def deftype ( _kind , other , caller ) do
526
532
type_spec = Macro . to_string ( other )
527
- compile_error caller , "invalid type specification ` #{ type_spec } ` "
533
+ compile_error caller , "invalid type specification: #{ type_spec } "
528
534
end
529
535
530
536
defp do_deftype ( kind , { name , _ , args } , definition , caller ) do
@@ -545,16 +551,17 @@ defmodule Kernel.Typespec do
545
551
end
546
552
547
553
@ doc false
548
- def defspec ( type , { :when , meta2 , [ { ::: , _ , [ { name , meta , args } , return ] } , guard ] } , caller ) do
554
+ def defspec ( type , { ::: , meta , [ { name , _ , args } , return_and_guard ] } , caller ) do
549
555
if is_atom ( args ) , do: args = [ ]
556
+ { return , guard } = split_return_and_guard ( return_and_guard )
550
557
551
558
unless Keyword . keyword? ( guard ) do
552
559
guard = Macro . to_string ( guard )
553
- compile_error caller , "invalid guard in function type specification ` #{ guard } ` "
560
+ compile_error caller , "expected keywords as guard in function type specification, got: #{ guard } "
554
561
end
555
562
556
563
vars = Keyword . keys ( guard )
557
- constraints = guard_to_constraints ( guard , vars , meta2 , caller )
564
+ constraints = guard_to_constraints ( guard , vars , meta , caller )
558
565
559
566
spec = { :type , line ( meta ) , :fun , fn_args ( meta , args , return , vars , caller ) }
560
567
if constraints != [ ] do
@@ -566,17 +573,22 @@ defmodule Kernel.Typespec do
566
573
code
567
574
end
568
575
569
- def defspec ( type , { ::: , _ , [ { name , meta , args } , return ] } , caller ) do
570
- if is_atom ( args ) , do: args = [ ]
571
- spec = { :type , line ( meta ) , :fun , fn_args ( meta , args , return , [ ] , caller ) }
572
- code = { { name , Kernel . length ( args ) } , spec }
573
- Module . compile_typespec ( caller . module , type , code )
574
- code
575
- end
576
-
577
576
def defspec ( _type , other , caller ) do
578
577
spec = Macro . to_string ( other )
579
- compile_error caller , "invalid function type specification `#{ spec } `"
578
+ compile_error caller , "invalid function type specification: #{ spec } "
579
+ end
580
+
581
+ defp split_return_and_guard ( { :when , _ , [ return , guard ] } ) do
582
+ { return , guard }
583
+ end
584
+
585
+ defp split_return_and_guard ( { :| , meta , [ left , right ] } ) do
586
+ { return , guard } = split_return_and_guard ( right )
587
+ { { :| , meta , [ left , return ] } , guard }
588
+ end
589
+
590
+ defp split_return_and_guard ( other ) do
591
+ { other , [ ] }
580
592
end
581
593
582
594
defp guard_to_constraints ( guard , vars , meta , caller ) do
@@ -656,8 +668,7 @@ defmodule Kernel.Typespec do
656
668
657
669
defp typespec_to_ast ( { :type , line , :union , args } ) do
658
670
args = lc arg inlist args , do: typespec_to_ast ( arg )
659
- Enum . reduce tl ( args ) , hd ( args ) ,
660
- fn ( arg , expr ) -> { :| , [ line: line ] , [ expr , arg ] } end
671
+ Enum . reduce Enum . reverse ( args ) , fn ( arg , expr ) -> { :| , [ line: line ] , [ arg , expr ] } end
661
672
end
662
673
663
674
defp typespec_to_ast ( { :type , line , :fun , [ { :type , _ , :product , args } , result ] } ) do
@@ -749,7 +760,7 @@ defmodule Kernel.Typespec do
749
760
750
761
# Handle unions
751
762
defp typespec ( { :| , meta , [ _ , _ ] } = exprs , vars , caller ) do
752
- exprs = Enum . reverse ( collect_union ( exprs ) )
763
+ exprs = collect_union ( exprs )
753
764
union = lc e inlist exprs , do: typespec ( e , vars , caller )
754
765
{ :type , line ( meta ) , :union , union }
755
766
end
@@ -886,9 +897,10 @@ defmodule Kernel.Typespec do
886
897
typespec ( { :nonempty_list , [ ] , [ spec ] } , vars , caller )
887
898
end
888
899
889
- defp typespec ( [ h | t ] = l , vars , caller ) do
890
- union = Enum . reduce ( t , validate_kw ( h , l , caller ) , fn ( x , acc ) ->
891
- { :| , [ ] , [ acc , validate_kw ( x , l , caller ) ] }
900
+ defp typespec ( list , vars , caller ) do
901
+ [ h | t ] = Enum . reverse ( list )
902
+ union = Enum . reduce ( t , validate_kw ( h , list , caller ) , fn ( x , acc ) ->
903
+ { :| , [ ] , [ validate_kw ( x , list , caller ) , acc ] }
892
904
end )
893
905
typespec ( { :list , [ ] , [ union ] } , vars , caller )
894
906
end
@@ -904,12 +916,12 @@ defmodule Kernel.Typespec do
904
916
{ :remote_type , line ( meta ) , [ remote , name , arguments ] }
905
917
end
906
918
907
- defp collect_union ( { :| , _ , [ a , b ] } ) , do: [ b | collect_union ( a ) ]
919
+ defp collect_union ( { :| , _ , [ a , b ] } ) , do: [ a | collect_union ( b ) ]
908
920
defp collect_union ( v ) , do: [ v ]
909
921
910
922
defp validate_kw ( { key , _ } = t , _ , _caller ) when is_atom ( key ) , do: t
911
923
defp validate_kw ( _ , original , caller ) do
912
- compile_error ( caller , "unexpected list ` #{ Macro . to_string original } ` in typespec " )
924
+ compile_error ( caller , "unexpected list in typespec: #{ Macro . to_string original } " )
913
925
end
914
926
915
927
defp fn_args ( meta , args , return , vars , caller ) do
0 commit comments