Skip to content

Commit

Permalink
Merge pull request #3 from dw-kihara/otp-update
Browse files Browse the repository at this point in the history
Support OTP 23 and later
  • Loading branch information
sile authored Sep 13, 2023
2 parents 1022fbc + 424ae2f commit 345278b
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 77 deletions.
2 changes: 1 addition & 1 deletion rebar.config
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

{dialyzer,
[
{warnings, [error_handling, race_conditions, unmatched_returns, unknown]},
{warnings, [error_handling, unmatched_returns, unknown]},
{plt_extra_apps, [compiler]}
]}.

Expand Down
62 changes: 35 additions & 27 deletions src/logi_location.erl
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@

-export_type([location/0]).
-export_type([map_form/0]).
-export_type([application/0, line/0]).
-export_type([application/0, line/0, line_or_anno/0]).

%%----------------------------------------------------------------------------------------------------------------------
%% Macros & Records & Types
Expand All @@ -56,11 +56,11 @@

-record(?LOCATION,
{
process :: pid(),
application :: application(),
module :: module(),
function :: atom(),
line :: line()
process :: pid(),
application :: application(),
module :: module(),
function :: atom(),
line_or_anno :: line_or_anno()
}).

-opaque location() :: #?LOCATION{}.
Expand All @@ -84,34 +84,41 @@
%%
%% `0' means "Unknown Line"

-type line_or_anno() :: line() | erl_anno:anno().
%% A line number or an erl_anno:anno()
%%
%% Starting from OTP 23, the abstract format uses annos instead of line numbers.
%% This type absorbs the change.
%% See: https://www.erlang.org/docs/23/apps/erts/absform.html

%%----------------------------------------------------------------------------------------------------------------------
%% Exported Functions
%%----------------------------------------------------------------------------------------------------------------------
%% @equiv new(self(), guess_application(Module), Module, Function, Line)
-spec new(module(), atom(), line()) -> location().
new(Module, Function, Line) ->
new(self(), guess_application(Module), Module, Function, Line).
-spec new(module(), atom(), line_or_anno()) -> location().
new(Module, Function, LineOrAnno) ->
new(self(), guess_application(Module), Module, Function, LineOrAnno).

%% @doc Creates a new location object
-spec new(pid(), application(), module(), atom(), line()) -> location().
new(Pid, Application, Module, Function, Line) ->
Args = [Pid, Application, Module, Function, Line],
-spec new(pid(), application(), module(), atom(), line_or_anno()) -> location().
new(Pid, Application, Module, Function, LineOrAnno) ->
Args = [Pid, Application, Module, Function, LineOrAnno],
_ = is_pid(Pid) orelse error(badarg, Args),
_ = is_atom(Application) orelse error(badarg, Args),
_ = is_atom(Module) orelse error(badarg, Args),
_ = is_atom(Function) orelse error(badarg, Args),
_ = (is_integer(Line) andalso Line >= 0) orelse error(badarg, Args),
unsafe_new(Pid, Application, Module, Function, Line).
_ = (is_integer(LineOrAnno) andalso LineOrAnno >= 0) orelse erl_anno:is_anno(LineOrAnno) orelse error(badarg, Args),
unsafe_new(Pid, Application, Module, Function, LineOrAnno).

%% @doc Equivalent to {@link new/5} except omission of the arguments validation
-spec unsafe_new(pid(), application(), module(), atom(), line()) -> location().
unsafe_new(Pid, Application, Module, Function, Line) ->
-spec unsafe_new(pid(), application(), module(), atom(), line_or_anno()) -> location().
unsafe_new(Pid, Application, Module, Function, LineOrAnno) ->
#?LOCATION{
process = Pid,
application = Application,
module = Module,
function = Function,
line = Line
process = Pid,
application = Application,
module = Module,
function = Function,
line_or_anno = LineOrAnno
}.

%% @doc Returns `true' if `X' is a location object, `false' otherwise.
Expand Down Expand Up @@ -145,11 +152,11 @@ from_map(Map) ->
-spec to_map(Location :: location()) -> map_form().
to_map(L) ->
#{
process => L#?LOCATION.process,
application => L#?LOCATION.application,
module => L#?LOCATION.module,
function => L#?LOCATION.function,
line => L#?LOCATION.line
process => get_process(L),
application => get_application(L),
module => get_module(L),
function => get_function(L),
line => get_line(L)
}.

%% @doc Guesses the location where the function is called (parse transformation fallback)
Expand Down Expand Up @@ -218,4 +225,5 @@ get_function(#?LOCATION{function = Function}) -> Function.

%% @doc Gets the line of `Location'
-spec get_line(Location :: location()) -> line().
get_line(#?LOCATION{line = Line}) -> Line.
get_line(#?LOCATION{line_or_anno = LineOrAnno}) when is_integer(LineOrAnno) -> LineOrAnno;
get_line(#?LOCATION{line_or_anno = LineOrAnno}) -> erl_anno:line(LineOrAnno).
82 changes: 41 additions & 41 deletions src/logi_transform.erl
Original file line number Diff line number Diff line change
Expand Up @@ -23,34 +23,34 @@
%% Exported API
%%----------------------------------------------------------------------------------------------------------------------
-export([parse_transform/2]).
-export_type([form/0, line/0, expr/0, expr_call_remote/0, expr_var/0]).
-export_type([form/0, line_or_anno/0, expr/0, expr_call_remote/0, expr_var/0]).

%%----------------------------------------------------------------------------------------------------------------------
%% Types & Records
%%----------------------------------------------------------------------------------------------------------------------
-type form() :: {attribute, line(), atom(), term()}
| {function, line(), atom(), non_neg_integer(), [clause()]}
-type form() :: {attribute, line_or_anno(), atom(), term()}
| {function, line_or_anno(), atom(), non_neg_integer(), [clause()]}
| erl_parse:abstract_form().

-type clause() :: {clause, line(), [term()], [term()], [expr()]}
-type clause() :: {clause, line_or_anno(), [term()], [term()], [expr()]}
| erl_parse:abstract_clause().

-type expr() :: expr_call_remote()
| expr_var()
| erl_parse:abstract_expr()
| term().

-type expr_call_remote() :: {call, line(), {remote, line(), expr(), expr()}, [expr()]}.
-type expr_var() :: {var, line(), atom()}.
-type expr_call_remote() :: {call, line_or_anno(), {remote, line_or_anno(), expr(), expr()}, [expr()]}.
-type expr_var() :: {var, line_or_anno(), atom()}.

-type line() :: non_neg_integer().
-type line_or_anno() :: non_neg_integer() | erl_anno:anno().

-record(location,
{
application :: atom(),
module :: module(),
function :: atom(),
line :: line()
application :: atom(),
module :: module(),
function :: atom(),
line_or_anno :: line_or_anno()
}).

%%----------------------------------------------------------------------------------------------------------------------
Expand All @@ -60,9 +60,9 @@
-spec parse_transform([form()], [compile:option()]) -> [form()].
parse_transform(AbstractForms, Options) ->
Loc = #location{
application = logi_transform_utils:guess_application(AbstractForms, Options),
module = logi_transform_utils:get_module(AbstractForms),
line = 0
application = logi_transform_utils:guess_application(AbstractForms, Options),
module = logi_transform_utils:get_module(AbstractForms),
line_or_anno = 0
},
walk_forms(AbstractForms, Loc).

Expand All @@ -79,14 +79,14 @@ walk_forms(Forms, Loc) ->
-spec walk_clauses([clause()], #location{}) -> [clause()].
walk_clauses(Clauses, Loc) ->
[case Clause of
{clause, Line, Args, Guards, Body} -> {clause, Line, Args, Guards, [walk_expr(E, Loc) || E <- Body]};
_ -> Clause
{clause, LineOrAnno, Args, Guards, Body} -> {clause, LineOrAnno, Args, Guards, [walk_expr(E, Loc) || E <- Body]};
_ -> Clause
end || Clause <- Clauses].

-spec walk_expr(expr(), #location{}) -> expr().
walk_expr({call, Line, {remote, _, {atom, _, M}, {atom, _, F}}, _} = C0, Loc) ->
walk_expr({call, LineOrAnno, {remote, _, {atom, _, M}, {atom, _, F}}, _} = C0, Loc) ->
C1 = list_to_tuple(walk_expr_parts(tuple_to_list(C0), Loc)),
transform_call(M, F, C1, Loc#location{line = Line});
transform_call(M, F, C1, Loc#location{line_or_anno = LineOrAnno});
walk_expr(Expr, Loc) when is_tuple(Expr) ->
list_to_tuple(walk_expr_parts(tuple_to_list(Expr), Loc));
walk_expr(Expr, Loc) when is_list(Expr) ->
Expand All @@ -101,25 +101,25 @@ walk_expr_parts(Parts, Loc) ->
-spec transform_call(module(), atom(), expr_call_remote(), #location{}) -> expr().
transform_call(logi_location, guess_location, _, Loc) ->
logi_location_expr(Loc);
transform_call(logi, Severity0, {_, _, _, Args} = Call, Loc = #location{line = Line}) ->
transform_call(logi, Severity0, {_, _, _, Args} = Call, Loc = #location{line_or_anno = LineOrAnno}) ->
Severity = normalize_severity(Severity0),
case logi:is_severity(Severity) of
false -> Call;
true ->
case Args of
%% For maintaining compatibility with v0.0.12
[Logger, {string, _, _} = Fmt] ->
Opts = {cons, Line, {tuple, Line, [{atom, Line, logger}, Logger]}, {nil, Line}},
logi_call_expr(Severity, Fmt, {nil, Line}, Opts, Loc);
[Logger, {string, _, _} = Fmt, {nil, Line} = Data] ->
Opts = {cons, Line, {tuple, Line, [{atom, Line, logger}, Logger]}, {nil, Line}},
Opts = {cons, LineOrAnno, {tuple, LineOrAnno, [{atom, LineOrAnno, logger}, Logger]}, {nil, LineOrAnno}},
logi_call_expr(Severity, Fmt, {nil, LineOrAnno}, Opts, Loc);
[Logger, {string, _, _} = Fmt, {nil, LineOrAnno} = Data] ->
Opts = {cons, LineOrAnno, {tuple, LineOrAnno, [{atom, LineOrAnno, logger}, Logger]}, {nil, LineOrAnno}},
logi_call_expr(Severity, Fmt, Data, Opts, Loc);
[Logger, {string, _, _} = Fmt, {cons, _, _, _} = Data] ->
Opts = {cons, Line, {tuple, Line, [{atom, Line, logger}, Logger]}, {nil, Line}},
Opts = {cons, LineOrAnno, {tuple, LineOrAnno, [{atom, LineOrAnno, logger}, Logger]}, {nil, LineOrAnno}},
logi_call_expr(Severity, Fmt, Data, Opts, Loc);

[Fmt] -> logi_call_expr(Severity, Fmt, {nil, Line}, {nil, Line}, Loc);
[Fmt, Data] -> logi_call_expr(Severity, Fmt, Data, {nil, Line}, Loc);
[Fmt] -> logi_call_expr(Severity, Fmt, {nil, LineOrAnno}, {nil, LineOrAnno}, Loc);
[Fmt, Data] -> logi_call_expr(Severity, Fmt, Data, {nil, LineOrAnno}, Loc);
[Fmt, Data, Opts] -> logi_call_expr(Severity, Fmt, Data, Opts, Loc);
_ -> Call
end
Expand All @@ -128,33 +128,33 @@ transform_call(_, _, Call, _Loc) ->
Call.

-spec logi_location_expr(#location{}) -> expr().
logi_location_expr(Loc = #location{line = Line}) ->
logi_location_expr(Loc = #location{line_or_anno = LineOrAnno}) ->
logi_transform_utils:make_call_remote(
Line, logi_location, unsafe_new,
LineOrAnno, logi_location, unsafe_new,
[
{call, Line, {atom, Line, self}, []},
{atom, Line, Loc#location.application},
{atom, Line, Loc#location.module},
{atom, Line, Loc#location.function},
{integer, Line, Line}
{call, LineOrAnno, {atom, LineOrAnno, self}, []},
{atom, LineOrAnno, Loc#location.application},
{atom, LineOrAnno, Loc#location.module},
{atom, LineOrAnno, Loc#location.function},
{integer, LineOrAnno, LineOrAnno}
]).

-spec logi_call_expr(logi:severity(), expr(), expr(), expr(), #location{}) -> expr().
logi_call_expr(Severity, FormatExpr, DataExpr, OptionsExpr, Loc = #location{line = Line}) ->
logi_call_expr(Severity, FormatExpr, DataExpr, OptionsExpr, Loc = #location{line_or_anno = LineOrAnno}) ->
LocationExpr = logi_location_expr(Loc),
LoggerVar = logi_transform_utils:make_var(Line, "__Logger"),
ResultVar = logi_transform_utils:make_var(Line, "__Result"),
LoggerVar = logi_transform_utils:make_var(LineOrAnno, "__Logger"),
ResultVar = logi_transform_utils:make_var(LineOrAnno, "__Result"),
LogiReadyCall = logi_transform_utils:make_call_remote(
Line, logi, '_ready', [{atom, Line, Severity}, LocationExpr, OptionsExpr]),
{'case', Line, LogiReadyCall,
LineOrAnno, logi, '_ready', [{atom, LineOrAnno, Severity}, LocationExpr, OptionsExpr]),
{'case', LineOrAnno, LogiReadyCall,
[
%% {Logger, []} -> Logger
{clause, Line, [{tuple, Line, [LoggerVar, {nil, Line}]}], [],
{clause, LineOrAnno, [{tuple, LineOrAnno, [LoggerVar, {nil, LineOrAnno}]}], [],
[LoggerVar]},

%% {Logger, Result} -> logi:'_write'(Result, Format, Data), Logger
{clause, Line, [{tuple, Line, [LoggerVar, ResultVar]}], [],
[logi_transform_utils:make_call_remote(Line, logi, '_write', [ResultVar, FormatExpr, DataExpr]),
{clause, LineOrAnno, [{tuple, LineOrAnno, [LoggerVar, ResultVar]}], [],
[logi_transform_utils:make_call_remote(LineOrAnno, logi, '_write', [ResultVar, FormatExpr, DataExpr]),
LoggerVar]}
]}.

Expand Down
20 changes: 13 additions & 7 deletions src/logi_transform_utils.erl
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,20 @@ guess_application(Forms, Options) ->
find_app_file([Dir || Dir <- [OutDir, SrcDir], Dir =/= undefined]).

%% @doc Makes a abstract term for variable
-spec make_var(logi_transform:line(), string()) -> logi_transform:expr_var().
make_var(Line, Prefix) ->
-spec make_var(logi_transform:line_or_anno(), string()) -> logi_transform:expr_var().
make_var(LineOrAnno, Prefix) ->
Seq = case get({?MODULE, seq}) of
undefined -> 0;
Seq0 -> Seq0
end,
_ = put({?MODULE, seq}, Seq + 1),
Name = list_to_atom(Prefix ++ "_line" ++ integer_to_list(Line) ++ "_" ++ integer_to_list(Seq)),
{var, Line, Name}.
Name = list_to_atom(Prefix ++ "_line" ++ line_or_anno_to_string(LineOrAnno) ++ "_" ++ integer_to_list(Seq)),
{var, LineOrAnno, Name}.

%% @doc Makes a abstract term for external function call
-spec make_call_remote(logi_transform:line(), module(), atom(), [logi_transform:expr()]) -> logi_transform:expr_call_remote().
make_call_remote(Line, Module, Function, ArgsExpr) ->
{call, Line, {remote, Line, {atom, Line, Module}, {atom, Line, Function}}, ArgsExpr}.
-spec make_call_remote(logi_transform:line_or_anno(), module(), atom(), [logi_transform:expr()]) -> logi_transform:expr_call_remote().
make_call_remote(LineOrAnno, Module, Function, ArgsExpr) ->
{call, LineOrAnno, {remote, LineOrAnno, {atom, LineOrAnno, Module}, {atom, LineOrAnno, Function}}, ArgsExpr}.

%%----------------------------------------------------------------------------------------------------------------------
%% Internal Functions
Expand All @@ -61,3 +61,9 @@ find_app_file([Dir | Dirs]) ->
end;
_ -> find_app_file(Dirs)
end.

-spec line_or_anno_to_string(logi_transform:line_or_anno()) -> string().
line_or_anno_to_string(LineOrAnno) when is_integer(LineOrAnno)
-> integer_to_list(LineOrAnno);
line_or_anno_to_string(LineOrAnno)
-> integer_to_list(erl_anno:line(LineOrAnno)).
15 changes: 15 additions & 0 deletions test/logi_location_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,18 @@ guess_test_() ->
?assertEqual(undefined, logi_location:guess_application('UNDEFINED_MODULE'))
end}
]}.

anno_test_() ->
[
{"Creates a new location object",
fun () ->
L = logi_location:new(lists, map, erl_anno:new({12, 10})),
?assert(logi_location:is_location(L)),
?assertEqual(12, logi_location:get_line(L))
end},
{"Converts to a map without anno",
fun () ->
L = logi_location:new(lists, map, erl_anno:new({12, 10})),
?assertMatch(#{line := 12}, logi_location:to_map(L))
end}
].
5 changes: 4 additions & 1 deletion test/logi_transform_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ log_test_() ->

%% [NO ERROR] transformed call
Logger = logi:info("hello world"),
?assertLog("hello world", [], fun (C) -> ?assertEqual(info, logi_context:get_severity(C)) end),
?assertLog("hello world", [], fun (C) ->
?assertEqual(info, logi_context:get_severity(C)),
?assertEqual(56, logi_location:get_line(logi_context:get_location(C)))
end),
?assert(logi:is_logger(Logger))
end},
{"`Data` arugment will not be evaluated if it is unnecessary",
Expand Down

0 comments on commit 345278b

Please sign in to comment.