Skip to content

Commit bcf8563

Browse files
committed
Kernel.raise should be a macro to avoid messing up the stack trace
1 parent e654f73 commit bcf8563

File tree

2 files changed

+37
-21
lines changed

2 files changed

+37
-21
lines changed

lib/elixir/lib/kernel.ex

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import Kernel, except: [raise: 1, raise: 2]
2-
31
defmodule Kernel do
42
@moduledoc """
53
`Kernel` provides the default macros and functions
@@ -2015,8 +2013,7 @@ defmodule Kernel do
20152013

20162014
case is_atom(expanded) do
20172015
false ->
2018-
raise ArgumentError,
2019-
message: "invalid arguments for use, expected an atom or alias as argument"
2016+
:erlang.error ArgumentError.exception(message: "invalid arguments for use, expected an atom or alias as argument")
20202017
true ->
20212018
quote do
20222019
require unquote(expanded)
@@ -2652,8 +2649,8 @@ defmodule Kernel do
26522649
new_acc =
26532650
case condition do
26542651
{ :_, _, atom } when is_atom(atom) ->
2655-
raise ArgumentError, message: <<"unbound variable _ inside cond. ",
2656-
"If you want the last clause to match, you probably meant to use true ->">>
2652+
:erlang.error ArgumentError.exception(message: <<"unbound variable _ inside cond. ",
2653+
"If you want the last clause to match, you probably meant to use true ->">>)
26572654
x when is_atom(x) and not x in [false, nil] ->
26582655
clause
26592656
_ ->
@@ -3095,7 +3092,7 @@ defmodule Kernel do
30953092
end
30963093

30973094
defp pipeline_op(_, arg) do
3098-
raise ArgumentError, message: "unsupported expression in pipeline |> operator: #{Macro.to_string arg}"
3095+
:erlang.error ArgumentError.exception(message: "unsupported expression in pipeline |> operator: #{Macro.to_string arg}")
30993096
end
31003097

31013098
@doc """
@@ -3120,12 +3117,10 @@ defmodule Kernel do
31203117
31213118
"""
31223119
@spec raise(binary | atom | tuple) :: no_return
3123-
def raise(msg) when is_binary(msg) do
3124-
:erlang.error RuntimeError[message: msg]
3125-
end
3126-
3127-
def raise(exception) do
3128-
raise(exception, [])
3120+
defmacro raise(msg) when is_binary(msg) do
3121+
quote do
3122+
:erlang.error RuntimeError[message: unquote(msg)]
3123+
end
31293124
end
31303125

31313126
@doc """
@@ -3146,8 +3141,16 @@ defmodule Kernel do
31463141
31473142
"""
31483143
@spec raise(tuple | atom, list) :: no_return
3149-
def raise(exception, args) do
3150-
:erlang.error exception.exception(args)
3144+
defmacro raise(exception, args // []) do
3145+
quote do
3146+
exception = unquote(exception)
3147+
case exception do
3148+
e when is_binary(e) ->
3149+
:erlang.error RuntimeError.new(message: exception)
3150+
_ ->
3151+
:erlang.error exception.exception(unquote(args))
3152+
end
3153+
end
31513154
end
31523155

31533156
@doc """
@@ -3288,15 +3291,15 @@ defmodule Kernel do
32883291
{ :error, _ } ->
32893292
:elixir_aliases.ensure_loaded(caller.line, caller.file, atom, caller.context_modules)
32903293
_ ->
3291-
raise ArgumentError, message: "cannot use module #{inspect atom} in access protocol because it does not export __record__/1"
3294+
:erlang.error ArgumentError.exception(message: "cannot use module #{inspect atom} in access protocol because it does not export __record__/1")
32923295
end
32933296
end
32943297

32953298
Record.access(atom, fields, args, caller)
32963299
false ->
32973300
case caller.in_match? do
3298-
true -> raise ArgumentError, message: << "the access protocol cannot be used inside match clauses ",
3299-
"(for example, on the left hand side of a match or in function signatures)" >>
3301+
true -> :erlang.error ArgumentError.exception(message: << "the access protocol cannot be used inside match clauses ",
3302+
"(for example, on the left hand side of a match or in function signatures)" >>)
33003303
false -> quote do: Access.access(unquote(element), unquote(args))
33013304
end
33023305
end
@@ -3347,14 +3350,14 @@ defmodule Kernel do
33473350
funs = Macro.escape(funs, unquote: true)
33483351
quote bind_quoted: [funs: funs, opts: opts] do
33493352
target = Keyword.get(opts, :to) ||
3350-
raise(ArgumentError, message: "Expected to: to be given as argument")
3353+
:erlang.error ArgumentError.exception(message: "Expected to: to be given as argument")
33513354

33523355
append_first = Keyword.get(opts, :append_first, false)
33533356

33543357
lc fun inlist List.wrap(funs) do
33553358
case Macro.extract_args(fun) do
33563359
{ name, args } -> :ok
3357-
:error -> raise ArgumentError, message: "invalid syntax in defdelegate #{Macro.to_string(fun)}"
3360+
:error -> :erlang.error ArgumentError.exception(message: "invalid syntax in defdelegate #{Macro.to_string(fun)}")
33583361
end
33593362

33603363
actual_args =
@@ -3616,7 +3619,7 @@ defmodule Kernel do
36163619
IO.write "%w()b is deprecated, please use %w()s instead\n#{Exception.format_stacktrace}"
36173620
?s
36183621
[mod] when mod in [?s, ?a, ?c] -> mod
3619-
_else -> raise ArgumentError, message: "modifier must be one of: s, a, c"
3622+
_else -> :erlang.error ArgumentError.exception(message: "modifier must be one of: s, a, c")
36203623
end
36213624

36223625
case is_binary(string) do

lib/elixir/test/elixir/exception_test.exs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,19 @@ defmodule Kernel.ExceptionTest do
8484
assert ErlangError.new(original: :sample).message == "erlang error: :sample"
8585
end
8686

87+
test :raise_preserves_the_stacktrace do
88+
stacktrace =
89+
try do
90+
raise "a"
91+
rescue _ ->
92+
[top|_] = System.stacktrace
93+
top
94+
end
95+
file = to_char_list(__FILE__)
96+
assert {Kernel.ExceptionTest, :test_raise_preserves_the_stacktrace, _,
97+
[file: ^file, line: 90]} = stacktrace # line #90 is sensitive
98+
end
99+
87100
defp empty_tuple, do: {}
88101
defp a_tuple, do: { :foo, :bar, :baz }
89102
defp a_list, do: [ :foo, :bar, :baz ]

0 commit comments

Comments
 (0)