dialyzer_contracts.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 313 行

ERL
313
字号
%% -*- erlang-indent-level: 2 -*--module(dialyzer_contracts).-export([type_fun/4, 	 type_domain/4, 	 check_contract/5,	 check_contract/3, 	 check_contract/2, 	 get_contract/2,	 get_contract_info/2,	 check_contract_refined/1,	 delete_contracts/3,	 pp_type/1]).%-define(DEBUG, true).%-define(DEBUG_PP, true).-ifdef(DEBUG).-define(debug(X__, Y__), io:format(X__, Y__)).-else.-define(debug(X__, Y__), ok).-endif.%%---------------------------------------------------------------------%%--------------------------   Interface   ----------------------------%% Equivalent to erl_bif_types type/4 function%% (MFA, Codeserver) -> (Domain -> Range)%% Output: Fun || errortype_fun(M, F, A, CS) ->  case get_contract({M, F, A}, CS) of    error -> io:format("No contract for function ~w/~w\n", [F, A]),	     error;    Contract_List -> %%      Print = fun([T1,T2]) ->%%		  T11 = pp_type(T1),%%		  T22 = pp_type(T2),%%		  io:format("Mismatch of types:\n Contract: ~s\n Call:  ~s\n", %%			    [T11, T22])%%	      end,      Fun = fun(Domain) -> 		Error_queue = queue:new(),		case process_contracts(Contract_List, Domain, Error_queue) of		  {none, _New_Error_queue} -> %%		    lists:map(Print, queue:to_list(New_Error_queue)),		    none;		  {error, _New_Error_queue} ->%%		    lists:map(Print, queue:to_list(New_Error_queue)),		    none;		  {Function, _} -> erl_types:t_fun_range(Function)		end	    end,      {ok, Fun}  end.%% (M, F, A, Codeserver) -> Most general form of Contract Domain%% Output: {of, Domain} || errortype_domain(M, F, A, CS) ->  case get_contract({M, F, A}, CS) of    error -> %% io:format("No contract for function ~w/~w", [F, A]),	     error;    Contract_List ->       Domains = [new_t_from_form_aux(Dom) || [Dom, _Range] <- Contract_List],      General_domain = contract_sup(Domains),      {ok, General_domain}  end.%% (General_Domain, Range, Success_type) -> ok || error%% (General_Domain, Range) <- get_contract_infocheck_contract(General_Domain, Contract_Range, Success_type) ->  Success_Domain = erl_types:t_fun_args(Success_type),  case check_subtype(General_Domain, Success_Domain) of    error ->       io:format("Number of arguments of the contract and                   of the function do not match\n"),       error;    {error, C_type, F_Type} ->       io:format("Invalid contract. ~s is not a subtype of ~s.\n",  		[erl_types:t_to_string(C_type),  		 erl_types:t_to_string(F_Type)]),      error;    ok -> % everything ok with arguments. Now we check the range      %% Anoying thing to solve ----> Arguments are in a list, Range not       %% but they use same functions      Success_Range = erl_types:t_fun_range(Success_type),      case check_subtype([Contract_Range], [Success_Range]) of 	error -> error; 	ok -> ok; % everything ok with this function 	{error, C_type, F_Type} ->  	  io:format("Invalid contract. ~s is not a subtype of ~s.\n",  		    [erl_types:t_to_string(C_type),  		     erl_types:t_to_string(F_Type)]), 	  error      end  end.check_contract(Contracts, Success_type) ->  Cont_Funs =     [erl_types:t_fun(       new_t_from_form_aux(Domain),       erl_types:t_from_form(Range))     || [Domain, Range] <- Contracts],  Infimum = process_contracts(Cont_Funs, Success_type),    Domains_inf = [erl_types:t_fun_args(Inf) || Inf <- Infimum, 					      erl_types:t_is_fun(Inf)],  General_domain = contract_sup(Domains_inf),  %%Range_inf = [[erl_types:t_fun_range(Inf)] || Inf <- Infimum, 	%%				       erl_types:t_is_fun(Inf)],  %%case Range_inf of  %%  [] -> General_range = none;   %%  _Other -> [General_range] = contract_sup(Range_inf)  %%end,    Fun = fun(Args) -> 	    %%io:format("Applying range_fun for aguments:\n"),	    %%lists:map(fun(X) -> io:format("Argumento: ~s\n", 	    %%     [erl_types:t_to_string(X)]) end, Args),	    Error_queue = queue:new(), 	    case process_contracts(Contracts, Args, Error_queue) of	      {none, _New_Error_queue} -> 		none;	      {error, _New_Error_queue} -> 		none;	      {Function, _} -> 		case (catch erl_types:t_unify_subtypes(Function,Success_type))of		  {mismatch, _T1, _T2} -> none;		  {T, _} ->		    ?debug("          Unified Final: ~s\n",[pp_type(T)]), 		    erl_types:t_fun_range(T)		end	    end	end,  %% We check if the contract is ok  case General_domain of    none -> none;    _Type -> {contract, Fun, General_domain}  end.%% (M, F, A, Codeserver, Success_type) -> ok || error check_contract(M, F, A, CS, Success_type) ->  case get_contract({M, F, A}, CS) of    error -> %% io:format("No contract for function ~w/~w\n", [F, A]),	     error;    Contract_List ->         check_contract(Contract_List, Success_type)  end.  %% (M, F, A, CS) -> error || Contract %% Contract = [Domain, Range] %% Domain = [Arguments in parse forms]%% Range = Range in parse formget_contract({M,F,A}, CS) ->  Contracts = dialyzer_codeserver:lookup_contracts(M, CS),  case dict:is_key({F,A}, Contracts) of    true ->       dict:fetch({F,A}, Contracts);    false ->      error  end.%% (MFA, CS) -> error || {ok, General_domain, Constrains(Range function)}get_contract_info({M, F, A}, CS) when is_atom(F) ->  case type_fun(M, F, A, CS) of    error -> error;    {ok, Range_Fun} ->       case type_domain(M, F, A, CS) of	error -> error;	{ok, Domain} -> {ok, Domain, Range_Fun}      end  end.	       check_contract_refined([{MFA, {value, {Cont_Rang, _Cont_Dom}}, Refined}|Left]) ->  RefinedRange = erl_types:t_fun_range(Refined),  %% If the contract range is a subtype of the refined ST range --> type clash  case erl_types:t_is_subtype(RefinedRange, Cont_Rang) of    true -> ok;    false ->      io:format("Mismach of types in function ~w range:\nContract: ~s\nInferred type:  ~s\n \n",		[MFA, erl_types:t_to_string(Cont_Rang), 		 erl_types:t_to_string(RefinedRange)])  end,  check_contract_refined(Left);check_contract_refined([]) ->   ok.%%---------------------------------------------------------------%%---------------------   Auxiliar funs  ------------------------%%% We dont want the first list to be transformednew_t_from_form_aux(Args) ->  lists:map(fun(Arg) -> erl_types:t_from_form(Arg) end, Args).subst_all_vars_to_any_aux(Args) ->  lists:map(fun(Arg) -> erl_types:subst_all_vars_to_any(Arg) end, Args).%%Loops over the different contracts in a function to find%% the proper range. Two contracts can match with the same calls%% (one is a subset of the other) so we must do a supremum of the ranges%% Contracts in parse formsprocess_contracts(Contracts_parse, Call_Types, Error_queue) ->  Contracts = [erl_types:t_fun(new_t_from_form_aux(Cont_Domain),			       erl_types:t_from_form(Cont_Range))	       || [Cont_Domain,Cont_Range] <- Contracts_parse],  Call_Fun = erl_types:t_fun(Call_Types, any),  process_contracts2(Contracts, Call_Fun, Error_queue).%% Contracts in erl_types format%% We only get the 1st successfull contract here, later we'll %% have to consider that one contract of the union can be %% subset of anotherprocess_contracts2([Contract|Left], CallTypes, Error_queue) ->  ContArgs = erl_types:t_fun_args(Contract),  ContArgsFun = erl_types:t_fun(ContArgs, any),  ?debug("Unifying: Contract ~s\n          Args ~s\n",	 [pp_type(ContArgsFun), pp_type(CallTypes)]),  case (catch erl_types:t_unify_subtypes(ContArgsFun, CallTypes)) of    {mismatch, T1, T2} ->      New_Error_queue = queue:in([T1, T2], Error_queue),       process_contracts2(Left, CallTypes, New_Error_queue);    {Function0, Parameters} ->       FunctionArgs = erl_types:t_fun_args(Function0),       FunctionRange = erl_types:t_subst(			erl_types:t_fun_range(Contract), 			Parameters),      Function = erl_types:t_fun(FunctionArgs, FunctionRange),      ?debug("          Unified C-A: ~s\n", [pp_type(Function)]),      {Type, New_Error_queue} =  	process_contracts2(Left, CallTypes, Error_queue),      {erl_types:t_sup(Type, Function), New_Error_queue}  end;process_contracts2([], _, Error_queue) -> {none, Error_queue}.%% Version with succ_typesprocess_contracts(Contracts, Succ_type) ->  lists:map(    fun(Contract) ->	?debug("Unifying: Contract ~s\n          Succ_type ~s\n", 	       [pp_type(Contract), pp_type(Succ_type)]),	case (catch erl_types:t_unify_subtypes(Contract, Succ_type)) of	  {mismatch, _T1, _T2} -> none;	  {Function, _Parameters} -> 	    ?debug("          Unified C-ST: ~s\n", [pp_type(Function)]),	    Function 	end    end, Contracts).check_subtype([Cont_Type|Rest1],[Type|Rest2]) ->   case erl_types:t_is_subtype(Cont_Type, Type) of     true -> check_subtype(Rest1, Rest2);     false -> {error, Cont_Type, Type}  end;check_subtype([],[]) -> ok;check_subtype([],_) -> error;check_subtype(_,[]) -> error.%% Takes a list of transformed domains of all the contracts (unions) %% of a functioncontract_sup([Dom1|Left]) ->  Fun = fun(D1, D2) ->	    lists:zipwith(fun(A1, A2) -> 			      erl_types:t_sup(A1, A2) end,			  D1, D2)        end,  General_domain = lists:foldl(Fun, Dom1, Left),  subst_all_vars_to_any_aux(General_domain);contract_sup([]) -> none.delete_contracts([M|Left], NotFixpoint, Codeserver) ->   Contracts = dialyzer_codeserver:lookup_contracts(M, Codeserver),  New_contracts =     dict:filter      (fun({F,A}, _Contract) ->  	   not lists:member({M,F,A}, NotFixpoint)       end, Contracts),  Codeserver2 =     dialyzer_codeserver:store_contracts(M, New_contracts, Codeserver),    delete_contracts(Left, NotFixpoint, Codeserver2);delete_contracts([], _NotFixpoint, Codeserver) -> Codeserver.-ifdef(DEBUG_PP).pp_type(Type) ->  case cerl:is_literal(Type) of    true -> io_lib:format("~w", [cerl:concrete(Type)]);    false -> erl_types:t_to_string(Type)  end.-else.pp_type(_Type) ->   ok.-endif.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?