Packages
otpbp
5.2.2
7.18.0
7.16.1
7.15.0
7.14.0
7.13.0
7.12.0
7.11.0
7.10.2
7.9.3
7.9.2
7.9.1
7.8.7
7.8.6
7.8.5
7.8.4
7.7.1
7.7.0
7.6.3
7.6.2
7.6.1
7.6.0
7.5.1
7.5.0
7.4.0
7.3.4
7.3.3
7.3.2
7.3.1
7.3.0
7.2.0
7.1.1
7.1.0
7.0.4
7.0.3
7.0.2
7.0.1
6.26.0
6.25.0
6.24.0
6.23.0
6.22.2
6.21.3
6.21.2
6.21.1
6.20.2
6.19.1
6.19.0
6.18.3
6.18.2
6.18.1
6.18.0
6.17.1
6.17.0
6.16.0
6.15.4
6.15.3
6.15.2
6.15.1
6.15.0
6.14.0
6.13.5
6.13.4
6.13.3
6.13.2
6.13.1
6.13.0
6.12.0
6.11.2
6.11.1
6.10.0
6.9.2
6.9.1
6.9.0
6.8.0
6.7.1
6.7.0
6.6.4
6.6.3
6.6.2
6.6.1
6.6.0
6.5.0
6.4.0
6.3.3
6.3.2
6.3.1
6.3.0
6.2.1
6.2.0
6.1.0
6.0.0
5.39.0
5.38.0
5.37.0
5.36.0
5.35.2
5.34.3
5.34.2
5.34.1
5.33.1
5.32.1
5.32.0
5.31.3
5.31.2
5.31.1
5.31.0
5.30.1
5.30.0
5.29.0
5.28.4
5.28.3
5.28.2
5.28.1
5.28.0
5.27.0
5.26.5
5.26.4
5.26.3
5.26.2
5.26.1
5.26.0
5.25.0
5.24.2
5.24.1
5.23.0
5.22.2
5.22.1
5.22.0
5.21.0
5.20.1
5.20.0
5.19.4
5.19.3
5.19.2
5.19.1
5.19.0
5.18.0
5.17.0
5.16.3
5.16.1
5.16.0
5.15.1
5.15.0
5.14.0
5.13.1
5.13.0
5.12.1
5.12.0
5.11.2
5.11.1
5.10.1
5.10.0
5.9.0
5.8.0
5.7.0
5.6.1
5.6.0
5.5.0
5.4.2
5.4.1
5.4.0
5.3.0
5.2.2
5.2.1
5.2.0
5.1.2
5.1.1
4.79.0
4.78.0
4.77.0
4.76.2
4.75.3
4.75.2
4.75.1
4.74.0
4.73.1
4.73.0
4.72.3
4.72.2
4.72.1
4.72.0
4.71.1
4.71.0
4.70.0
4.69.4
4.69.3
4.69.2
4.69.1
4.69.0
4.68.0
4.67.5
4.67.4
4.67.3
4.67.2
4.67.0
4.66.0
4.65.2
4.65.1
4.64.0
4.63.2
4.63.1
4.63.0
4.62.0
4.61.1
4.61.0
4.60.4
4.60.3
4.60.2
4.60.1
4.60.0
4.59.0
4.58.0
4.57.2
4.57.1
4.57.0
4.56.2
4.56.1
4.56.0
4.55.0
4.54.0
4.53.0
4.52.1
4.52.0
4.51.3
4.51.2
4.51.0
4.50.0
4.49.3
4.49.2
OTP backports
Current section
Files
Jump to
Current section
Files
src/otpbp_pt.erl
%%% Copyright 2015-2019 Oleksandr Chumachenko <ledest@gmail.com>
%%%
%%% This file is part of OTPBP.
%%%
%%% OTPBP is free software: you can redistribute it and/or modify it
%%% under the terms of the GNU General Public License as published by
%%% the Free Software Foundation, either version 3 of the License, or
%%% (at your option) any later version.
%%%
%%% OTPBP is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
%%% See the GNU General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with OTPBP. If not, see <http://www.gnu.org/licenses/>.
-module(otpbp_pt).
-export([parse_transform/2]).
-dialyzer({no_opaque, application_guard/3}).
-define(TRANSFORM_FUNCTIONS, [{{is_map_key, 2}, {maps, is_key}},
{{map_get, 2}, {maps, get}},
{{[ceil, floor], 1}, otpbp_erlang},
{{[atom_to_binary, binary_to_atom, binary_to_existing_atom], 1}, otpbp_erlang},
{{error, 3}, otpbp_erlang},
{{term_to_iovec, [1, 2]}, otpbp_erlang},
{{application, get_supervisor, 1}, otpbp_application},
{{application, set_env, [1, 2]}, otpbp_application},
{{argparse, format_error, [1, 2]}, otpbp_argparse},
{{argparse, help, [1, 2]}, otpbp_argparse},
{{argparse, parse, [2, 3]}, otpbp_argparse},
{{argparse, run, 3}, otpbp_argparse},
{{argparse, validate, [1, 2]}, otpbp_argparse},
{{beam_lib, significant_chunks, 0}, otpbp_beam_lib},
{{beam_lib, [strip, strip_files, strip_release], 2}, otpbp_beam_lib},
{{binary, [decode_hex, encode_hex], 1}, otpbp_binary},
{{binary, encode_hex, 2}, otpbp_binary},
{{c, lm, 0}, otpbp_c},
{{c, mm, 0}, {otpbp_code, modified_modules}},
{{calendar, [rfc3339_to_system_time, system_time_to_rfc3339], [1, 2]}, otpbp_calendar},
{{calendar, [system_time_to_local_time, system_time_to_universal_time], 2},
otpbp_calendar},
{{code, [all_available, modified_modules], 0}, otpbp_code},
{{code, rehash, 0}, otpbp_code},
{{code, module_status, 1}, otpbp_code},
{{code, is_module_native, 1}, otpbp_code},
{{compile, iofile, 1}, otpbp_compile},
{{crypto, [block_encrypt, block_decrypt], [3, 4]}, otpbp_crypto},
{{crypto, hash_equals, 2}, otpbp_crypto},
{{crypto, [cmac, hmac], [3, 4]}, otpbp_crypto},
{{crypto, [hmac_init, hmac_update], 2}, otpbp_crypto},
{{crypto, hmac_final, 1}, otpbp_crypto},
{{crypto, hmac_final_n, 2}, otpbp_crypto},
{{crypto, poly1305, 2}, otpbp_crypto},
{{crypto, mac, [3, 4]}, otpbp_crypto},
{{crypto, [dss_sign, rsa_sign], [2, 3]}, otpbp_crypto},
{{crypto, [dss_verify, rsa_verify], [3, 4]}, otpbp_crypto},
{{crypto, [md4_init, md5_init, sha_init], 0}, otpbp_crypto},
{{crypto, [md4, md5, sha], 1}, otpbp_crypto},
{{crypto, [md4_final, md5_final, sha_final], 2}, {crypto, hash_final}},
{{crypto, [md4_update, md5_update, sha_update], 2}, {crypto, hash_update}},
{{crypto, rand_bytes, 1}, {crypto, strong_rand_bytes}},
{{crypto, supports, 1}, otpbp_crypto},
{{crypto, [sha224, sha256, sha384, sha512], 1}, otpbp_crypto},
{{crypto, [sha224_final, sha256_final, sha384_final, sha512_final], 1},
{crypto, hash_final}},
{{crypto, [sha224_init, sha256_init, sha384_init, sha512_init], 0}, otpbp_crypto},
{{crypto, [sha224_mac, sha256_mac, sha384_mac, sha512_mac], 2}, otpbp_crypto},
{{crypto, [sha224_update, sha256_update, sha384_update, sha512_update], 2},
{crypto, hash_update}},
{{crypto, stream_init, [2, 3]}, otpbp_crypto},
{{crypto, [stream_decrypt, stream_encrypt], 2}, otpbp_crypto},
{{ct, get_progname, 0}, otpbp_ct},
{{dbg, stop_clear, 0}, {dbg, stop}}, % OTP < 27
{{dict, foreach, 2}, otpbp_dict},
{{dict, take, 2}, otpbp_dict},
{{disk_log, accessible_logs, 0}, otpbp_disk_log},
{{disk_log, lclose, 1}, {disk_log, close}},
{{disk_log, lclose, 2}, otpbp_disk_log},
{{epp, scan_file, [1, 2]}, otpbp_epp},
{{erl_error, format_fun, [1, 2]}, lib},
{{erl_error, [format_call, format_stacktrace], [4, 5]}, lib},
{{erl_error, format_exception, [6, 7]}, lib},
{{erl_error, format_exception, [3, 4]}, otpbp_erl_error},
{{erl_eval, [eval_str, extended_parse_exprs, extended_parse_term], 1}, lib},
{{erl_eval, subst_values_for_vars, 2}, lib},
{{erl_features, [all, configurable, enabled, info, long, short, used], 0},
otpbp_erl_features},
{{erl_internal, add_predefined_functions, 1}, otpbp_erl_internal},
{{erl_pp, legalize_vars, 1}, otpbp_erl_pp},
{{erlang, get_stacktrace, 0}, otpbp_erlang},
{{erlang, [get_cookie, set_cookie], 1}, otpbp_erlang},
{{erlang, iolist_to_iovec, 1}, otpbp_erlang},
{{error_logger, get_format_depth, 0}, otpbp_error_logger},
{{error_logger, limit_term, 1}, otpbp_error_logger},
{{erts_internal, [binary_to_integer, list_to_integer], 2}, otpbp_erts_internal},
{{erts_internal, map_next, 3}, otpbp_erts_internal},
{{ets, lookup_element, 4}, otpbp_ets},
{{ets, whereis, 1}, otpbp_ets},
{{file, del_dir_r, 1}, otpbp_file},
{{file, delete, 2}, otpbp_file},
{{filelib, ensure_path, 1}, otpbp_filelib},
{{filelib, find_file, [2, 3]}, otpbp_filelib},
{{filelib, safe_relative_path, 2}, otpbp_filelib},
{{filelib, find_source, [1, 2, 3]}, otpbp_filelib},
{{filename, basedir, [2, 3]}, otpbp_filename},
{{filename, safe_relative_path, 1}, otpbp_filename},
{{ftp, start_service, 1}, ftp_internal},
{{ftp, stop_service, 1}, {ftp, close}},
{{http_uri, scheme_defaults, 0}, otpbp_http_uri},
{{http_uri, [decode, encode], 1}, otpbp_http_uri},
{{http_uri, parse, [1, 2]}, otpbp_http_uri},
{{httpd_util, hexlist_to_integer, 1}, http_util},
{{httpd_util, [flatlength, integer_to_hexlist, strip, suffix], 1}, otpbp_httpd_util},
{{gb_sets, [filtermap, is_equal, map], 2}, otp_gb_sets},
{{gb_sets, foreach, 2}, otp_gb_sets},
{{gb_trees, [take, take_any], 2}, otpbp_gb_trees},
{{gb_trees, foreach, 2}, otpbp_gb_trees},
{{gen, debug_options, 2}, otpbp_gen},
{{gen_event, [start, start_link], 2}, otpbp_gen_event},
{{gen_event, start_monitor, [0, 1, 2]}, otpbp_gen_event},
{{gen_statem, call, [2, 3]}, otpbp_gen_statem},
{{gen_statem, cast, 2}, otpbp_gen_statem},
{{gen_statem, enter_loop, [4, 5, 6]}, otpbp_gen_statem},
{{gen_statem, reply, [1, 2]}, otpbp_gen_statem},
{{gen_statem, [start, start_link], [3, 4]}, otpbp_gen_statem},
{{gen_statem, stop, [1, 3]}, otpbp_gen_statem},
{{gen_statem, init_it, 6}, otpbp_gen_statem},
{{gen_statem, system_continue, 3}, otpbp_gen_statem},
{{gen_statem, system_get_state, 1}, otpbp_gen_statem},
{{gen_statem, [system_code_change, system_terminate], 4}, otpbp_gen_statem},
{{gen_statem, [format_status, system_replace_state], 2}, otpbp_gen_statem},
{{gen_statem, wakeup_from_hibernate, 3}, otpbp_gen_statem},
{{inet, [info, ipv4_mapped_ipv6_address], 1}, otpbp_inet},
{{io_lib, format, 3}, otpbp_io_lib},
{{io_lib, fwrite, 3}, {otpbp_io_lib, format}},
{{io_lib, limit_term, 2}, otpbp_io_lib},
{{io_lib, write_atom_as_latin1, 1}, {io_lib, write_atom}},
{{lib, [flush_receive, progname], 0}, otpbp_lib},
{{lib, nonl, 1}, otpbp_lib},
{{lib, [error_message, send, sendw], 2}, otpbp_lib},
{{lib, [eval_str, extended_parse_exprs, extended_parse_term], 1}, erl_eval},
{{lib, [format_call, format_stacktrace], [4, 5]}, erl_error},
{{lib, format_exception, [6, 7]}, erl_error},
{{lib, format_fun, [1, 2]}, erl_error},
{{lib, subst_values_for_vars, 2}, erl_eval},
{{lists, enumerate, [1, 2, 3]}, otpbp_lists},
{{lists, search, 2}, otpbp_lists},
{{lists, uniq, [1, 2]}, otpbp_lists},
{{lists, zip, 3}, otpbp_lists},
{{lists, [zip3, zipwith], 4}, otpbp_lists},
{{lists, zipwith3, 5}, otpbp_lists},
{{maps, [iterator, next], 1}, otpbp_maps},
{{maps, [filtermap, foreach, from_keys, intersect], 2}, otpbp_maps},
{{maps, groups_from_list, [2, 3]}, otpbp_maps},
{{maps, [intersect_with, merge_with], 3}, otpbp_maps},
{{math, [ceil, floor], 1}, otpbp_math},
{{math, fmod, 2}, otpbp_math},
{{math, tau, 0}, otpbp_math},
{{orddict, foreach, 2}, otpbp_orddict},
{{orddict, take, 2}, otpbp_orddict},
{{ordsets, [filtermap, is_equal, map], 2}, otpbp_ordsets},
{{ordsets, foreach, 2}, otpbp_orddict},
{{ordsets, is_empty, 1}, otpbp_ordsets},
{{os, cmd, 2}, otpbp_os},
{{os, [env, list_env_vars], 0}, otpbp_os},
{{peer, call, [4, 5]}, otpbp_peer},
{{peer, cast, 4}, otpbp_peer},
{{peer, [get_state, start, stop], 1}, otpbp_peer},
{{peer, [random_name, start_link], [0, 1]}, otpbp_peer},
{{peer, send, 3}, otpbp_peer},
{{pg2, [start, start_link, which_groups], 0}, otpbp_pg2},
{{pg2, [create, delete, get_closest_pid, get_local_members, get_members], 1}, otpbp_pg2},
{{pg2, [join, leave], 2}, otpbp_pg2},
{{proc_lib, start_monitor, [3, 4, 5]}, otpbp_proc_lib},
{{proplists, from_map, 1}, {maps, to_list}}, % OTP 24.0
{{proplists, to_map, [1, 2]}, otpbp_proplists},
{{public_key, ssh_decode, 2}, {ssh_file, decode}},
{{public_key, ssh_encode, 2}, {ssh_file, encode}},
{{public_key, ssh_hostkey_fingerprint, [1, 2]}, {ssh, hostkey_fingerprint}},
{{queue, [all, any, delete, delete_r, delete_with, delete_with_r, filtermap, foreach], 2},
otpbp_queue},
{{queue, fold, 3}, otpbp_queue},
{{rand, bytes, 1}, otpbp_rand},
{{rand, bytes_s, 2}, otpbp_rand},
{{rand, [exsp_jump, exsp_next], 1}, otpbp_rand},
{{rand, [mwc59, mwc59_float, mwc59_value, mwc59_value32], 1}, otpbp_rand},
{{rand, mwc59_seed, [0, 1]}, otpbp_rand},
{{rand, normal, 2}, otpbp_rand},
{{rand, normal_s, 3}, otpbp_rand},
{{rand, splitmix64_next, 1}, otpbp_rand},
{{rand, uniform_real, 0}, otpbp_rand},
{{rand, uniform_real_s, 1}, otpbp_rand},
{{re, version, 0}, otpbp_re},
{{sets, foreach, 2}, otpbp_sets},
{{sets, [filtermap, is_equal, map], 2}, otpbp_sets},
{{sets, from_list, 2}, otpbp_sets},
{{sets, is_empty, 1}, otpbp_sets},
{{sets, new, 1}, otpbp_sets},
{{shell_docs, [render, render_callback], 5}, otpbp_shell_docs},
{{shell_docs, supported_tags, 0}, otpbp_shell_docs},
{{snmpa, old_info_format, 1}, otpbp_snmpa},
{{snmpm, async_get, [3, 4, 5, 6]}, {snmpm, async_get2}},
{{snmpm, async_get_bulk, [5, 6, 7, 8]}, {snmpm, async_get_bulk2}},
{{snmpm, async_get_next, [3, 4, 5, 6]}, {snmpm, async_get_next2}},
{{snmpm, async_set, [3, 4, 5, 6]}, {snmpm, async_set2}},
{{snmpm, sync_get, [3, 4, 5, 6]}, {snmpm, sync_get2}},
{{snmpm, sync_get_bulk, [5, 6, 7, 8]}, {snmpm, sync_get_bulk2}},
{{snmpm, sync_get_next, [3, 4, 5, 6]}, {snmpm, sync_get_next2}},
{{snmpm, sync_set, [3, 4, 5, 6]}, {snmpm, sync_set2}},
{{ssl, cipher_suites, [0, 1]}, otpbp_ssl},
{{ssl, [handshake, ssl_accept], [1, 2, 3]}, otpbp_ssl},
{{string, [casefold, chomp, is_empty, length, lowercase, next_codepoint,
next_grapheme, reverse, titlecase, to_graphemes, trim, uppercase], 1},
otpbp_string},
{{string, [equal, find, lexemes, pad, prefix, slice, split, take, trim], 2}, otpbp_string},
{{string, [equal, find, nth_lexeme, pad, replace, slice, split, take, trim], 3},
otpbp_string},
{{string, [equal, pad, replace, take], 4}, otpbp_string},
{{supervisor, check_childspecs, 2}, otpbp_supervisor},
{{sys, get_log, 1}, otpbp_sys},
{{timer, tc, 4}, otpbp_timer},
{{unicode_util, [spec_version, whitespace], 0}, otpbp_unicode_util},
{{unicode_util, [casefold, cp, gc, get_case, is_whitespace, lookup, lowercase, nfc, nfd,
nfkc, nfkd, titlecase,uppercase], 1},
otpbp_unicode_util},
{{uri_string, [compose_query, normalize], [1, 2]}, otpbp_uri_string},
{{uri_string, [dissect_query, is_host, is_path, parse, recompose], 1}, otpbp_uri_string},
{{uri_string, transcode, 2}, otpbp_uri_string},
{{user, interfaces, 1}, otpbp_user},
{{zlib, [adler32, crc32], [2, 3]}, otpbp_zlib},
{{zlib, [adler32_combine, crc32_combine], 4}, otpbp_zlib},
{{zlib, inflate, 3}, otpbp_zlib},
{{zlib, inflateGetDictionary, 1}, otpbp_zlib},
{{zlib, inflateChunk, [1, 2]}, otpbp_zlib},
{{zlib, getBufSize, 1}, otpbp_zlib},
{{zlib, safeInflate, 2}, otpbp_zlib},
{{zlib, setBufSize, 2}, otpbp_zlib},
{{zlib, [compress, gzip, zip], 2}, otpbp_zlib}]).
-define(TRANSFORM_BEHAVIOURS, [{gen_statem, otpbp_gen_statem}]).
-record(param, {options = [] :: list(),
verbose = false :: boolean(),
otp_release = otp_release() :: 19..26,
erts_version = erts_version() :: [non_neg_integer(),...],
funs = #{} :: #{{module(), {atom(), arity()}} => {module(), atom()}},
behaviours = maps:from_list(?TRANSFORM_BEHAVIOURS) :: #{module() => module()},
apply = lists:foldr(fun(FA, A) -> [FA, {erlang, FA}|A] end,
[{erlang, {hibernate, 3}},
{timer, {tc, 3}}, {timer, {tc, 4}},
{fprof, {apply, 3}}, {fprof, {apply, 4}}],
[{apply, 3}, {spawn, 3}, {spawn_link, 3}, {spawn_monitor, 3}, {spawn_request, 3},
{spawn_opt, 4}, {spawn_request, 4}]) ::
[{atom(), arity()}|{atom(), {atom(), arity()}}],
file = "" :: string()}).
parse_transform(Forms, Options) ->
case transform_list() of
TL when map_size(TL) =/= 0 ->
try erl_syntax_lib:analyze_forms(Forms) of
AF ->
{NF, _} = lists:mapfoldl(fun transform/2,
#param{options = Options,
verbose = proplists:get_bool(verbose, Options),
funs = get_funs(AF, TL)},
Forms),
NF
catch
C:E ->
io:fwrite(standard_error,
?MODULE_STRING ": error erl_syntax_lib:analyze_forms/1 {~p:~p}, see below.~n",
[C, E]),
Forms
end;
_ -> Forms
end.
get_funs(AF, TL) ->
lists:foldl(fun({M, Fs}, IA) ->
lists:foldl(fun(FA, IAM) ->
case maps:find({M, FA}, TL) of
{ok, V} -> IAM#{FA => V};
_ -> IAM
end
end, IA, Fs)
end,
maps:without(get_no_auto_import(AF), TL),
proplists:get_value(imports, AF, [])).
get_no_auto_import(AF) ->
proplists:append_values(no_auto_import, proplists:append_values(compile, proplists:get_value(attributes, AF, []))).
-compile({inline, [get_funs/2, get_no_auto_import/1]}).
transform(Tree, P) ->
case type(Tree) of
function -> {transform_function(Tree, P), P};
attribute -> transform_attribute(Tree, P);
_ -> {Tree, P}
end.
transform_function(Tree, P) ->
case erl_syntax_lib:mapfold(fun(E, F) ->
case function_transform(E, P) of
false -> {E, F};
N -> {N, true}
end
end, false, Tree) of
{T, true} -> erl_syntax:revert(T);
_ -> Tree
end.
transform_attribute(Tree, P) ->
case erl_syntax_lib:analyze_attribute(Tree) of
{file, {F, _}} -> {Tree, P#param{file = F}};
{behaviour, _} -> {transform_behaviour(Tree, P), P};
{behavior, _} -> {transform_behaviour(setelement(3, Tree, behaviour), P), P};
_ -> {Tree, P}
end.
transform_behaviour({_, _, _, B} = Tree, #param{behaviours = Bs}) ->
case Bs of
#{B := M} ->
case check_behaviour(B) of
true -> Tree;
_false -> setelement(4, Tree, M)
end;
_ -> Tree
end;
transform_behaviour(Tree, _) -> Tree.
check_behaviour(B) ->
try B:module_info(exports) of
Exports -> lists:member({behaviour_info, 1}, Exports)
catch
_:_ -> false
end.
-compile({inline, [transform_function/2, transform_attribute/2, check_behaviour/1]}).
add_func(F, MF, D, I) -> lists:foldl(fun(A, Acc) -> add_func(setelement(I, F, A), MF, Acc) end, D, element(I, F)).
add_func(F, MF, D) when is_list(element(tuple_size(F), F)) -> add_func(F, MF, D, tuple_size(F));
add_func(F, MF, D) when is_list(element(tuple_size(F) - 1, F)) -> add_func(F, MF, D, tuple_size(F) - 1);
add_func(FA, MF, D) ->
case check_func(FA) orelse FA of
true -> D;
{M, F, A} -> store_func({M, {F, A}}, MF, D);
{_, _} -> store_func({erlang, FA}, MF, store_func(FA, MF, D))
end.
check_func({M, F, A}) -> check_func(M, F, A);
check_func({F, A}) -> check_func(erlang, F, A).
check_func(M, F, A) ->
erlang:is_builtin(M, F, A) orelse try M:module_info(exports) of
Exports -> lists:member({F, A}, Exports)
catch
_:_ -> false
end.
store_func(F, {_, _} = MF, D) -> D#{F => MF};
store_func({_, {F, _}} = MFA, M, D) -> store_func(MFA, {M, F}, D);
store_func({F, _} = FA, M, D) -> store_func(FA, {M, F}, D).
transform_list() -> lists:foldl(fun({F, D}, Acc) -> add_func(F, D, Acc) end, #{}, ?TRANSFORM_FUNCTIONS).
-compile({inline, [transform_list/0]}).
function_transform(Node, #param{} = P) ->
case type(Node) of
application -> application_transform(Node, P);
implicit_fun -> implicit_fun_transform(Node, P);
conjunction -> conjunction_transform(Node);
try_expr -> try_expr_transform(Node);
_ -> false
end.
conjunction_transform(Node) ->
case erl_syntax_lib:mapfold(fun(E, F) ->
case type(E) =:= application andalso application_transform_guard(E) of
false -> {E, F};
N -> {N, true}
end
end, false, Node) of
{T, true} -> T;
_ -> Node
end.
-compile({inline, [conjunction_transform/1]}).
application_transform_guard(Node) ->
case erl_syntax_lib:analyze_application(Node) of
{M, {N, _}} -> application_guard(Node, M, N);
_ -> false
end.
-compile({inline, [application_transform_guard/1]}).
application_guard(Node, otpbp_erlang, ceil) -> application_guard_ceil_floor(Node, '+');
application_guard(Node, otpbp_erlang, floor) -> application_guard_ceil_floor(Node, '-');
application_guard(_, _, _) -> false.
-compile({inline, [application_guard/3]}).
application_guard_ceil_floor(Node, Op) ->
[A] = erl_syntax:application_arguments(Node),
O = erl_syntax:application_operator(Node),
cp(Node,
applicationr(erlang, round,
[cp(A, erl_syntax:infix_expr(A, cp(A, erl_syntax:operator(Op)), cp(A, erl_syntax:float(0.5))))],
O)).
application_transform(Node, #param{funs = FL} = P) ->
A = erl_syntax_lib:analyze_application(Node),
case apply_transform(Node, P, A) of
false -> application_transform(Node, P, A, FL);
NodeApply ->
case application_transform(NodeApply, P, A, FL) of
false -> NodeApply;
NodeApplication -> NodeApplication
end
end.
application_transform(Node, P, A, L) ->
case L of
#{A := {M, N}} ->
replace_message(Node, P, A, M, N),
O = erl_syntax:application_operator(Node),
As = erl_syntax:application_arguments(Node),
cp(Node,
case A of
{_, {_, _}} -> applicationr(M, N, As, O);
{_, _} -> applicationl(M, N, As, O)
end);
#{} -> false
end.
apply_transform(Node, #param{apply = AL} = P, A) ->
lists:member(A, AL) andalso
case erl_syntax:application_arguments(Node) of
[MT, FT|[AT|_] = T] ->
case type(MT) =:= atom andalso type(FT) =:= atom andalso erl_syntax:is_list_skeleton(AT) of
true ->
AA = {erl_syntax:atom_value(MT), {erl_syntax:atom_value(FT), erl_syntax:list_length(AT)}},
case P#param.funs of
#{AA := {M, F}} ->
replace_message(Node, P, AA, M, F),
erl_syntax:application(erl_syntax:application_operator(Node),
[atom(MT, M), atom(FT, F)|T]);
#{} -> false
end;
_false -> false
end;
_ -> false
end.
-compile({inline, [application_transform/2, apply_transform/3]}).
applicationl(M, N, As, O) -> application(atom(O, M), atom(O, N), As).
applicationr(M, N, As, O) -> application(atom(mqa(O), M), atom(mqb(O), N), As).
application(M, N, As) -> erl_syntax:application(cp(M, erl_syntax:module_qualifier(M, N)), As).
implicit_fun_transform(Node, #param{funs = L} = P) ->
try erl_syntax_lib:analyze_implicit_fun(Node) of
F -> case L of
#{F := {M, N}} ->
Q = erl_syntax:implicit_fun_name(Node),
{AQ, MP} = case type(Q) of
arity_qualifier -> {Q, erl_syntax:arity_qualifier_body(Q)};
module_qualifier -> {mqb(Q), mqa(Q)}
end,
replace_message(Node, P, F, M, N),
cp(Node,
erl_syntax:implicit_fun(atom(MP, M), atom(erl_syntax:arity_qualifier_body(AQ), N),
erl_syntax:arity_qualifier_argument(AQ)));
#{} -> false
end
catch
throw:syntax_error -> false
end.
-compile({inline, [implicit_fun_transform/2]}).
try_expr_transform(Node) ->
case lists:mapfoldr(fun(H, A) ->
case try_expr_handler_transform(H) of
false -> {H, A};
NH -> {NH, true}
end
end, false, erl_syntax:try_expr_handlers(Node)) of
{_, false} -> false;
{Hs, _} ->
cp(Node,
erl_syntax:try_expr(erl_syntax:try_expr_body(Node), erl_syntax:try_expr_clauses(Node),
Hs, erl_syntax:try_expr_after(Node)))
end.
try_expr_handler_transform(Node) ->
case type(Node) =:= clause andalso try_expr_clause_patterns_transform(erl_syntax:clause_patterns(Node)) of
{P, {true, B}} ->
cp(Node, erl_syntax:clause(P, erl_syntax:clause_guard(Node), B ++ erl_syntax:clause_body(Node)));
_ -> false
end.
try_expr_clause_patterns_transform(Ps) ->
lists:mapfoldr(fun(P, {_, L} = A) ->
case type(P) of
class_qualifier ->
B = erl_syntax:class_qualifier_body(P),
case type(B) of
module_qualifier ->
M = mqb(B),
case type(M) of
variable -> {class_qualifier(P, B), {true, match_expr_list(M, L)}};
underscore -> {class_qualifier(P, B), {true, L}};
_ -> {P, A}
end;
_ -> {P, A}
end;
module_qualifier ->
M = mqb(P),
case type(M) of
variable -> {class_qualifier(P), {true, match_expr_list(M, L)}};
underscore -> {class_qualifier(P), {true, L}};
_ -> {P, A}
end;
_ -> {P, A}
end
end, {false, []}, Ps).
class_qualifier(P, B) -> class_qualifier(P, B, erl_syntax:class_qualifier_argument(P)).
class_qualifier(P) -> class_qualifier(P, P, erl_syntax:atom('throw')).
class_qualifier(P, B, C) -> cp(P, erl_syntax:class_qualifier(C, mqa(B))).
match_expr_list(M, L) -> [cp(M, erl_syntax:match_expr(M, cp(M, applicationl(erlang, get_stacktrace, [], M))))|L].
-compile({inline, [try_expr_transform/1, try_expr_handler_transform/1, try_expr_clause_patterns_transform/1]}).
atom(P, A) when is_tuple(P), is_atom(A) -> cp(P, erl_syntax:atom(A)).
otp_release() -> list_to_integer(erlang:system_info(otp_release)).
erts_version() -> lists:map(fun list_to_integer/1, string:tokens(erlang:system_info(version), ".")).
-compile({inline, [otp_release/0, erts_version/0]}).
replace_message(_Node, #param{verbose = false}, _F, _NM, _NN) -> ok;
replace_message(Node, #param{file = File}, F, NM, NN) -> replace_message_(erl_syntax:get_pos(Node), File, F, NM, NN).
replace_message_(P, F, {M, {N, A}}, NM, NN) -> replace_message_(P, F, lists:concat([M, ":", N]), NM, NN, A);
replace_message_(P, F, {N, A}, NM, NN) -> replace_message_(P, F, N, NM, NN, A).
replace_message_(P, F, N, NM, NN, A) -> io:fwrite("~ts:~p: replace ~ts/~B to ~s:~ts/~B~n", [F, P, N, A, NM, NN, A]).
cp(S, T) -> erl_syntax:copy_pos(S, T).
type(N) -> erl_syntax:type(N).
mqa(N) -> erl_syntax:module_qualifier_argument(N).
mqb(N) -> erl_syntax:module_qualifier_body(N).
-compile({inline, [cp/2, type/1, mqa/1, mqb/1]}).