aboutsummaryrefslogtreecommitdiffstats
path: root/lib/typer/src/typer_info.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/typer/src/typer_info.erl')
-rw-r--r--lib/typer/src/typer_info.erl162
1 files changed, 162 insertions, 0 deletions
diff --git a/lib/typer/src/typer_info.erl b/lib/typer/src/typer_info.erl
new file mode 100644
index 0000000000..ea25fa6f68
--- /dev/null
+++ b/lib/typer/src/typer_info.erl
@@ -0,0 +1,162 @@
+%% -*- erlang-indent-level: 2 -*-
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2006-2009. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(typer_info).
+
+-export([collect/1]).
+
+-type func_info() :: {non_neg_integer(), atom(), arity()}.
+-type inc_file_info() :: {string(), func_info()}.
+
+-record(tmpAcc, {file :: string(),
+ module :: atom(),
+ funcAcc=[] :: [func_info()],
+ incFuncAcc=[] :: [inc_file_info()],
+ dialyzerObj=[] :: [{mfa(), {_, _}}]}).
+
+-include("typer.hrl").
+
+-spec collect(#typer_analysis{}) -> #typer_analysis{}.
+
+collect(Analysis) ->
+ NewPlt =
+ try get_dialyzer_plt(Analysis) of
+ DialyzerPlt ->
+ dialyzer_plt:merge_plts([Analysis#typer_analysis.trust_plt, DialyzerPlt])
+ catch
+ throw:{dialyzer_error,_Reason} ->
+ typer:error("Dialyzer's PLT is missing or is not up-to-date; please (re)create it")
+ end,
+ NewAnalysis = lists:foldl(fun collect_one_file_info/2,
+ Analysis#typer_analysis{trust_plt = NewPlt},
+ Analysis#typer_analysis.ana_files),
+ %% Process Remote Types
+ TmpCServer = NewAnalysis#typer_analysis.code_server,
+ NewCServer =
+ try
+ NewRecords = dialyzer_codeserver:get_temp_records(TmpCServer),
+ OldRecords = dialyzer_plt:get_types(NewPlt),
+ MergedRecords = dialyzer_utils:merge_records(NewRecords, OldRecords),
+ %% io:format("Merged Records ~p",[MergedRecords]),
+ TmpCServer1 = dialyzer_codeserver:set_temp_records(MergedRecords, TmpCServer),
+ TmpCServer2 = dialyzer_utils:process_record_remote_types(TmpCServer1),
+ dialyzer_contracts:process_contract_remote_types(TmpCServer2)
+ catch
+ throw:{error, ErrorMsg} ->
+ typer:error(ErrorMsg)
+ end,
+ NewAnalysis#typer_analysis{code_server = NewCServer}.
+
+collect_one_file_info(File, Analysis) ->
+ Ds = [{d,Name,Val} || {Name,Val} <- Analysis#typer_analysis.macros],
+ %% Current directory should also be included in "Includes".
+ Includes = [filename:dirname(File)|Analysis#typer_analysis.includes],
+ Is = [{i,Dir} || Dir <- Includes],
+ Options = dialyzer_utils:src_compiler_opts() ++ Is ++ Ds,
+ case dialyzer_utils:get_abstract_code_from_src(File, Options) of
+ {error, Reason} ->
+ %% io:format("File=~p\n,Options=~p\n,Error=~p\n", [File,Options,Reason]),
+ typer:compile_error(Reason);
+ {ok, AbstractCode} ->
+ case dialyzer_utils:get_core_from_abstract_code(AbstractCode, Options) of
+ error -> typer:compile_error(["Could not get core erlang for "++File]);
+ {ok, Core} ->
+ case dialyzer_utils:get_record_and_type_info(AbstractCode) of
+ {error, Reason} -> typer:compile_error([Reason]);
+ {ok, Records} ->
+ Mod = list_to_atom(filename:basename(File, ".erl")),
+ case dialyzer_utils:get_spec_info(Mod, AbstractCode, Records) of
+ {error, Reason} -> typer:compile_error([Reason]);
+ {ok, SpecInfo} ->
+ analyze_core_tree(Core, Records, SpecInfo, Analysis, File)
+ end
+ end
+ end
+ end.
+
+analyze_core_tree(Core, Records, SpecInfo, Analysis, File) ->
+ Module = list_to_atom(filename:basename(File, ".erl")),
+ TmpTree = cerl:from_records(Core),
+ CS1 = Analysis#typer_analysis.code_server,
+ NextLabel = dialyzer_codeserver:get_next_core_label(CS1),
+ {Tree, NewLabel} = cerl_trees:label(TmpTree, NextLabel),
+ CS2 = dialyzer_codeserver:insert(Module, Tree, CS1),
+ CS3 = dialyzer_codeserver:set_next_core_label(NewLabel, CS2),
+ CS4 = dialyzer_codeserver:store_temp_records(Module, Records, CS3),
+ CS5 = dialyzer_codeserver:store_temp_contracts(Module, SpecInfo, CS4),
+ Ex_Funcs = [{0,F,A} || {_,_,{F,A}} <- cerl:module_exports(Tree)],
+ TmpCG = Analysis#typer_analysis.callgraph,
+ CG = dialyzer_callgraph:scan_core_tree(Tree, TmpCG),
+ Fun = fun analyze_one_function/2,
+ All_Defs = cerl:module_defs(Tree),
+ Acc = lists:foldl(Fun, #tmpAcc{file=File, module=Module}, All_Defs),
+ Exported_FuncMap = typer_map:insert({File, Ex_Funcs},
+ Analysis#typer_analysis.ex_func),
+ %% NOTE: we must sort all functions in the file which
+ %% originate from this file by *numerical order* of lineNo
+ Sorted_Functions = lists:keysort(1, Acc#tmpAcc.funcAcc),
+ FuncMap = typer_map:insert({File, Sorted_Functions},
+ Analysis#typer_analysis.func),
+ %% NOTE: However we do not need to sort functions
+ %% which are imported from included files.
+ IncFuncMap = typer_map:insert({File, Acc#tmpAcc.incFuncAcc},
+ Analysis#typer_analysis.inc_func),
+ Final_Files = Analysis#typer_analysis.final_files ++ [{File, Module}],
+ RecordMap = typer_map:insert({File, Records}, Analysis#typer_analysis.record),
+ Analysis#typer_analysis{final_files=Final_Files,
+ callgraph=CG,
+ code_server=CS5,
+ ex_func=Exported_FuncMap,
+ inc_func=IncFuncMap,
+ record=RecordMap,
+ func=FuncMap}.
+
+analyze_one_function({Var, FunBody} = Function, Acc) ->
+ F = cerl:fname_id(Var),
+ A = cerl:fname_arity(Var),
+ TmpDialyzerObj = {{Acc#tmpAcc.module, F, A}, Function},
+ NewDialyzerObj = Acc#tmpAcc.dialyzerObj ++ [TmpDialyzerObj],
+ [_, LineNo, {file, FileName}] = cerl:get_ann(FunBody),
+ BaseName = filename:basename(FileName),
+ FuncInfo = {LineNo, F, A},
+ OriginalName = Acc#tmpAcc.file,
+ {FuncAcc, IncFuncAcc} =
+ case (FileName =:= OriginalName) orelse (BaseName =:= OriginalName) of
+ true -> %% Coming from original file
+ %% io:format("Added function ~p\n", [{LineNo, F, A}]),
+ {Acc#tmpAcc.funcAcc ++ [FuncInfo], Acc#tmpAcc.incFuncAcc};
+ false ->
+ %% Coming from other sourses, including:
+ %% -- .yrl (yecc-generated file)
+ %% -- yeccpre.hrl (yecc-generated file)
+ %% -- other cases
+ {Acc#tmpAcc.funcAcc, Acc#tmpAcc.incFuncAcc ++ [{FileName, FuncInfo}]}
+ end,
+ Acc#tmpAcc{funcAcc = FuncAcc,
+ incFuncAcc = IncFuncAcc,
+ dialyzerObj = NewDialyzerObj}.
+
+get_dialyzer_plt(#typer_analysis{plt = PltFile0}) ->
+ PltFile =
+ case PltFile0 =:= none of
+ true -> dialyzer_plt:get_default_plt();
+ false -> PltFile0
+ end,
+ dialyzer_plt:from_file(PltFile).