Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions lib/gradient/elixir_expr.ex
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ defmodule Gradient.ElixirExpr do
"\'" <> List.to_string(charlist) <> "\'"
end

def pp_expr({:remote, _, _, _} = remote_name) do
pp_name(remote_name)
end

def pp_expr({:cons, _, _, _} = cons) do
case cons_to_int_list(cons) do
{:ok, l} ->
Expand Down Expand Up @@ -291,6 +295,9 @@ defmodule Gradient.ElixirExpr do
{:throw, :not_found} ->
# throw
pp_expr(type) <> ", " <> pp_expr(var) <> " -> " <> pp_expr(body)

{_variable, :not_found} ->
pp_expr(type) <> ", " <> pp_expr(var) <> " -> " <> pp_expr(body)
end
end

Expand Down Expand Up @@ -482,6 +489,9 @@ defmodule Gradient.ElixirExpr do
defp pp_cons({:cons, _, h, {:var, _, _} = v}), do: pp_expr(h) <> " | " <> pp_expr(v)
defp pp_cons({:cons, _, h, t}), do: pp_expr(h) <> ", " <> pp_cons(t)

defp pp_name({:remote, _, {:var, _, _} = var, {:atom, _, n}}),
do: pp_expr(var) <> "." <> to_string(n)

defp pp_name({:remote, _, {:atom, _, m}, {:atom, _, n}}),
do: ElixirFmt.parse_module(m) <> to_string(n)

Expand Down
Binary file added test/examples/Elixir.CallRemoteException.beam
Binary file not shown.
22 changes: 22 additions & 0 deletions test/examples/call_remote_exception.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
defmodule CallRemoteException do
def call(conn, opts) do
try do
:ok
rescue
e in Plug.Conn.WrapperError ->
exception = Exception.normalize(:error, e.reason, e.stack)
_ = Sentry.capture_exception(exception, stacktrace: e.stack, event_source: :plug)
Plug.Conn.WrapperError.reraise(e)

e ->
_ = Sentry.capture_exception(e, stacktrace: __STACKTRACE__, event_source: :plug)
:erlang.raise(:error, e, __STACKTRACE__)
catch
kind, reason ->
message = "Uncaught #{kind} - #{inspect(reason)}"
stack = __STACKTRACE__
_ = Sentry.capture_message(message, stacktrace: stack, event_source: :plug)
:erlang.raise(kind, reason, stack)
end
end
end
65 changes: 65 additions & 0 deletions test/gradient/elixir_expr_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -326,4 +326,69 @@ defmodule Gradient.ElixirExprTest do
actual
end
end

test "pp and format complex try expression" do
{_tokens, ast} =
Gradient.TestHelpers.load("Elixir.CallRemoteException.beam", "call_remote_exception.ex")

{:function, _, :call, 2, [{:clause, _ann, _args, _guards, [try_expr]}]} =
Enum.reverse(ast) |> List.first()

res = ElixirExpr.pp_expr_format(try_expr)

# FIXME `raise {:badkey, :stack, _gen}` is not correct

expected = ~s"""
try do
:ok
catch
:error, %Plug.Conn.WrapperError{} = e ->
exception =
Exception.normalize(
:error,
case e do
%{reason: _gen} -> _gen
_gen when :erlang.is_map(_gen) -> raise {:badkey, :reason, _gen}
_gen -> _gen.reason()
end,
case e do
%{stack: _gen} -> _gen
_gen when :erlang.is_map(_gen) -> raise {:badkey, :stack, _gen}
_gen -> _gen.stack()
end
)

_ =
Sentry.capture_exception(exception, [
{:stacktrace,
case e do
%{stack: _gen} -> _gen
_gen when :erlang.is_map(_gen) -> raise {:badkey, :stack, _gen}
_gen -> _gen.stack()
end},
{:event_source, :plug}
])

Plug.Conn.WrapperError.reraise(e)

:error, e ->
_ = Sentry.capture_exception(e, [{:stacktrace, __STACKTRACE__}, {:event_source, :plug}])
:erlang.raise(:error, e, __STACKTRACE__)

kind, reason ->
message =
<<"Uncaught ",
case kind do
_gen when :erlang.is_binary(_gen) -> _gen
_gen -> String.Chars.to_string(_gen)
end::binary, " - ", Kernel.inspect(reason)::binary>>

stack = __STACKTRACE__
_ = Sentry.capture_message(message, [{:stacktrace, stack}, {:event_source, :plug}])
:erlang.raise(kind, reason, stack)
end
"""

assert expected == :erlang.iolist_to_binary(res) <> "\n"
end
end
47 changes: 47 additions & 0 deletions test/gradient/elixir_fmt_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,53 @@ defmodule Gradient.ElixirFmtTest do
end
end

@tag :skip
test "format call_intersect error" do
error =
{:type_error, :call_intersect, 7,
[
{:type, 0, :bounded_fun,
[
{:type, 0, :fun,
[
{:type, 0, :product,
[
{:atom, 0, :error},
{:type, 0, :any, []},
{:user_type, [file: 'Elixir.Exception.erl', location: 0], :stacktrace, []}
]},
{:user_type, [file: 'Elixir.Exception.erl', location: 0], :t, []}
]},
[]
]},
{:type, 0, :bounded_fun,
[
{:type, 0, :fun,
[
{:type, 0, :product,
[
{:user_type, [file: 'Elixir.Exception.erl', location: 0], :non_error_kind, []},
{:var, 0, :payload},
{:user_type, [file: 'Elixir.Exception.erl', location: 0], :stacktrace, []}
]},
{:var, 0, :payload}
]},
[]
]}
], {:remote, 7, {:atom, 7, Exception}, {:atom, 7, :normalize}}}

res = ElixirFmt.format_error(error, [])

expected = ~s"""
The type of the function Exception.normalize, called on line 7 doesn't match the surrounding calling context.
It has the following type
\e[35m(:error, any(), stacktrace() -> t())\e[0m
\e[35m(non_error_kind(), payload, stacktrace() -> payload)\e[0m\n
"""

assert expected == :erlang.iolist_to_binary(res)
end

@tag :skip
test "format_expr_type_error/4" do
opts = [forms: basic_erlang_forms()]
Expand Down
4 changes: 3 additions & 1 deletion test/support/expr_data.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ defmodule Gradient.ExprData do
{"char", {:char, 0, ?c}, "?c"},
{"float", {:float, 0, 12.0}, "12.0"},
{"integer", {:integer, 0, 1}, "1"},
{"erlang string", {:string, 0, 'ala ma kota'}, ~s('ala ma kota')}
{"erlang string", {:string, 0, 'ala ma kota'}, ~s('ala ma kota')},
{"remote name", {:remote, 7, {:atom, 7, Exception}, {:atom, 7, :normalize}},
"Exception.normalize"}
]
end

Expand Down