Current section

Files

Jump to
zigler lib zig error_prong.ex
Raw

lib/zig/error_prong.ex

# credo:disable-for-this-file Credo.Check.Refactor.CyclomaticComplexity
defmodule Zig.ErrorProng do
@moduledoc false
# default parameter errors handling.
def argument_error_prong(:elixir, file, line) do
quote do
:error, {:badarg, index, error_lines} ->
new_stacktrace =
case __STACKTRACE__ do
# this module is only created when the function is being marshalled.
[{_m, _f, a, _}, {m, f, _a, opts} | rest] ->
indentation = &["\n ", List.duplicate("| ", &1)]
new_opts =
Keyword.merge(opts,
error_info: %{module: __MODULE__, function: :_format_error},
line: unquote(line),
file: unquote(file),
zigler_error: %{
(index + 1) =>
error_lines
|> Enum.reduce({[], 0}, fn
:enter, {so_far, indents} ->
{so_far, indents + 1}
error_line, {so_far, indents} ->
error_msg =
error_line
|> Tuple.to_list()
|> Enum.map(fn
list when is_list(list) ->
list
string when is_binary(string) ->
string
{:inspect, content} ->
"#{inspect(content, custom_options: [sort_maps: true])}"
{:typename, typename} ->
String.replace(typename, ".#{__MODULE__}.", "")
end)
|> List.wrap()
|> List.insert_at(0, indentation.(indents))
{[error_msg | so_far], indents}
end)
|> elem(0)
|> Enum.reverse()
|> List.insert_at(0, "\n")
|> IO.iodata_to_binary()
}
)
[{m, f, a, new_opts} | rest]
stacktrace ->
stacktrace
end
:erlang.raise(:error, :badarg, new_stacktrace)
end
end
def argument_error_prong(:erlang) do
[
"error:{error, badarg, _ExtraStacktrace}:Stacktrace -> erlang:raise(error, badarg, Stacktrace)"
]
end
def return_error_prong(:elixir, ignored, function_name, line_number) do
stacktrace_prep =
case ignored do
[] ->
quote do
__STACKTRACE__
end
list when is_list(list) ->
quote do
Enum.reject(
__STACKTRACE__,
&match?({__MODULE__, ignore, _, _} when ignore in unquote(ignored), &1)
)
end
end
quote do
:error, {:error, type, nil} ->
fun_name = unquote(function_name)
case unquote(stacktrace_prep) do
[{__MODULE__, ^fun_name, arity, opts} | rest] ->
new_opts = Keyword.replace_lazy(opts, :line, fn _ -> unquote(line_number) end)
:erlang.raise(:error, type, [{__MODULE__, fun_name, arity, new_opts} | rest])
other ->
:erlang.raise(:error, type, other)
end
:error, {:error, type, extra_stacktrace} ->
stacktrace = unquote(stacktrace_prep)
new_stacktrace =
extra_stacktrace
|> Enum.map(fn %{
compile_unit_name: module_str,
symbol_name: fn_str,
source_location: source_location
} ->
{file, line} = __resolve(source_location)
# Normalize Windows paths from debug info to match Elixir's Path.expand format:
# 1. Convert backslashes to forward slashes
# 2. Lowercase drive letter (debug info returns "D:/..." but Path.expand returns "d:/...")
file =
case file do
<<drive, ":/", rest::binary>> when drive in ?A..?Z ->
<<drive + 32, ":/", rest::binary>>
<<drive, ":\\", rest::binary>> when drive in ?A..?Z ->
<<drive + 32, ":/", String.replace(rest, "\\", "/")::binary>>
path when is_binary(path) ->
String.replace(path, "\\", "/")
other ->
other
end
module =
if module_str do
# Normalize compile_unit_name to an Elixir module atom:
# - Windows PDB: "Elixir.MyModule_zcu.obj" → strip "_zcu.obj" suffix
# - Native backend: ".Elixir.MyModule.zig" → strip leading "." and ".zig" suffix
# - LLVM backend: "Elixir.MyModule" → use as-is
module_str
|> String.replace_suffix("_zcu.obj", "")
|> String.replace_suffix(".zig", "")
|> String.replace_prefix(".", "")
|> String.to_atom()
else
:unknown
end
func = if(fn_str, do: String.to_atom(fn_str), else: :unknown)
{module, func, [:...], [file: file, line: line]}
end)
|> Enum.reverse(stacktrace)
:erlang.raise(:error, type, new_stacktrace)
end
end
def return_error_prong(:erlang, _, _, _) do
["error:{error, Type, _ExtraStacktrace}:Stacktrace -> erlang:raise(error, Type, Stacktrace)"]
end
end