Skip to content

Commit c98fee3

Browse files
committed
Improve none reporting
1 parent c64a3f5 commit c98fee3

File tree

4 files changed

+53
-3
lines changed

4 files changed

+53
-3
lines changed

lib/elixir/lib/module/types.ex

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ defmodule Module.Types do
2727
# TODO: Reuse context from patterns and guards
2828
# TODO: Simplify pair types
2929
# TODO: Handle local calls
30-
# TODO: Improve errors on bad apply if one of the arguments are empty
3130
{{fun, arity}, {:infer, pair_types}}
3231
end
3332
end

lib/elixir/lib/module/types/helpers.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,15 @@ defmodule Module.Types.Helpers do
202202
|> Macro.to_string()
203203
end
204204

205+
defp erl_to_ex(
206+
:erlang,
207+
:error,
208+
[expr, :none, [error_info: {:%{}, _, [module: Exception]}]],
209+
meta
210+
) do
211+
{:raise, meta, [expr]}
212+
end
213+
205214
defp erl_to_ex(mod, fun, args, meta) do
206215
case :elixir_rewrite.erl_to_ex(mod, fun, args) do
207216
{Kernel, fun, args, _} -> {fun, meta, args}

lib/elixir/lib/module/types/of.ex

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,6 +1076,21 @@ defmodule Module.Types.Of do
10761076
{{:., _, [mod, fun]}, _, args} = expr
10771077
{mod, fun, args, converter} = :elixir_rewrite.erl_to_ex(mod, fun, args)
10781078

1079+
explanation =
1080+
if i = Enum.find_index(args_types, &empty?/1) do
1081+
"""
1082+
the #{integer_to_ordinal(i + 1)} argument is empty (often represented as none()), \
1083+
most likely because it is the result of an expression that always fails, such as \
1084+
a `raise` or a previous invalid call. This causes any subsequent function call with \
1085+
said value to always fail
1086+
"""
1087+
else
1088+
"""
1089+
but expected one of:
1090+
#{clauses_args_to_quoted_string(clauses, converter)}
1091+
"""
1092+
end
1093+
10791094
%{
10801095
details: %{typing_traces: traces},
10811096
message:
@@ -1089,9 +1104,8 @@ defmodule Module.Types.Of do
10891104
10901105
#{args_to_quoted_string(args_types, domain, converter) |> indent(4)}
10911106
1092-
but expected one of:
1093-
#{clauses_args_to_quoted_string(clauses, converter)}
10941107
""",
1108+
explanation,
10951109
format_traces(traces)
10961110
])
10971111
}
@@ -1278,4 +1292,13 @@ defmodule Module.Types.Of do
12781292
single_line -> binary_slice(single_line, 1..-2//1)
12791293
end
12801294
end
1295+
1296+
defp integer_to_ordinal(i) do
1297+
case rem(i, 10) in [1, 2, 3] do
1298+
1 when rem(i, 100) != 11 -> "#{i}st"
1299+
2 when rem(i, 100) != 12 -> "#{i}nd"
1300+
3 when rem(i, 100) != 13 -> "#{i}rd"
1301+
_ -> "#{i}th"
1302+
end
1303+
end
12811304
end

lib/elixir/test/elixir/module/types/expr_test.exs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,25 @@ defmodule Module.Types.ExprTest do
165165
{union(dynamic(), binary()), "GenServer.to_string/1 is undefined or private"}
166166
end
167167

168+
test "calling a function with none()" do
169+
assert typeerror!(Integer.to_string(raise "oops")) |> strip_ansi() ==
170+
~l"""
171+
incompatible types given to Integer.to_string/1:
172+
173+
Integer.to_string(raise RuntimeError.exception("oops"))
174+
175+
given types:
176+
177+
none()
178+
179+
the 1th argument is empty (often represented as none()), \
180+
most likely because it is the result of an expression that \
181+
always fails, such as a `raise` or a previous invalid call. \
182+
This causes any subsequent function call with said value \
183+
to always fail
184+
"""
185+
end
186+
168187
test "calling a nullary function on non atoms" do
169188
assert typeerror!([<<x::integer>>], x.foo_bar()) ==
170189
~l"""

0 commit comments

Comments
 (0)