@@ -243,24 +243,41 @@ defmodule Kernel.Typespec do
243
243
Converts a spec clause back to Elixir AST.
244
244
"""
245
245
def spec_to_ast ( name , { :type , line , :fun , [ { :type , _ , :product , args } , result ] } ) do
246
- args = lc arg inlist args , do: typespec_to_ast ( arg )
247
- { ::: , [ line: line ] , [ { name , [ line: line ] , args } , typespec_to_ast ( result ) ] }
246
+ ast_args = lc arg inlist args , do: typespec_to_ast ( arg )
247
+ ast = { ::: , [ line: line ] , [ { name , [ line: line ] , ast_args } , typespec_to_ast ( result ) ] }
248
+
249
+ vars = args ++ [ result ]
250
+ |> Enum . flat_map ( & collect_vars / 1 )
251
+ |> Enum . uniq
252
+ |> Enum . map ( & { & 1 , { :var , [ line: line ] , nil } } )
253
+
254
+ unless vars == [ ] do
255
+ ast = { :when , [ line: line ] , [ ast , vars ] }
256
+ end
257
+
258
+ ast
248
259
end
249
260
250
261
def spec_to_ast ( name , { :type , line , :fun , [ ] } ) do
251
262
{ ::: , [ line: line ] , [ { name , [ line: line ] , [ ] } , quote ( do: term ) ] }
252
263
end
253
264
254
265
def spec_to_ast ( name , { :type , line , :bounded_fun , [ { :type , _ , :fun , [ { :type , _ , :product , args } , result ] } , constraints ] } ) do
255
- [ h | t ] =
256
- lc { :type , line , :constraint , [ { :atom , _ , :is_subtype } , [ var , type ] ] } in list constraints do
257
- { :is_subtype , [ line: line ] , [ typespec_to_ast ( var ) , typespec_to_ast ( type ) ] }
266
+ guards =
267
+ lc { :type , _ , :constraint , [ { :atom , _ , :is_subtype } , [ { : var, _ , var } , type ] ] } in list constraints do
268
+ { var , typespec_to_ast ( type ) }
258
269
end
259
270
260
- args = lc arg inlist args , do: typespec_to_ast ( arg )
261
- guards = Enum . reduce t , h , fn ( x , acc ) -> { :and , line , [ acc , x ] } end
271
+ ast_args = lc arg inlist args , do: typespec_to_ast ( arg )
272
+
273
+ vars = args ++ [ result ]
274
+ |> Enum . flat_map ( & collect_vars / 1 )
275
+ |> Enum . uniq
276
+
277
+ vars = vars -- Keyword . keys ( guards )
278
+ |> Enum . map ( & { & 1 , { :var , [ line: line ] , nil } } )
262
279
263
- { :when , [ line: line ] , [ { ::: , [ line: line ] , [ { name , [ line: line ] , args } , typespec_to_ast ( result ) ] } , guards ] }
280
+ { :when , [ line: line ] , [ { ::: , [ line: line ] , [ { name , [ line: line ] , ast_args } , typespec_to_ast ( result ) ] } , guards ++ vars ] }
264
281
end
265
282
266
283
@ doc """
@@ -418,28 +435,27 @@ defmodule Kernel.Typespec do
418
435
end
419
436
420
437
@ doc false
421
- def defspec ( type , { :when , _ , [ { ::: , _ , [ { name , meta , args } , return ] } , constraints_guard ] } , caller ) do
438
+ def defspec ( type , { :when , meta2 , [ { ::: , _ , [ { name , meta , args } , return ] } , guard ] } , caller ) do
422
439
if is_atom ( args ) , do: args = [ ]
423
- vars = guard_to_vars ( constraints_guard )
424
- constraints = guard_to_constraints ( constraints_guard , vars , caller )
440
+
441
+ unless Keyword . keyword? ( guard ) do
442
+ guard = Macro . to_string ( guard )
443
+ compile_error caller , "invalid guard in function type specification `#{ guard } `"
444
+ end
445
+
446
+ vars = Keyword . keys ( guard )
447
+ constraints = guard_to_constraints ( guard , vars , meta2 , caller )
448
+
425
449
spec = { :type , line ( meta ) , :fun , fn_args ( meta , args , return , vars , caller ) }
426
450
if constraints != [ ] do
427
451
spec = { :type , line ( meta ) , :bounded_fun , [ spec , constraints ] }
428
452
end
453
+
429
454
code = { { name , Kernel . length ( args ) } , spec }
430
455
Module . compile_typespec ( caller . module , type , code )
431
456
code
432
457
end
433
458
434
- def defspec ( type , { :when , _ , [ fun , { ::: , _ , [ guards , return ] } ] } = spec , caller ) do
435
- new_spec = { :when , [ ] , [ { ::: , [ ] , [ fun , return ] } , guards ] }
436
- IO . write "typespec format is deprecated `#{ Macro . to_string ( spec ) } `\n " <>
437
- "new format is: `#{ Macro . to_string ( new_spec ) } `\n " <>
438
- Exception . format_stacktrace
439
-
440
- defspec ( type , new_spec , caller )
441
- end
442
-
443
459
def defspec ( type , { ::: , _ , [ { name , meta , args } , return ] } , caller ) do
444
460
if is_atom ( args ) , do: args = [ ]
445
461
spec = { :type , line ( meta ) , :fun , fn_args ( meta , args , return , [ ] , caller ) }
@@ -453,40 +469,48 @@ defmodule Kernel.Typespec do
453
469
compile_error caller , "invalid function type specification `#{ spec } `"
454
470
end
455
471
456
- defp guard_to_vars ( { :is_subtype , _ , [ { name , _ , _ } , _ ] } ) do
457
- [ name ]
472
+ defp guard_to_constraints ( guard , vars , meta , caller ) do
473
+ line = line ( meta )
474
+
475
+ Enum . reduce ( guard , [ ] , fn
476
+ { _name , { :var , _ , context } } , acc when is_atom ( context ) ->
477
+ acc
478
+ { name , type } , acc ->
479
+ constraint = [ { :atom , line , :is_subtype } , [ { :var , line , name } , typespec ( type , vars , caller ) ] ]
480
+ type = { :type , line , :constraint , constraint }
481
+ [ type | acc ]
482
+ end ) |> Enum . reverse
458
483
end
459
484
460
- defp guard_to_vars ( { :is_var , _ , [ { name , _ , _ } ] } ) do
461
- [ name ]
485
+ ## To AST conversion
486
+
487
+ defp collect_vars ( { :ann_type , _line , args } ) do
488
+ Enum . flat_map ( args , & collect_vars / 1 )
462
489
end
463
490
464
- defp guard_to_vars ( { :and , _ , [ left , right ] } ) do
465
- guard_to_vars ( left ) ++ guard_to_vars ( right )
491
+ defp collect_vars ( { :type , _line , _kind , args } ) do
492
+ Enum . flat_map ( args , & collect_vars / 1 )
466
493
end
467
494
468
- defp guard_to_constraints ( { :is_subtype , meta , [ { name , _ , context } , type ] } , vars , caller )
469
- when is_atom ( name ) and is_atom ( context ) do
470
- line = line ( meta )
471
- contraints = [ { :atom , line , :is_subtype } , [ { :var , line , name } , typespec ( type , vars , caller ) ] ]
472
- [ { :type , line , :constraint , contraints } ]
495
+ defp collect_vars ( { :remote_type , _line , args } ) do
496
+ Enum . flat_map ( args , & collect_vars / 1 )
473
497
end
474
498
475
- defp guard_to_constraints ( { :is_var , _ , [ { name , _ , context } ] } , _ , _ )
476
- when is_atom ( name ) and is_atom ( context ) do
477
- [ ]
499
+ defp collect_vars ( { :typed_record_field , _line , type } ) do
500
+ collect_vars ( type )
478
501
end
479
502
480
- defp guard_to_constraints ( { :and , _ , [ left , right ] } , vars , caller ) do
481
- guard_to_constraints ( left , vars , caller ) ++ guard_to_constraints ( right , vars , caller )
503
+ defp collect_vars ( { :paren_type , _line , [ type ] } ) do
504
+ collect_vars ( type )
482
505
end
483
506
484
- defp guard_to_constraints ( other , _vars , caller ) do
485
- guard = Macro . to_string ( other )
486
- compile_error caller , "invalid guard in function type specification `#{ guard } `"
507
+ defp collect_vars ( { :var , _line , var } ) do
508
+ [ erl_to_ex_var ( var ) ]
487
509
end
488
510
489
- ## To AST conversion
511
+ defp collect_vars ( _ ) do
512
+ [ ]
513
+ end
490
514
491
515
defp typespec_to_ast ( { :type , line , :tuple , :any } ) do
492
516
typespec_to_ast ( { :type , line , :tuple , [ ] } )
@@ -549,14 +573,7 @@ defmodule Kernel.Typespec do
549
573
end
550
574
551
575
defp typespec_to_ast ( { :var , line , var } ) do
552
- var =
553
- case atom_to_binary ( var ) do
554
- << "_" , c :: [ binary , size ( 1 ) ] , rest :: binary >> ->
555
- binary_to_atom ( "_#{ String . downcase ( c ) } #{ rest } " )
556
- << c :: [ binary , size ( 1 ) ] , rest :: binary >> ->
557
- binary_to_atom ( "#{ String . downcase ( c ) } #{ rest } " )
558
- end
559
- { var , line , nil }
576
+ { erl_to_ex_var ( var ) , line , nil }
560
577
end
561
578
562
579
# Special shortcut(s)
@@ -598,6 +615,15 @@ defmodule Kernel.Typespec do
598
615
599
616
defp typespec_to_ast ( other ) , do: other
600
617
618
+ defp erl_to_ex_var ( var ) do
619
+ case atom_to_binary ( var ) do
620
+ << "_" , c :: [ binary , size ( 1 ) ] , rest :: binary >> ->
621
+ binary_to_atom ( "_#{ String . downcase ( c ) } #{ rest } " )
622
+ << c :: [ binary , size ( 1 ) ] , rest :: binary >> ->
623
+ binary_to_atom ( "#{ String . downcase ( c ) } #{ rest } " )
624
+ end
625
+ end
626
+
601
627
## From AST conversion
602
628
603
629
defp line ( meta ) do
0 commit comments