diff options
-rw-r--r-- | lib/typer/src/Makefile | 4 | ||||
-rw-r--r-- | lib/typer/src/typer.app.src | 4 | ||||
-rw-r--r-- | lib/typer/src/typer.erl | 188 | ||||
-rw-r--r-- | lib/typer/src/typer_info.erl | 190 |
4 files changed, 180 insertions, 206 deletions
diff --git a/lib/typer/src/Makefile b/lib/typer/src/Makefile index f814e943ea..41ed719d1b 100644 --- a/lib/typer/src/Makefile +++ b/lib/typer/src/Makefile @@ -45,9 +45,7 @@ DIALYZER_DIR = $(ERL_TOP)/lib/dialyzer # ---------------------------------------------------- # Target Specs # ---------------------------------------------------- -MODULES = typer \ - typer_annotator \ - typer_info +MODULES = typer typer_annotator HRL_FILES= typer.hrl ERL_FILES= $(MODULES:%=%.erl) diff --git a/lib/typer/src/typer.app.src b/lib/typer/src/typer.app.src index 2109f7fc37..d3923e1953 100644 --- a/lib/typer/src/typer.app.src +++ b/lib/typer/src/typer.app.src @@ -4,9 +4,7 @@ [{description, "TYPe annotator for ERlang programs, version %VSN%"}, {vsn, "%VSN%"}, {modules, [typer, - typer_annotator, - typer_info, - typer_options]}, + typer_annotator]}, {registered, []}, {applications, [compiler, dialyzer, hipe, kernel, stdlib]}, {env, []}]}. diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index 54818553da..01a068a85d 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -31,7 +31,7 @@ -module(typer). -export([start/0]). --export([fatal_error/1, compile_error/1]). % for error reporting +-export([fatal_error/1]). % for error reporting -export([map__new/0, map__insert/2, map__lookup/2, map__from_list/1, map__remove/2, map__fold/3]). %%----------------------------------------------------------------------- @@ -59,8 +59,8 @@ no_spec = false :: boolean(), %% --- for typer --- t_files = [] :: files(), - %% For choosing between contracts or comments - contracts = true :: boolean(), + %% For choosing between specs or edoc @spec comments + edoc = false :: boolean(), %% Files in 'final_files' are compilable with option 'to_pp'; we keep %% them as {FileName, ModuleName} in case the ModuleName is different final_files = [] :: [{file:filename(), module()}], @@ -89,7 +89,7 @@ start() -> All_Files = get_all_files(Args), %% io:format("All_Files: ~p\n", [All_Files]), Analysis3 = Analysis2#typer_analysis{ana_files = All_Files}, - Analysis4 = typer_info:collect(Analysis3), + Analysis4 = collect_info(Analysis3), %% io:format("Final: ~p\n", [Analysis4#typer_analysis.final_files]), TypeInfo = get_type_info(Analysis4), typer_annotator:annotate(TypeInfo), @@ -236,7 +236,7 @@ cl(["-h"|_]) -> help_message(); cl(["--help"|_]) -> help_message(); cl(["-v"|_]) -> version_message(); cl(["--version"|_]) -> version_message(); -cl(["--comments"|Opts]) -> {comments, Opts}; +cl(["--edoc"|Opts]) -> {edoc, Opts}; cl(["--show"|Opts]) -> {{mode, ?SHOW}, Opts}; cl(["--show_exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts}; cl(["--show-exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts}; @@ -291,8 +291,8 @@ analyze_result({files_r, Val}, Args, Analysis) -> analyze_result({trusted, Val}, Args, Analysis) -> NewVal = Args#args.trusted ++ Val, {Args#args{trusted = NewVal}, Analysis}; -analyze_result(comments, Args, Analysis) -> - {Args, Analysis#typer_analysis{contracts = false}}; +analyze_result(edoc, Args, Analysis) -> + {Args, Analysis#typer_analysis{edoc = true}}; %% Get useful information for actual analysis analyze_result({mode, Mode}, Args, Analysis) -> case Analysis#typer_analysis.mode of @@ -427,6 +427,174 @@ remove_dup(Files) -> lists:reverse(Reversed_Elems). %%-------------------------------------------------------------------- +%% Collect information. +%%-------------------------------------------------------------------- + +-type func_info() :: {non_neg_integer(), atom(), arity()}. +-type inc_file_info() :: {file:filename(), func_info()}. + +-record(tmpAcc, {file :: file:filename(), + module :: atom(), + funcAcc = [] :: [func_info()], + incFuncAcc = [] :: [inc_file_info()], + dialyzerObj = [] :: [{mfa(), {_, _}}]}). + +-spec collect_info(analysis()) -> analysis(). + +collect_info(Analysis) -> + NewPlt = + try get_dialyzer_plt(Analysis) of + DialyzerPlt -> + dialyzer_plt:merge_plts([Analysis#typer_analysis.trust_plt, DialyzerPlt]) + catch + throw:{dialyzer_error,_Reason} -> + fatal_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), + NewExpTypes = dialyzer_codeserver:get_temp_exported_types(TmpCServer), + OldRecords = dialyzer_plt:get_types(NewPlt), + OldExpTypes = dialyzer_plt:get_exported_types(NewPlt), + MergedRecords = dialyzer_utils:merge_records(NewRecords, OldRecords), + MergedExpTypes = sets:union(NewExpTypes, OldExpTypes), + %% io:format("Merged Records ~p",[MergedRecords]), + TmpCServer1 = dialyzer_codeserver:set_temp_records(MergedRecords, TmpCServer), + 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} -> + fatal_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]), + compile_error(Reason); + {ok, AbstractCode} -> + case dialyzer_utils:get_core_from_abstract_code(AbstractCode, Options) of + error -> compile_error(["Could not get core erlang for "++File]); + {ok, Core} -> + case dialyzer_utils:get_record_and_type_info(AbstractCode) of + {error, Reason} -> compile_error([Reason]); + {ok, Records} -> + Mod = cerl:concrete(cerl:module_name(Core)), + case dialyzer_utils:get_spec_info(Mod, AbstractCode, Records) of + {error, Reason} -> compile_error([Reason]); + {ok, SpecInfo} -> + ExpTypes = get_exported_types_from_core(Core), + analyze_core_tree(Core, Records, SpecInfo, ExpTypes, + Analysis, File) + end + end + end + end. + +analyze_core_tree(Core, Records, SpecInfo, ExpTypes, Analysis, File) -> + Module = cerl:concrete(cerl:module_name(Core)), + 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 = + case Analysis#typer_analysis.no_spec of + true -> CS4; + false -> dialyzer_codeserver:store_temp_contracts(Module, SpecInfo, CS4) + end, + OldExpTypes = dialyzer_codeserver:get_temp_exported_types(CS5), + MergedExpTypes = sets:union(ExpTypes, OldExpTypes), + CS6 = dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, CS5), + 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 = 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 = 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 = map__insert({File, Acc#tmpAcc.incFuncAcc}, + Analysis#typer_analysis.inc_func), + Final_Files = Analysis#typer_analysis.final_files ++ [{File, Module}], + RecordMap = map__insert({File, Records}, Analysis#typer_analysis.record), + Analysis#typer_analysis{final_files=Final_Files, + callgraph=CG, + code_server=CS6, + 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). + + +%% Exported Types + +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]). + +%%-------------------------------------------------------------------- %% Utilities for error reporting. %%-------------------------------------------------------------------- @@ -474,7 +642,7 @@ version_message() -> -spec help_message() -> no_return(). help_message() -> - S = " Usage: typer [--help] [--version] [--comments] [--plt PLT] + S = " Usage: typer [--help] [--version] [--plt PLT] [--edoc] [--show | --show-exported | --annotate | --annotate-inc-files] [-Ddefine]* [-I include_dir]* [-T application]* [-r] file* @@ -492,8 +660,8 @@ help_message() -> --annotate-inc-files Same as --annotate but annotates all -include() files as well as all .erl files (use this option with caution - has not been tested much) - --comments - Prints type information using Edoc comments, not type specs + --edoc + Prints type information as Edoc @spec comments, not as type specs --plt PLT Use the specified dialyzer PLT file rather than the default one -T file* diff --git a/lib/typer/src/typer_info.erl b/lib/typer/src/typer_info.erl deleted file mode 100644 index a568518ffe..0000000000 --- a/lib/typer/src/typer_info.erl +++ /dev/null @@ -1,190 +0,0 @@ -%% -*- erlang-indent-level: 2 -*- -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2011. 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() :: {file:filename(), func_info()}. - --record(tmpAcc, {file :: file:filename(), - 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:fatal_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), - NewExpTypes = dialyzer_codeserver:get_temp_exported_types(TmpCServer), - OldRecords = dialyzer_plt:get_types(NewPlt), - OldExpTypes = dialyzer_plt:get_exported_types(NewPlt), - MergedRecords = dialyzer_utils:merge_records(NewRecords, OldRecords), - MergedExpTypes = sets:union(NewExpTypes, OldExpTypes), - %% io:format("Merged Records ~p",[MergedRecords]), - TmpCServer1 = dialyzer_codeserver:set_temp_records(MergedRecords, TmpCServer), - 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} -> - typer:fatal_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 = cerl:concrete(cerl:module_name(Core)), - case dialyzer_utils:get_spec_info(Mod, AbstractCode, Records) of - {error, Reason} -> typer:compile_error([Reason]); - {ok, SpecInfo} -> - ExpTypes = get_exported_types_from_core(Core), - analyze_core_tree(Core, Records, SpecInfo, ExpTypes, - Analysis, File) - end - end - end - end. - -analyze_core_tree(Core, Records, SpecInfo, ExpTypes, Analysis, File) -> - Module = cerl:concrete(cerl:module_name(Core)), - 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 = - case Analysis#typer_analysis.no_spec of - true -> CS4; - false -> dialyzer_codeserver:store_temp_contracts(Module, SpecInfo, CS4) - end, - OldExpTypes = dialyzer_codeserver:get_temp_exported_types(CS5), - MergedExpTypes = sets:union(ExpTypes, OldExpTypes), - CS6 = dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, CS5), - 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=CS6, - 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). - - -%% Exported Types - -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]). |