diff options
Diffstat (limited to 'lib/dialyzer/src/dialyzer_plt.erl')
-rw-r--r-- | lib/dialyzer/src/dialyzer_plt.erl | 97 |
1 files changed, 82 insertions, 15 deletions
diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl index 0f5be3b7f2..8d62f2c529 100644 --- a/lib/dialyzer/src/dialyzer_plt.erl +++ b/lib/dialyzer/src/dialyzer_plt.erl @@ -2,7 +2,7 @@ %%---------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,8 +28,6 @@ %%%------------------------------------------------------------------- -module(dialyzer_plt). -%% Avoid warning for local function error/1 clashing with autoimported BIF. --compile({no_auto_import,[error/1]}). -export([check_plt/3, compute_md5_from_files/1, contains_mfa/2, @@ -51,12 +49,12 @@ lookup_contract/2, lookup_module/2, merge_plts/1, + merge_plts_or_report_conflicts/2, new/0, plt_and_info_from_file/1, get_specs/1, get_specs/4, - to_file/4 - ]). + to_file/4]). %% Debug utilities -export([pp_non_returning/0, pp_mod/1]). @@ -67,6 +65,8 @@ -type mod_deps() :: dict(). +-type deep_string() :: string() | [deep_string()]. + %% The following are used for searching the PLT when using the GUI %% (e.g. in show or search PLT contents). The user might be searching %% with a partial specification, in which case the missing items @@ -202,8 +202,8 @@ get_default_plt() -> false -> case os:getenv("HOME") of false -> - error("The HOME environment variable needs to be set " ++ - "so that Dialyzer knows where to find the default PLT"); + plt_error("The HOME environment variable needs to be set " ++ + "so that Dialyzer knows where to find the default PLT"); HomeDir -> filename:join(HomeDir, ".dialyzer_plt") end; UserSpecPlt -> UserSpecPlt @@ -225,7 +225,7 @@ from_file(FileName, ReturnInfo) -> case check_version(Rec) of error -> Msg = io_lib:format("Old PLT file ~s\n", [FileName]), - error(Msg); + plt_error(Msg); ok -> Plt = #plt{info = Rec#file_plt.info, types = Rec#file_plt.types, @@ -240,13 +240,15 @@ from_file(FileName, ReturnInfo) -> end end; {error, Reason} -> - error(io_lib:format("Could not read PLT file ~s: ~p\n", - [FileName, Reason])) + Msg = io_lib:format("Could not read PLT file ~s: ~p\n", + [FileName, Reason]), + plt_error(Msg) end. --type inc_file_err_rsn() :: 'no_such_file' | 'read_error'. +-type err_rsn() :: 'not_valid' | 'no_such_file' | 'read_error'. + -spec included_files(file:filename()) -> {'ok', [file:filename()]} - | {'error', inc_file_err_rsn()}. + | {'error', err_rsn()}. included_files(FileName) -> case get_record_from_file(FileName) of @@ -291,6 +293,38 @@ merge_plts(List) -> exported_types = sets_merge(ExpTypesList), contracts = table_merge(ContractsList)}. +-spec merge_disj_plts([plt()]) -> plt(). + +merge_disj_plts(List) -> + InfoList = [Info || #plt{info = Info} <- List], + TypesList = [Types || #plt{types = Types} <- List], + ExpTypesList = [ExpTypes || #plt{exported_types = ExpTypes} <- List], + ContractsList = [Contracts || #plt{contracts = Contracts} <- List], + #plt{info = table_disj_merge(InfoList), + types = table_disj_merge(TypesList), + exported_types = sets_disj_merge(ExpTypesList), + contracts = table_disj_merge(ContractsList)}. + +-spec merge_plts_or_report_conflicts([file:filename()], [plt()]) -> plt(). + +merge_plts_or_report_conflicts(PltFiles, Plts) -> + try + merge_disj_plts(Plts) + catch throw:{dialyzer_error, not_disjoint_plts} -> + IncFiles = lists:append([begin {ok, Fs} = included_files(F), Fs end + || F <- PltFiles]), + ConfFiles = find_duplicates(IncFiles), + Msg = io_lib:format("Could not merge PLTs since they are not disjoint\n" + "The following files are included in more than one " + "PLTs:\n~p\n", [ConfFiles]), + error(Msg) + end. + +find_duplicates(List) -> + ModList = [filename:basename(E) || E <- List], + SortedList = lists:usort(ModList), + lists:usort(ModList -- SortedList). + -spec to_file(file:filename(), plt(), mod_deps(), {[file_md5()], mod_deps()}) -> 'ok'. to_file(FileName, @@ -320,8 +354,7 @@ to_file(FileName, end. -type md5_diff() :: [{'differ', atom()} | {'removed', atom()}]. --type check_error() :: 'not_valid' | 'no_such_file' | 'read_error' - | {'no_file_to_remove', file:filename()}. +-type check_error() :: err_rsn() | {'no_file_to_remove', file:filename()}. -spec check_plt(file:filename(), [file:filename()], [file:filename()]) -> 'ok' @@ -485,7 +518,9 @@ expand_args([ArgType|Left]) -> end ++ ","|expand_args(Left)]. -error(Msg) -> +-spec plt_error(deep_string()) -> no_return(). + +plt_error(Msg) -> throw({dialyzer_error, lists:flatten(Msg)}). %%--------------------------------------------------------------------------- @@ -556,6 +591,25 @@ table_merge([Plt|Plts], Acc) -> NewAcc = dict:merge(fun(_Key, Val, Val) -> Val end, Plt, Acc), table_merge(Plts, NewAcc). +table_disj_merge([H|T]) -> + table_disj_merge(T, H). + +table_disj_merge([], Acc) -> + Acc; +table_disj_merge([Plt|Plts], Acc) -> + case table_is_disjoint(Plt, Acc) of + true -> + NewAcc = dict:merge(fun(_Key, _Val1, _Val2) -> gazonk end, + Plt, Acc), + table_disj_merge(Plts, NewAcc); + false -> throw({dialyzer_error, not_disjoint_plts}) + end. + +table_is_disjoint(T1, T2) -> + K1 = dict:fetch_keys(T1), + K2 = dict:fetch_keys(T2), + lists:all(fun(E) -> not lists:member(E, K2) end, K1). + sets_merge([H|T]) -> sets_merge(T, H). @@ -565,6 +619,19 @@ sets_merge([Plt|Plts], Acc) -> NewAcc = sets:union(Plt, Acc), sets_merge(Plts, NewAcc). +sets_disj_merge([H|T]) -> + sets_disj_merge(T, H). + +sets_disj_merge([], Acc) -> + Acc; +sets_disj_merge([Plt|Plts], Acc) -> + case sets:is_disjoint(Plt, Acc) of + true -> + NewAcc = sets:union(Plt, Acc), + sets_disj_merge(Plts, NewAcc); + false -> throw({dialyzer_error, not_disjoint_plts}) + end. + %%--------------------------------------------------------------------------- %% Debug utilities. |