@@ -31,6 +31,7 @@ defmodule Module.Types.Apply do
3131 end
3232
3333 fas = list ( tuple ( [ atom ( ) , integer ( ) ] ) )
34+ struct_info = list ( closed_map ( default: if_set ( term ( ) ) , field: atom ( ) ) )
3435
3536 shared_info = [
3637 attributes: list ( tuple ( [ atom ( ) , list ( term ( ) ) ] ) ) ,
@@ -40,31 +41,32 @@ defmodule Module.Types.Apply do
4041 module: atom ( )
4142 ]
4243
44+ module_info = [ functions: fas , nifs: fas ] ++ shared_info
45+
46+ elixir_info =
47+ [
48+ deprecated: list ( tuple ( [ tuple ( [ atom ( ) , integer ( ) ] ) , binary ( ) ] ) ) ,
49+ exports_md5: binary ( ) ,
50+ functions: fas ,
51+ macros: fas ,
52+ struct: struct_info |> union ( atom ( [ nil ] ) )
53+ ] ++ shared_info
54+
4355 infos =
44- % {
45- behaviour_info: [
46- callbacks: fas ,
47- optional_callbacks: fas
48- ] ,
49- module_info: [ functions: fas , nifs: fas ] ++ shared_info ,
50- __info__:
51- [
52- deprecated: list ( tuple ( [ tuple ( [ atom ( ) , integer ( ) ] ) , binary ( ) ] ) ) ,
53- exports_md5: binary ( ) ,
54- functions: fas ,
55- macros: fas ,
56- struct:
57- list ( closed_map ( default: if_set ( term ( ) ) , field: atom ( ) ) )
58- |> union ( atom ( [ nil ] ) )
59- ] ++ shared_info ,
60- # TODO: Move this to a type signature in the long term
61- __protocol__: [
62- module: atom ( ) ,
63- functions: fas ,
64- consolidated?: boolean ( ) ,
65- impls: union ( atom ( [ :not_consolidated ] ) , tuple ( [ atom ( [ :consolidated ] ) , list ( atom ( ) ) ] ) )
66- ]
67- }
56+ [
57+ # We have a special key that tracks if something is a struct or not
58+ { { :__info__ , true } , Keyword . put ( elixir_info , :struct , struct_info ) } ,
59+ { { :__info__ , false } , Keyword . put ( elixir_info , :struct , atom ( [ nil ] ) ) } ,
60+ { :__info__ , elixir_info } ,
61+ { :behaviour_info , callbacks: fas , optional_callbacks: fas } ,
62+ { :module_info , module_info } ,
63+ # TODO: Move this to a type signature declared by `defprotocol` (or perhaps part of the behaviour)
64+ { :__protocol__ ,
65+ module: atom ( ) ,
66+ functions: fas ,
67+ consolidated?: boolean ( ) ,
68+ impls: union ( atom ( [ :not_consolidated ] ) , tuple ( [ atom ( [ :consolidated ] ) , list ( atom ( ) ) ] ) ) }
69+ ]
6870
6971 for { name , clauses } <- infos do
7072 domain = atom ( Keyword . keys ( clauses ) )
@@ -76,7 +78,7 @@ defmodule Module.Types.Apply do
7678 end
7779
7880 defp signature ( :module_info , 0 ) do
79- { :strong , nil , [ { [ ] , unquote ( Macro . escape ( kw . ( infos . module_info ) ) ) } ] }
81+ { :strong , nil , [ { [ ] , unquote ( Macro . escape ( kw . ( module_info ) ) ) } ] }
8082 end
8183
8284 defp signature ( _ , _ ) , do: :none
@@ -512,10 +514,20 @@ defmodule Module.Types.Apply do
512514 info = if info == :none , do: signature ( fun , arity ) , else: info
513515 { info , check_deprecated ( mode , module , fun , arity , reason , meta , stack , context ) }
514516
515- { :error , type } ->
517+ { :badfunction , :elixir } when fun == :__info__ and arity == 1 ->
518+ key =
519+ cond do
520+ not Code . ensure_loaded? ( module ) -> :__info__
521+ module . __info__ ( :struct ) != nil -> { :__info__ , true }
522+ true -> { :__info__ , false }
523+ end
524+
525+ { signature ( key , arity ) , context }
526+
527+ error ->
516528 context =
517529 if warn_undefined? ( module , fun , arity , stack ) do
518- warn ( __MODULE__ , { :undefined , type , module , fun , arity } , meta , stack , context )
530+ warn ( __MODULE__ , { :undefined , error , module , fun , arity } , meta , stack , context )
519531 else
520532 context
521533 end
@@ -525,14 +537,6 @@ defmodule Module.Types.Apply do
525537 end
526538 end
527539
528- defp check_deprecated ( :elixir , module , fun , arity , reason , meta , stack , context ) do
529- if reason do
530- warn ( __MODULE__ , { :deprecated , module , fun , arity , reason } , meta , stack , context )
531- else
532- context
533- end
534- end
535-
536540 defp check_deprecated ( :erlang , module , fun , arity , _reason , meta , stack , context ) do
537541 case :otp_internal . obsolete ( module , fun , arity ) do
538542 { :deprecated , string } when is_list ( string ) ->
@@ -549,6 +553,14 @@ defmodule Module.Types.Apply do
549553 end
550554 end
551555
556+ defp check_deprecated ( _ , module , fun , arity , reason , meta , stack , context ) do
557+ if reason do
558+ warn ( __MODULE__ , { :deprecated , module , fun , arity , reason } , meta , stack , context )
559+ else
560+ context
561+ end
562+ end
563+
552564 defp builtin_module? ( module ) do
553565 is_map_key ( builtin_modules ( ) , module )
554566 end
@@ -952,7 +964,7 @@ defmodule Module.Types.Apply do
952964 }
953965 end
954966
955- def format_diagnostic ( { :undefined , :module , module , fun , arity } ) do
967+ def format_diagnostic ( { :undefined , :badmodule , module , fun , arity } ) do
956968 top =
957969 if fun == :__struct__ and arity == 0 do
958970 "struct #{ inspect ( module ) } "
@@ -973,15 +985,15 @@ defmodule Module.Types.Apply do
973985 }
974986 end
975987
976- def format_diagnostic ( { :undefined , :function , module , :__struct__ , 0 } ) do
988+ def format_diagnostic ( { :undefined , { :badfunction , _ } , module , :__struct__ , 0 } ) do
977989 % {
978990 message:
979991 "struct #{ inspect ( module ) } is undefined (there is such module but it does not define a struct)" ,
980992 group: true
981993 }
982994 end
983995
984- def format_diagnostic ( { :undefined , :function , module , fun , arity } ) do
996+ def format_diagnostic ( { :undefined , { :badfunction , _ } , module , fun , arity } ) do
985997 % {
986998 message:
987999 IO . iodata_to_binary ( [
0 commit comments