1
%% =====================================================================
2
%% Tidies Erlang source code, removing unused functions, updating
3
%% obsolete constructs and function calls, etc.
5
%% Copyright (C) 1999-2002 Richard Carlsson
7
%% This library is free software; you can redistribute it and/or
8
%% modify it under the terms of the GNU Lesser General Public License
9
%% as published by the Free Software Foundation; either version 2 of
10
%% the License, or (at your option) any later version.
12
%% This library is distributed in the hope that it will be useful, but
13
%% WITHOUT ANY WARRANTY; without even the implied warranty of
14
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
%% Lesser General Public License for more details.
17
%% You should have received a copy of the GNU Lesser General Public
18
%% License along with this library; if not, write to the Free Software
19
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22
%% Author contact: richardc@csd.uu.se
24
%% $Id: erl_tidy.erl,v 1.39 2004/11/22 07:24:12 richardc Exp $
26
%% =====================================================================
28
%% @doc Tidies and pretty-prints Erlang source code, removing unused
29
%% functions, updating obsolete constructs and function calls, etc.
31
%% <p>Caveats: It is possible that in some intricate uses of macros,
32
%% the automatic addition or removal of parentheses around uses or
33
%% arguments could cause the resulting program to be rejected by the
34
%% compiler; however, we have found no such case in existing
35
%% code. Programs defining strange macros can usually not be read by
36
%% this program, and in those cases, no changes will be made.</p>
38
%% <p>If you really, really want to, you may call it "Inga".</p>
40
%% <p>Disclaimer: The author accepts no responsibility for errors
41
%% introduced in code that has been processed by the program. It has
42
%% been reasonably well tested, but the possibility of errors remains.
43
%% Keep backups of your original code safely stored, until you feel
44
%% confident that the new, modified code can be trusted.</p>
48
-export([dir/0, dir/1, dir/2, file/1, file/2, module/1, module/2]).
50
-include_lib("kernel/include/file.hrl").
52
-define(DEFAULT_BACKUP_SUFFIX, ".bak").
53
-define(DEFAULT_DIR, "").
54
-define(DEFAULT_REGEXP, ".*\\.erl$").
58
[{follow_links, false},
60
{regexp, ?DEFAULT_REGEXP},
63
%% =====================================================================
70
%% =====================================================================
71
%% @spec dir(Dir) -> ok
72
%% @equiv dir(Dir, [])
77
%% =====================================================================
78
%% @spec dir(Directory::filename(), Options::[term()]) -> ok
79
%% filename() = file:filename()
81
%% @doc Tidies Erlang source files in a directory and its
84
%% <p>Available options:
86
%% <dt>{follow_links, bool()}</dt>
88
%% <dd>If the value is <code>true</code>, symbolic directory
89
%% links will be followed. The default value is
90
%% <code>false</code>.</dd>
92
%% <dt>{recursive, bool()}</dt>
94
%% <dd>If the value is <code>true</code>, subdirectories will be
95
%% visited recursively. The default value is
96
%% <code>true</code>.</dd>
98
%% <dt>{regexp, string()}</dt>
100
%% <dd>The value denotes a regular expression (see module
101
%% <code>regexp</code>). Tidying will only be applied to those
102
%% regular files whose names match this pattern. The default
103
%% value is <code>".*\\.erl$"</code>, which matches normal
104
%% Erlang source file names.</dd>
106
%% <dt>{test, bool()}</dt>
108
%% <dd>If the value is <code>true</code>, no files will be
109
%% modified. The default value is <code>false</code>.</dd>
111
%% <dt>{verbose, bool()}</dt>
113
%% <dd>If the value is <code>true</code>, progress messages will
114
%% be output while the program is running, unless the
115
%% <code>quiet</code> option is <code>true</code>. The default
116
%% value when calling <code>dir/2</code> is
117
%% <code>true</code>.</dd>
121
%% <p>See the function <code>file/2</code> for further options.</p>
126
-record(dir, {follow_links = false, recursive = true, options}).
129
Opts1 = Opts ++ dir__defaults(),
130
Env = #dir{follow_links = proplists:get_bool(follow_links, Opts1),
131
recursive = proplists:get_bool(recursive, Opts1),
133
Regexp = proplists:get_value(regexp, Opts1),
134
case filename(Dir) of
140
dir_1(Dir1, Regexp, Env).
142
dir_1(Dir, Regexp, Env) ->
143
case file:list_dir(Dir) of
145
lists:foreach(fun (X) -> dir_2(X, Regexp, Dir, Env) end,
148
report_error("error reading directory `~s'",
153
dir_2(Name, Regexp, Dir, Env) ->
154
File = if Dir == "" ->
157
filename:join(Dir, Name)
159
case file_type(File) of
161
dir_4(File, Regexp, Env);
162
{value, directory} when Env#dir.recursive == true ->
163
case is_symlink(Name) of
165
dir_3(Name, Dir, Regexp, Env);
166
true when Env#dir.follow_links == true ->
167
dir_3(Name, Dir, Regexp, Env);
175
dir_3(Name, Dir, Regexp, Env) ->
176
Dir1 = filename:join(Dir, Name),
177
verbose("tidying directory `~s'.", [Dir1], Env#dir.options),
178
dir_1(Dir1, Regexp, Env).
180
dir_4(File, Regexp, Env) ->
181
case regexp:first_match(File, Regexp) of
183
Opts = [{outfile, File}, {dir, ""} | Env#dir.options],
184
case catch file(File, Opts) of
186
warn("error tidying `~s'.", [File], Opts);
196
[{backup_suffix, ?DEFAULT_BACKUP_SUFFIX},
199
{printer, default_printer()},
204
fun (Tree, Options) -> erl_prettypr:format(Tree, Options) end.
206
%% =====================================================================
207
%% @spec file(Name) -> ok
208
%% @equiv file(Name, [])
213
%% =====================================================================
214
%% @spec file(Name::filename(), Options::[term()]) -> ok
216
%% @doc Tidies an Erlang source code file.
218
%% <p>Available options are:
220
%% <dt>{backup_suffix, string()}</dt>
222
%% <dd>Specifies the file name suffix to be used when a backup
223
%% file is created; the default value is <code>".bak"</code>
224
%% (cf. the <code>backups</code> option).</dd>
226
%% <dt>{backups, bool()}</dt>
228
%% <dd>If the value is <code>true</code>, existing files will be
229
%% renamed before new files are opened for writing. The new
230
%% names are formed by appending the string given by the
231
%% <code>backup_suffix</code> option to the original name. The
232
%% default value is <code>true</code>.</dd>
234
%% <dt>{dir, filename()}</dt>
236
%% <dd>Specifies the name of the directory in which the output
237
%% file is to be written. By default, the current directory is
238
%% used. If the value is an empty string, the current directory
241
%% <dt>{outfile, filename()}</dt>
243
%% <dd>Specifies the name of the file (without suffix) to which
244
%% the resulting source code is to be written. If this option is
245
%% not specified, the <code>Name</code> argument is used.</dd>
247
%% <dt>{printer, Function}</dt>
249
%% <li><code>Function = (syntaxTree()) -> string()</code></li>
252
%% <p>Specifies a function for prettyprinting Erlang syntax trees.
253
%% This is used for outputting the resulting module definition.
254
%% The function is assumed to return formatted text for the given
255
%% syntax tree, and should raise an exception if an error occurs.
256
%% The default formatting function calls
257
%% <code>erl_prettypr:format/2</code>.</p></dd>
259
%% <dt>{test, bool()}</dt>
261
%% <dd>If the value is <code>true</code>, no files will be
262
%% modified; this is typically most useful if the
263
%% <code>verbose</code> flag is enabled, to generate reports
264
%% about the program files without affecting them. The default
265
%% value is <code>false</code>.</dd>
268
%% <p>See the function <code>module/2</code> for further options.</p>
270
%% @see erl_prettypr:format/2
275
Child = spawn_link(fun () -> file_1(Parent, Name, Opts) end),
279
{Child, {error, Reason}} ->
283
file_1(Parent, Name, Opts) ->
284
case catch file_2(Name, Opts) of
286
Parent ! {self(), {error, Reason}};
288
Parent ! {self(), ok}
291
file_2(Name, Opts) ->
292
Opts1 = Opts ++ file__defaults(),
293
Forms = read_module(Name, Opts1),
294
Comments = erl_comment_scan:file(Name),
295
Forms1 = erl_recomment:recomment_forms(Forms, Comments),
296
Tree = module(Forms1, [{file, Name} | Opts1]),
297
case proplists:get_bool(test, Opts1) of
301
write_module(Tree, Name, Opts1),
305
read_module(Name, Opts) ->
306
verbose("reading module `~s'.", [filename(Name)], Opts),
307
case epp_dodger:parse_file(Name) of
309
check_forms(Forms, Name),
312
error_read_file(Name),
316
check_forms(Fs, Name) ->
318
case erl_syntax:type(F) of
320
S = case erl_syntax:error_marker_info(F) of
326
report_error({Name, erl_syntax:get_pos(F),
333
lists:foreach(Fun, Fs).
335
%% Create the target directory and make a backup file if necessary,
336
%% then open the file, output the text and close the file
337
%% safely. Returns the file name.
339
write_module(Tree, Name, Opts) ->
340
Name1 = proplists:get_value(outfile, Opts, filename(Name)),
341
Dir = filename(proplists:get_value(dir, Opts, "")),
342
File = if Dir == "" ->
345
case file_type(Dir) of
346
{value, directory} ->
349
report_error("`~s' is not a directory.",
353
case file:make_dir(Dir) of
355
verbose("created directory `~s'.",
356
[filename(Dir)], Opts),
359
report_error("failed to create "
365
filename(filename:join(Dir, Name1))
367
case proplists:get_bool(backups, Opts) of
369
backup_file(File, Opts);
373
Printer = proplists:get_value(printer, Opts),
374
FD = open_output_file(File),
375
verbose("writing to file `~s'.", [File], Opts),
376
V = (catch {ok, output(FD, Printer, Tree, Opts)}),
382
error_write_file(File),
385
error_write_file(File),
389
output(FD, Printer, Tree, Opts) ->
390
io:put_chars(FD, Printer(Tree, Opts)),
393
%% file_type(filename()) -> {value, Type} | none
396
file_type(Name, false).
399
file_type(Name, true) == {value, symlink}.
401
file_type(Name, Links) ->
404
catch file:read_link_info(Name);
406
catch file:read_file_info(Name)
410
{value, Env#file_info.type};
414
error_read_file(Name),
417
error_read_file(Name),
420
error_read_file(Name),
424
open_output_file(FName) ->
425
case catch file:open(FName, [write]) of
429
error_open_output(FName),
432
error_open_output(FName),
435
error_open_output(FName),
439
%% If the file exists, rename it by appending the given suffix to the
442
backup_file(Name, Opts) ->
443
case file_type(Name) of
445
backup_file_1(Name, Opts);
447
error_backup_file(Name),
453
%% The file should exist and be a regular file here.
455
backup_file_1(Name, Opts) ->
456
Suffix = proplists:get_value(backup_suffix, Opts, ""),
457
Dest = filename:join(filename:dirname(Name),
458
filename:basename(Name) ++ Suffix),
459
case catch file:rename(Name, Dest) of
461
verbose("made backup of file `~s'.", [Name], Opts);
463
error_backup_file(Name),
466
error_backup_file(Name),
469
error_backup_file(Name),
474
%% =====================================================================
475
%% @spec module(Forms) -> syntaxTree()
476
%% @equiv module(Forms, [])
481
%% =====================================================================
482
%% @spec module(Forms, Options::[term()]) -> syntaxTree()
484
%% Forms = syntaxTree() | [syntaxTree()]
485
%% syntaxTree() = erl_syntax:syntaxTree()
487
%% @doc Tidies a syntax tree representation of a module
488
%% definition. The given <code>Forms</code> may be either a single
489
%% syntax tree of type <code>form_list</code>, or a list of syntax
490
%% trees representing "program forms". In either case,
491
%% <code>Forms</code> must represents a single complete module
492
%% definition. The returned syntax tree has type
493
%% <code>form_list</code> and represents a tidied-up version of the
496
%% <p>Available options are:
498
%% <dt>{auto_export_vars, bool()}</dt>
500
%% <dd>If the value is <code>true</code>, all matches
501
%% "<code>{V1, ..., Vn} = E</code>" where <code>E</code> is a
502
%% case-, if- or receive-expression whose branches all return
503
%% n-tuples (or explicitly throw exceptions) will be rewritten
504
%% to bind and export the variables <code>V1</code>, ...,
505
%% <code>Vn</code> directly. The default value is
506
%% <code>false</code>.
510
%% {X, Y} = case ... of
511
%% ... -> {17, foo()};
512
%% ... -> {42, bar()}
515
%% will be rewritten to:
518
%% ... -> X = 17, Y = foo(), {X, Y};
519
%% ... -> X = 42, Y = bar(), {X, Y}
523
%% <dt>{auto_list_comp, bool()}</dt>
525
%% <dd>If the value is <code>true</code>, calls to
526
%% <code>lists:map/2</code> and <code>lists:filter/2</code> will
527
%% be rewritten using list comprehensions. The default value is
528
%% <code>true</code>.</dd>
530
%% <dt>{file, string()}</dt>
532
%% <dd>Specifies the name of the file from which the source code
533
%% was taken. This is only used for generation of error
534
%% reports. The default value is the empty string.</dd>
536
%% <dt>{idem, bool()}</dt>
538
%% <dd>If the value is <code>true</code>, all options that affect
539
%% how the code is modified are set to "no changes". For example,
540
%% to only update guard tests, and nothing else, use the options
541
%% <code>[new_guard_tests, idem]</code>. (Recall that options
542
%% closer to the beginning of the list have higher
545
%% <dt>{keep_unused, bool()}</dt>
547
%% <dd>If the value is <code>true</code>, unused functions will
548
%% not be removed from the code. The default value is
549
%% <code>false</code>.</dd>
551
%% <dt>{new_guard_tests, bool()}</dt>
553
%% <dd>If the value is <code>true</code>, guard tests will be
554
%% updated to use the new names, e.g. "<code>is_integer(X)</code>"
555
%% instead of "<code>integer(X)</code>". The default value is
556
%% <code>true</code>. See also <code>old_guard_tests</code>.</dd>
558
%% <dt>{no_imports, bool()}</dt>
560
%% <dd>If the value is <code>true</code>, all import statements
561
%% will be removed and calls to imported functions will be
562
%% expanded to explicit remote calls. The default value is
563
%% <code>false</code>.</dd>
565
%% <dt>{old_guard_tests, bool()}</dt>
567
%% <dd>If the value is <code>true</code>, guard tests will be
568
%% changed to use the old names instead of the new ones,
569
%% e.g. "<code>integer(X)</code>" instead of
570
%% "<code>is_integer(X)</code>". The default value is
571
%% <code>false</code>. This option overrides the
572
%% <code>new_guard_tests</code> option.</dd>
574
%% <dt>{quiet, bool()}</dt>
576
%% <dd>If the value is <code>true</code>, all information
577
%% messages and warning messages will be suppressed. The default
578
%% value is <code>false</code>.</dd>
580
%% <dt>{rename, [{{atom(), atom(), integer()},
581
%% {atom(), atom()}}]}</dt>
583
%% <dd>The value is a list of pairs, associating tuples
584
%% <code>{Module, Name, Arity}</code> with tuples
585
%% <code>{NewModule, NewName}</code>, specifying renamings of
586
%% calls to remote functions. By default, the value is the empty
589
%% <p>The renaming affects only remote calls (also when
590
%% disguised by import declarations); local calls within a
591
%% module are not affected, and no function definitions are
592
%% renamed. Since the arity cannot change, the new name is
593
%% represented by <code>{NewModule, NewName}</code> only. Only
594
%% calls matching the specified arity will match; multiple
595
%% entries are necessary for renaming calls to functions that
596
%% have the same module and function name, but different
599
%% <p>This option can also be used to override the default
600
%% renaming of calls which use obsolete function names.</p></dd>
602
%% <dt>{verbose, bool()}</dt>
604
%% <dd>If the value is <code>true</code>, progress messages
605
%% will be output while the program is running, unless the
606
%% <code>quiet</code> option is <code>true</code>. The default
607
%% value is <code>false</code>.</dd>
611
module(Forms, Opts) when list(Forms) ->
612
module(erl_syntax:form_list(Forms), Opts);
613
module(Forms, Opts) ->
614
Opts1 = proplists:expand(module__expansions(), Opts)
615
++ module__defaults(),
616
File = proplists:get_value(file, Opts1, ""),
617
Forms1 = erl_syntax:flatten_form_list(Forms),
618
module_1(Forms1, File, Opts1).
620
module__defaults() ->
621
[{auto_export_vars, false},
622
{auto_list_comp, true},
623
{keep_unused, false},
624
{new_guard_tests, true},
626
{old_guard_tests, false},
630
module__expansions() ->
631
[{idem, [{auto_export_vars, false},
632
{auto_list_comp, false},
634
{new_guard_tests, false},
636
{old_guard_tests, false}]}].
638
module_1(Forms, File, Opts) ->
639
Info = analyze_forms(Forms, File),
640
Module = get_module_name(Info, File),
641
Attrs = get_module_attributes(Info),
642
Exports = get_module_exports(Info),
643
Imports = get_module_imports(Info),
644
Opts1 = check_imports(Imports, Opts, File),
645
Fs = erl_syntax:form_list_elements(Forms),
646
{Names, Defs} = collect_functions(Fs),
647
Exports1 = check_export_all(Attrs, Names, Exports),
648
Roots = ordsets:union(ordsets:from_list(Exports1),
649
hidden_uses(Fs, Imports)),
650
{Names1, Used, Imported, Defs1} = visit_used(Names, Defs, Roots,
653
Fs1 = update_forms(Fs, Defs1, Imported, Opts1),
654
Fs2 = filter_forms(Fs1, Names1, Used, Opts1),
655
rewrite(Forms, erl_syntax:form_list(Fs2)).
657
analyze_forms(Forms, File) ->
658
case catch {ok, erl_syntax_lib:analyze_forms(Forms)} of
662
report_error({File, 0, "syntax error."}),
663
erlang:fault(badarg);
670
get_module_name(List, File) ->
671
case lists:keysearch(module, 1, List) of
672
{value, {module, M}} ->
675
report_error({File, 0,
676
"cannot determine module name."}),
680
get_module_attributes(List) ->
681
case lists:keysearch(attributes, 1, List) of
682
{value, {attributes, As}} ->
688
get_module_exports(List) ->
689
case lists:keysearch(exports, 1, List) of
690
{value, {exports, Es}} ->
696
get_module_imports(List) ->
697
case lists:keysearch(imports, 1, List) of
698
{value, {imports, Is}} ->
705
lists:append([if list(T) -> T; true -> [T] end
706
|| {compile, T} <- As]).
708
flatten_imports(Is) ->
709
[{F, M} || {M, Fs} <- Is, F <- Fs].
711
check_imports(Is, Opts, File) ->
712
case check_imports_1(lists:sort(Is)) of
716
case proplists:get_bool(no_imports, Opts) of
719
"conflicting import declarations - "
720
"will not expand imports."},
722
%% prevent expansion of imports
723
[{no_imports, false} | Opts];
729
check_imports_1([{F1, M1}, {F2, M2} | _Is]) when F1 == F2, M1 /= M2 ->
731
check_imports_1([_ | Is]) ->
733
check_imports_1([]) ->
736
check_export_all(Attrs, Names, Exports) ->
737
case lists:member(export_all, compile_attrs(Attrs)) of
739
Exports ++ sets:to_list(Names);
744
filter_forms(Fs, Names, Used, Opts) ->
745
Keep = case proplists:get_bool(keep_unused, Opts) of
751
[F || F <- Fs, keep_form(F, Keep, Opts)].
753
keep_form(Form, Used, Opts) ->
754
case erl_syntax:type(Form) of
756
N = erl_syntax_lib:analyze_function(Form),
757
case sets:is_element(N, Used) of
759
report_removed_def("function", N, Form, Opts),
765
N = erl_syntax_lib:analyze_rule(Form),
766
case sets:is_element(N, Used) of
768
report_removed_def("rule", N, Form, Opts),
774
case erl_syntax_lib:analyze_attribute(Form) of
790
report_removed_def(Type, {N, A}, Form, Opts) ->
791
File = proplists:get_value(file, Opts, ""),
792
report({File, erl_syntax:get_pos(Form),
793
"removing unused ~s `~w/~w'."},
796
collect_functions(Forms) ->
798
fun (F, {Names, Defs}) ->
799
case erl_syntax:type(F) of
801
N = erl_syntax_lib:analyze_function(F),
802
{sets:add_element(N, Names),
803
dict:store(N, {F, []}, Defs)};
805
N = erl_syntax_lib:analyze_rule(F),
806
{sets:add_element(N, Names),
807
dict:store(N, {F, []}, Defs)};
812
{sets:new(), dict:new()},
815
update_forms([F | Fs], Defs, Imports, Opts) ->
816
case erl_syntax:type(F) of
818
N = erl_syntax_lib:analyze_function(F),
819
{F1, Fs1} = dict:fetch(N, Defs),
820
[F1 | lists:reverse(Fs1)] ++ update_forms(Fs, Defs, Imports,
823
N = erl_syntax_lib:analyze_rule(F),
824
{F1, Fs1} = dict:fetch(N, Defs),
825
[F1 | lists:reverse(Fs1)] ++ update_forms(Fs, Defs, Imports,
828
[update_attribute(F, Imports, Opts)
829
| update_forms(Fs, Defs, Imports, Opts)];
831
[F | update_forms(Fs, Defs, Imports, Opts)]
833
update_forms([], _, _, _) ->
836
update_attribute(F, Imports, Opts) ->
837
case erl_syntax_lib:analyze_attribute(F) of
839
Ns1 = ordsets:from_list([N || N <- Ns,
840
sets:is_element(N, Imports)]),
841
case ordsets:subtract(ordsets:from_list(Ns), Ns1) of
845
File = proplists:get_value(file, Opts, ""),
846
report({File, erl_syntax:get_pos(F),
847
"removing unused imports:~s"},
848
[[io_lib:fwrite("\n\t`~w:~w/~w'", [M, N, A])
849
|| {N, A} <- Names]], Opts)
851
Is = [make_fname(N) || N <- Ns1],
853
%% This will be filtered out later.
854
erl_syntax:warning_marker(deleted);
856
F1 = erl_syntax:attribute(erl_syntax:atom(import),
858
erl_syntax:list(Is)]),
862
Es = [make_fname(N) || N <- ordsets:from_list(Ns)],
863
F1 = erl_syntax:attribute(erl_syntax:atom(export),
864
[erl_syntax:list(Es)]),
870
make_fname({F, A}) ->
871
erl_syntax:arity_qualifier(erl_syntax:atom(F),
872
erl_syntax:integer(A)).
874
hidden_uses(Fs, Imports) ->
875
Used = lists:foldl(fun (F, S) ->
876
case erl_syntax:type(F) of
884
ordsets:subtract(Used, ordsets:from_list([F || {F, _M} <- Imports])).
886
hidden_uses_1(Tree, Used) ->
887
erl_syntax_lib:fold(fun hidden_uses_2/2, Used, Tree).
889
hidden_uses_2(Tree, Used) ->
890
case erl_syntax:type(Tree) of
892
F = erl_syntax:application_operator(Tree),
893
case erl_syntax:type(F) of
895
As = erl_syntax:application_arguments(Tree),
896
N = {erl_syntax:atom_value(F), length(As)},
897
case is_auto_imported(N) of
901
ordsets:add_element(N, Used)
907
F = erl_syntax:implicit_fun_name(Tree),
908
case catch {ok, erl_syntax_lib:analyze_function_name(F)} of
909
{ok, {Name, Arity} = N}
910
when atom(Name), integer(Arity) ->
911
ordsets:add_element(N, Used);
928
auto_list_comp = true,
929
auto_export_vars = false,
930
new_guard_tests = true,
931
old_guard_tests = false}).
933
-record(st, {varc, used, imported, vars, functions, new_forms, rename}).
935
visit_used(Names, Defs, Roots, Imports, Module, Opts) ->
936
File = proplists:get_value(file, Opts, ""),
937
NoImports = proplists:get_bool(no_imports, Opts),
938
Rename = proplists:append_values(rename, Opts),
939
loop(Roots, sets:new(), Defs,
942
imports = dict:from_list(Imports),
943
verbosity = verbosity(Opts),
944
no_imports = NoImports,
945
spawn_funs = proplists:get_bool(spawn_funs, Opts),
946
auto_list_comp = proplists:get_bool(auto_list_comp, Opts),
947
auto_export_vars = proplists:get_bool(auto_export_vars,
949
new_guard_tests = proplists:get_bool(new_guard_tests,
951
old_guard_tests = proplists:get_bool(old_guard_tests,
953
#st{used = sets:from_list(Roots),
954
imported = sets:new(),
956
rename = dict:from_list([X || {F1, F2} = X <- Rename,
958
is_atom_pair(F2)])}).
960
loop([F | Work], Seen0, Defs0, Env, St0) ->
961
case sets:is_element(F, Seen0) of
963
loop(Work, Seen0, Defs0, Env, St0);
965
Seen1 = sets:add_element(F, Seen0),
966
case dict:find(F, Defs0) of
968
Vars = erl_syntax_lib:variables(Form),
969
Form1 = erl_syntax_lib:annotate_bindings(Form, []),
970
{Form2, St1} = visit(Form1, Env#env{current = F},
975
Fs1 = St1#st.new_forms ++ Fs,
976
Defs1 = dict:store(F, {Form2, Fs1}, Defs0),
978
Work1 = sets:to_list(Used) ++ Work,
979
St2 = St1#st{used = sets:union(Used, St0#st.used)},
980
loop(Work1, Seen1, Defs1, Env, St2);
982
%% Quietly ignore any names that have no definition.
983
loop(Work, Seen1, Defs0, Env, St0)
986
loop([], _, Defs, _, St) ->
987
{St#st.functions, St#st.used, St#st.imported, Defs}.
989
visit(Tree, Env, St0) ->
990
case erl_syntax:type(Tree) of
992
visit_application(Tree, Env, St0);
994
visit_infix_expr(Tree, Env, St0);
996
visit_prefix_expr(Tree, Env, St0);
998
visit_implicit_fun(Tree, Env, St0);
1000
visit_clause(Tree, Env, St0);
1002
visit_list_comp(Tree, Env, St0);
1004
visit_match_expr(Tree, Env, St0);
1006
visit_other(Tree, Env, St0)
1009
visit_other(Tree, Env, St) ->
1010
F = fun (T, S) -> visit(T, Env, S) end,
1011
erl_syntax_lib:mapfold_subtrees(F, St, Tree).
1013
visit_list(Ts, Env, St0) ->
1014
lists:mapfoldl(fun (T, S) -> visit(T, Env, S) end, St0, Ts).
1016
visit_implicit_fun(Tree, _Env, St0) ->
1017
F = erl_syntax:implicit_fun_name(Tree),
1018
case catch {ok, erl_syntax_lib:analyze_function_name(F)} of
1019
{ok, {Name, Arity} = N}
1020
when atom(Name), integer(Arity) ->
1021
Used = sets:add_element(N, St0#st.used),
1022
{Tree, St0#st{used = Used}};
1027
visit_clause(Tree, Env, St0) ->
1028
%% We do not visit the patterns (for now, anyway).
1029
Ps = erl_syntax:clause_patterns(Tree),
1030
{G, St1} = case erl_syntax:clause_guard(Tree) of
1034
visit(G0, Env#env{context = guard_test}, St0)
1036
{B, St2} = visit_list(erl_syntax:clause_body(Tree), Env, St1),
1037
{rewrite(Tree, erl_syntax:clause(Ps, G, B)), St2}.
1039
visit_infix_expr(Tree, #env{context = guard_test}, St0) ->
1040
%% Detect transition from guard test to guard expression.
1041
visit_other(Tree, #env{context = guard_expr}, St0);
1042
visit_infix_expr(Tree, Env, St0) ->
1043
visit_other(Tree, Env, St0).
1045
visit_prefix_expr(Tree, #env{context = guard_test}, St0) ->
1046
%% Detect transition from guard test to guard expression.
1047
visit_other(Tree, #env{context = guard_expr}, St0);
1048
visit_prefix_expr(Tree, Env, St0) ->
1049
visit_other(Tree, Env, St0).
1051
visit_application(Tree, Env, St0) ->
1053
#env{context = guard_test} ->
1054
Env#env{context = guard_expr};
1058
{F, St1} = visit(erl_syntax:application_operator(Tree), Env1, St0),
1059
{As, St2} = visit_list(erl_syntax:application_arguments(Tree), Env1,
1061
case erl_syntax:type(F) of
1063
visit_atom_application(F, As, Tree, Env, St2);
1065
visit_named_fun_application(F, As, Tree, Env, St2);
1067
visit_lambda_application(F, As, Tree, Env, St2);
1069
visit_nonlocal_application(F, As, Tree, Env, St2)
1072
visit_application_final(F, As, Tree, St0) ->
1073
{rewrite(Tree, erl_syntax:application(F, As)), St0}.
1075
revisit_application(F, As, Tree, Env, St0) ->
1076
visit(rewrite(Tree, erl_syntax:application(F, As)), Env, St0).
1078
visit_atom_application(F, As, Tree, #env{context = guard_test} = Env,
1080
N = erl_syntax:atom_value(F),
1082
N1 = case Env#env.old_guard_tests of
1084
reverse_guard_test(N, A);
1086
case Env#env.new_guard_tests of
1088
rewrite_guard_test(N, A);
1094
report({Env#env.file, erl_syntax:get_pos(F),
1095
"changing guard test `~w' to `~w'."},
1096
[N, N1], Env#env.verbosity);
1100
%% No need to revisit here.
1101
F1 = rewrite(F, erl_syntax:atom(N1)),
1102
visit_application_final(F1, As, Tree, St0);
1103
visit_atom_application(F, As, Tree, #env{context = guard_expr}, St0) ->
1104
%% Atom applications in guard expressions are never local calls.
1105
visit_application_final(F, As, Tree, St0);
1106
visit_atom_application(F, As, Tree, Env, St0) ->
1107
N = {erl_syntax:atom_value(F), length(As)},
1108
case is_auto_imported(N) of
1110
visit_bif_call(N, F, As, Tree, Env, St0);
1112
case is_imported(N, Env) of
1114
visit_import_application(N, F, As, Tree, Env, St0);
1116
Used = sets:add_element(N, St0#st.used),
1117
visit_application_final(F, As, Tree,
1118
St0#st{used = Used})
1122
visit_import_application({N, A} = Name, F, As, Tree, Env, St0) ->
1123
M = dict:fetch(Name, Env#env.imports),
1124
Expand = case Env#env.no_imports of
1128
auto_expand_import({M, N, A}, St0)
1132
report({Env#env.file, erl_syntax:get_pos(F),
1133
"expanding call to imported function `~w:~w/~w'."},
1134
[M, N, A], Env#env.verbosity),
1135
F1 = erl_syntax:module_qualifier(erl_syntax:atom(M),
1136
erl_syntax:atom(N)),
1137
revisit_application(rewrite(F, F1), As, Tree, Env, St0);
1139
Is = sets:add_element(Name, St0#st.imported),
1140
visit_application_final(F, As, Tree, St0#st{imported = Is})
1143
visit_bif_call({apply, 2}, F, [E, Args] = As, Tree, Env, St0) ->
1144
case erl_syntax:is_proper_list(Args) of
1146
report({Env#env.file, erl_syntax:get_pos(F),
1147
"changing use of `apply/2' "
1148
"to direct function call."},
1149
[], Env#env.verbosity),
1150
As1 = erl_syntax:list_elements(Args),
1151
revisit_application(E, As1, Tree, Env, St0);
1153
visit_application_final(F, As, Tree, St0)
1155
visit_bif_call({apply, 3}, F, [M, N, Args] = As, Tree, Env, St0) ->
1156
case erl_syntax:is_proper_list(Args) of
1158
report({Env#env.file, erl_syntax:get_pos(F),
1159
"changing use of `apply/3' "
1160
"to direct remote call."},
1161
[], Env#env.verbosity),
1162
F1 = rewrite(F, erl_syntax:module_qualifier(M, N)),
1163
As1 = erl_syntax:list_elements(Args),
1164
visit_nonlocal_application(F1, As1, Tree, Env, St0);
1166
visit_application_final(F, As, Tree, St0)
1168
visit_bif_call({spawn, 3} = N, F, [_, _, _] = As, Tree, Env, St0) ->
1169
visit_spawn_call(N, F, [], As, Tree, Env, St0);
1170
visit_bif_call({spawn_link, 3} = N, F, [_, _, _] = As, Tree, Env,
1172
visit_spawn_call(N, F, [], As, Tree, Env, St0);
1173
visit_bif_call({spawn, 4} = N, F, [A | [_, _, _] = As], Tree, Env,
1175
visit_spawn_call(N, F, [A], As, Tree, Env, St0);
1176
visit_bif_call({spawn_link, 4} = N, F, [A | [_, _, _] = As], Tree, Env,
1178
visit_spawn_call(N, F, [A], As, Tree, Env, St0);
1179
visit_bif_call(_, F, As, Tree, _Env, St0) ->
1180
visit_application_final(F, As, Tree, St0).
1182
visit_spawn_call({N, A}, F, Ps, [A1, A2, A3] = As, Tree,
1183
#env{spawn_funs = true} = Env, St0) ->
1184
case erl_syntax:is_proper_list(A3) of
1186
report({Env#env.file, erl_syntax:get_pos(F),
1187
"changing use of `~w/~w' to `~w/~w' with a fun."},
1188
[N, A, N, 1 + length(Ps)], Env#env.verbosity),
1189
F1 = case erl_syntax:is_atom(A1, Env#env.module) of
1194
erl_syntax:module_qualifier(A1, A2))
1196
%% Need to do some scoping tricks here to make sure the
1197
%% arguments are evaluated by the parent, not by the spawned
1199
As1 = erl_syntax:list_elements(A3),
1200
{Vs, St1} = new_variables(length(As1), St0),
1201
E1 = clone(F1, erl_syntax:application(F1, Vs)),
1202
C1 = clone(E1, erl_syntax:clause([], [E1])),
1203
E2 = clone(C1, erl_syntax:fun_expr([C1])),
1204
C2 = clone(E2, erl_syntax:clause(Vs, [], [E2])),
1205
E3 = clone(C2, erl_syntax:fun_expr([C2])),
1206
E4 = clone(E3, erl_syntax:application(E3, As1)),
1207
E5 = erl_syntax_lib:annotate_bindings(E4, get_env(A1)),
1208
{E6, St2} = visit(E5, Env, St1),
1209
F2 = rewrite(F, erl_syntax:atom(N)),
1210
visit_nonlocal_application(F2, Ps ++ [E6], Tree, Env, St2);
1212
visit_application_final(F, Ps ++ As, Tree, St0)
1214
visit_spawn_call(_, F, Ps, As, Tree, _Env, St0) ->
1215
visit_application_final(F, Ps ++ As, Tree, St0).
1217
visit_named_fun_application(F, As, Tree, Env, St0) ->
1218
Name = erl_syntax:implicit_fun_name(F),
1219
case catch {ok, erl_syntax_lib:analyze_function_name(Name)} of
1220
{ok, {A, N}} when atom(A), integer(N), N == length(As) ->
1221
case is_nonlocal({A, N}, Env) of
1223
%% Making this a direct call would be an error.
1224
visit_application_final(F, As, Tree, St0);
1226
report({Env#env.file, erl_syntax:get_pos(F),
1227
"changing application of implicit fun "
1228
"to direct local call."},
1229
[], Env#env.verbosity),
1230
Used = sets:add_element({A, N}, St0#st.used),
1231
F1 = rewrite(F, erl_syntax:atom(A)),
1232
revisit_application(F1, As, Tree, Env,
1233
St0#st{used = Used})
1236
visit_application_final(F, As, Tree, St0)
1239
visit_lambda_application(F, As, Tree, Env, St0) ->
1240
A = erl_syntax:fun_expr_arity(F),
1241
case A == length(As) of
1243
report({Env#env.file, erl_syntax:get_pos(F),
1244
"changing application of fun-expression "
1245
"to local function call."},
1246
[], Env#env.verbosity),
1247
{Base, _} = Env#env.current,
1248
Free = [erl_syntax:variable(V) || V <- get_free_vars(F)],
1251
{Name, St1} = new_fname({Base, A1}, St0),
1252
Cs = augment_clauses(erl_syntax:fun_expr_clauses(F), Free),
1253
F1 = erl_syntax:atom(Name),
1254
New = rewrite(F, erl_syntax:function(F1, Cs)),
1255
Used = sets:add_element({Name, A1}, St1#st.used),
1256
Forms = [New | St1#st.new_forms],
1257
St2 = St1#st{new_forms = Forms, used = Used},
1258
visit_application_final(F1, As ++ Free, Tree, St2);
1260
warn({Env#env.file, erl_syntax:get_pos(F),
1261
"arity mismatch in fun-expression application."},
1262
[], Env#env.verbosity),
1263
visit_application_final(F, As, Tree, St0)
1266
augment_clauses(Cs, Vs) ->
1268
Ps = erl_syntax:clause_patterns(C),
1269
G = erl_syntax:clause_guard(C),
1270
Es = erl_syntax:clause_body(C),
1271
rewrite(C, erl_syntax:clause(Ps ++ Vs, G, Es))
1275
visit_nonlocal_application(F, As, Tree, Env, St0) ->
1276
case erl_syntax:type(F) of
1278
case erl_syntax:tuple_elements(F) of
1280
report({Env#env.file, erl_syntax:get_pos(F),
1281
"changing application of 2-tuple "
1282
"to direct remote call."},
1283
[], Env#env.verbosity),
1284
F1 = erl_syntax:module_qualifier(X1, X2),
1285
revisit_application(rewrite(F, F1), As, Tree, Env,
1288
visit_application_final(F, As, Tree, St0)
1291
case catch {ok, erl_syntax_lib:analyze_function_name(F)} of
1292
{ok, {M, N}} when atom(M), atom(N) ->
1293
visit_remote_application({M, N, length(As)}, F, As,
1296
visit_application_final(F, As, Tree, St0)
1299
visit_application_final(F, As, Tree, St0)
1302
visit_remote_application({lists, append, 2}, F, [A1, A2], Tree, Env,
1304
report({Env#env.file, erl_syntax:get_pos(F),
1305
"replacing call to `lists:append/2' "
1306
"with the `++' operator."},
1307
[], Env#env.verbosity),
1308
Tree1 = erl_syntax:infix_expr(A1, erl_syntax:operator('++'), A2),
1309
visit(rewrite(Tree, Tree1), Env, St0);
1310
visit_remote_application({lists, subtract, 2}, F, [A1, A2], Tree, Env,
1312
report({Env#env.file, erl_syntax:get_pos(F),
1313
"replacing call to `lists:subtract/2' "
1314
"with the `--' operator."},
1315
[], Env#env.verbosity),
1316
Tree1 = erl_syntax:infix_expr(A1, erl_syntax:operator('--'), A2),
1317
visit(rewrite(Tree, Tree1), Env, St0);
1319
visit_remote_application({lists, filter, 2}, F, [A1, A2] = As, Tree,
1321
case Env#env.auto_list_comp
1322
and (get_var_exports(A1) == [])
1323
and (get_var_exports(A2) == []) of
1325
report({Env#env.file, erl_syntax:get_pos(F),
1326
"replacing call to `lists:filter/2' "
1327
"with a list comprehension."},
1328
[], Env#env.verbosity),
1329
{V, St1} = new_variable(St0),
1330
G = clone(A2, erl_syntax:generator(V, A2)),
1331
T = clone(A1, erl_syntax:application(A1, [V])),
1332
L = erl_syntax:list_comp(V, [G, T]),
1333
L1 = erl_syntax_lib:annotate_bindings(L, get_env(Tree)),
1334
visit(rewrite(Tree, L1), Env, St1);
1336
visit_application_final(F, As, Tree, St0)
1338
visit_remote_application({lists, map, 2}, F, [A1, A2] = As, Tree, Env,
1340
case Env#env.auto_list_comp
1341
and (get_var_exports(A1) == [])
1342
and (get_var_exports(A2) == []) of
1344
report({Env#env.file, erl_syntax:get_pos(F),
1345
"replacing call to `lists:map/2' "
1346
"with a list comprehension."},
1347
[], Env#env.verbosity),
1348
{V, St1} = new_variable(St0),
1349
T = clone(A1, erl_syntax:application(A1, [V])),
1350
G = clone(A2, erl_syntax:generator(V, A2)),
1351
L = erl_syntax:list_comp(T, [G]),
1352
L1 = erl_syntax_lib:annotate_bindings(L, get_env(Tree)),
1353
visit(rewrite(Tree, L1), Env, St1);
1355
visit_application_final(F, As, Tree, St0)
1357
visit_remote_application({M, N, A} = Name, F, As, Tree, Env, St) ->
1358
case is_auto_imported(Name) of
1360
%% We don't remove the qualifier - it might be there for the
1362
visit_bif_call({N, A}, F, As, Tree, Env, St);
1364
case rename_remote_call(Name, St) of
1366
report({Env#env.file, erl_syntax:get_pos(F),
1367
"updating obsolete call to `~w:~w/~w' "
1368
"to use `~w:~w/~w' instead."},
1369
[M, N, A, M1, N1, A], Env#env.verbosity),
1370
M2 = erl_syntax:atom(M1),
1371
N2 = erl_syntax:atom(N1),
1372
F1 = erl_syntax:module_qualifier(M2, N2),
1373
revisit_application(rewrite(F, F1), As, Tree, Env,
1376
visit_application_final(F, As, Tree, St)
1380
auto_expand_import({lists, append, 2}, _St) -> true;
1381
auto_expand_import({lists, filter, 2}, _St) -> true;
1382
auto_expand_import({lists, map, 2}, _St) -> true;
1383
auto_expand_import(Name, St) ->
1384
case is_auto_imported(Name) of
1388
case rename_remote_call(Name, St) of
1396
visit_list_comp(Tree, Env, St0) ->
1397
Es = erl_syntax:list_comp_body(Tree),
1398
{Es1, St1} = visit_list_comp_body(Es, Env, St0),
1399
{T, St2} = visit(erl_syntax:list_comp_template(Tree), Env, St1),
1400
{rewrite(Tree, erl_syntax:list_comp(T, Es1)), St2}.
1402
visit_list_comp_body_join(Env) ->
1404
case is_generator(E) of
1406
visit_generator(E, Env, St0);
1408
visit_filter(E, Env, St0)
1412
visit_list_comp_body(Es, Env, St0) ->
1413
lists:mapfoldl(visit_list_comp_body_join(Env), St0, Es).
1415
%% 'visit_filter' also handles uninteresting generators.
1417
visit_filter(E, Env, St0) ->
1420
%% "interesting" generators have the form V <- [V || ...]; this can be
1421
%% unfolded as long as no bindings become erroneously shadowed.
1423
visit_generator(G, Env, St0) ->
1424
P = erl_syntax:generator_pattern(G),
1425
case erl_syntax:type(P) of
1427
B = erl_syntax:generator_body(G),
1428
case erl_syntax:type(B) of
1430
T = erl_syntax:list_comp_template(B),
1431
case erl_syntax:type(T) of
1433
visit_generator_1(G, Env, St0);
1435
visit_filter(G, Env, St0)
1438
visit_filter(G, Env, St0)
1441
visit_filter(G, Env, St0)
1444
visit_generator_1(G, Env, St0) ->
1445
recommend({Env#env.file, erl_syntax:get_pos(G),
1446
"unfold that this nested list comprehension can be unfolded "
1447
"by hand to get better efficiency."},
1448
[], Env#env.verbosity),
1449
visit_filter(G, Env, St0).
1451
visit_match_expr(Tree, Env, St0) ->
1452
%% We do not visit the pattern (for now, anyway).
1453
P = erl_syntax:match_expr_pattern(Tree),
1454
{B, St1} = visit(erl_syntax:match_expr_body(Tree), Env, St0),
1455
case erl_syntax:type(P) of
1457
Ps = erl_syntax:tuple_elements(P),
1458
case lists:all(fun is_variable/1, Ps) of
1460
Vs = lists:sort([erl_syntax:variable_name(X)
1462
case ordsets:is_set(Vs) of
1464
Xs = get_var_exports(B),
1465
case ordsets:intersection(Vs, Xs) of
1467
visit_match_body(Ps, P, B, Tree,
1470
visit_match_expr_final(P, B, Tree,
1474
visit_match_expr_final(P, B, Tree, Env, St1)
1477
visit_match_expr_final(P, B, Tree, Env, St1)
1480
visit_match_expr_final(P, B, Tree, Env, St1)
1483
visit_match_expr_final(P, B, Tree, _Env, St0) ->
1484
{rewrite(Tree, erl_syntax:match_expr(P, B)), St0}.
1486
visit_match_body(_Ps, P, B, Tree, #env{auto_export_vars = false} = Env,
1488
visit_match_expr_final(P, B, Tree, Env, St0);
1489
visit_match_body(Ps, P, B, Tree, Env, St0) ->
1490
case erl_syntax:type(B) of
1492
Cs = erl_syntax:case_expr_clauses(B),
1493
case multival_clauses(Cs, length(Ps), Ps) of
1495
report_export_vars(Env#env.file,
1496
erl_syntax:get_pos(B),
1497
"case", Env#env.verbosity),
1498
A = erl_syntax:case_expr_argument(B),
1499
Tree1 = erl_syntax:case_expr(A, Cs1),
1500
{rewrite(Tree, Tree1), St0};
1502
visit_match_expr_final(P, B, Tree, Env, St0)
1505
Cs = erl_syntax:if_expr_clauses(B),
1506
case multival_clauses(Cs, length(Ps), Ps) of
1508
report_export_vars(Env#env.file,
1509
erl_syntax:get_pos(B),
1510
"if", Env#env.verbosity),
1511
Tree1 = erl_syntax:if_expr(Cs1),
1512
{rewrite(Tree, Tree1), St0};
1514
visit_match_expr_final(P, B, Tree, Env, St0)
1517
Cs = erl_syntax:cond_expr_clauses(B),
1518
case multival_clauses(Cs, length(Ps), Ps) of
1520
report_export_vars(Env#env.file,
1521
erl_syntax:get_pos(B),
1522
"cond", Env#env.verbosity),
1523
Tree1 = erl_syntax:cond_expr(Cs1),
1524
{rewrite(Tree, Tree1), St0};
1526
visit_match_expr_final(P, B, Tree, Env, St0)
1529
%% Handle the timeout case as an extra clause.
1530
As = erl_syntax:receive_expr_action(B),
1531
C = erl_syntax:clause([], As),
1532
Cs = erl_syntax:receive_expr_clauses(B),
1533
case multival_clauses([C | Cs], length(Ps), Ps) of
1534
{true, [C1 | Cs1]} ->
1535
report_export_vars(Env#env.file,
1536
erl_syntax:get_pos(B),
1537
"receive", Env#env.verbosity),
1538
T = erl_syntax:receive_expr_timeout(B),
1539
As1 = erl_syntax:clause_body(C1),
1540
Tree1 = erl_syntax:receive_expr(Cs1, T, As1),
1541
{rewrite(Tree, Tree1), St0};
1543
visit_match_expr_final(P, B, Tree, Env, St0)
1546
visit_match_expr_final(P, B, Tree, Env, St0)
1549
multival_clauses(Cs, N, Vs) ->
1550
multival_clauses(Cs, N, Vs, []).
1552
multival_clauses([C | Cs], N, Vs, Cs1) ->
1553
case erl_syntax:clause_body(C) of
1558
case erl_syntax:type(E) of
1560
Ts = erl_syntax:tuple_elements(E),
1561
if length(Ts) == N ->
1562
Bs = make_matches(E, Vs, Ts),
1563
Es1 = replace_last(Es, Bs),
1564
Ps = erl_syntax:clause_patterns(C),
1565
G = erl_syntax:clause_guard(C),
1566
C1 = erl_syntax:clause(Ps, G, Es1),
1567
multival_clauses(Cs, N, Vs,
1568
[rewrite(C, C1) | Cs1]);
1573
case erl_syntax_lib:is_fail_expr(E) of
1575
%% We must add dummy bindings here so we
1576
%% don't introduce compilation errors due to
1577
%% "unsafe" variable exports.
1578
Bs = make_matches(Vs,
1579
erl_syntax:atom(false)),
1580
Es1 = replace_last(Es, Bs ++ [E]),
1581
Ps = erl_syntax:clause_patterns(C),
1582
G = erl_syntax:clause_guard(C),
1583
C1 = erl_syntax:clause(Ps, G, Es1),
1584
multival_clauses(Cs, N, Vs,
1585
[rewrite(C, C1) | Cs1]);
1591
multival_clauses([], _N, _Vs, Cs) ->
1592
{true, lists:reverse(Cs)}.
1594
make_matches(E, Vs, Ts) ->
1595
case make_matches(Vs, Ts) of
1599
[rewrite(E, B) | Bs] % preserve comments on E (but not B)
1602
make_matches([V | Vs], [T | Ts]) ->
1603
[erl_syntax:match_expr(V, T) | make_matches(Vs, Ts)];
1604
make_matches([V | Vs], T) when T /= [] ->
1605
[erl_syntax:match_expr(V, T) | make_matches(Vs, T)];
1606
make_matches([], _) ->
1609
rename_remote_call(F, St) ->
1610
case dict:find(F, St#st.rename) of
1612
rename_remote_call_1(F);
1616
rename_remote_call_1({dict, dict_to_list, 1}) -> {dict, to_list};
1617
rename_remote_call_1({dict, list_to_dict, 1}) -> {dict, from_list};
1618
rename_remote_call_1({erl_eval, arg_list, 2}) -> {erl_eval, expr_list};
1619
rename_remote_call_1({erl_eval, arg_list, 3}) -> {erl_eval, expr_list};
1620
rename_remote_call_1({erl_eval, seq, 2}) -> {erl_eval, exprs};
1621
rename_remote_call_1({erl_eval, seq, 3}) -> {erl_eval, exprs};
1622
rename_remote_call_1({erl_pp, seq, 1}) -> {erl_eval, seq};
1623
rename_remote_call_1({erl_pp, seq, 2}) -> {erl_eval, seq};
1624
rename_remote_call_1({erlang, info, 1}) -> {erlang, system_info};
1625
rename_remote_call_1({io, parse_erl_seq, 1}) -> {io, parse_erl_exprs};
1626
rename_remote_call_1({io, parse_erl_seq, 2}) -> {io, parse_erl_exprs};
1627
rename_remote_call_1({io, parse_erl_seq, 3}) -> {io, parse_erl_exprs};
1628
rename_remote_call_1({io, scan_erl_seq, 1}) -> {io, scan_erl_exprs};
1629
rename_remote_call_1({io, scan_erl_seq, 2}) -> {io, scan_erl_exprs};
1630
rename_remote_call_1({io, scan_erl_seq, 3}) -> {io, scan_erl_exprs};
1631
rename_remote_call_1({io_lib, reserved_word, 1}) -> {erl_scan, reserved_word};
1632
rename_remote_call_1({io_lib, scan, 1}) -> {erl_scan, string};
1633
rename_remote_call_1({io_lib, scan, 2}) -> {erl_scan, string};
1634
rename_remote_call_1({io_lib, scan, 3}) -> {erl_scan, tokens};
1635
rename_remote_call_1({orddict, dict_to_list, 1}) -> {orddict, to_list};
1636
rename_remote_call_1({orddict, list_to_dict, 1}) -> {orddict, from_list};
1637
rename_remote_call_1({ordsets, list_to_set, 1}) -> {ordsets, from_list};
1638
rename_remote_call_1({ordsets, new_set, 0}) -> {ordsets, new};
1639
rename_remote_call_1({ordsets, set_to_list, 1}) -> {ordsets, to_list};
1640
rename_remote_call_1({ordsets, subset, 2}) -> {ordsets, is_subset};
1641
rename_remote_call_1({sets, list_to_set, 1}) -> {sets, from_list};
1642
rename_remote_call_1({sets, new_set, 0}) -> {sets, new};
1643
rename_remote_call_1({sets, set_to_list, 1}) -> {sets, to_list};
1644
rename_remote_call_1({sets, subset, 2}) -> {sets, is_subset};
1645
rename_remote_call_1({string, index, 2}) -> {string, str};
1646
rename_remote_call_1({unix, cmd, 1}) -> {os, cmd};
1647
rename_remote_call_1(_) -> false.
1649
rewrite_guard_test(atom, 1) -> is_atom;
1650
rewrite_guard_test(binary, 1) -> is_binary;
1651
rewrite_guard_test(constant, 1) -> is_constant;
1652
rewrite_guard_test(float, 1) -> is_float;
1653
rewrite_guard_test(function, 1) -> is_function;
1654
rewrite_guard_test(integer, 1) -> is_integer;
1655
rewrite_guard_test(list, 1) -> is_list;
1656
rewrite_guard_test(number, 1) -> is_number;
1657
rewrite_guard_test(pid, 1) -> is_pid;
1658
rewrite_guard_test(port, 1) -> is_port;
1659
rewrite_guard_test(reference, 1) -> is_reference;
1660
rewrite_guard_test(tuple, 1) -> is_tuple;
1661
rewrite_guard_test(record, 2) -> is_record;
1662
rewrite_guard_test(N, _A) -> N.
1664
reverse_guard_test(is_atom, 1) -> atom;
1665
reverse_guard_test(is_binary, 1) -> binary;
1666
reverse_guard_test(is_constant, 1) -> constant;
1667
reverse_guard_test(is_float, 1) -> float;
1668
reverse_guard_test(is_function, 1) -> function;
1669
reverse_guard_test(is_integer, 1) -> integer;
1670
reverse_guard_test(is_list, 1) -> list;
1671
reverse_guard_test(is_number, 1) -> number;
1672
reverse_guard_test(is_pid, 1) -> pid;
1673
reverse_guard_test(is_port, 1) -> port;
1674
reverse_guard_test(is_reference, 1) -> reference;
1675
reverse_guard_test(is_tuple, 1) -> tuple;
1676
reverse_guard_test(is_record, 2) -> record;
1677
reverse_guard_test(N, _A) -> N.
1680
%% =====================================================================
1681
%% Utility functions
1683
is_remote_name({M,F,A}) when atom(M), atom(F), integer(A) -> true;
1684
is_remote_name(_) -> false.
1686
is_atom_pair({M,F}) when atom(M), atom(F) -> true;
1687
is_atom_pair(_) -> false.
1689
replace_last([_E], Xs) ->
1691
replace_last([E | Es], Xs) ->
1692
[E | replace_last(Es, Xs)].
1695
erl_syntax:type(E) == generator.
1698
erl_syntax:type(E) == variable.
1700
new_variables(N, St0) when N > 0 ->
1701
{V, St1} = new_variable(St0),
1702
{Vs, St2} = new_variables(N - 1, St1),
1704
new_variables(0, St) ->
1707
new_variable(St0) ->
1709
list_to_atom("V" ++ integer_to_list(N))
1712
{Name, N} = new_name(St0#st.varc, Fun, Vs),
1713
St1 = St0#st{varc = N + 1, vars = sets:add_element(Name, Vs)},
1714
{erl_syntax:variable(Name), St1}.
1716
new_fname({F, A}, St0) ->
1717
Base = atom_to_list(F),
1719
{list_to_atom(Base ++ "_" ++ integer_to_list(N)), A}
1721
Fs = St0#st.functions,
1722
{{F1, _A} = Name, _N} = new_name(1, Fun, Fs),
1723
{F1, St0#st{functions = sets:add_element(Name, Fs)}}.
1725
new_name(N, F, Set) ->
1727
case sets:is_element(Name, Set) of
1729
new_name(N + 1, F, Set);
1734
is_imported(F, Env) ->
1735
dict:is_key(F, Env#env.imports).
1737
is_auto_imported({erlang, N, A}) ->
1738
is_auto_imported({N, A});
1739
is_auto_imported({_, _N, _A}) ->
1741
is_auto_imported({N, A}) ->
1742
erl_internal:bif(N, A).
1744
is_nonlocal(N, Env) ->
1745
case is_imported(N, Env) of
1752
get_var_exports(Node) ->
1753
get_var_exports_1(erl_syntax:get_ann(Node)).
1755
get_var_exports_1([{bound, B} | _Bs]) -> B;
1756
get_var_exports_1([_ | Bs]) -> get_var_exports_1(Bs);
1757
get_var_exports_1([]) -> [].
1759
get_free_vars(Node) ->
1760
get_free_vars_1(erl_syntax:get_ann(Node)).
1762
get_free_vars_1([{free, B} | _Bs]) -> B;
1763
get_free_vars_1([_ | Bs]) -> get_free_vars_1(Bs);
1764
get_free_vars_1([]) -> [].
1766
filename([C | T]) when integer(C), C > 0, C =< 255 ->
1769
filename(H) ++ filename(T);
1772
filename(N) when atom(N) ->
1775
report_error("bad filename: `~P'.", [N, 25]),
1779
case lists:keysearch(env, 1, erl_syntax:get_ann(Tree)) of
1780
{value, {env, Env}} ->
1786
rewrite(Source, Target) ->
1787
erl_syntax:copy_attrs(Source, Target).
1789
clone(Source, Target) ->
1790
erl_syntax:copy_pos(Source, Target).
1793
%% =====================================================================
1796
report_export_vars(F, L, Type, Opts) ->
1797
report({F, L, "rewrote ~s-expression to export variables."},
1800
error_read_file(Name) ->
1801
report_error("error reading file `~s'.", [filename(Name)]).
1803
error_write_file(Name) ->
1804
report_error("error writing to file `~s'.", [filename(Name)]).
1806
error_backup_file(Name) ->
1807
report_error("could not create backup of file `~s'.",
1810
error_open_output(Name) ->
1811
report_error("cannot open file `~s' for output.", [filename(Name)]).
1814
case proplists:get_bool(quiet, Opts) of
1817
case proplists:get_value(verbose, Opts) of
1819
N when integer(N) -> N;
1825
report_error(D, []).
1827
report_error({F, L, D}, Vs) ->
1828
report({F, L, {error, D}}, Vs);
1829
report_error(D, Vs) ->
1830
report({error, D}, Vs).
1835
warn({F, L, D}, Vs, N) ->
1836
report({F, L, {warning, D}}, Vs, N);
1838
report({warning, D}, Vs, N).
1840
recommend(D, Vs, N) ->
1841
report({recommend, D}, Vs, N).
1843
verbose(D, Vs, N) ->
1844
report(2, D, Vs, N).
1850
report(1, D, Vs, N).
1852
report(Level, _D, _Vs, N) when integer(N), N < Level ->
1854
report(_Level, D, Vs, N) when integer(N) ->
1855
io:put_chars(format(D, Vs));
1856
report(Level, D, Vs, Options) when list(Options) ->
1857
report(Level, D, Vs, verbosity(Options)).
1859
format({error, D}, Vs) ->
1860
["error: ", format(D, Vs)];
1861
format({warning, D}, Vs) ->
1862
["warning: ", format(D, Vs)];
1863
format({recommend, D}, Vs) ->
1864
["recommendation: ", format(D, Vs)];
1865
format({"", L, D}, Vs) when integer(L), L > 0 ->
1866
[io_lib:fwrite("~w: ", [L]), format(D, Vs)];
1867
format({"", _L, D}, Vs) ->
1869
format({F, L, D}, Vs) when integer(L), L > 0 ->
1870
[io_lib:fwrite("~s:~w: ", [filename(F), L]), format(D, Vs)];
1871
format({F, _L, D}, Vs) ->
1872
[io_lib:fwrite("~s: ", [filename(F)]), format(D, Vs)];
1873
format(S, Vs) when list(S) ->
1874
[io_lib:fwrite(S, Vs), $\n].
1877
%% =====================================================================