dialyzer_dataflow.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 2,097 行 · 第 1/5 页
ERL
2,097 行
enter_subst(Key, Val, MS = {Map, Subst}) -> KeyLabel = get_label(Key), case cerl:is_literal(Val) of true -> NewMap = dict:store(KeyLabel, literal_type(Val), Map), {NewMap, Subst}; false -> case cerl:is_c_var(Val) of false -> MS; true -> ValLabel = get_label(Val), case dict:find(ValLabel, Subst) of {ok, NewVal} -> enter_subst(Key, NewVal, MS); error -> if KeyLabel =:= ValLabel -> MS; true -> ?debug("Subst: storing ~p = ~p\n", [KeyLabel, ValLabel]), NewSubst = dict:store(KeyLabel, ValLabel, Subst), {Map, NewSubst} end end end end.lookup_type(Key, {Map, Subst}) -> lookup(Key, Map, Subst, t_none()).lookup(Key, Map, Subst, AnyNone) -> case cerl:is_literal(Key) of true -> literal_type(Key); false -> Label = get_label(Key), case dict:find(Label, Subst) of {ok, NewKey} -> lookup(NewKey, Map, Subst, AnyNone); error -> case dict:find(Label, Map) of {ok, Val} -> Val; error -> AnyNone end end end.lookup_fun_name(Fun, Callgraph) -> case dialyzer_callgraph:lookup_name(Fun, Callgraph) of {ok, MFA} -> MFA; error -> Fun end.lookup_fun_sig(Fun, Callgraph, Plt, Args) -> MFAorLabel = lookup_fun_name(Fun, Callgraph), dialyzer_plt:lookup(Plt, MFAorLabel, Args).literal_type(Lit) -> t_from_term(cerl:concrete(Lit)).mark_as_fresh(Trees, Map) -> Fun = fun(T, AccMap) -> case cerl:is_literal(T) of true -> enter_type(T, literal_type(T), AccMap); false -> enter_type(T, t_any(), AccMap) end end, mark_as_fresh(Trees, Fun, Map).mark_as_fresh([Tree|Left], Fun, Map) -> Map1 = cerl_trees:fold(Fun, Map, Tree), mark_as_fresh(Left, Map1);mark_as_fresh([], _Fun, Map) -> Map. -ifdef(DEBUG).debug_pp_map(Map = {Map0, _Subst}) -> Keys = dict:fetch_keys(Map0), io:format("Map:\n", []), [io:format("\t~w :: ~s\n", [Key, t_to_string(lookup_type(Key, Map))]) || Key <- Keys], ok.-else.debug_pp_map(_Map) -> ok.-endif.%%% ============================================================================%%%%%% Utilities%%%%%% ============================================================================add_def_vars([H|T], DefVars) -> add_def_vars(T, ordsets:add_element(get_label(H), DefVars));add_def_vars([], DefVars) -> DefVars.is_def_var(Var, DefVars) -> ordsets:is_element(get_label(Var), DefVars).get_label(L) when is_integer(L) -> L;get_label(T) -> cerl_trees:get_label(T).any_none([X|Xs]) -> case t_is_none(X) of true -> true; false -> any_none(Xs) end;any_none([]) -> false.any_unit([X|Xs]) -> case t_is_unit(X) of true -> true; false -> any_unit(Xs) end;any_unit([]) -> false.filter_match_fail([Clause]) -> Body = cerl:clause_body(Clause), case cerl:type(Body) of primop -> case cerl:atom_val(cerl:primop_name(Body)) of match_fail -> []; raise -> []; _ -> [Clause] end; _ -> [Clause] end;filter_match_fail([H|T]) -> [H|filter_match_fail(T)];filter_match_fail([]) -> %% This can actually happen, for example in %% receive after 1 -> ok end [].%%% ============================================================================%%%%%% The State.%%%%%% ============================================================================-record(state, {callgraph, envs, forward_mode, fun_tab, plt, records, tree_map, warning_mode, warnings, work}).state__new(Callgraph, Tree, Plt, Records, ForwardMode) -> TreeMap = build_tree_map(Tree), Funs = dict:fetch_keys(TreeMap), FunTab = init_fun_tab(Funs, dict:new(), TreeMap, Callgraph, Plt, ForwardMode), Work = init_work([get_label(Tree)]), Env = dict:store(top, {map__new(), []}, dict:new()), #state{callgraph=Callgraph, envs=Env, fun_tab=FunTab, forward_mode=ForwardMode, plt=Plt, records=Records, warning_mode=false, warnings=[], work=Work, tree_map=TreeMap}.state__mark_fun_as_handled(State = #state{fun_tab=FunTab}, Fun0) -> Fun = get_label(Fun0), case dict:find(Fun, FunTab) of {ok, {not_handled, Entry}} -> State#state{fun_tab=dict:store(Fun, Entry, FunTab)}; {ok, {_, _}} -> State end.state__warning_mode(#state{warning_mode=WM}) -> WM.state__set_warning_mode(State = #state{tree_map=TreeMap, fun_tab=FunTab}) -> ?debug("Starting warning pass\n", []), Funs = dict:fetch_keys(TreeMap), State#state{work=init_work([top|Funs--[top]]), fun_tab=FunTab, warning_mode=true}.state__add_warning(State, Tag, Tree, Msg) -> state__add_warning(State, Tag, Tree, Msg, false).state__add_warning(S = #state{warning_mode=false}, _Tag, _Tree, _Msg, _Force) -> S;state__add_warning(State = #state{warnings=Warnings, warning_mode=true}, Tag, Tree, Msg, Force) -> Ann = cerl:get_ann(Tree), case Force of true -> Warn = {Tag, {{get_file(Ann), abs(get_line(Ann))}, Msg}}, State#state{warnings=[Warn|Warnings]}; false -> case is_compiler_generated(Ann) of true -> State; false -> Warn = {Tag, {{get_file(Ann), get_line(Ann)}, Msg}}, State#state{warnings=[Warn|Warnings]} end end.state__get_warnings(State = #state{tree_map=TreeMap, fun_tab=FunTab, callgraph=Callgraph}, NoWarnUnused) -> FoldFun = fun({top, _}, AccState) -> AccState; ({FunLbl, Fun}, AccState) -> {NotCalled, Ret} = case dict:fetch(get_label(Fun), FunTab) of {not_handled, {_Args0, Ret0}} -> {true, Ret0}; {Args0, Ret0} -> {any_none(Args0), Ret0} end, case NotCalled of true -> {Warn, NameString} = case lookup_fun_name(FunLbl, Callgraph) of FunLbl -> {true, "Function "}; {_M, F, A} = MFA-> {not sets:is_element(MFA, NoWarnUnused), io_lib:format("Function ~w/~w ", [F, A])} end, Msg = NameString ++ "will never be called\n", if Warn -> state__add_warning(AccState, ?WARN_NOT_CALLED, Fun, Msg); true -> AccState end; false -> NameString = case lookup_fun_name(FunLbl, Callgraph) of FunLbl -> "Function "; {_M, F, A} -> io_lib:format("Function ~w/~w ", [F, A]) end, case t_is_none_or_unit(Ret) of true -> case classify_returns(Fun) of only_explicit -> Msg = NameString ++ "only terminates with explicit exception\n", state__add_warning(AccState, ?WARN_RETURN_ONLY_EXIT, Fun, Msg); only_normal -> Msg = NameString ++ "has no local return\n", state__add_warning(AccState, ?WARN_RETURN_NO_RETURN, Fun, Msg); both -> Msg = NameString ++ "has no local return\n", state__add_warning(AccState, ?WARN_RETURN_NO_RETURN, Fun, Msg) end; false -> AccState end end end, #state{warnings=Warn} = lists:foldl(FoldFun, State, dict:to_list(TreeMap)), Warn.state__is_escaping(Fun, #state{callgraph=Callgraph}) -> dialyzer_callgraph:is_escaping(Fun, Callgraph).state__lookup_type_for_rec_var(Var, S = #state{callgraph=Callgraph}) -> Label = get_label(Var), case dialyzer_callgraph:lookup_rec_var(Label, Callgraph) of error -> error; {ok, MFA} -> case dialyzer_callgraph:lookup_label(MFA, Callgraph) of error -> error; {ok, FunLabel} -> {ok, state__fun_type(FunLabel, S)} end end.state__lookup_record(Tag, Arity, #state{records=Records}) -> case erl_types:lookup_record(Tag, Arity, Records) of {ok, Fields} -> {ok, t_tuple([t_from_term(Tag)| [FieldType || {_FieldName, FieldType} <- Fields]])}; error -> error end.state__get_args(Tree, #state{fun_tab=FunTab}) -> Fun = get_label(Tree), case dict:find(Fun, FunTab) of {ok, {not_handled, {ArgTypes, _}}} -> ArgTypes; {ok, {ArgTypes, _}} -> ArgTypes end.build_tree_map(Tree) -> Fun = fun(T, Dict) -> case cerl:is_c_fun(T) of true -> dict:store(get_label(T), T, Dict); false -> Dict end end, cerl_trees:fold(Fun, dict:new(), Tree).init_fun_tab([top|Left], Dict, TreeMap, Callgraph, Plt, ForwardMode) -> NewDict = dict:store(top, {not_handled, {[], t_none()}}, Dict), init_fun_tab(Left, NewDict, TreeMap, Callgraph, Plt, ForwardMode);init_fun_tab([Fun|Left], Dict, TreeMap, Callgraph, Plt, ForwardMode) -> Arity = cerl:fun_arity(dict:fetch(Fun, TreeMap)), FunEntry = case dialyzer_callgraph:is_escaping(Fun, Callgraph) of true -> case lookup_fun_sig(Fun, Callgraph, Plt, none) of none -> {duplicate(Arity, t_any()), t_unit()}; {value, {RetType, ArgTypes0}} -> ArgTypes = case ForwardMode of unconstrained -> duplicate(Arity, t_any()); constrained -> ArgTypes0 end, case t_is_none(RetType) of true -> {ArgTypes, t_none()}; false -> {ArgTypes, t_unit()} end end; false -> {duplicate(Arity, t_none()), t_unit()} end, NewDict = dict:store(Fun, {not_handled, FunEntry}, Dict), init_fun_tab(Left, NewDict, TreeMap, Callgraph, Plt, ForwardMode);init_fun_tab([], Dict, _TreeMap, _Callgraph, _Plt, _ForwardMode) -> Dict.state__update_fun_env(Tree, MapAndDef, State = #state{envs=Envs}) -> NewEnvs = dict:store(get_label(Tree), MapAndDef, Envs), State#state{envs=NewEnvs}.state__fun_env(Tree, #state{envs=Envs}) -> Fun = get_label(Tree), case dict:find(Fun, Envs) of error -> none; {ok, MapAndDef} -> MapAndDef end.state__clean_not_called(State = #state{fun_tab=FunTab}) -> NewFunTab = dict:map(fun(top, Entry) -> Entry; (_Fun, {not_handled, {Args, _}}) -> {Args, t_none()}; (_Fun, Entry) -> Entry end, FunTab), State#state{fun_tab=NewFunTab}.state__all_fun_types(#state{fun_tab=FunTab}) -> Tab1 = dict:erase(top, FunTab), dict:map(fun(_Fun, {Args, Ret}) -> t_fun(Args, Ret)end, Tab1).state__fun_type(Fun, #state{fun_tab=FunTab}) -> Label = if is_integer(Fun) -> Fun; true -> get_label(Fun) end, case dict:find(Label, FunTab) of {ok, {not_handled, {A, R}}} -> t_fun(A, R); {ok, {A, R}} -> t_fun(A, R) end.state__update_fun_entry(Tree, ArgTypes, Out0, State = #state{fun_tab=FunTab, callgraph=CG, plt=Plt})-> Fun = get_label(Tree), Out1 = if Fun =:= top -> Out0; true -> case lookup_fun_sig(Fun, CG, Plt, ArgTypes) of {value, {SigRet, _}} -> t_inf(SigRet, Out0); none -> Out0 end end, Out = t_limit(Out1, ?TYPE_LIMIT), case dict:find(Fun, FunTab) of {ok, {ArgTypes, OldOut}} -> case t_is_equal(OldOut, Out) of true -> ?debug("Fixpoint for ~w: ~s\n", [debug_lookup_name(Fun), t_to_string(t_fun(ArgTypes, Out))]), State; false -> NewEntry = {ArgTypes, Out}, ?debug("New Entry for ~w: ~s\n", [debug_lookup_name(Fun), t_to_string(t_fun(ArgTypes, Out))]), NewFunTab = dict:store(Fun, NewEntry, FunTab), State1 = State#state{fun_tab=NewFunTab}, state__add_work_from_fun(Tree, State1) end; {ok, {NewArgTypes, _OldOut}} -> %% Can only happen in self-recursive functions. Only update the out type. NewEntry = {NewArgTypes, Out}, ?debug("New Entry for ~w: ~s\n", [debug_lookup_name(Fun), t_to_string(t_fun(NewArgTypes, Out))]), NewFunTab = dict:store(Fun, NewEntry, FunTab), State1 = State#state{fun_tab=NewFunTab}, state__add_work_from_fun(Tree, State1) end.state__add_work_from_fun(_Tree, State = #state{warning_mode=true}) -> State;state__add_work_from_fun(Tree, State = #state{callgraph=Callgraph, tree_map=TreeMap}) -> case get_label(Tree) of top -> State; Label when is_integer(Label) -> case dialyzer_callgraph:in_neighbours(Label, Callgraph) of none -> State; MFAList -> LabelList = [dialyzer_callgraph:lookup_label(MFA, Callgraph) || MFA <- MFAList], %% Must filter the result for results in this module. FilteredList = lists:filter(fun({ok, L})->dict:is_key(
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?