@@ -467,32 +467,32 @@ defmodule Module.Types.Of do
467467 # TODO: Type check the fields match the struct
468468 def struct_instance ( struct , args , expected , meta , stack , context , of_fun )
469469 when is_atom ( struct ) do
470- { info , context } = struct_info ( struct , :expr , meta , stack , context )
470+ { info , context } = struct_info ( struct , :expr , meta , stack , context , true )
471471
472472 if is_nil ( info ) do
473- raise "expected #{ inspect ( struct ) } to return struct metadata, but got none"
473+ { dynamic ( ) , context }
474+ else
475+ # The compiler has already checked the keys are atoms and which ones are required.
476+ { args_types , context } =
477+ Enum . map_reduce ( args , context , fn { key , value } , context when is_atom ( key ) ->
478+ value_type =
479+ case map_fetch_key ( expected , key ) do
480+ { _ , expected_value_type } -> expected_value_type
481+ _ -> term ( )
482+ end
483+
484+ { type , context } = of_fun . ( value , value_type , stack , context )
485+ { { key , type } , context }
486+ end )
487+
488+ { closed_map ( [ { :__struct__ , atom ( [ struct ] ) } | args_types ] ) , context }
474489 end
475-
476- # The compiler has already checked the keys are atoms and which ones are required.
477- { args_types , context } =
478- Enum . map_reduce ( args , context , fn { key , value } , context when is_atom ( key ) ->
479- value_type =
480- case map_fetch_key ( expected , key ) do
481- { _ , expected_value_type } -> expected_value_type
482- _ -> term ( )
483- end
484-
485- { type , context } = of_fun . ( value , value_type , stack , context )
486- { { key , type } , context }
487- end )
488-
489- { closed_map ( [ { :__struct__ , atom ( [ struct ] ) } | args_types ] ) , context }
490490 end
491491
492492 @ doc """
493493 Returns `__info__(:struct)` information about a struct.
494494 """
495- def struct_info ( struct , kind , meta , stack , context ) do
495+ def struct_info ( struct , kind , meta , stack , context , must_exist? \\ false ) do
496496 case stack . no_warn_undefined do
497497 % Macro.Env { } = env ->
498498 case :elixir_map . maybe_load_struct_info ( meta , struct , :soft , env ) do
@@ -511,7 +511,7 @@ defmodule Module.Types.Of do
511511
512512 { info , context }
513513 else
514- error = { :unknown_struct , kind , struct }
514+ error = { :unknown_struct , kind , struct , must_exist? }
515515 { nil , error ( error , meta , stack , context ) }
516516 end
517517 end
@@ -848,19 +848,26 @@ defmodule Module.Types.Of do
848848 }
849849 end
850850
851- def format_diagnostic ( { :unknown_struct , kind , module } ) do
852- message =
853- if Code . ensure_loaded? ( module ) do
854- "struct #{ inspect ( module ) } is undefined (there is such module but it does not define a struct)"
855- else
856- "struct #{ inspect ( module ) } is undefined " <>
857- "(module #{ inspect ( module ) } is not available or is yet to be defined)"
851+ def format_diagnostic ( { :unknown_struct , kind , module , must_exist? } ) do
852+ detail =
853+ case { Code . ensure_loaded? ( module ) , must_exist? } do
854+ { true , false } ->
855+ "there is such module but it does not define a struct"
856+
857+ { false , false } ->
858+ "module #{ inspect ( module ) } is not available or is yet to be defined"
859+
860+ { true , true } ->
861+ "the module may have been redefined as it no longer defines a struct"
862+
863+ { false , true } ->
864+ "the module was also only available but may have been removed during compilation"
858865 end
859866
860867 % {
861- message: message ,
868+ message: "struct #{ inspect ( module ) } is undefined ( #{ detail } )" ,
862869 group: true ,
863- severity: if ( kind == :pattern , do: :error , else: :warning )
870+ severity: if ( kind == :pattern or must_exist? , do: :error , else: :warning )
864871 }
865872 end
866873
0 commit comments