Skip to content

Commit 8de27bf

Browse files
author
José Valim
committed
Clean up and speed up exceptions
1 parent 9118b36 commit 8de27bf

File tree

5 files changed

+117
-121
lines changed

5 files changed

+117
-121
lines changed

lib/elixir/lib/exception.ex

Lines changed: 110 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,94 @@
1+
defexception RuntimeError, message: "runtime error"
2+
defexception ArgumentError, message: "argument error"
3+
defexception ArithmeticError, message: "bad argument in arithmetic expression"
4+
defexception SystemLimitError, message: "a system limit has been reached"
5+
6+
defexception SyntaxError, [file: nil, line: nil, description: "syntax error"] do
7+
def message(exception) do
8+
"#{Exception.format_file_line(exception.file, exception.line, nil)}#{exception.description}"
9+
end
10+
end
11+
12+
defexception TokenMissingError, [file: nil, line: nil, description: "expression is incomplete"] do
13+
def message(exception) do
14+
"#{Exception.format_file_line(exception.file, exception.line, nil)}#{exception.description}"
15+
end
16+
end
17+
18+
defexception CompileError, [file: nil, line: nil, description: "compile error"] do
19+
def message(exception) do
20+
"#{Exception.format_file_line(exception.file, exception.line, nil)}#{exception.description}"
21+
end
22+
end
23+
24+
defexception BadFunctionError, [actual: nil] do
25+
def message(exception) do
26+
"expected a function, got: #{inspect(exception.actual)}"
27+
end
28+
end
29+
30+
defexception MatchError, [actual: nil] do
31+
def message(exception) do
32+
"no match of right hand side value: #{inspect(exception.actual)}"
33+
end
34+
end
35+
36+
defexception CaseClauseError, [actual: nil] do
37+
def message(exception) do
38+
"no case clause matching: #{inspect(exception.actual)}"
39+
end
40+
end
41+
42+
defexception BadArityError, [function: nil, args: nil] do
43+
def message(exception) do
44+
"bad arity error: #{inspect(exception.function)} called with #{inspect(exception.args)}"
45+
end
46+
end
47+
48+
defexception UndefinedFunctionError, [module: nil, function: nil, arity: nil] do
49+
def message(exception) do
50+
if exception.function do
51+
formatted = Exception.format_module_fun_arity exception.module, exception.function, exception.arity
52+
"undefined function: #{formatted}"
53+
else
54+
"undefined function"
55+
end
56+
end
57+
end
58+
59+
defexception FunctionClauseError, [module: nil, function: nil, arity: nil] do
60+
def message(exception) do
61+
if exception.function do
62+
formatted = Exception.format_module_fun_arity exception.module, exception.function, exception.arity
63+
"no function clause matching in #{formatted}"
64+
else
65+
"no function clause matches"
66+
end
67+
end
68+
end
69+
70+
defexception Protocol.UndefinedError, [protocol: nil, structure: nil] do
71+
def message(exception) do
72+
"protocol #{inspect exception.protocol} not implemented for #{inspect exception.structure}"
73+
end
74+
end
75+
76+
defexception ErlangError, [original: nil] do
77+
def message(exception) do
78+
"erlang error: #{inspect(exception.original)}"
79+
end
80+
end
81+
82+
defexception KeyError, key: nil do
83+
def message(exception) do
84+
"key not found: #{inspect exception.key}"
85+
end
86+
end
87+
88+
defexception Enum.OutOfBoundsError, message: "out of bounds error"
89+
90+
defexception Enum.EmptyError, message: "empty error"
91+
192
defmodule Exception do
293
@moduledoc """
394
Several convenience functions to work and pretty print
@@ -14,47 +105,49 @@ defmodule Exception do
14105
end
15106

16107
def normalize(:badarg) do
17-
ArgumentError.new
108+
ArgumentError[]
18109
end
19110

20111
def normalize(:badarith) do
21-
ArithmeticError.new
112+
ArithmeticError[]
22113
end
23114

24115
def normalize(:system_limit) do
25-
SystemLimitError.new
116+
SystemLimitError[]
26117
end
27118

28119
def normalize({ :badarity, { fun, args } }) do
29-
BadArityError.new(function: fun, args: args)
120+
BadArityError[function: fun, args: args]
30121
end
31122

32123
def normalize({ :badfun, actual }) do
33-
BadFunctionError.new(actual: actual)
124+
BadFunctionError[actual: actual]
34125
end
35126

36127
def normalize({ :badmatch, actual }) do
37-
MatchError.new(actual: actual)
128+
MatchError[actual: actual]
38129
end
39130

40131
def normalize({ :case_clause, actual }) do
41-
CaseClauseError.new(actual: actual)
132+
CaseClauseError[actual: actual]
42133
end
43134

44135
def normalize(:undef) do
45-
UndefinedFunctionError.new from_stacktrace(:erlang.get_stacktrace)
136+
{ mod, fun, arity } = from_stacktrace(:erlang.get_stacktrace)
137+
UndefinedFunctionError[module: mod, function: fun, arity: arity]
46138
end
47139

48140
def normalize(:function_clause) do
49-
FunctionClauseError.new from_stacktrace(:erlang.get_stacktrace)
141+
{ mod, fun, arity } = from_stacktrace(:erlang.get_stacktrace)
142+
FunctionClauseError[module: mod, function: fun, arity: arity]
50143
end
51144

52145
def normalize({ :badarg, payload }) do
53-
ArgumentError.new message: "argument error: #{inspect(payload)}"
146+
ArgumentError[message: "argument error: #{inspect(payload)}"]
54147
end
55148

56149
def normalize(other) do
57-
ErlangError.new original: other
150+
ErlangError[original: other]
58151
end
59152

60153
@doc """
@@ -122,14 +215,6 @@ defmodule Exception do
122215

123216
## Helpers
124217

125-
# Check the given module is a valid exception record.
126-
@doc false
127-
def check!(module) do
128-
unless :erlang.function_exported(module, :message, 1) do
129-
raise "Expected #{inspect module} to implement message/1"
130-
end
131-
end
132-
133218
# Format fun and arity
134219
@doc false
135220
def format_fun_arity(fun, arity) do
@@ -184,103 +269,15 @@ defmodule Exception do
184269
end
185270
end
186271

187-
defp from_stacktrace([{ module, function, arity, _ }|_]) do
188-
[module: module, function: function, arity: arity]
189-
end
190-
191-
defp from_stacktrace(_), do: []
192-
end
193-
194-
defexception RuntimeError, message: "runtime error"
195-
defexception ArgumentError, message: "argument error"
196-
defexception ArithmeticError, message: "bad argument in arithmetic expression"
197-
defexception SystemLimitError, message: "a system limit has been reached"
198-
199-
defexception SyntaxError, [file: nil, line: nil, description: "syntax error"] do
200-
def message(exception) do
201-
"#{Exception.format_file_line(exception.file, exception.line, nil)}#{exception.description}"
202-
end
203-
end
204-
205-
defexception TokenMissingError, [file: nil, line: nil, description: "expression is incomplete"] do
206-
def message(exception) do
207-
"#{Exception.format_file_line(exception.file, exception.line, nil)}#{exception.description}"
208-
end
209-
end
210-
211-
defexception CompileError, [file: nil, line: nil, description: "compile error"] do
212-
def message(exception) do
213-
"#{Exception.format_file_line(exception.file, exception.line, nil)}#{exception.description}"
214-
end
215-
end
216-
217-
defexception BadFunctionError, [actual: nil] do
218-
def message(exception) do
219-
"expected a function, got: #{inspect(exception.actual)}"
272+
defp from_stacktrace([{ module, function, args, _ }|_]) when is_list(args) do
273+
{ module, function, length(args) }
220274
end
221-
end
222-
223-
defexception MatchError, [actual: nil] do
224-
def message(exception) do
225-
"no match of right hand side value: #{inspect(exception.actual)}"
226-
end
227-
end
228-
229-
defexception CaseClauseError, [actual: nil] do
230-
def message(exception) do
231-
"no case clause matching: #{inspect(exception.actual)}"
232-
end
233-
end
234275

235-
defexception BadArityError, [function: nil, args: nil] do
236-
def message(exception) do
237-
"bad arity error: #{inspect(exception.function)} called with #{inspect(exception.args)}"
238-
end
239-
end
240-
241-
defexception UndefinedFunctionError, [module: nil, function: nil, arity: nil] do
242-
def message(exception) do
243-
if exception.function do
244-
formatted = Exception.format_module_fun_arity exception.module, exception.function, to_arity(exception.arity)
245-
"undefined function: #{formatted}"
246-
else
247-
"undefined function"
248-
end
249-
end
250-
251-
defp to_arity(arity) when is_integer(arity), do: arity
252-
defp to_arity(list) when is_list(list), do: length(list)
253-
end
254-
255-
defexception FunctionClauseError, [module: nil, function: nil, arity: nil] do
256-
def message(exception) do
257-
if exception.function do
258-
formatted = Exception.format_module_fun_arity exception.module, exception.function, exception.arity
259-
"no function clause matching: #{formatted}"
260-
else
261-
"no function clause matches"
262-
end
263-
end
264-
end
265-
266-
defexception Protocol.UndefinedError, [protocol: nil, structure: nil] do
267-
def message(exception) do
268-
"protocol #{inspect exception.protocol} not implemented for #{inspect exception.structure}"
269-
end
270-
end
271-
272-
defexception ErlangError, [original: nil] do
273-
def message(exception) do
274-
"erlang error: #{inspect(exception.original)}"
276+
defp from_stacktrace([{ module, function, arity, _ }|_]) do
277+
{ module, function, arity }
275278
end
276-
end
277279

278-
defexception KeyError, key: nil do
279-
def message(exception) do
280-
"key not found: #{inspect exception.key}"
280+
defp from_stacktrace(_) do
281+
{ nil, nil, nil }
281282
end
282283
end
283-
284-
defexception Enum.OutOfBoundsError, message: "out of bounds error"
285-
286-
defexception Enum.EmptyError, message: "empty error"

lib/elixir/lib/kernel.ex

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1569,7 +1569,10 @@ defmodule Kernel do
15691569

15701570
quote do
15711571
unquote(record)
1572-
Exception.check! unquote(name)
1572+
name = unquote(name)
1573+
unless :erlang.function_exported(name, :message, 1) do
1574+
raise "expected exception #{inspect name} to implement message/1"
1575+
end
15731576
end
15741577
end
15751578

lib/elixir/test/elixir/exception_test.exs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,11 @@ defmodule Kernel.ExceptionTest do
6262
test :undefined_function_message do
6363
assert UndefinedFunctionError.new.message == "undefined function"
6464
assert UndefinedFunctionError.new(module: Foo, function: :bar, arity: 1).message == "undefined function: Foo.bar/1"
65-
assert UndefinedFunctionError.new(module: Foo, function: :bar, arity: []).message == "undefined function: Foo.bar/0"
66-
assert UndefinedFunctionError.new(module: :foo, function: :bar, arity: []).message == "undefined function: :foo.bar/0"
6765
end
6866

6967
test :function_clause_message do
7068
assert FunctionClauseError.new.message == "no function clause matches"
71-
assert FunctionClauseError.new(module: Foo, function: :bar, arity: 1).message == "no function clause matching: Foo.bar/1"
72-
assert FunctionClauseError.new(module: Foo, function: :bar, arity: []).message == "no function clause matching: Foo.bar()"
73-
assert FunctionClauseError.new(module: :foo, function: :bar, arity: []).message == "no function clause matching: :foo.bar()"
69+
assert FunctionClauseError.new(module: Foo, function: :bar, arity: 1).message == "no function clause matching in Foo.bar/1"
7470
end
7571

7672
test :erlang_error_message do

lib/elixir/test/elixir/kernel/rescue_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ defmodule Kernel.RescueTest do
146146
x in [FunctionClauseError] -> x.message
147147
end
148148

149-
assert result == "no function clause matching: Kernel.RescueTest.zero(1)"
149+
assert result == "no function clause matching in Kernel.RescueTest.zero/1"
150150
end
151151

152152
test :badarg_error do

lib/ex_unit/test/ex_unit/assertions_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ defmodule ExUnit.AssertionsTest do
219219
end
220220
rescue
221221
error in [ExUnit.AssertionError] ->
222-
"Expected exception SyntaxError, got FunctionClauseError (no function clause matching: :lists.flatten(1))" = error.message
222+
"Expected exception SyntaxError, got FunctionClauseError (no function clause matching in :lists.flatten/1)" = error.message
223223
end
224224

225225
test :assert_operator_greater_pass do

0 commit comments

Comments
 (0)