Files
n8n-openai-adapter/lib/elixir/src/elixir_errors.erl
T

156 lines
5.4 KiB
Erlang

% A bunch of helpers to help to deal with errors in Elixir source code.
% This is not exposed in the Elixir language.
-module(elixir_errors).
-export([syntax_error/3, syntax_error/4, inspect/1,
form_error/4, parse_error/4, assert_module_scope/3,
assert_no_function_scope/3, assert_function_scope/3,
assert_no_assign_scope/3, assert_no_guard_scope/3,
assert_no_assign_or_guard_scope/3,
handle_file_warning/2, handle_file_warning/3, handle_file_error/2,
deprecation/3, deprecation/4, file_format/3]).
-include("elixir.hrl").
-compile({parse_transform, elixir_transform}).
%% Handle inspecting for exceptions
inspect(Atom) when is_atom(Atom) ->
case atom_to_list(Atom) of
"Elixir-" ++ Rest -> [to_dot(R) || R <- Rest];
Else -> Else
end;
inspect(Other) -> Other.
to_dot($-) -> $.;
to_dot(L) -> L.
%% Raised during macros translation.
syntax_error(Line, File, Message) when is_list(Message) ->
syntax_error(Line, File, iolist_to_binary(Message));
syntax_error(Line, File, Message) when is_binary(Message) ->
raise(Line, File, 'Elixir.SyntaxError', Message).
syntax_error(Line, File, Format, Args) ->
Message = io_lib:format(Format, Args),
raise(Line, File, 'Elixir.SyntaxError', iolist_to_binary(Message)).
%% Raised on tokenizing/parsing
parse_error(Line, File, Error, []) ->
Message = case Error of
"syntax error before: " -> <<"syntax error: expression is incomplete">>;
_ -> iolist_to_binary(Error)
end,
raise(Line, File, 'Elixir.TokenMissingError', Message);
parse_error(Line, File, "syntax error before: ", "'end'") ->
raise(Line, File, 'Elixir.SyntaxError', <<"unexpected token: end">>);
parse_error(Line, File, Error, Token) ->
BinError = if
is_atom(Error) -> atom_to_binary(Error, utf8);
true -> iolist_to_binary(Error)
end,
BinToken = if
Token == [] -> <<>>;
true -> iolist_to_binary(Token)
end,
Message = <<BinError / binary, BinToken / binary >>,
raise(Line, File, 'Elixir.SyntaxError', Message).
%% Raised during compilation
form_error(Line, File, Module, Desc) ->
Message = iolist_to_binary(format_error(Module, Desc)),
raise(Line, File, 'Elixir.CompileError', Message).
%% Shows a deprecation message
deprecation(Line, File, Message) -> deprecation(Line, File, Message, []).
deprecation(Line, File, Message, Args) ->
io:format(file_format(Line, File, io_lib:format(Message, Args))).
%% Handle warnings and errors (called during module compilation)
%% Ignore on bootstrap
handle_file_warning(true, _File, { _Line, sys_core_fold, nomatch_guard }) -> [];
handle_file_warning(true, _File, { _Line, sys_core_fold, { nomatch_shadow, _ } }) -> [];
%% Ignore always
handle_file_warning(_, _File, { _Line, sys_core_fold, useless_building }) -> [];
%% This is an Erlang bug, it considers { tuple, _ }.call to always fail
handle_file_warning(_, _File, { _Line, v3_kernel, bad_call }) -> [];
%% Rewrite
handle_file_warning(_, File, {Line,erl_lint,{undefined_behaviour_func,{Fun,Arity},Module}}) ->
Raw = "undefined callback function ~s/~B (behaviour ~s)",
Message = io_lib:format(Raw, [Fun,Arity,inspect(Module)]),
io:format(file_format(Line, File, Message));
handle_file_warning(_, File, {Line,erl_lint,{undefined_behaviour,Module}}) ->
Raw = io_lib:format("behaviour ~s undefined", [inspect(Module)]),
Message = case erlang:function_exported(Module, behavior_info, 1) of
true -> Raw ++ " (maybe you meant behaviour_info instead of behavior_info?)";
false -> Raw
end,
io:format(file_format(Line, File, Message));
%% Default behavior
handle_file_warning(_, File, {Line,Module,Desc}) ->
Message = format_error(Module, Desc),
io:format(file_format(Line, File, Message)).
handle_file_warning(File, Desc) ->
handle_file_warning(false, File, Desc).
handle_file_error(File, {Line,Module,Desc}) ->
form_error(Line, File, Module, Desc).
%% Assertions
assert_no_function_scope(_Line, _Kind, #elixir_scope{function=nil}) -> [];
assert_no_function_scope(Line, Kind, S) ->
syntax_error(Line, S#elixir_scope.file, "cannot invoke ~s inside a function", [Kind]).
assert_no_assign_or_guard_scope(Line, Kind, S) ->
assert_no_assign_scope(Line, Kind, S),
assert_no_guard_scope(Line, Kind, S).
assert_no_assign_scope(Line, Kind, #elixir_scope{context=assign} = S) ->
syntax_error(Line, S#elixir_scope.file, "cannot invoke ~s inside assign", [Kind]);
assert_no_assign_scope(_Line, _Kind, _S) -> [].
assert_no_guard_scope(Line, Kind, #elixir_scope{context=guard} = S) ->
syntax_error(Line, S#elixir_scope.file, "cannot invoke ~s inside guard", [Kind]);
assert_no_guard_scope(_Line, _Kind, _S) -> [].
assert_module_scope(Line, Kind, #elixir_scope{module=nil,file=File}) ->
syntax_error(Line, File, "cannot invoke ~s outside module", [Kind]);
assert_module_scope(_Line, _Kind, #elixir_scope{module=Module}) -> Module.
assert_function_scope(Line, Kind, #elixir_scope{function=nil,file=File}) ->
syntax_error(Line, File, "cannot invoke ~s outside function", [Kind]);
assert_function_scope(_Line, _Kind, #elixir_scope{function=Function}) -> Function.
%% Helpers
raise(Line, File, Kind, Message) ->
Stacktrace = erlang:get_stacktrace(),
erlang:raise(error, { Kind, '__exception__', Message, iolist_to_binary(File), Line }, Stacktrace).
file_format(Line, File, Message) ->
io_lib:format("~ts:~w: ~ts~n", [File, Line, Message]).
format_error([], Desc) ->
io_lib:format("~p", [Desc]);
format_error(Module, Desc) ->
Module:format_error(Desc).