From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- lib/typer/src/typer_info.erl | 162 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 lib/typer/src/typer_info.erl (limited to 'lib/typer/src/typer_info.erl') 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). -- cgit v1.2.3