diff options
Diffstat (limited to 'lib/dialyzer/src/dialyzer_plt.erl')
-rw-r--r-- | lib/dialyzer/src/dialyzer_plt.erl | 119 |
1 files changed, 79 insertions, 40 deletions
diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl index e387077a46..0f5be3b7f2 100644 --- a/lib/dialyzer/src/dialyzer_plt.erl +++ b/lib/dialyzer/src/dialyzer_plt.erl @@ -21,13 +21,15 @@ %%%------------------------------------------------------------------- %%% File : dialyzer_plt.erl %%% Author : Tobias Lindahl <[email protected]> -%%% Description : Interface to display information in the persistent +%%% Description : Interface to display information in the persistent %%% lookup tables. %%% %%% Created : 23 Jul 2004 by Tobias Lindahl <[email protected]> %%%------------------------------------------------------------------- -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, @@ -39,10 +41,12 @@ from_file/1, get_default_plt/0, get_types/1, + get_exported_types/1, %% insert/3, insert_list/2, insert_contract_list/2, insert_types/2, + insert_exported_types/2, lookup/2, lookup_contract/2, lookup_module/2, @@ -57,6 +61,8 @@ %% Debug utilities -export([pp_non_returning/0, pp_mod/1]). +-export_type([plt/0, plt_info/0]). + %%---------------------------------------------------------------------- -type mod_deps() :: dict(). @@ -70,9 +76,10 @@ %%---------------------------------------------------------------------- --record(plt, {info = table_new() :: dict(), - types = table_new() :: dict(), - contracts = table_new() :: dict()}). +-record(plt, {info = table_new() :: dict(), + types = table_new() :: dict(), + contracts = table_new() :: dict(), + exported_types = sets:new() :: set()}). -opaque plt() :: #plt{}. -include("dialyzer.hrl"). @@ -80,13 +87,14 @@ -type file_md5() :: {file:filename(), binary()}. -type plt_info() :: {[file_md5()], dict()}. --record(file_plt, {version = "" :: string(), - file_md5_list = [] :: [file_md5()], - info = dict:new() :: dict(), - contracts = dict:new() :: dict(), - types = dict:new() :: dict(), - mod_deps :: mod_deps(), - implementation_md5 = [] :: [file_md5()]}). +-record(file_plt, {version = "" :: string(), + file_md5_list = [] :: [file_md5()], + info = dict:new() :: dict(), + contracts = dict:new() :: dict(), + types = dict:new() :: dict(), + exported_types = sets:new() :: set(), + mod_deps :: mod_deps(), + implementation_md5 = [] :: [file_md5()]}). %%---------------------------------------------------------------------- @@ -95,19 +103,23 @@ new() -> #plt{}. --spec delete_module(plt(), module()) -> plt(). +-spec delete_module(plt(), atom()) -> plt(). -delete_module(#plt{info = Info, types = Types, contracts = Contracts}, Mod) -> +delete_module(#plt{info = Info, types = Types, contracts = Contracts, + exported_types = ExpTypes}, Mod) -> #plt{info = table_delete_module(Info, Mod), types = table_delete_module2(Types, Mod), - contracts = table_delete_module(Contracts, Mod)}. + contracts = table_delete_module(Contracts, Mod), + exported_types = table_delete_module1(ExpTypes, Mod)}. -spec delete_list(plt(), [mfa() | integer()]) -> plt(). -delete_list(#plt{info = Info, types = Types, contracts = Contracts}, List) -> +delete_list(#plt{info = Info, types = Types, contracts = Contracts, + exported_types = ExpTypes}, List) -> #plt{info = table_delete_list(Info, List), types = Types, - contracts = table_delete_list(Contracts, List)}. + contracts = table_delete_list(Contracts, List), + exported_types = ExpTypes}. -spec insert_contract_list(plt(), dialyzer_contracts:plt_contracts()) -> plt(). @@ -126,7 +138,7 @@ delete_contract_list(#plt{contracts = Contracts} = PLT, List) -> PLT#plt{contracts = table_delete_list(Contracts, List)}. %% -spec insert(plt(), mfa() | integer(), {_, _}) -> plt(). -%% +%% %% insert(#plt{info = Info} = PLT, Id, Types) -> %% PLT#plt{info = table_insert(Info, Id, Types)}. @@ -150,19 +162,29 @@ lookup(#plt{info = Info}, Label) when is_integer(Label) -> insert_types(PLT, Rec) -> PLT#plt{types = Rec}. +-spec insert_exported_types(plt(), set()) -> plt(). + +insert_exported_types(PLT, Set) -> + PLT#plt{exported_types = Set}. + -spec get_types(plt()) -> dict(). get_types(#plt{types = Types}) -> Types. +-spec get_exported_types(plt()) -> set(). + +get_exported_types(#plt{exported_types = ExpTypes}) -> + ExpTypes. + -type mfa_types() :: {mfa(), erl_types:erl_type(), [erl_types:erl_type()]}. --spec lookup_module(plt(), module()) -> 'none' | {'value', [mfa_types()]}. +-spec lookup_module(plt(), atom()) -> 'none' | {'value', [mfa_types()]}. lookup_module(#plt{info = Info}, M) when is_atom(M) -> table_lookup_module(Info, M). --spec contains_module(plt(), module()) -> boolean(). +-spec contains_module(plt(), atom()) -> boolean(). contains_module(#plt{info = Info, contracts = Cs}, M) when is_atom(M) -> table_contains_module(Info, M) orelse table_contains_module(Cs, M). @@ -170,7 +192,7 @@ contains_module(#plt{info = Info, contracts = Cs}, M) when is_atom(M) -> -spec contains_mfa(plt(), mfa()) -> boolean(). contains_mfa(#plt{info = Info, contracts = Contracts}, MFA) -> - (table_lookup(Info, MFA) =/= none) + (table_lookup(Info, MFA) =/= none) orelse (table_lookup(Contracts, MFA) =/= none). -spec get_default_plt() -> file:filename(). @@ -201,13 +223,14 @@ from_file(FileName, ReturnInfo) -> case get_record_from_file(FileName) of {ok, Rec} -> case check_version(Rec) of - error -> + error -> Msg = io_lib:format("Old PLT file ~s\n", [FileName]), error(Msg); - ok -> + ok -> Plt = #plt{info = Rec#file_plt.info, types = Rec#file_plt.types, - contracts = Rec#file_plt.contracts}, + contracts = Rec#file_plt.contracts, + exported_types = Rec#file_plt.exported_types}, case ReturnInfo of false -> Plt; true -> @@ -217,12 +240,12 @@ from_file(FileName, ReturnInfo) -> end end; {error, Reason} -> - error(io_lib:format("Could not read PLT file ~s: ~p\n", + error(io_lib:format("Could not read PLT file ~s: ~p\n", [FileName, Reason])) end. -type inc_file_err_rsn() :: 'no_such_file' | 'read_error'. --spec included_files(file:filename()) -> {'ok', [file:filename()]} +-spec included_files(file:filename()) -> {'ok', [file:filename()]} | {'error', inc_file_err_rsn()}. included_files(FileName) -> @@ -247,12 +270,12 @@ get_record_from_file(FileName) -> try binary_to_term(Bin) of #file_plt{} = FilePLT -> {ok, FilePLT}; _ -> {error, not_valid} - catch + catch _:_ -> {error, not_valid} end; {error, enoent} -> {error, no_such_file}; - {error, _} -> + {error, _} -> {error, read_error} end. @@ -261,19 +284,22 @@ get_record_from_file(FileName) -> merge_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_merge(InfoList), types = table_merge(TypesList), + exported_types = sets_merge(ExpTypesList), contracts = table_merge(ContractsList)}. -spec to_file(file:filename(), plt(), mod_deps(), {[file_md5()], mod_deps()}) -> 'ok'. to_file(FileName, - #plt{info = Info, types = Types, contracts = Contracts}, + #plt{info = Info, types = Types, contracts = Contracts, + exported_types = ExpTypes}, ModDeps, {MD5, OldModDeps}) -> - NewModDeps = dict:merge(fun(_Key, OldVal, NewVal) -> + NewModDeps = dict:merge(fun(_Key, OldVal, NewVal) -> ordsets:union(OldVal, NewVal) - end, + end, OldModDeps, ModDeps), ImplMd5 = compute_implementation_md5(), Record = #file_plt{version = ?VSN, @@ -281,13 +307,14 @@ to_file(FileName, info = Info, contracts = Contracts, types = Types, + exported_types = ExpTypes, mod_deps = NewModDeps, implementation_md5 = ImplMd5}, Bin = term_to_binary(Record, [compressed]), case file:write_file(FileName, Bin) of ok -> ok; {error, Reason} -> - Msg = io_lib:format("Could not write PLT file ~s: ~w\n", + Msg = io_lib:format("Could not write PLT file ~s: ~w\n", [FileName, Reason]), throw({dialyzer_error, Msg}) end. @@ -295,8 +322,8 @@ to_file(FileName, -type md5_diff() :: [{'differ', atom()} | {'removed', atom()}]. -type check_error() :: 'not_valid' | 'no_such_file' | 'read_error' | {'no_file_to_remove', file:filename()}. - --spec check_plt(file:filename(), [file:filename()], [file:filename()]) -> + +-spec check_plt(file:filename(), [file:filename()], [file:filename()]) -> 'ok' | {'error', check_error()} | {'differ', [file_md5()], md5_diff(), mod_deps()} @@ -306,7 +333,7 @@ check_plt(FileName, RemoveFiles, AddFiles) -> case get_record_from_file(FileName) of {ok, #file_plt{file_md5_list = Md5, mod_deps = ModDeps} = Rec} -> case check_version(Rec) of - ok -> + ok -> case compute_new_md5(Md5, RemoveFiles, AddFiles) of ok -> ok; {differ, NewMd5, DiffMd5} -> {differ, NewMd5, DiffMd5, ModDeps}; @@ -363,7 +390,7 @@ compute_md5_from_files(Files) -> compute_md5_from_file(File) -> case filelib:is_regular(File) of - false -> + false -> Msg = io_lib:format("Not a regular file: ~s\n", [File]), throw({dialyzer_error, Msg}); true -> @@ -394,7 +421,7 @@ init_md5_list_1([{File, _Md5}|Md5Left], [{remove, File}|DiffLeft], Acc) -> init_md5_list_1(Md5Left, DiffLeft, Acc); init_md5_list_1([{File, _Md5} = Entry|Md5Left], [{add, File}|DiffLeft], Acc) -> init_md5_list_1(Md5Left, DiffLeft, [Entry|Acc]); -init_md5_list_1([{File1, _Md5} = Entry|Md5Left] = Md5List, +init_md5_list_1([{File1, _Md5} = Entry|Md5Left] = Md5List, [{Tag, File2}|DiffLeft] = DiffList, Acc) -> case File1 < File2 of true -> init_md5_list_1(Md5Left, DiffList, [Entry|Acc]); @@ -425,7 +452,7 @@ get_specs(#plt{info = Info}) -> beam_file_to_module(Filename) -> list_to_atom(filename:basename(Filename, ".beam")). --spec get_specs(plt(), module(), atom(), arity_patt()) -> 'none' | string(). +-spec get_specs(plt(), atom(), atom(), arity_patt()) -> 'none' | string(). get_specs(#plt{info = Info}, M, F, A) when is_atom(M), is_atom(F) -> MFA = {M, F, A}, @@ -435,7 +462,7 @@ get_specs(#plt{info = Info}, M, F, A) when is_atom(M), is_atom(F) -> end. create_specs([{{M, F, _A}, {Ret, Args}}|Left], M) -> - [io_lib:format("-spec ~w(~s) -> ~s\n", + [io_lib:format("-spec ~w(~s) -> ~s\n", [F, expand_args(Args), erl_types:t_to_string(Ret)]) | create_specs(Left, M)]; create_specs(List = [{{M, _F, _A}, {_Ret, _Args}}| _], _M) -> @@ -475,6 +502,9 @@ table_delete_module(Plt, Mod) -> (_, _) -> true end, Plt). +table_delete_module1(Plt, Mod) -> + sets:filter(fun({M, _F, _A}) -> M =/= Mod end, Plt). + table_delete_module2(Plt, Mod) -> dict:filter(fun(M, _Val) -> M =/= Mod end, Plt). @@ -488,7 +518,7 @@ table_insert_list(Plt, [{Key, Val}|Left]) -> table_insert_list(Plt, []) -> Plt. -table_insert(Plt, Key, {_Ret, _Arg} = Obj) -> +table_insert(Plt, Key, {_Ret, _Arg} = Obj) -> dict:store(Key, Obj, Plt); table_insert(Plt, Key, #contract{} = C) -> dict:store(Key, C, Plt). @@ -526,6 +556,15 @@ table_merge([Plt|Plts], Acc) -> NewAcc = dict:merge(fun(_Key, Val, Val) -> Val end, Plt, Acc), table_merge(Plts, NewAcc). +sets_merge([H|T]) -> + sets_merge(T, H). + +sets_merge([], Acc) -> + Acc; +sets_merge([Plt|Plts], Acc) -> + NewAcc = sets:union(Plt, Acc), + sets_merge(Plts, NewAcc). + %%--------------------------------------------------------------------------- %% Debug utilities. @@ -555,7 +594,7 @@ pp_non_returning() -> [M, F, dialyzer_utils:format_sig(Type)]) end, lists:sort(None)). --spec pp_mod(module()) -> 'ok'. +-spec pp_mod(atom()) -> 'ok'. pp_mod(Mod) when is_atom(Mod) -> PltFile = get_default_plt(), |