./ct_report/coverage/safely.COVER.html

1 -module(safely).
2 %% This module preserves the return types of a `catch' statement,
3 %% but doesn't silently convert `throw's to normal values.
4 %% The issue is that throws have different semantics than errors:
5 %% https://erlang.org/doc/reference_manual/expressions.html#catch-and-throw,
6 %% so a throw is actually a control flow escape, while an error is an actual crash.
7 %% If it is an error, we want the stacktrace, if not, we don't.
8 %% For more information, see usage in test/safely_SUITE.erl
9
10 -include_lib("kernel/include/logger.hrl").
11
12 -export([apply/2, apply/3]).
13 -export([apply_and_log/3, apply_and_log/4]).
14 -compile({no_auto_import, [apply/2, apply/3]}).
15 -ignore_xref([apply/3, apply_and_log/4]).
16
17 -type error_class() :: error | exit | throw.
18 -type catch_result(A) :: A | {error_class(), term()}.
19
20 -define(MATCH_EXCEPTIONS_DO_LOG(F, Context),
21 try F catch
22 error:R:S ->
23 ?LOG_ERROR(Context#{class => error, reason => R, stacktrace => S}),
24 {error, {R, S}};
25 throw:R ->
26 ?LOG_ERROR(Context#{class => throw, reason => R}),
27 {throw, R};
28 exit:R:S ->
29 ?LOG_ERROR(Context#{class => exit, reason => R, stacktrace => S}),
30 {exit, {R, S}}
31 end).
32
33 -define(MATCH_EXCEPTIONS(F),
34 try F catch
35 error:R:S -> {error, {R, S}};
36 throw:R -> {throw, R};
37 exit:R:S -> {exit, {R, S}}
38 end).
39
40 -spec apply(fun((...) -> A), [term()]) -> catch_result(A).
41 apply(Function, Args) when is_function(Function), is_list(Args) ->
42 153619 ?MATCH_EXCEPTIONS(erlang:apply(Function, Args)).
43
44 -spec apply(atom(), atom(), [term()]) -> catch_result(any()).
45 apply(Module, Function, Args) when is_atom(Function), is_list(Args) ->
46
:-(
?MATCH_EXCEPTIONS(erlang:apply(Module, Function, Args)).
47
48 -spec apply_and_log(fun((...) -> A), [term()], map()) -> catch_result(A).
49 apply_and_log(Function, Args, Context)
50 when is_function(Function), is_list(Args), is_map(Context) ->
51 344 ?MATCH_EXCEPTIONS_DO_LOG(erlang:apply(Function, Args), Context).
52
53 -spec apply_and_log(atom(), atom(), [term()], map()) -> catch_result(any()).
54 apply_and_log(Module, Function, Args, Context)
55 when is_atom(Function), is_list(Args), is_map(Context) ->
56
:-(
?MATCH_EXCEPTIONS_DO_LOG(erlang:apply(Module, Function, Args), Context).
Line Hits Source