From c6c4b045bbf7c7fb1ada2f6043db107e52a89ed1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= Date: Mon, 21 Feb 2022 13:14:33 +0100 Subject: [PATCH 1/4] Fix formatting call_intersect error --- lib/gradient/elixir_expr.ex | 10 ++++ test/examples/Elixir.CallRemoteException.beam | Bin 0 -> 2036 bytes test/examples/call_remote_exception.ex | 22 +++++++++ test/gradient/elixir_fmt_test.exs | 46 ++++++++++++++++++ test/support/expr_data.ex | 4 +- 5 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 test/examples/Elixir.CallRemoteException.beam create mode 100644 test/examples/call_remote_exception.ex diff --git a/lib/gradient/elixir_expr.ex b/lib/gradient/elixir_expr.ex index a28c30ca..d7429458 100644 --- a/lib/gradient/elixir_expr.ex +++ b/lib/gradient/elixir_expr.ex @@ -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} -> @@ -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 @@ -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) diff --git a/test/examples/Elixir.CallRemoteException.beam b/test/examples/Elixir.CallRemoteException.beam new file mode 100644 index 0000000000000000000000000000000000000000..8f6a7d1733ab3d954ce13c8d1752871b9dba2654 GIT binary patch literal 2036 zcmZ`)3s6&68ooC{UQvl4MG#%2r81QyphTof8^9C-7?jr{)FzkQn-EBHLvEt61qCTC zunh80fr?V8bO2F#DvzQCT@=cz3Q}DW5D^s^B~Xx3WzS9NY-eZo%zXDd=llQjpEGm* z`~RN>2Dw0x(WDpGbC0JwbO!`M1psC?T$xlWRWZF0nJfsC<7$kn6=6!X6jvAv1X6_< z7YIxdwOS>O(x@@QNQBFkQW<87VoDVzLev;a!?a3Vr6vS&lx-r`C`9C5!cdNgR5(Ee zqM;l|H8RW)Q^^p8#7u&z1%D{|@CG7qG6j#N{Y3*8^TtsOP&NP^fE;B2s{ssPdI3Z+ zx2MB)JE@jd#`LWkFHH~@6{_MM!*VpmY}lD&PPKqJ93KeKUXBkPkdQ+GUnPfX0gMF@ z-wuKk3+NQ{yLNCE=Ls-s zu=0QAFjuShaRs^ofEfS`uxfb@NWYIx!OE!jqd;1wjG0h>Ss=4S9lR z1mu`317A3rf=>1kh?DHJ*VxB{K2ET2mF{AZ=OO7JiGP&RMOw*Af_U~ z^Tt9e+?d;$Zh8&ZXA42bF+d?}1uh_z$RUMbIaO2wr3x2e1c5>(Vihh2x(Y)fGvG(g zfE80Bq_P?)h(?VIq;fK$V#wfz1g=quuvo~K6-t1!SxVKHnEWsyLk_V(ibxd_mPCc1 zQcR&{f%H_fFfAfi$}oaOrj_rh}w|67bH}Owy^H+gZM*d zdSH^Ss?~1W(`HD14)Ia)aKNq>Y6EB0LI zv~JwDZU*vQ8#{BPZ8)2D`S1rJiAMY%G6$mgw@W-Z(}C;fYcw@BO1O4x{8-%tgIYZL znHP%J4@M@3L??cH)%oRnyHXyxgqPpl94Z}h)5#e}r+!gL@;kqihebNI`ply54mmiw zB{Aynk4<&IP#oVC$u|GI&p*G*qSQ?{lkm zDJOr`@4Z#g&`ST_fY(q?KRk?2yiE*Fmd{U5EZ%;= zUn;#fQ1ve&fn7ILK6bw{De|URu}aW=2_YWt3|;waVN`dl*Y%;J5%CR6bJ94Uxi4SK z%ri{K8+W`d4c{~sLwG!tM;Lk7?4K#Kep`zA_>BhV#SCnb@KPA#4w2Q!!mSk-kGah!?w8>c2}J}_bBNGd}GIsDYLw#ZBAUX1xgUKePN@O zVYb5fwoCk=MV3eMT=CPG?Pz)9;?>!zlz8O3((tpcwZlAuc5X7ZWt>av_E)%N{icjswop>pQmW#f z9{!nqyWYOT$HvhS8nNYBX3bh8)H&B5n8WY80D#1Y) zHL$^;RxbC&U$kRAsxt_|LvimP$2}a00mhgl>xt4sR9KH6cdAT1qDbIWG{3E U_;VP63E3lH^G(pY|5xh%17*V>Qvd(} literal 0 HcmV?d00001 diff --git a/test/examples/call_remote_exception.ex b/test/examples/call_remote_exception.ex new file mode 100644 index 00000000..feaa1bf5 --- /dev/null +++ b/test/examples/call_remote_exception.ex @@ -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 diff --git a/test/gradient/elixir_fmt_test.exs b/test/gradient/elixir_fmt_test.exs index 78eafb14..575a5b36 100644 --- a/test/gradient/elixir_fmt_test.exs +++ b/test/gradient/elixir_fmt_test.exs @@ -239,6 +239,52 @@ defmodule Gradient.ElixirFmtTest do end end + 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()] diff --git a/test/support/expr_data.ex b/test/support/expr_data.ex index 7be58f73..c263e96c 100644 --- a/test/support/expr_data.ex +++ b/test/support/expr_data.ex @@ -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 From aaabb4de4a9908f9fdd87f88db6d3b4849cf8d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= Date: Mon, 21 Feb 2022 13:15:37 +0100 Subject: [PATCH 2/4] Add a testcase that pp_expr from call_remote_exception.ex example --- test/gradient/elixir_expr_test.exs | 65 ++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/test/gradient/elixir_expr_test.exs b/test/gradient/elixir_expr_test.exs index 619acf7b..6acdd6cd 100644 --- a/test/gradient/elixir_expr_test.exs +++ b/test/gradient/elixir_expr_test.exs @@ -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 From 0409ac2e5edebe42b374d53d5cdb51cdb760dd31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Wojtasik?= Date: Mon, 21 Feb 2022 13:36:52 +0100 Subject: [PATCH 3/4] Skip test that won't work on CI --- test/gradient/elixir_fmt_test.exs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/gradient/elixir_fmt_test.exs b/test/gradient/elixir_fmt_test.exs index 575a5b36..ebd374fb 100644 --- a/test/gradient/elixir_fmt_test.exs +++ b/test/gradient/elixir_fmt_test.exs @@ -239,6 +239,7 @@ defmodule Gradient.ElixirFmtTest do end end + @tag :skip test "format call_intersect error" do error = {:type_error, :call_intersect, 7, From 9504b3f1e266b29938708b02cfbd89bb10b3180a Mon Sep 17 00:00:00 2001 From: Radek Szymczyszyn Date: Wed, 2 Mar 2022 13:58:36 +0100 Subject: [PATCH 4/4] Don't use absolute paths --- test/gradient/elixir_expr_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/gradient/elixir_expr_test.exs b/test/gradient/elixir_expr_test.exs index 6acdd6cd..4fb27c27 100644 --- a/test/gradient/elixir_expr_test.exs +++ b/test/gradient/elixir_expr_test.exs @@ -329,7 +329,7 @@ defmodule Gradient.ElixirExprTest do test "pp and format complex try expression" do {_tokens, ast} = - Gradient.TestHelpers.load("/Elixir.CallRemoteException.beam", "/call_remote_exception.ex") + Gradient.TestHelpers.load("Elixir.CallRemoteException.beam", "call_remote_exception.ex") {:function, _, :call, 2, [{:clause, _ann, _args, _guards, [try_expr]}]} = Enum.reverse(ast) |> List.first()