Skip to content

Commit

Permalink
dialyzer: Add support for multiple PLTs
Browse files Browse the repository at this point in the history
This new feature is able to take multiple PLTs, merge them during the
start of the analysis, and work from there. This works provided that
the PLTs do not have a module with the same name appearing in more
than one PLT.

The PLTs are created in the usual way:

    dialyzer --build_plt --output_plt PLT_1  FILES_TO_INCLUDE
    ...
    dialyzer --build_plt --output_plt PLT_N  FILES_TO_INCLUDE

and then can be used in either of the following ways:

    dialyzer  FILES_TO_ANALYZE  --plts PLT_1 ... PLT_N
or:
    dialyzer --plts PLT_1 ... PLT_N -- FILES_TO_ANALYZE

(Note the -- delimiter in the second case)
  • Loading branch information
mariachris committed Nov 30, 2010
1 parent 4101091 commit f1d81c8
Show file tree
Hide file tree
Showing 9 changed files with 231 additions and 79 deletions.
12 changes: 9 additions & 3 deletions lib/dialyzer/doc/manual.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,10 @@ The exit status of the command line version is:


Usage: dialyzer [--help] [--version] [--shell] [--quiet] [--verbose]
[-pa dir]* [--plt plt] [-Ddefine]* [-I include_dir]*
[--output_plt file] [-Wwarn]* [--src] [--gui | --wx]
[files_or_dirs] [-r dirs] [--apps applications] [-o outfile]
[-pa dir]* [--plt plt] [--plts plts] [-Ddefine]*
[-I include_dir]* [--output_plt file] [-Wwarn]*
[--src] [--gui | --wx] [files_or_dirs] [-r dirs]
[--apps applications] [-o outfile]
[--build_plt] [--add_to_plt] [--remove_from_plt]
[--check_plt] [--no_check_plt] [--plt_info] [--get_warnings]
[--no_native]
Expand Down Expand Up @@ -167,6 +168,10 @@ Options:
--plt plt
Use the specified plt as the initial plt (if the plt was built
during setup the files will be checked for consistency)
--plts plts
Merges the specified plts to create the initial plt -- requires
that the plts are disjoint (i.e., do not have any module
appearing in more than one plt)
-Wwarn
A family of options which selectively turn on/off warnings
(for help on the names of warnings use dialyzer -Whelp)
Expand Down Expand Up @@ -294,6 +299,7 @@ Option :: {files, [Filename :: string()]}
| {defines, [{Macro :: atom(), Value :: term()}]}
| {from, src_code | byte_code} %% Defaults to byte_code
| {init_plt, FileName :: string()} %% If changed from default
| {plts, [FileName :: string()]} %% If changed from default
| {include_dirs, [DirName :: string()]}
| {output_file, FileName :: string()}
| {output_plt, FileName :: string()}
Expand Down
20 changes: 14 additions & 6 deletions lib/dialyzer/src/dialyzer.erl
Original file line number Diff line number Diff line change
Expand Up @@ -106,27 +106,35 @@ cl_print_plt_info(Opts) ->
end,
doit(F).

print_plt_info(#options{init_plt = PLT, output_file = OutputFile}) ->
print_plt_info(#options{init_plts = PLTs, output_file = OutputFile}) ->
PLTInfo = get_plt_info(PLTs),
do_print_plt_info(PLTInfo, OutputFile).

get_plt_info([PLT|PLTs]) ->
String =
case dialyzer_plt:included_files(PLT) of
{ok, Files} ->
io_lib:format("The PLT ~s includes the following files:\n~p\n",
io_lib:format("The PLT ~s includes the following files:\n~p\n\n",
[PLT, Files]);
{error, read_error} ->
Msg = io_lib:format("Could not read the PLT file ~p\n", [PLT]),
Msg = io_lib:format("Could not read the PLT file ~p\n\n", [PLT]),
throw({dialyzer_error, Msg});
{error, no_such_file} ->
Msg = io_lib:format("The PLT file ~p does not exist\n", [PLT]),
Msg = io_lib:format("The PLT file ~p does not exist\n\n", [PLT]),
throw({dialyzer_error, Msg})
end,
String ++ get_plt_info(PLTs);
get_plt_info([]) -> "".

do_print_plt_info(PLTInfo, OutputFile) ->
case OutputFile =:= none of
true ->
io:format("~s", [String]),
io:format("~s", [PLTInfo]),
?RET_NOTHING_SUSPICIOUS;
false ->
case file:open(OutputFile, [write]) of
{ok, FileDesc} ->
io:format(FileDesc, "~s", [String]),
io:format(FileDesc, "~s", [PLTInfo]),
ok = file:close(FileDesc),
?RET_NOTHING_SUSPICIOUS;
{error, Reason} ->
Expand Down
2 changes: 1 addition & 1 deletion lib/dialyzer/src/dialyzer.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
defines = [] :: [dial_define()],
from = byte_code :: start_from(),
get_warnings = maybe :: boolean() | 'maybe',
init_plt = none :: 'none' | file:filename(),
init_plts = [] :: [file:filename()],
include_dirs = [] :: [file:filename()],
output_plt = none :: 'none' | file:filename(),
legal_warnings = ordsets:new() :: ordset(dial_warn_tag()),
Expand Down
120 changes: 80 additions & 40 deletions lib/dialyzer/src/dialyzer_cl.erl
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,15 @@ build_plt(Opts) ->
init_opts_for_build(Opts) ->
case Opts#options.output_plt =:= none of
true ->
case Opts#options.init_plt of
none -> Opts#options{init_plt = none, output_plt = get_default_plt()};
Plt -> Opts#options{init_plt = none, output_plt = Plt}
case Opts#options.init_plts of
[] -> Opts#options{output_plt = get_default_output_plt()};
[Plt] -> Opts#options{init_plts = [], output_plt = Plt};
Plts ->
Msg = io_lib:format("Could not build multiple PLT files: ~s\n",
[format_plts(Plts)]),
error(Msg)
end;
false -> Opts#options{init_plt = none}
false -> Opts#options{init_plts = []}
end.

%%--------------------------------------------------------------------
Expand All @@ -98,39 +102,58 @@ add_to_plt(Opts) ->
init_opts_for_add(Opts) ->
case Opts#options.output_plt =:= none of
true ->
case Opts#options.init_plt of
none -> Opts#options{output_plt = get_default_plt(),
init_plt = get_default_plt()};
Plt -> Opts#options{output_plt = Plt}
case Opts#options.init_plts of
[] -> Opts#options{output_plt = get_default_output_plt(),
init_plts = get_default_init_plt()};
[Plt] -> Opts#options{output_plt = Plt};
Plts ->
Msg = io_lib:format("Could not add to multiple PLT files: ~s\n",
[format_plts(Plts)]),
error(Msg)
end;
false ->
case Opts#options.init_plt =:= none of
true -> Opts#options{init_plt = get_default_plt()};
case Opts#options.init_plts =:= [] of
true -> Opts#options{init_plts = get_default_init_plt()};
false -> Opts
end
end.

%%--------------------------------------------------------------------

check_plt(Opts) ->
check_plt(#options{init_plts = []} = Opts) ->
Opts1 = init_opts_for_check(Opts),
report_check(Opts),
plt_common(Opts1, [], []).
report_check(Opts1),
plt_common(Opts1, [], []);
check_plt(#options{init_plts = Plts} = Opts) ->
check_plt_aux(Plts, Opts).

check_plt_aux([_] = Plt, Opts) ->
Opts1 = Opts#options{init_plts = Plt},
Opts2 = init_opts_for_check(Opts1),
report_check(Opts2),
plt_common(Opts2, [], []);
check_plt_aux([Plt|Plts], Opts) ->
Opts1 = Opts#options{init_plts = [Plt]},
Opts2 = init_opts_for_check(Opts1),
report_check(Opts2),
plt_common(Opts2, [], []),
check_plt_aux(Plts, Opts).

init_opts_for_check(Opts) ->
Plt =
case Opts#options.init_plt of
none -> get_default_plt();
Plt0 -> Plt0
InitPlt =
case Opts#options.init_plts of
[]-> get_default_init_plt();
Plt -> Plt
end,
[OutputPlt] = InitPlt,
Opts#options{files = [],
files_rec = [],
analysis_type = plt_check,
defines = [],
from = byte_code,
init_plt = Plt,
init_plts = InitPlt,
include_dirs = [],
output_plt = Plt,
output_plt = OutputPlt,
use_contracts = true
}.

Expand All @@ -144,21 +167,25 @@ remove_from_plt(Opts) ->
init_opts_for_remove(Opts) ->
case Opts#options.output_plt =:= none of
true ->
case Opts#options.init_plt of
none -> Opts#options{output_plt = get_default_plt(),
init_plt = get_default_plt()};
Plt -> Opts#options{output_plt = Plt}
case Opts#options.init_plts of
[] -> Opts#options{output_plt = get_default_output_plt(),
init_plts = get_default_init_plt()};
[Plt] -> Opts#options{output_plt = Plt};
Plts ->
Msg = io_lib:format("Could not remove from multiple PLT files: ~s\n",
[format_plts(Plts)]),
error(Msg)
end;
false ->
case Opts#options.init_plt =:= none of
true -> Opts#options{init_plt = get_default_plt()};
case Opts#options.init_plts =:= [] of
true -> Opts#options{init_plts = get_default_init_plt()};
false -> Opts
end
end.

%%--------------------------------------------------------------------

plt_common(Opts, RemoveFiles, AddFiles) ->
plt_common(#options{init_plts = [InitPlt]} = Opts, RemoveFiles, AddFiles) ->
case check_plt(Opts, RemoveFiles, AddFiles) of
ok ->
case Opts#options.report_mode of
Expand All @@ -174,7 +201,7 @@ plt_common(Opts, RemoveFiles, AddFiles) ->
report_failed_plt_check(Opts, DiffMd5),
{AnalFiles, RemovedMods, ModDeps1} =
expand_dependent_modules(Md5, DiffMd5, ModDeps),
Plt = clean_plt(Opts#options.init_plt, RemovedMods),
Plt = clean_plt(InitPlt, RemovedMods),
case AnalFiles =:= [] of
true ->
%% Only removed stuff. Just write the PLT.
Expand All @@ -186,19 +213,19 @@ plt_common(Opts, RemoveFiles, AddFiles) ->
end;
{error, no_such_file} ->
Msg = io_lib:format("Could not find the PLT: ~s\n~s",
[Opts#options.init_plt, default_plt_error_msg()]),
[InitPlt, default_plt_error_msg()]),
error(Msg);
{error, not_valid} ->
Msg = io_lib:format("The file: ~s is not a valid PLT file\n~s",
[Opts#options.init_plt, default_plt_error_msg()]),
[InitPlt, default_plt_error_msg()]),
error(Msg);
{error, read_error} ->
Msg = io_lib:format("Could not read the PLT: ~s\n~s",
[Opts#options.init_plt, default_plt_error_msg()]),
[InitPlt, default_plt_error_msg()]),
error(Msg);
{error, {no_file_to_remove, F}} ->
Msg = io_lib:format("Could not remove the file ~s from the PLT: ~s\n",
[F, Opts#options.init_plt]),
[F, InitPlt]),
error(Msg)
end.

Expand All @@ -218,8 +245,7 @@ default_plt_error_msg() ->

%%--------------------------------------------------------------------

check_plt(Opts, RemoveFiles, AddFiles) ->
Plt = Opts#options.init_plt,
check_plt(#options{init_plts = [Plt]} = Opts, RemoveFiles, AddFiles) ->
case dialyzer_plt:check_plt(Plt, RemoveFiles, AddFiles) of
{old_version, _MD5} = OldVersion ->
report_old_version(Opts),
Expand All @@ -234,14 +260,14 @@ check_plt(Opts, RemoveFiles, AddFiles) ->

%%--------------------------------------------------------------------

report_check(#options{report_mode = ReportMode, init_plt = InitPlt}) ->
report_check(#options{report_mode = ReportMode, init_plts = [InitPlt]}) ->
case ReportMode of
quiet -> ok;
_ ->
io:format(" Checking whether the PLT ~s is up-to-date...", [InitPlt])
end.

report_old_version(#options{report_mode = ReportMode, init_plt = InitPlt}) ->
report_old_version(#options{report_mode = ReportMode, init_plts = [InitPlt]}) ->
case ReportMode of
quiet -> ok;
_ ->
Expand All @@ -264,14 +290,15 @@ report_failed_plt_check(#options{analysis_type = AnalType,

report_analysis_start(#options{analysis_type = Type,
report_mode = ReportMode,
init_plt = InitPlt,
init_plts = InitPlts,
output_plt = OutputPlt}) ->
case ReportMode of
quiet -> ok;
_ ->
io:format(" "),
case Type of
plt_add ->
[InitPlt] = InitPlts,
case InitPlt =:= OutputPlt of
true -> io:format("Adding information to ~s...", [OutputPlt]);
false -> io:format("Adding information from ~s to ~s...",
Expand All @@ -282,6 +309,7 @@ report_analysis_start(#options{analysis_type = Type,
plt_check ->
io:format("Rebuilding the information in ~s...", [OutputPlt]);
plt_remove ->
[InitPlt] = InitPlts,
case InitPlt =:= OutputPlt of
true -> io:format("Removing information from ~s...", [OutputPlt]);
false -> io:format("Removing information from ~s to ~s...",
Expand Down Expand Up @@ -320,16 +348,28 @@ report_md5_diff(List) ->

%%--------------------------------------------------------------------

get_default_plt() ->
get_default_init_plt() ->
[dialyzer_plt:get_default_plt()].

get_default_output_plt() ->
dialyzer_plt:get_default_plt().

%%--------------------------------------------------------------------

format_plts([Plt]) -> Plt;
format_plts([Plt|Plts]) ->
Plt ++ ", " ++ format_plts(Plts).

%%--------------------------------------------------------------------

do_analysis(Options) ->
Files = get_files_from_opts(Options),
case Options#options.init_plt of
none -> do_analysis(Files, Options, dialyzer_plt:new(), none);
File -> do_analysis(Files, Options, dialyzer_plt:from_file(File), none)
case Options#options.init_plts of
[] -> do_analysis(Files, Options, dialyzer_plt:new(), none);
PltFiles ->
Plts = [dialyzer_plt:from_file(F) || F <- PltFiles],
Plt = dialyzer_plt:merge_plts_or_report_conflicts(PltFiles, Plts),
do_analysis(Files, Options, Plt, none)
end.

do_analysis(Files, Options, Plt, PltInfo) ->
Expand Down
Loading

0 comments on commit f1d81c8

Please sign in to comment.