diff --git a/lib/type_check.ex b/lib/type_check.ex index 68a4b55..acd6865 100644 --- a/lib/type_check.ex +++ b/lib/type_check.ex @@ -177,7 +177,7 @@ defmodule TypeCheck do case unquote(check) do {:ok, bindings, altered_value} -> {:ok, altered_value} {:error, problem} -> - exception = TypeCheck.TypeError.exception({problem, unquote(Macro.Env.location(__CALLER__))}) + exception = TypeCheck.TypeError.exception({unquote(TypeCheck.Internals.Escaper.escape(type)), {problem, unquote(Macro.Env.location(__CALLER__))}}) {:error, exception} end end @@ -230,7 +230,11 @@ defmodule TypeCheck do res = quote generated: true, location: :keep do case unquote(check) do {:ok, _bindings, altered_value} -> altered_value - {:error, other} -> raise TypeCheck.TypeError, other + {:error, other} -> + type = unquote(TypeCheck.Internals.Escaper.escape(type)) + IO.inspect(type, label: :conforms_type) + exception = TypeCheck.TypeError.exception({type, {other, []}}) + raise exception end end @@ -281,7 +285,7 @@ defmodule TypeCheck do {:current_stacktrace, [_ , _, caller | _]} = Process.info(self(), :current_stacktrace) location = elem(caller, 3) location = update_in(location[:file], &to_string/1) - exception = TypeCheck.TypeError.exception({problem, location}) + exception = TypeCheck.TypeError.exception({type, {problem, location}}) {:error, exception} end end diff --git a/lib/type_check/builtin/atom.ex b/lib/type_check/builtin/atom.ex index 054fd18..fe0e696 100644 --- a/lib/type_check/builtin/atom.ex +++ b/lib/type_check/builtin/atom.ex @@ -9,7 +9,7 @@ defmodule TypeCheck.Builtin.Atom do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -19,7 +19,7 @@ defmodule TypeCheck.Builtin.Atom do {:ok, [], x} _ -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/binary.ex b/lib/type_check/builtin/binary.ex index 241e39a..a3e750c 100644 --- a/lib/type_check/builtin/binary.ex +++ b/lib/type_check/builtin/binary.ex @@ -3,7 +3,7 @@ defmodule TypeCheck.Builtin.Binary do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -13,7 +13,7 @@ defmodule TypeCheck.Builtin.Binary do {:ok, [], x} _ -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/bitstring.ex b/lib/type_check/builtin/bitstring.ex index 578a8a2..9674914 100644 --- a/lib/type_check/builtin/bitstring.ex +++ b/lib/type_check/builtin/bitstring.ex @@ -3,7 +3,7 @@ defmodule TypeCheck.Builtin.Bitstring do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -13,7 +13,7 @@ defmodule TypeCheck.Builtin.Bitstring do {:ok, [], x} _ -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/boolean.ex b/lib/type_check/builtin/boolean.ex index 12ec2ca..7ee0557 100644 --- a/lib/type_check/builtin/boolean.ex +++ b/lib/type_check/builtin/boolean.ex @@ -3,7 +3,7 @@ defmodule TypeCheck.Builtin.Boolean do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -13,7 +13,7 @@ defmodule TypeCheck.Builtin.Boolean do {:ok, [], x} _ -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/compound_fixed_map.ex b/lib/type_check/builtin/compound_fixed_map.ex index bd75b8f..0afd853 100644 --- a/lib/type_check/builtin/compound_fixed_map.ex +++ b/lib/type_check/builtin/compound_fixed_map.ex @@ -12,12 +12,12 @@ defmodule TypeCheck.Builtin.CompoundFixedMap do @type! t :: %__MODULE__{fixed: TypeCheck.Builtin.FixedMap.t(), flexible: TypeCheck.Builtin.Map.t()} @type! problem_tuple :: - {t(), :not_a_map, %{}, any()} - | {t(), :missing_keys, %{keys: list(atom())}, map()} - | {t(), :superfluous_keys, %{keys: list(atom())}, map()} - | {t(), :value_error, + {:not_a_map, %{}, any()} + | {:missing_keys, %{keys: list(atom())}, map()} + | {:superfluous_keys, %{keys: list(atom())}, map()} + | {:value_error, %{problem: lazy(TypeCheck.TypeError.Formatter.problem_tuple()), key: any()}, map()} - | {t(), :key_error, + | {:key_error, %{problem: lazy(TypeCheck.TypeError.Formatter.problem_tuple()), key: any()}, map()} defimpl TypeCheck.Protocols.ToCheck do @@ -36,21 +36,21 @@ defmodule TypeCheck.Builtin.CompoundFixedMap do do {:ok, bindings1 ++ bindings2, Map.merge(fixed_part, flexible_part)} else - {:error, {_, reason, info, _val}} -> - {:error, {unquote(TypeCheck.Internals.Escaper.escape(s)), reason, info, unquote(param)}} + {:error, {reason, info, _val}} -> + {:error, {reason, info, unquote(param)}} end end res end - defp map_check(param, s) do + defp map_check(param, _s) do quote generated: true, location: :keep do case unquote(param) do val when is_map(val) -> {:ok, [], val} other -> - {:error, {unquote(TypeCheck.Internals.Escaper.escape(s)), :not_a_map, %{}, other}} + {:error, {:not_a_map, %{}, other}} end end end diff --git a/lib/type_check/builtin/fixed_list.ex b/lib/type_check/builtin/fixed_list.ex index 2ad639e..b00a783 100644 --- a/lib/type_check/builtin/fixed_list.ex +++ b/lib/type_check/builtin/fixed_list.ex @@ -14,9 +14,9 @@ defmodule TypeCheck.Builtin.FixedList do @type! t :: %__MODULE__{element_types: list(TypeCheck.Type.t())} @type! problem_tuple :: - {t(), :not_a_list, %{}, any()} - | {t(), :different_length, %{expected_length: non_neg_integer()}, list()} - | {t(), :element_error, + {:not_a_list, %{}, any()} + | {:different_length, %{expected_length: non_neg_integer()}, list()} + | {:element_error, %{problem: lazy(TypeCheck.TypeError.Formatter.problem_tuple()), index: non_neg_integer()}, list()} @@ -28,11 +28,11 @@ defmodule TypeCheck.Builtin.FixedList do quote generated: :true, location: :keep do case unquote(param) do x when not is_list(x) -> - {:error, {unquote(Macro.escape(s)), :not_a_list, %{}, x}} + {:error, {:not_a_list, %{}, x}} x when length(x) != unquote(expected_length) -> {:error, - {unquote(Macro.escape(s)), :different_length, + {:different_length, %{expected_length: unquote(expected_length)}, x}} _ -> @@ -41,7 +41,7 @@ defmodule TypeCheck.Builtin.FixedList do end end - def build_element_checks_ast(element_types, param, s) do + def build_element_checks_ast(element_types, param, _s) do element_checks = element_types |> Enum.with_index() @@ -75,7 +75,7 @@ defmodule TypeCheck.Builtin.FixedList do else {{:error, error}, index, _rest} -> {:error, - {unquote(Macro.escape(s)), :element_error, %{problem: error, index: index}, + {:element_error, %{problem: error, index: index}, unquote(param)}} end end diff --git a/lib/type_check/builtin/fixed_map.ex b/lib/type_check/builtin/fixed_map.ex index ac34adb..fc55c77 100644 --- a/lib/type_check/builtin/fixed_map.ex +++ b/lib/type_check/builtin/fixed_map.ex @@ -14,10 +14,10 @@ defmodule TypeCheck.Builtin.FixedMap do @type! t :: %__MODULE__{keypairs: list({term(), TypeCheck.Type.t()})} @type! problem_tuple :: - {t(), :not_a_map, %{}, any()} - | {t(), :missing_keys, %{keys: list(atom())}, map()} - | {t(), :superfluous_keys, %{keys: list(atom())}, map()} - | {t(), :value_error, + {:not_a_map, %{}, any()} + | {:missing_keys, %{keys: list(atom())}, map()} + | {:superfluous_keys, %{keys: list(atom())}, map()} + | {:value_error, %{problem: lazy(TypeCheck.TypeError.Formatter.problem_tuple()), key: any()}, map()} @@ -55,7 +55,7 @@ defmodule TypeCheck.Builtin.FixedMap do val when is_map(val) -> {:ok, [], val} other -> - {:error, {unquote(TypeCheck.Internals.Escaper.escape(s)), :not_a_map, %{}, other}} + {:error, {:not_a_map, %{}, other}} end end end @@ -76,7 +76,7 @@ defmodule TypeCheck.Builtin.FixedMap do missing_keys -> {:error, - {unquote(TypeCheck.Internals.Escaper.escape(s)), :missing_keys, %{keys: missing_keys}, unquote(param)}} + {:missing_keys, %{keys: missing_keys}, unquote(param)}} end end end @@ -96,7 +96,7 @@ defmodule TypeCheck.Builtin.FixedMap do superfluous_keys -> {:error, - {unquote(TypeCheck.Internals.Escaper.escape(s)), :superfluous_keys, %{keys: superfluous_keys}, + {:superfluous_keys, %{keys: superfluous_keys}, unquote(param)}} end end @@ -133,7 +133,7 @@ defmodule TypeCheck.Builtin.FixedMap do else {{:error, error}, key} -> {:error, - {unquote(TypeCheck.Internals.Escaper.escape(s)), :value_error, %{problem: error, key: key}, unquote(param)}} + {:value_error, %{problem: error, key: key}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/fixed_tuple.ex b/lib/type_check/builtin/fixed_tuple.ex index fc2fecb..fb80ad4 100644 --- a/lib/type_check/builtin/fixed_tuple.ex +++ b/lib/type_check/builtin/fixed_tuple.ex @@ -5,9 +5,9 @@ defmodule TypeCheck.Builtin.FixedTuple do @type! t :: %__MODULE__{element_types: list(TypeCheck.Type.t())} @type! problem_tuple :: - {t(), :not_a_tuple, %{}, any()} - | {t(), :different_size, %{expected_size: integer()}, tuple()} - | {t(), :element_error, + {:not_a_tuple, %{}, any()} + | {:different_size, %{expected_size: integer()}, tuple()} + | {:element_error, %{problem: lazy(TypeCheck.TypeError.Formatter.problem_tuple()), index: integer()}, tuple()} @@ -25,11 +25,11 @@ defmodule TypeCheck.Builtin.FixedTuple do quote generated: true, location: :keep do case unquote(param) do x when not is_tuple(x) -> - {:error, {unquote(Macro.escape(s)), :not_a_tuple, %{}, x}} + {:error, {:not_a_tuple, %{}, x}} x when tuple_size(x) != unquote(expected_size) -> {:error, - {unquote(TypeCheck.Internals.Escaper.escape(s)), :different_size, %{expected_size: unquote(expected_size)}, + {:different_size, %{expected_size: unquote(expected_size)}, x}} _ -> @@ -69,7 +69,7 @@ defmodule TypeCheck.Builtin.FixedTuple do else {{:error, error}, index} -> {:error, - {unquote(Macro.escape(s)), :element_error, %{problem: error, index: index}, + {:element_error, %{problem: error, index: index}, unquote(param)}} end end diff --git a/lib/type_check/builtin/float.ex b/lib/type_check/builtin/float.ex index 1e99338..c4e89ea 100644 --- a/lib/type_check/builtin/float.ex +++ b/lib/type_check/builtin/float.ex @@ -3,7 +3,7 @@ defmodule TypeCheck.Builtin.Float do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -13,7 +13,7 @@ defmodule TypeCheck.Builtin.Float do {:ok, [], x} _ -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/function.ex b/lib/type_check/builtin/function.ex index b58888a..cf80555 100644 --- a/lib/type_check/builtin/function.ex +++ b/lib/type_check/builtin/function.ex @@ -7,7 +7,7 @@ defmodule TypeCheck.Builtin.Function do param_types: list(TypeCheck.Type.t()) | nil, return_type: TypeCheck.Type.t() } - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.Escape do def escape(s) do @@ -31,7 +31,7 @@ defmodule TypeCheck.Builtin.Function do {:ok, [], wrapped_fun} _ -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end @@ -84,7 +84,7 @@ defmodule TypeCheck.Builtin.Function do {:error, problem} -> raise TypeCheck.TypeError, - {unquote(Macro.escape(s)), :return_error, + {:return_error, %{problem: problem, arguments: unquote(clean_params)}, var!(result, nil)} end diff --git a/lib/type_check/builtin/guarded.ex b/lib/type_check/builtin/guarded.ex index d96edb3..61e57e3 100644 --- a/lib/type_check/builtin/guarded.ex +++ b/lib/type_check/builtin/guarded.ex @@ -10,6 +10,8 @@ defmodule TypeCheck.Builtin.Guarded do @type! t() :: %TypeCheck.Builtin.Guarded{type: TypeCheck.Type.t(), guard: ast()} + # TODO problem_tuple type + defimpl TypeCheck.Protocols.Escape do def escape(s) do @@ -95,13 +97,13 @@ defmodule TypeCheck.Builtin.Guarded do {:ok, bindings, altered_param} else {:error, - {unquote(Macro.escape(s)), :guard_failed, %{bindings: bindings_map}, + {:guard_failed, %{bindings: bindings_map}, unquote(param)}} end {:error, problem} -> {:error, - {unquote(Macro.escape(s)), :type_failed, %{problem: problem}, unquote(param)}} + {:type_failed, %{problem: problem}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/implements_protocol.ex b/lib/type_check/builtin/implements_protocol.ex index b59af07..9f8c286 100644 --- a/lib/type_check/builtin/implements_protocol.ex +++ b/lib/type_check/builtin/implements_protocol.ex @@ -9,7 +9,7 @@ defmodule TypeCheck.Builtin.ImplementsProtocol do use TypeCheck @type! t :: %__MODULE__{protocol: module()} - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -17,7 +17,7 @@ defmodule TypeCheck.Builtin.ImplementsProtocol do x = unquote(param) case unquote(s.protocol).impl_for(x) do nil -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, x}} + {:error, {:no_match, %{}, x}} _ -> {:ok, [], x} end diff --git a/lib/type_check/builtin/integer.ex b/lib/type_check/builtin/integer.ex index 8082196..1842d5e 100644 --- a/lib/type_check/builtin/integer.ex +++ b/lib/type_check/builtin/integer.ex @@ -3,7 +3,7 @@ defmodule TypeCheck.Builtin.Integer do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -13,7 +13,7 @@ defmodule TypeCheck.Builtin.Integer do {:ok, [], x} _ -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/list.ex b/lib/type_check/builtin/list.ex index aee96b9..cfdb1c6 100644 --- a/lib/type_check/builtin/list.ex +++ b/lib/type_check/builtin/list.ex @@ -6,8 +6,8 @@ defmodule TypeCheck.Builtin.List do @type! t(element_type) :: %__MODULE__{element_type: element_type} @type! problem_tuple :: - {t(), :not_a_list, %{}, any()} - | {t(), :element_error, + {:not_a_list, %{}, any()} + | {:element_error, %{ problem: lazy(TypeCheck.TypeError.Formatter.problem_tuple()), index: non_neg_integer() @@ -24,7 +24,7 @@ defmodule TypeCheck.Builtin.List do quote generated: true, location: :keep do case unquote(param) do x when not is_list(x) -> - {:error, {unquote(TypeCheck.Internals.Escaper.escape(s)), :not_a_list, %{}, unquote(param)}} + {:error, {:not_a_list, %{}, unquote(param)}} _ -> unquote(build_element_check(element_type, param, s)) @@ -58,7 +58,7 @@ defmodule TypeCheck.Builtin.List do {:error, problem} -> problem = {:error, - {unquote(TypeCheck.Internals.Escaper.escape(s)), :element_error, %{problem: problem, index: index}, + {:element_error, %{problem: problem, index: index}, orig_param}} {:halt, problem} diff --git a/lib/type_check/builtin/literal.ex b/lib/type_check/builtin/literal.ex index 63f2604..82c8586 100644 --- a/lib/type_check/builtin/literal.ex +++ b/lib/type_check/builtin/literal.ex @@ -3,7 +3,7 @@ defmodule TypeCheck.Builtin.Literal do use TypeCheck @type! t :: %__MODULE__{value: term()} - @type! problem_tuple :: {t(), :not_same_value, %{}, value :: term()} + @type! problem_tuple :: {:not_same_value, %{}, value :: term()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s = %{value: value}, param) do @@ -13,7 +13,7 @@ defmodule TypeCheck.Builtin.Literal do {:ok, [], x} _ -> - {:error, {unquote(Macro.escape(s)), :not_same_value, %{}, unquote(param)}} + {:error, {:not_same_value, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/map.ex b/lib/type_check/builtin/map.ex index 2ea2149..7573862 100644 --- a/lib/type_check/builtin/map.ex +++ b/lib/type_check/builtin/map.ex @@ -5,10 +5,10 @@ defmodule TypeCheck.Builtin.Map do @opaque! t :: %__MODULE__{key_type: TypeCheck.Type.t(), value_type: TypeCheck.Type.t()} @type! problem_tuple :: - {t(), :not_a_map, %{}, any()} - | {t(), :key_error, + {:not_a_map, %{}, any()} + | {:key_error, %{problem: lazy(TypeCheck.TypeError.Formatter.problem_tuple()), key: any()}, any()} - | {t(), :value_error, + | {:value_error, %{problem: lazy(TypeCheck.TypeError.Formatter.problem_tuple()), key: any()}, any()} defimpl TypeCheck.Protocols.Escape do @@ -22,7 +22,7 @@ defmodule TypeCheck.Builtin.Map do quote generated: true, location: :keep do case unquote(param) do x when not is_map(x) -> - {:error, {unquote(TypeCheck.Internals.Escaper.escape(s)), :not_a_map, %{}, unquote(param)}} + {:error, {:not_a_map, %{}, unquote(param)}} _ -> unquote(build_keypairs_check(s.key_type, s.value_type, param, s)) @@ -64,7 +64,7 @@ defmodule TypeCheck.Builtin.Map do {{:error, problem}, _} -> res = {:error, - {unquote(TypeCheck.Internals.Escaper.escape(s)), :key_error, %{problem: problem, key: key}, + {:key_error, %{problem: problem, key: key}, orig_param}} {:halt, res} @@ -72,7 +72,7 @@ defmodule TypeCheck.Builtin.Map do {_, {:error, problem}} -> res = {:error, - {unquote(TypeCheck.Internals.Escaper.escape(s)), :value_error, %{problem: problem, key: key}, + {:value_error, %{problem: problem, key: key}, orig_param}} {:halt, res} diff --git a/lib/type_check/builtin/maybe_improper_list.ex b/lib/type_check/builtin/maybe_improper_list.ex index fecd25c..1e4bdb6 100644 --- a/lib/type_check/builtin/maybe_improper_list.ex +++ b/lib/type_check/builtin/maybe_improper_list.ex @@ -10,13 +10,13 @@ defmodule TypeCheck.Builtin.MaybeImproperList do @type! problem_tuple :: - {t(), :not_a_list, %{}, any()} - | {t(), :element_error, + {:not_a_list, %{}, any()} + | {:element_error, %{ problem: lazy(TypeCheck.TypeError.Formatter.problem_tuple()), index: non_neg_integer() }, any()} - | {t(), :terminator_error, + | {:terminator_error, %{problem: lazy(TypeCheck.TypeError.Formatter.problem_tuple())}, any()} defimpl TypeCheck.Protocols.Escape do @@ -30,7 +30,7 @@ defmodule TypeCheck.Builtin.MaybeImproperList do quote generated: true, location: :keep do case unquote(param) do x when not is_list(x) -> - {:error, {unquote(Macro.escape(s)), :not_a_list, %{}, unquote(param)}} + {:error, {:not_a_list, %{}, unquote(param)}} _ -> unquote(build_element_check(s, param)) @@ -70,7 +70,7 @@ defmodule TypeCheck.Builtin.MaybeImproperList do {:error, problem} -> {:error, - {unquote(Macro.escape(s)), :terminator_error, %{problem: problem, index: index}, + {:terminator_error, %{problem: problem, index: index}, orig_param}} end else @@ -81,7 +81,7 @@ defmodule TypeCheck.Builtin.MaybeImproperList do {:error, problem} -> problem = {:error, - {unquote(Macro.escape(s)), :element_error, %{problem: problem, index: index}, + {:element_error, %{problem: problem, index: index}, orig_param}} {:halt, problem} diff --git a/lib/type_check/builtin/named_type.ex b/lib/type_check/builtin/named_type.ex index eb37cdf..c0fc7ae 100644 --- a/lib/type_check/builtin/named_type.ex +++ b/lib/type_check/builtin/named_type.ex @@ -45,7 +45,7 @@ defmodule TypeCheck.Builtin.NamedType do # Reset bindings {:ok, [], altered_inner} {:error, problem} -> - {:error, {unquote(TypeCheck.Internals.Escaper.escape(s)), :named_type, %{problem: problem}, unquote(param)}} + {:error, {:named_type, %{problem: problem}, unquote(param)}} end end else @@ -59,7 +59,7 @@ defmodule TypeCheck.Builtin.NamedType do {:ok, [{unquote(s.name), unquote(param)} | bindings], altered_inner} {:error, problem} -> - {:error, {unquote(TypeCheck.Internals.Escaper.escape(s)), :named_type, %{problem: problem}, unquote(param)}} + {:error, {:named_type, %{problem: problem}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/neg_integer.ex b/lib/type_check/builtin/neg_integer.ex index d8380b0..ed1b223 100644 --- a/lib/type_check/builtin/neg_integer.ex +++ b/lib/type_check/builtin/neg_integer.ex @@ -3,7 +3,7 @@ defmodule TypeCheck.Builtin.NegInteger do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -13,7 +13,7 @@ defmodule TypeCheck.Builtin.NegInteger do {:ok, [], unquote(param)} _ -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/non_neg_integer.ex b/lib/type_check/builtin/non_neg_integer.ex index dec5f63..29b838f 100644 --- a/lib/type_check/builtin/non_neg_integer.ex +++ b/lib/type_check/builtin/non_neg_integer.ex @@ -3,7 +3,7 @@ defmodule TypeCheck.Builtin.NonNegInteger do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -13,7 +13,7 @@ defmodule TypeCheck.Builtin.NonNegInteger do {:ok, [], x} _ -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/none.ex b/lib/type_check/builtin/none.ex index 8b41202..e03aabd 100644 --- a/lib/type_check/builtin/none.ex +++ b/lib/type_check/builtin/none.ex @@ -15,12 +15,12 @@ defmodule TypeCheck.Builtin.None do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, val :: any()} + @type! problem_tuple :: {:no_match, %{}, val :: any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do quote generated: true, location: :keep do - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/number.ex b/lib/type_check/builtin/number.ex index 1212fd4..d89b980 100644 --- a/lib/type_check/builtin/number.ex +++ b/lib/type_check/builtin/number.ex @@ -3,7 +3,7 @@ defmodule TypeCheck.Builtin.Number do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, val :: any()} + @type! problem_tuple :: {:no_match, %{}, val :: any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -13,7 +13,7 @@ defmodule TypeCheck.Builtin.Number do {:ok, [], x} _ -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/one_of.ex b/lib/type_check/builtin/one_of.ex index 503e80b..da49d64 100644 --- a/lib/type_check/builtin/one_of.ex +++ b/lib/type_check/builtin/one_of.ex @@ -3,7 +3,7 @@ defmodule TypeCheck.Builtin.OneOf do use TypeCheck @type! t() :: %TypeCheck.Builtin.OneOf{choices: list(TypeCheck.Type.t())} - @type! problem_tuple :: {t(), :all_failed, %{problems: list(lazy(TypeCheck.TypeError.Formatter.problem_tuple))}, term()} + @type! problem_tuple :: {:all_failed, %{problems: list(lazy(TypeCheck.TypeError.Formatter.problem_tuple))}, term()} defimpl TypeCheck.Protocols.Escape do @@ -32,7 +32,7 @@ defmodule TypeCheck.Builtin.OneOf do with unquote_splicing(snippets) do {:error, - {unquote(TypeCheck.Internals.Escaper.escape(x)), :all_failed, %{problems: Enum.reverse(problems)}, + {:all_failed, %{problems: Enum.reverse(problems)}, unquote(param)}} else {:ok, bindings, altered_param} -> diff --git a/lib/type_check/builtin/pid.ex b/lib/type_check/builtin/pid.ex index 8b4c81f..48d02db 100644 --- a/lib/type_check/builtin/pid.ex +++ b/lib/type_check/builtin/pid.ex @@ -3,7 +3,7 @@ defmodule TypeCheck.Builtin.PID do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -13,7 +13,7 @@ defmodule TypeCheck.Builtin.PID do {:ok, [], x} _ -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/port.ex b/lib/type_check/builtin/port.ex index b940db5..e92aaea 100644 --- a/lib/type_check/builtin/port.ex +++ b/lib/type_check/builtin/port.ex @@ -13,7 +13,7 @@ defmodule TypeCheck.Builtin.Port do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -22,7 +22,7 @@ defmodule TypeCheck.Builtin.Port do x when is_port(x) -> {:ok, [], x} _ -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/pos_integer.ex b/lib/type_check/builtin/pos_integer.ex index c4fa710..c7854de 100644 --- a/lib/type_check/builtin/pos_integer.ex +++ b/lib/type_check/builtin/pos_integer.ex @@ -3,7 +3,7 @@ defmodule TypeCheck.Builtin.PosInteger do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -13,7 +13,7 @@ defmodule TypeCheck.Builtin.PosInteger do {:ok, [], x} _ -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/range.ex b/lib/type_check/builtin/range.ex index 0775536..68002de 100644 --- a/lib/type_check/builtin/range.ex +++ b/lib/type_check/builtin/range.ex @@ -10,18 +10,18 @@ defmodule TypeCheck.Builtin.Range do end @type! problem_tuple :: - {t(), :not_an_integer, %{}, any()} - | {t(), :not_in_range, %{}, integer()} + {:not_an_integer, %{}, any()} + | {:not_in_range, %{}, integer()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s = %{range: range}, param) do quote generated: true, location: :keep do case unquote(param) do x when not is_integer(x) -> - {:error, {unquote(Macro.escape(s)), :not_an_integer, %{}, unquote(param)}} + {:error, {:not_an_integer, %{}, unquote(param)}} x when x not in unquote(Macro.escape(range)) -> - {:error, {unquote(Macro.escape(s)), :not_in_range, %{}, unquote(param)}} + {:error, {:not_in_range, %{}, unquote(param)}} correct_value -> {:ok, [], correct_value} diff --git a/lib/type_check/builtin/reference.ex b/lib/type_check/builtin/reference.ex index 1ef5ff7..2e7661f 100644 --- a/lib/type_check/builtin/reference.ex +++ b/lib/type_check/builtin/reference.ex @@ -14,7 +14,7 @@ defmodule TypeCheck.Builtin.Reference do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -23,7 +23,7 @@ defmodule TypeCheck.Builtin.Reference do x when is_reference(x) -> {:ok, [], x} _ -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} end end end diff --git a/lib/type_check/builtin/sized_bitstring.ex b/lib/type_check/builtin/sized_bitstring.ex index cabb4b8..c014d21 100644 --- a/lib/type_check/builtin/sized_bitstring.ex +++ b/lib/type_check/builtin/sized_bitstring.ex @@ -5,8 +5,8 @@ defmodule TypeCheck.Builtin.SizedBitstring do @type! t :: %__MODULE__{prefix_size: non_neg_integer(), unit_size: nil | 1..256} @type! problem_tuple :: - {t(), :no_match, %{}, any()} - | {t(), :wrong_size, %{}, any()} + {:no_match, %{}, any()} + | { :wrong_size, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -14,9 +14,9 @@ defmodule TypeCheck.Builtin.SizedBitstring do quote generated: true, location: :keep do case unquote(param) do x when not is_bitstring(x) -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, unquote(param)}} + {:error, {:no_match, %{}, unquote(param)}} x when bit_size(x) != unquote(s.prefix_size) -> - {:error, {unquote(Macro.escape(s)), :wrong_size, %{}, unquote(param)}} + {:error, {:wrong_size, %{}, unquote(param)}} _ -> {:ok, [], unquote(param)} end @@ -25,9 +25,9 @@ defmodule TypeCheck.Builtin.SizedBitstring do quote generated: true, location: :keep do case unquote(param) do x when not is_bitstring(x) -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, x}} + {:error, {:no_match, %{}, x}} x when bit_size(x) < unquote(s.prefix_size) or rem(bit_size(x) - unquote(s.prefix_size), unquote(s.unit_size)) != 0 -> - {:error, {unquote(Macro.escape(s)), :wrong_size, %{}, x}} + {:error, {:wrong_size, %{}, x}} correct_value -> {:ok, [], correct_value} end diff --git a/lib/type_check/builtin/tuple.ex b/lib/type_check/builtin/tuple.ex index 8edde99..694c9d0 100644 --- a/lib/type_check/builtin/tuple.ex +++ b/lib/type_check/builtin/tuple.ex @@ -9,7 +9,7 @@ defmodule TypeCheck.Builtin.Tuple do use TypeCheck @type! t :: %__MODULE__{} - @type! problem_tuple :: {t(), :no_match, %{}, any()} + @type! problem_tuple :: {:no_match, %{}, any()} defimpl TypeCheck.Protocols.ToCheck do def to_check(s, param) do @@ -19,7 +19,7 @@ defmodule TypeCheck.Builtin.Tuple do {:ok, [], x} other -> - {:error, {unquote(Macro.escape(s)), :no_match, %{}, other}} + {:error, {:no_match, %{}, other}} end end end diff --git a/lib/type_check/spec.ex b/lib/type_check/spec.ex index 76c23b2..15aa11a 100644 --- a/lib/type_check/spec.ex +++ b/lib/type_check/spec.ex @@ -12,8 +12,8 @@ defmodule TypeCheck.Spec do use TypeCheck alias TypeCheck.DefaultOverrides.String @type! t() :: %__MODULE__{name: String.t(), param_types: list(TypeCheck.Type.t()), return_type: TypeCheck.Type.t(), location: [] | list({:file, String.t()} | {:line, non_neg_integer()})} - @type! problem_tuple :: {t(), :param_error, %{index: non_neg_integer(), problem: lazy(TypeCheck.TypeError.Formatter.problem_tuple())}, list(any())} - | {t(), :return_error, %{arguments: list(term()), problem: lazy(TypeCheck.TypeError.Formatter.problem_tuple())}, list(any())} + @type! problem_tuple :: {:param_error, %{index: non_neg_integer(), problem: lazy(TypeCheck.TypeError.Formatter.problem_tuple())}, list(any())} + | {:return_error, %{arguments: list(term()), problem: lazy(TypeCheck.TypeError.Formatter.problem_tuple())}, list(any())} end defp spec_fun_name(function, arity) do @@ -200,10 +200,10 @@ defmodule TypeCheck.Spec do # Run actual code else {{:error, problem}, index} -> - raise TypeCheck.TypeError, - { - {__MODULE__.unquote(spec_fun_name(name, arity))(), :param_error, - %{index: index, problem: problem}, unquote(clean_params)}, unquote(Macro.Env.location(caller))} + type = __MODULE__.unquote(spec_fun_name(name, arity))() + problem_tuple = {:param_error, %{index: index, problem: problem}, unquote(clean_params)} + location = unquote(Macro.Env.location(caller)) + raise TypeCheck.TypeError, {type, {problem_tuple, location}} end end end @@ -220,7 +220,7 @@ defmodule TypeCheck.Spec do end end - defp return_check_code(name, arity, clean_params, return_type, _caller, _location) do + defp return_check_code(name, arity, clean_params, return_type, caller, _location) do return_code_check = TypeCheck.Protocols.ToCheck.to_check(return_type, Macro.var(:super_result, nil)) @@ -233,9 +233,10 @@ defmodule TypeCheck.Spec do altered_return_value {:error, problem} -> - raise TypeCheck.TypeError, - {__MODULE__.unquote(spec_fun_name(name, arity))(), :return_error, - %{problem: problem, arguments: unquote(clean_params)}, var!(super_result, nil)} + type = __MODULE__.unquote(spec_fun_name(name, arity))() + problem_tuple = {:return_error, %{problem: problem, arguments: unquote(clean_params)}, var!(super_result, nil)} + location = unquote(Macro.Env.location(caller)) + raise TypeCheck.TypeError, {type, {problem_tuple, location}} end end end diff --git a/lib/type_check/type_error.ex b/lib/type_check/type_error.ex index 8c81e53..4806b22 100644 --- a/lib/type_check/type_error.ex +++ b/lib/type_check/type_error.ex @@ -54,6 +54,14 @@ defmodule TypeCheck.TypeError do @impl true + + def exception({s, {problem_tuple, location}}) do + hydrated_problem_tuple = hydrate_problem_tuple(s, problem_tuple) + message = TypeCheck.TypeError.DefaultFormatter.format(hydrated_problem_tuple, location) + + %__MODULE__{message: message, raw: hydrated_problem_tuple, location: location} + end + def exception({problem_tuple, location}) do message = TypeCheck.TypeError.DefaultFormatter.format(problem_tuple, location) @@ -63,4 +71,85 @@ defmodule TypeCheck.TypeError do def exception(problem_tuple) do exception({problem_tuple, []}) end + + @simple_problems ~w[no_match not_same_value not_a_map not_a_list missing_keys superfluous_keys different_length different_size not_an_integer not_in_range wrong_size guard_failed]a + + def hydrate_problem_tuple(s = %TypeCheck.Type.StreamData{}, problem_tuple) do + hydrate_problem_tuple(s.type, problem_tuple) + end + + def hydrate_problem_tuple(s, problem_tuple) do + case problem_tuple do + {simple, meta, param} when simple in @simple_problems -> {s, simple, meta, param} + {:value_error, meta, param} -> + case s do + %TypeCheck.Builtin.CompoundFixedMap{} -> + s2 = s.fixed.keypairs[meta.key] || s.flexible.value_type + meta2 = update_in(meta.problem, &hydrate_problem_tuple(s2, &1)) + {s, :value_error, meta2, param} + %TypeCheck.Builtin.FixedMap{} -> + s2 = s.keypairs[meta.key] + meta2 = update_in(meta.problem, &hydrate_problem_tuple(s2, &1)) + {s, :value_error, meta2, param} + %TypeCheck.Builtin.Map{} -> + s2 = s.value_type + meta2 = update_in(meta.problem, &hydrate_problem_tuple(s2, &1)) + {s, :value_error, meta2, param} + end + {:key_error, meta, param} -> + s2 = meta.key + meta2 = update_in(meta.problem, &hydrate_problem_tuple(s2, &1)) + {s, :key_error, meta2, param} + {:element_error, meta, param} -> + case s do + %TypeCheck.Builtin.List{} -> + s2 = s.element_type + meta2 = update_in(meta.problem, &hydrate_problem_tuple(s2, &1)) + {s, :element_error, meta2, param} + %TypeCheck.Builtin.MaybeImproperList{} -> + s2 = s.element_type + meta2 = update_in(meta.problem, &hydrate_problem_tuple(s2, &1)) + {s, :element_error, meta2, param} + %TypeCheck.Builtin.FixedList{} -> + s2 = Enum.at(s.element_types, meta.index) + meta2 = update_in(meta.problem, &hydrate_problem_tuple(s2, &1)) + {s, :element_error, meta2, param} + %TypeCheck.Builtin.FixedTuple{} -> + s2 = Enum.at(s.element_types, meta.index) + meta2 = update_in(meta.problem, &hydrate_problem_tuple(s2, &1)) + {s, :element_error, meta2, param} + end + {:terminator_error, meta, param} -> + s2 = s.terminator_type + meta2 = update_in(meta.problem, &hydrate_problem_tuple(s2, &1)) + {s, :terminator_error, meta2, param} + {:named_type, meta, param} -> + s2 = s.type + meta2 = update_in(meta.problem, &hydrate_problem_tuple(s2, &1)) + {s, :named_type, meta2, param} + {:type_failed, meta, param} -> + s2 = s.type + meta2 = update_in(meta.problem, &hydrate_problem_tuple(s2, &1)) + {s, :type_failed, meta2, param} + {:all_failed, meta, param} -> + hydrated_problems = + Enum.zip(s.choices, meta.problems) + |> Enum.map(fn s2, problem -> + hydrate_problem_tuple(s2, problem) + end) + meta2 = put_in(meta.problems, hydrated_problems) + {s, :all_failed, meta2, param} + {:return_error, meta, param} -> + s2 = s.return_type + meta2 = update_in(meta.problem, &hydrate_problem_tuple(s2, &1)) + {s, :return_error, meta2, param} + {:param_error, meta, param} -> + s2 = Enum.at(s.param_types, meta.index) + meta2 = update_in(meta.problem, &hydrate_problem_tuple(s2, &1)) + {s, :param_error, meta2, param} + other -> + IO.warn("TODO: #{inspect({s, problem_tuple})}") + other + end + end end diff --git a/lib/type_check/type_error/default_formatter.ex b/lib/type_check/type_error/default_formatter.ex index 166a6aa..4f8be96 100644 --- a/lib/type_check/type_error/default_formatter.ex +++ b/lib/type_check/type_error/default_formatter.ex @@ -1,6 +1,10 @@ defmodule TypeCheck.TypeError.DefaultFormatter do @behaviour TypeCheck.TypeError.Formatter + # def format(problem_tuple, location \\ []) do + # inspect(problem_tuple) + # end + @spec format(TypeCheck.TypeError.problem_tuple(), TypeCheck.TypeError.location()) :: String.t() def format(problem_tuple, location \\ []) do res =