Skip to content

Commit 8bd43ed

Browse files
committed
Merge pull request #1726 from yrashk/raise-macro
Kernel.raise should be a macro to avoid messing up the stack trace
2 parents f8c66c8 + bcf8563 commit 8bd43ed

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
@@ -1994,8 +1992,7 @@ defmodule Kernel do
19941992

19951993
case is_atom(expanded) do
19961994
false ->
1997-
raise ArgumentError,
1998-
message: "invalid arguments for use, expected an atom or alias as argument"
1995+
:erlang.error ArgumentError.exception(message: "invalid arguments for use, expected an atom or alias as argument")
19991996
true ->
20001997
quote do
20011998
require unquote(expanded)
@@ -2632,8 +2629,8 @@ defmodule Kernel do
26322629
new_acc =
26332630
case condition do
26342631
{ :_, _, atom } when is_atom(atom) ->
2635-
raise ArgumentError, message: <<"unbound variable _ inside cond. ",
2636-
"If you want the last clause to match, you probably meant to use true ->">>
2632+
:erlang.error ArgumentError.exception(message: <<"unbound variable _ inside cond. ",
2633+
"If you want the last clause to match, you probably meant to use true ->">>)
26372634
x when is_atom(x) and not x in [false, nil] ->
26382635
clause
26392636
_ ->
@@ -3075,7 +3072,7 @@ defmodule Kernel do
30753072
end
30763073

30773074
defp pipeline_op(_, arg) do
3078-
raise ArgumentError, message: "unsupported expression in pipeline |> operator: #{Macro.to_string arg}"
3075+
:erlang.error ArgumentError.exception(message: "unsupported expression in pipeline |> operator: #{Macro.to_string arg}")
30793076
end
30803077

30813078
@doc """
@@ -3100,12 +3097,10 @@ defmodule Kernel do
31003097
31013098
"""
31023099
@spec raise(binary | atom | tuple) :: no_return
3103-
def raise(msg) when is_binary(msg) do
3104-
:erlang.error RuntimeError[message: msg]
3105-
end
3106-
3107-
def raise(exception) do
3108-
raise(exception, [])
3100+
defmacro raise(msg) when is_binary(msg) do
3101+
quote do
3102+
:erlang.error RuntimeError[message: unquote(msg)]
3103+
end
31093104
end
31103105

31113106
@doc """
@@ -3126,8 +3121,16 @@ defmodule Kernel do
31263121
31273122
"""
31283123
@spec raise(tuple | atom, list) :: no_return
3129-
def raise(exception, args) do
3130-
:erlang.error exception.exception(args)
3124+
defmacro raise(exception, args // []) do
3125+
quote do
3126+
exception = unquote(exception)
3127+
case exception do
3128+
e when is_binary(e) ->
3129+
:erlang.error RuntimeError.new(message: exception)
3130+
_ ->
3131+
:erlang.error exception.exception(unquote(args))
3132+
end
3133+
end
31313134
end
31323135

31333136
@doc """
@@ -3268,15 +3271,15 @@ defmodule Kernel do
32683271
{ :error, _ } ->
32693272
:elixir_aliases.ensure_loaded(caller.line, caller.file, atom, caller.context_modules)
32703273
_ ->
3271-
raise ArgumentError, message: "cannot use module #{inspect atom} in access protocol because it does not export __record__/1"
3274+
:erlang.error ArgumentError.exception(message: "cannot use module #{inspect atom} in access protocol because it does not export __record__/1")
32723275
end
32733276
end
32743277

32753278
Record.access(atom, fields, args, caller)
32763279
false ->
32773280
case caller.in_match? do
3278-
true -> raise ArgumentError, message: << "the access protocol cannot be used inside match clauses ",
3279-
"(for example, on the left hand side of a match or in function signatures)" >>
3281+
true -> :erlang.error ArgumentError.exception(message: << "the access protocol cannot be used inside match clauses ",
3282+
"(for example, on the left hand side of a match or in function signatures)" >>)
32803283
false -> quote do: Access.access(unquote(element), unquote(args))
32813284
end
32823285
end
@@ -3327,14 +3330,14 @@ defmodule Kernel do
33273330
funs = Macro.escape(funs, unquote: true)
33283331
quote bind_quoted: [funs: funs, opts: opts] do
33293332
target = Keyword.get(opts, :to) ||
3330-
raise(ArgumentError, message: "Expected to: to be given as argument")
3333+
:erlang.error ArgumentError.exception(message: "Expected to: to be given as argument")
33313334

33323335
append_first = Keyword.get(opts, :append_first, false)
33333336

33343337
lc fun inlist List.wrap(funs) do
33353338
case Macro.extract_args(fun) do
33363339
{ name, args } -> :ok
3337-
:error -> raise ArgumentError, message: "invalid syntax in defdelegate #{Macro.to_string(fun)}"
3340+
:error -> :erlang.error ArgumentError.exception(message: "invalid syntax in defdelegate #{Macro.to_string(fun)}")
33383341
end
33393342

33403343
actual_args =
@@ -3581,7 +3584,7 @@ defmodule Kernel do
35813584
mod = case modifiers do
35823585
[] -> ?s
35833586
[mod] when mod in [?s, ?a, ?c] -> mod
3584-
_else -> raise ArgumentError, message: "modifier must be one of: s, a, c"
3587+
_else -> :erlang.error ArgumentError.exception(message: "modifier must be one of: s, a, c")
35853588
end
35863589

35873590
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)