diff options
author | Erlang/OTP <[email protected]> | 2010-06-04 08:37:43 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2010-06-04 08:37:43 +0000 |
commit | 02a3667376af950bb75523e32d2a974c4939261b (patch) | |
tree | 26cfed89729c549e576035c2d7b3557183a4c20a /lib/dialyzer | |
parent | 5c1f9b342c3569b37b674b8671fb243782af4aa0 (diff) | |
parent | 169507cb9238ff527d13df0fe945838a2169aa0d (diff) | |
download | otp-02a3667376af950bb75523e32d2a974c4939261b.tar.gz otp-02a3667376af950bb75523e32d2a974c4939261b.tar.bz2 otp-02a3667376af950bb75523e32d2a974c4939261b.zip |
Merge branch 'ks/dialyzer' into dev
* ks/dialyzer:
dialyzer: Build the PLT even if there are unresolved remote types
proplists: Export the type property()
erl_lint: Issue warnings for undefined exported types
Minor fix in a print message
Add handling of unknown types
Add declaration for exported types
Add types and specs; performed some cleanups also
erl_scan: Add declarations for exported types
stdlib: Add declarations for exported types
hipe: Add declarations for exported types
compiler: Add declarations for exported types
syntax_tools: Add declarations for exported types
kernel: Add declaration for exported types
Support -export_type() in dialyzer and erl_types
Add infrastructure for the -export_type() attribute
OTP-8678 ks/dialyzer
Diffstat (limited to 'lib/dialyzer')
-rw-r--r-- | lib/dialyzer/src/dialyzer_analysis_callgraph.erl | 68 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_behaviours.erl | 13 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_callgraph.erl | 2 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_cl.erl | 37 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_codeserver.erl | 46 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_contracts.erl | 28 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_dataflow.erl | 2 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_plt.erl | 69 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_races.erl | 3 | ||||
-rw-r--r-- | lib/dialyzer/src/dialyzer_utils.erl | 27 |
10 files changed, 238 insertions, 57 deletions
diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index ab1bbe5ade..e3dd690470 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -96,6 +96,9 @@ loop(#server_state{parent = Parent, legal_warnings = LegalWarnings} = State, end; {AnalPid, ext_calls, NewExtCalls} -> loop(State, Analysis, NewExtCalls); + {AnalPid, ext_types, ExtTypes} -> + send_ext_types(Parent, ExtTypes), + loop(State, Analysis, ExtCalls); {AnalPid, unknown_behaviours, UnknownBehaviour} -> send_unknown_behaviours(Parent, UnknownBehaviour), loop(State, Analysis, ExtCalls); @@ -123,8 +126,7 @@ analysis_start(Parent, Analysis) -> parent = Parent, start_from = Analysis#analysis.start_from, use_contracts = Analysis#analysis.use_contracts, - behaviours = {Analysis#analysis.behaviours_chk, - []} + behaviours = {Analysis#analysis.behaviours_chk, []} }, Files = ordsets:from_list(Analysis#analysis.files), {Callgraph, NoWarn, TmpCServer0} = compile_and_store(Files, State), @@ -132,22 +134,36 @@ analysis_start(Parent, Analysis) -> NewCServer = try NewRecords = dialyzer_codeserver:get_temp_records(TmpCServer0), - OldRecords = dialyzer_plt:get_types(State#analysis_state.plt), + NewExpTypes = dialyzer_codeserver:get_temp_exported_types(TmpCServer0), + OldRecords = dialyzer_plt:get_types(Plt), + OldExpTypes0 = dialyzer_plt:get_exported_types(Plt), MergedRecords = dialyzer_utils:merge_records(NewRecords, OldRecords), + RemMods = + [case Analysis#analysis.start_from of + byte_code -> list_to_atom(filename:basename(F, ".beam")); + src_code -> list_to_atom(filename:basename(F, ".erl")) + end || F <- Files], + OldExpTypes1 = dialyzer_utils:sets_filter(RemMods, OldExpTypes0), + MergedExpTypes = sets:union(NewExpTypes, OldExpTypes1), TmpCServer1 = dialyzer_codeserver:set_temp_records(MergedRecords, TmpCServer0), - TmpCServer2 = dialyzer_utils:process_record_remote_types(TmpCServer1), - dialyzer_contracts:process_contract_remote_types(TmpCServer2) + TmpCServer2 = + dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, + TmpCServer1), + TmpCServer3 = dialyzer_utils:process_record_remote_types(TmpCServer2), + dialyzer_contracts:process_contract_remote_types(TmpCServer3) catch throw:{error, _ErrorMsg} = Error -> exit(Error) end, - NewPlt = dialyzer_plt:insert_types(Plt, dialyzer_codeserver:get_records(NewCServer)), - State0 = State#analysis_state{plt = NewPlt}, + NewPlt0 = dialyzer_plt:insert_types(Plt, dialyzer_codeserver:get_records(NewCServer)), + ExpTypes = dialyzer_codeserver:get_exported_types(NewCServer), + NewPlt1 = dialyzer_plt:insert_exported_types(NewPlt0, ExpTypes), + State0 = State#analysis_state{plt = NewPlt1}, dump_callgraph(Callgraph, State0, Analysis), State1 = State0#analysis_state{codeserver = NewCServer}, State2 = State1#analysis_state{no_warn_unused = NoWarn}, %% Remove all old versions of the files being analyzed AllNodes = dialyzer_callgraph:all_nodes(Callgraph), - Plt1 = dialyzer_plt:delete_list(NewPlt, AllNodes), + Plt1 = dialyzer_plt:delete_list(NewPlt1, AllNodes), Exports = dialyzer_codeserver:get_exports(NewCServer), NewCallgraph = case Analysis#analysis.race_detection of @@ -155,6 +171,7 @@ analysis_start(Parent, Analysis) -> false -> Callgraph end, State3 = analyze_callgraph(NewCallgraph, State2#analysis_state{plt = Plt1}), + rcv_and_send_ext_types(Parent), NonExports = sets:subtract(sets:from_list(AllNodes), Exports), NonExportsList = sets:to_list(NonExports), Plt3 = dialyzer_plt:delete_list(State3#analysis_state.plt, NonExportsList), @@ -371,14 +388,28 @@ compile_byte(File, Callgraph, CServer, UseContracts) -> store_core(Mod, Core, NoWarn, Callgraph, CServer) -> Exp = get_exports_from_core(Core), + OldExpTypes = dialyzer_codeserver:get_temp_exported_types(CServer), + NewExpTypes = get_exported_types_from_core(Core), + MergedExpTypes = sets:union(NewExpTypes, OldExpTypes), CServer1 = dialyzer_codeserver:insert_exports(Exp, CServer), - {LabeledCore, CServer2} = label_core(Core, CServer1), - store_code_and_build_callgraph(Mod, LabeledCore, Callgraph, CServer2, NoWarn). + CServer2 = dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, + CServer1), + {LabeledCore, CServer3} = label_core(Core, CServer2), + store_code_and_build_callgraph(Mod, LabeledCore, Callgraph, CServer3, NoWarn). abs_get_nowarn(Abs, M) -> [{M, F, A} || {attribute, _, compile, {nowarn_unused_function, {F, A}}} <- Abs]. +get_exported_types_from_core(Core) -> + Attrs = cerl:module_attrs(Core), + ExpTypes1 = [cerl:concrete(L2) || {L1, L2} <- Attrs, cerl:is_literal(L1), + cerl:is_literal(L2), + cerl:concrete(L1) =:= 'export_type'], + ExpTypes2 = lists:flatten(ExpTypes1), + M = cerl:atom_val(cerl:module_name(Core)), + sets:from_list([{M, F, A} || {F, A} <- ExpTypes2]). + get_exports_from_core(Core) -> Tree = cerl:from_records(Core), Exports1 = cerl:module_exports(Tree), @@ -454,6 +485,19 @@ default_includes(Dir) -> %% Handle Messages %%------------------------------------------------------------------- +rcv_and_send_ext_types(Parent) -> + Self = self(), + Self ! {Self, done}, + ExtTypes = rcv_ext_types(Self, []), + Parent ! {Self, ext_types, ExtTypes}. + +rcv_ext_types(Self, ExtTypes) -> + receive + {Self, ext_types, ExtType} -> + rcv_ext_types(Self, [ExtType|ExtTypes]); + {Self, done} -> lists:usort(ExtTypes) + end. + send_log(Parent, Msg) -> Parent ! {self(), log, Msg}, ok. @@ -476,6 +520,10 @@ send_ext_calls(Parent, ExtCalls) -> Parent ! {self(), ext_calls, ExtCalls}, ok. +send_ext_types(Parent, ExtTypes) -> + Parent ! {self(), ext_types, ExtTypes}, + ok. + send_unknown_behaviours(Parent, UnknownBehaviours) -> Parent ! {self(), unknown_behaviours, UnknownBehaviours}, ok. diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl index 4e8dceaa8e..3fae816cfe 100644 --- a/lib/dialyzer/src/dialyzer_behaviours.erl +++ b/lib/dialyzer/src/dialyzer_behaviours.erl @@ -156,9 +156,11 @@ check_all_callbacks(Module, Behaviour, Callbacks, State) -> check_all_callbacks(_Module, _Behaviour, [], _State, Acc) -> Acc; -check_all_callbacks(Module, Behaviour, [{Fun, Arity, Spec}|Rest], State, Acc) -> - Records = dialyzer_codeserver:get_records(State#state.codeserver), - case parse_spec(Spec, Records) of +check_all_callbacks(Module, Behaviour, [{Fun, Arity, Spec}|Rest], + #state{codeserver = CServer} = State, Acc) -> + Records = dialyzer_codeserver:get_records(CServer), + ExpTypes = dialyzer_codeserver:get_exported_types(CServer), + case parse_spec(Spec, ExpTypes, Records) of {ok, Fun, Type} -> RetType = erl_types:t_fun_range(Type), ArgTypes = erl_types:t_fun_args(Type), @@ -172,7 +174,7 @@ check_all_callbacks(Module, Behaviour, [{Fun, Arity}|Rest], State, Acc) -> Warns = {spec_missing, [Behaviour, Fun, Arity]}, check_all_callbacks(Module, Behaviour, Rest, State, [Warns|Acc]). -parse_spec(String, Records) -> +parse_spec(String, ExpTypes, Records) -> case erl_scan:string(String) of {ok, Tokens, _} -> case erl_parse:parse(Tokens) of @@ -181,7 +183,8 @@ parse_spec(String, Records) -> {attribute, _, 'spec', {{Fun, _}, [TypeForm|_Constraint]}} -> MaybeRemoteType = erl_types:t_from_form(TypeForm), try - Type = erl_types:t_solve_remote(MaybeRemoteType, Records), + Type = erl_types:t_solve_remote(MaybeRemoteType, ExpTypes, + Records), {ok, Fun, Type} catch throw:{error,Msg} -> {spec_remote_error, Msg} diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index f932f43548..d3de5aaf45 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -59,6 +59,8 @@ put_named_tables/2, put_public_tables/2, put_behaviour_api_calls/2, get_behaviour_api_calls/1]). +-export_type([callgraph/0]). + -include("dialyzer.hrl"). %%---------------------------------------------------------------------- diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl index d533e734db..1d02c4f0dc 100644 --- a/lib/dialyzer/src/dialyzer_cl.erl +++ b/lib/dialyzer/src/dialyzer_cl.erl @@ -38,6 +38,7 @@ {backend_pid :: pid(), erlang_mode = false :: boolean(), external_calls = [] :: [mfa()], + external_types = [] :: [mfa()], legal_warnings = ordsets:new() :: [dial_warn_tag()], mod_deps = dict:new() :: dict(), output = standard_io :: io:device(), @@ -538,6 +539,8 @@ cl_loop(State, LogCache) -> return_value(State, NewPlt); {BackendPid, ext_calls, ExtCalls} -> cl_loop(State#cl_state{external_calls = ExtCalls}, LogCache); + {BackendPid, ext_types, ExtTypes} -> + cl_loop(State#cl_state{external_types = ExtTypes}, LogCache); {BackendPid, mod_deps, ModDeps} -> NewState = State#cl_state{mod_deps = ModDeps}, cl_loop(NewState, LogCache); @@ -613,6 +616,7 @@ return_value(State = #cl_state{erlang_mode = ErlangMode, false -> print_warnings(State), print_ext_calls(State), + print_ext_types(State), print_unknown_behaviours(State), maybe_close_output_file(State), {RetValue, []}; @@ -649,10 +653,41 @@ do_print_ext_calls(Output, [{M,F,A}|T], Before) -> do_print_ext_calls(_, [], _) -> ok. +print_ext_types(#cl_state{report_mode = quiet}) -> + ok; +print_ext_types(#cl_state{output = Output, + external_calls = Calls, + external_types = Types, + stored_warnings = Warnings, + output_format = Format}) -> + case Types =:= [] of + true -> ok; + false -> + case Warnings =:= [] andalso Calls =:= [] of + true -> io:nl(Output); %% Need to do a newline first + false -> ok + end, + case Format of + formatted -> + io:put_chars(Output, "Unknown types:\n"), + do_print_ext_types(Output, Types, " "); + raw -> + io:put_chars(Output, "%% Unknown types:\n"), + do_print_ext_types(Output, Types, "%% ") + end + end. + +do_print_ext_types(Output, [{M,F,A}|T], Before) -> + io:format(Output, "~s~p:~p/~p\n", [Before,M,F,A]), + do_print_ext_types(Output, T, Before); +do_print_ext_types(_, [], _) -> + ok. + %%print_unknown_behaviours(#cl_state{report_mode = quiet}) -> %% ok; print_unknown_behaviours(#cl_state{output = Output, external_calls = Calls, + external_types = Types, stored_warnings = Warnings, unknown_behaviours = DupBehaviours, legal_warnings = LegalWarnings, @@ -662,7 +697,7 @@ print_unknown_behaviours(#cl_state{output = Output, false -> ok; true -> Behaviours = lists:usort(DupBehaviours), - case Warnings =:= [] andalso Calls =:= [] of + case Warnings =:= [] andalso Calls =:= [] andalso Types =:= [] of true -> io:nl(Output); %% Need to do a newline first false -> ok end, diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl index 3bc5fadc21..3cf090712c 100644 --- a/lib/dialyzer/src/dialyzer_codeserver.erl +++ b/lib/dialyzer/src/dialyzer_codeserver.erl @@ -29,15 +29,19 @@ -export([delete/1, finalize_contracts/2, + finalize_exported_types/2, finalize_records/2, get_contracts/1, + get_exported_types/1, get_exports/1, get_records/1, get_next_core_label/1, get_temp_contracts/1, + get_temp_exported_types/1, get_temp_records/1, - insert/3, - insert_exports/2, + insert/3, + insert_exports/2, + insert_temp_exported_types/2, is_exported/2, lookup_mod_code/2, lookup_mfa_code/2, @@ -52,17 +56,21 @@ store_contracts/3, store_temp_contracts/3]). +-export_type([codeserver/0]). + -include("dialyzer.hrl"). %%-------------------------------------------------------------------- --record(codeserver, {table_pid :: pid(), - exports = sets:new() :: set(), % set(mfa()) - next_core_label = 0 :: label(), - records = dict:new() :: dict(), - temp_records = dict:new() :: dict(), - contracts = dict:new() :: dict(), - temp_contracts = dict:new() :: dict()}). +-record(codeserver, {table_pid :: pid(), + exported_types = sets:new() :: set(), % set(mfa()) + temp_exported_types = sets:new() :: set(), % set(mfa()) + exports = sets:new() :: set(), % set(mfa()) + next_core_label = 0 :: label(), + records = dict:new() :: dict(), + temp_records = dict:new() :: dict(), + contracts = dict:new() :: dict(), + temp_contracts = dict:new() :: dict()}). -opaque codeserver() :: #codeserver{}. @@ -84,6 +92,11 @@ insert(Mod, ModCode, CS) -> NewTablePid = table__insert(CS#codeserver.table_pid, Mod, ModCode), CS#codeserver{table_pid = NewTablePid}. +-spec insert_temp_exported_types(set(), codeserver()) -> codeserver(). + +insert_temp_exported_types(Set, CS) -> + CS#codeserver{temp_exported_types = Set}. + -spec insert_exports([mfa()], codeserver()) -> codeserver(). insert_exports(List, #codeserver{exports = Exports} = CS) -> @@ -96,11 +109,26 @@ insert_exports(List, #codeserver{exports = Exports} = CS) -> is_exported(MFA, #codeserver{exports = Exports}) -> sets:is_element(MFA, Exports). +-spec get_exported_types(codeserver()) -> set(). % set(mfa()) + +get_exported_types(#codeserver{exported_types = ExpTypes}) -> + ExpTypes. + +-spec get_temp_exported_types(codeserver()) -> set(). + +get_temp_exported_types(#codeserver{temp_exported_types = TempExpTypes}) -> + TempExpTypes. + -spec get_exports(codeserver()) -> set(). % set(mfa()) get_exports(#codeserver{exports = Exports}) -> Exports. +-spec finalize_exported_types(set(), codeserver()) -> codeserver(). + +finalize_exported_types(Set, CS) -> + CS#codeserver{exported_types = Set, temp_exported_types = sets:new()}. + -spec lookup_mod_code(module(), codeserver()) -> cerl:c_module(). lookup_mod_code(Mod, CS) when is_atom(Mod) -> diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl index 3486c72748..2bedf99e42 100644 --- a/lib/dialyzer/src/dialyzer_contracts.erl +++ b/lib/dialyzer/src/dialyzer_contracts.erl @@ -33,6 +33,8 @@ process_contract_remote_types/1, store_tmp_contract/5]). +-export_type([file_contract/0, plt_contracts/0]). + %%----------------------------------------------------------------------- -include("dialyzer.hrl"). @@ -50,7 +52,7 @@ %% to expand records and/or remote types that they might contain. %%----------------------------------------------------------------------- --type tmp_contract_fun() :: fun((dict()) -> contract_pair()). +-type tmp_contract_fun() :: fun((set(), dict()) -> contract_pair()). -record(tmp_contract, {contract_funs = [] :: [tmp_contract_fun()], forms = [] :: [{_, _}]}). @@ -140,10 +142,11 @@ sequence([H|T], Delimiter) -> H ++ Delimiter ++ sequence(T, Delimiter). process_contract_remote_types(CodeServer) -> TmpContractDict = dialyzer_codeserver:get_temp_contracts(CodeServer), + ExpTypes = dialyzer_codeserver:get_exported_types(CodeServer), RecordDict = dialyzer_codeserver:get_records(CodeServer), ContractFun = fun({_M, _F, _A}, {File, #tmp_contract{contract_funs = CFuns, forms = Forms}}) -> - NewCs = [CFun(RecordDict) || CFun <- CFuns], + NewCs = [CFun(ExpTypes, RecordDict) || CFun <- CFuns], Args = general_domain(NewCs), {File, #contract{contracts = NewCs, args = Args, forms = Forms}} end, @@ -354,9 +357,9 @@ contract_from_form(Forms, RecDict) -> contract_from_form([{type, _, 'fun', [_, _]} = Form | Left], RecDict, TypeAcc, FormAcc) -> TypeFun = - fun(AllRecords) -> + fun(ExpTypes, AllRecords) -> Type = erl_types:t_from_form(Form, RecDict), - NewType = erl_types:t_solve_remote(Type, AllRecords), + NewType = erl_types:t_solve_remote(Type, ExpTypes, AllRecords), {NewType, []} end, NewTypeAcc = [TypeFun | TypeAcc], @@ -366,11 +369,12 @@ contract_from_form([{type, _L1, bounded_fun, [{type, _L2, 'fun', [_, _]} = Form, Constr]}| Left], RecDict, TypeAcc, FormAcc) -> TypeFun = - fun(AllRecords) -> - Constr1 = [constraint_from_form(C, RecDict, AllRecords) || C <- Constr], + fun(ExpTypes, AllRecords) -> + Constr1 = [constraint_from_form(C, RecDict, ExpTypes, AllRecords) + || C <- Constr], VarDict = insert_constraints(Constr1, dict:new()), Type = erl_types:t_from_form(Form, RecDict, VarDict), - NewType = erl_types:t_solve_remote(Type, AllRecords), + NewType = erl_types:t_solve_remote(Type, ExpTypes, AllRecords), {NewType, Constr1} end, NewTypeAcc = [TypeFun | TypeAcc], @@ -380,13 +384,15 @@ contract_from_form([], _RecDict, TypeAcc, FormAcc) -> {lists:reverse(TypeAcc), lists:reverse(FormAcc)}. constraint_from_form({type, _, constraint, [{atom, _, is_subtype}, - [Type1, Type2]]}, RecDict, AllRecords) -> + [Type1, Type2]]}, RecDict, + ExpTypes, AllRecords) -> T1 = erl_types:t_from_form(Type1, RecDict), T2 = erl_types:t_from_form(Type2, RecDict), - T3 = erl_types:t_solve_remote(T1, AllRecords), - T4 = erl_types:t_solve_remote(T2, AllRecords), + T3 = erl_types:t_solve_remote(T1, ExpTypes, AllRecords), + T4 = erl_types:t_solve_remote(T2, ExpTypes, AllRecords), {subtype, T3, T4}; -constraint_from_form({type, _, constraint, [{atom,_,Name}, List]}, _RecDict, _) -> +constraint_from_form({type, _, constraint, [{atom,_,Name}, List]}, _RecDict, + _ExpTypes, _AllRecords) -> N = length(List), throw({error, io_lib:format("Unsupported type guard ~w/~w\n", [Name, N])}). diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index 1ccfaaa52f..a3c7114ee1 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -38,6 +38,8 @@ %% Debug and test interfaces. -export([get_top_level_signatures/2, pp/1]). +-export_type([state/0]). + -include("dialyzer.hrl"). -import(erl_types, diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl index e387077a46..c10375eea2 100644 --- a/lib/dialyzer/src/dialyzer_plt.erl +++ b/lib/dialyzer/src/dialyzer_plt.erl @@ -39,10 +39,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 +59,8 @@ %% Debug utilities -export([pp_non_returning/0, pp_mod/1]). +-export_type([plt/0, plt_info/0]). + %%---------------------------------------------------------------------- -type mod_deps() :: dict(). @@ -70,9 +74,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 +85,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()]}). %%---------------------------------------------------------------------- @@ -97,17 +103,21 @@ new() -> -spec delete_module(plt(), module()) -> 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(). @@ -150,11 +160,21 @@ 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()]}. @@ -207,7 +227,8 @@ from_file(FileName, ReturnInfo) -> 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 -> @@ -261,15 +282,18 @@ 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) -> ordsets:union(OldVal, NewVal) @@ -281,6 +305,7 @@ to_file(FileName, info = Info, contracts = Contracts, types = Types, + exported_types = ExpTypes, mod_deps = NewModDeps, implementation_md5 = ImplMd5}, Bin = term_to_binary(Record, [compressed]), @@ -475,6 +500,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). @@ -526,6 +554,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. diff --git a/lib/dialyzer/src/dialyzer_races.erl b/lib/dialyzer/src/dialyzer_races.erl index 4972967960..fb16e6a75f 100644 --- a/lib/dialyzer/src/dialyzer_races.erl +++ b/lib/dialyzer/src/dialyzer_races.erl @@ -39,6 +39,8 @@ let_tag_new/2, new/0, put_curr_fun/3, put_fun_args/2, put_race_analysis/2, put_race_list/3]). +-export_type([races/0]). + -include("dialyzer.hrl"). %%% =========================================================================== @@ -1704,7 +1706,6 @@ compare_types(VarArgs, WarnVarArgs, RaceWarnTag, RaceVarMap) -> false -> compare_var_list(VA1, WVA1, RaceVarMap) orelse compare_argtypes(VA2, WVA2) - end end; ?WARN_ETS_LOOKUP_INSERT -> diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl index 6ea243c26f..f5bfc6ad2f 100644 --- a/lib/dialyzer/src/dialyzer_utils.erl +++ b/lib/dialyzer/src/dialyzer_utils.erl @@ -42,6 +42,7 @@ merge_records/2, pp_hook/0, process_record_remote_types/1, + sets_filter/2, src_compiler_opts/0 ]). @@ -78,7 +79,7 @@ print_types1([{record, _Name} = Key|T], RecDict) -> %% -type abstract_code() :: [tuple()]. %% XXX: refine --type comp_options() :: [atom()]. %% XXX: only a resticted set of options used +-type comp_options() :: [atom()]. %% XXX: a restricted set of options is used %% ============================================================================ %% @@ -169,7 +170,7 @@ get_record_and_type_info(AbstractCode) -> Module = get_module(AbstractCode), get_record_and_type_info(AbstractCode, Module, dict:new()). --spec get_record_and_type_info(abstract_code(), atom(), dict()) -> +-spec get_record_and_type_info(abstract_code(), module(), dict()) -> {'ok', dict()} | {'error', string()}. get_record_and_type_info(AbstractCode, Module, RecDict) -> @@ -278,13 +279,16 @@ type_record_fields([RecKey|Recs], RecDict) -> process_record_remote_types(CServer) -> TempRecords = dialyzer_codeserver:get_temp_records(CServer), + TempExpTypes = dialyzer_codeserver:get_temp_exported_types(CServer), RecordFun = fun(Key, Value) -> case Key of {record, _Name} -> FieldFun = fun(_Arity, Fields) -> - [{Name, erl_types:t_solve_remote(Field, TempRecords)} || {Name, Field} <- Fields] + [{Name, erl_types:t_solve_remote(Field, TempExpTypes, + TempRecords)} + || {Name, Field} <- Fields] end, orddict:map(FieldFun, Value); _Other -> Value @@ -295,7 +299,8 @@ process_record_remote_types(CServer) -> dict:map(RecordFun, Record) end, NewRecords = dict:map(ModuleFun, TempRecords), - dialyzer_codeserver:finalize_records(NewRecords, CServer). + CServer1 = dialyzer_codeserver:finalize_records(NewRecords, CServer), + dialyzer_codeserver:finalize_exported_types(TempExpTypes, CServer1). -spec merge_records(dict(), dict()) -> dict(). @@ -353,6 +358,20 @@ get_spec_info([], SpecDict, _RecordsDict, _ModName, _File) -> %% ============================================================================ %% +%% Exported types +%% +%% ============================================================================ + +-spec sets_filter([module()], set()) -> set(). + +sets_filter([], ExpTypes) -> + ExpTypes; +sets_filter([Mod|Mods], ExpTypes) -> + NewExpTypes = sets:filter(fun({M, _F, _A}) -> M =/= Mod end, ExpTypes), + sets_filter(Mods, NewExpTypes). + +%% ============================================================================ +%% %% Util utils %% %% ============================================================================ |