diff options
author | Kostis Sagonas <[email protected]> | 2011-02-06 18:19:46 +0200 |
---|---|---|
committer | Kostis Sagonas <[email protected]> | 2011-02-06 18:19:46 +0200 |
commit | a3e7c43db2b7774a5bd528da3adb453d3a6dcfa7 (patch) | |
tree | 2ff71e62f1b65b5a861385d45b372608bede18a0 /lib | |
parent | b5bfd43fe718ec020f99238c561fdba6a7123c2c (diff) | |
download | otp-a3e7c43db2b7774a5bd528da3adb453d3a6dcfa7.tar.gz otp-a3e7c43db2b7774a5bd528da3adb453d3a6dcfa7.tar.bz2 otp-a3e7c43db2b7774a5bd528da3adb453d3a6dcfa7.zip |
Remove typer_annotator.erl and typer.hrl
Diffstat (limited to 'lib')
-rw-r--r-- | lib/typer/src/Makefile | 9 | ||||
-rw-r--r-- | lib/typer/src/typer.app.src | 3 | ||||
-rw-r--r-- | lib/typer/src/typer.erl | 450 | ||||
-rw-r--r-- | lib/typer/src/typer.hrl | 53 | ||||
-rw-r--r-- | lib/typer/src/typer_annotator.erl | 383 |
5 files changed, 400 insertions, 498 deletions
diff --git a/lib/typer/src/Makefile b/lib/typer/src/Makefile index 41ed719d1b..3d7827b5b5 100644 --- a/lib/typer/src/Makefile +++ b/lib/typer/src/Makefile @@ -45,9 +45,9 @@ DIALYZER_DIR = $(ERL_TOP)/lib/dialyzer # ---------------------------------------------------- # Target Specs # ---------------------------------------------------- -MODULES = typer typer_annotator +MODULES = typer -HRL_FILES= typer.hrl +HRL_FILES= ERL_FILES= $(MODULES:%=%.erl) INSTALL_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET) TARGET_FILES= $(INSTALL_FILES) @@ -91,12 +91,9 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk sed -e 's;%VSN%;$(VSN);' $< > $@ # --------------------------------------------------------------------- -# dependencies -- I wish they were somehow automatically generated +# dependencies # --------------------------------------------------------------------- -$(EBIN)/typer.beam: typer.hrl -$(EBIN)/typer_annotator.beam: typer.hrl -$(EBIN)/typer_info.beam: typer.hrl # ---------------------------------------------------- # Release Target diff --git a/lib/typer/src/typer.app.src b/lib/typer/src/typer.app.src index d3923e1953..850829e1dc 100644 --- a/lib/typer/src/typer.app.src +++ b/lib/typer/src/typer.app.src @@ -3,8 +3,7 @@ {application, typer, [{description, "TYPe annotator for ERlang programs, version %VSN%"}, {vsn, "%VSN%"}, - {modules, [typer, - typer_annotator]}, + {modules, [typer]}, {registered, []}, {applications, [compiler, dialyzer, hipe, kernel, stdlib]}, {env, []}]}. diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index 01a068a85d..0393076c1f 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -31,8 +31,6 @@ -module(typer). -export([start/0]). --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]). %%----------------------------------------------------------------------- @@ -47,7 +45,7 @@ -type files() :: [file:filename()]. --record(typer_analysis, +-record(analysis, {mode :: mode(), macros = [] :: [{atom(), term()}], % {macro_name, value} includes = [] :: files(), @@ -69,11 +67,12 @@ func = map__new() :: map(), inc_func = map__new() :: map(), trust_plt = dialyzer_plt:new() :: dialyzer_plt:plt()}). --type analysis() :: #typer_analysis{}. +-type analysis() :: #analysis{}. -record(args, {files = [] :: files(), files_r = [] :: files(), trusted = [] :: files()}). +-type args() :: #args{}. %%-------------------------------------------------------------------- @@ -84,15 +83,15 @@ start() -> %% io:format("Args: ~p\n", [Args]), %% io:format("Analysis: ~p\n", [Analysis]), TrustedFiles = filter_fd(Args#args.trusted, [], fun is_erl_file/1), - Analysis1 = Analysis#typer_analysis{t_files = TrustedFiles}, + Analysis1 = Analysis#analysis{t_files = TrustedFiles}, Analysis2 = extract(Analysis1), All_Files = get_all_files(Args), %% io:format("All_Files: ~p\n", [All_Files]), - Analysis3 = Analysis2#typer_analysis{ana_files = All_Files}, + Analysis3 = Analysis2#analysis{ana_files = All_Files}, Analysis4 = collect_info(Analysis3), - %% io:format("Final: ~p\n", [Analysis4#typer_analysis.final_files]), + %% io:format("Final: ~p\n", [Analysis4#analysis.final_files]), TypeInfo = get_type_info(Analysis4), - typer_annotator:annotate(TypeInfo), + show_or_annotate(TypeInfo), %% io:format("\nTyper analysis finished\n"), erlang:halt(0). @@ -100,8 +99,8 @@ start() -> -spec extract(analysis()) -> analysis(). -extract(#typer_analysis{macros = Macros, includes = Includes, - t_files = TFiles, trust_plt = TrustPLT} = Analysis) -> +extract(#analysis{macros = Macros, includes = Includes, + t_files = TFiles, trust_plt = TrustPLT} = Analysis) -> %% io:format("--- Extracting trusted typer_info... "), Ds = [{d, Name, Value} || {Name, Value} <- Macros], CodeServer = dialyzer_codeserver:new(), @@ -154,21 +153,21 @@ extract(#typer_analysis{macros = Macros, includes = Includes, dialyzer_plt:insert_contract_list(TmpPlt, SpecList) end, NewTrustPLT = lists:foldl(FoldFun, TrustPLT, Modules), - Analysis#typer_analysis{trust_plt = NewTrustPLT}. + Analysis#analysis{trust_plt = NewTrustPLT}. %%-------------------------------------------------------------------- -spec get_type_info(analysis()) -> analysis(). -get_type_info(#typer_analysis{callgraph = CallGraph, - trust_plt = TrustPLT, - code_server = CodeServer} = Analysis) -> +get_type_info(#analysis{callgraph = CallGraph, + trust_plt = TrustPLT, + code_server = CodeServer} = Analysis) -> StrippedCallGraph = remove_external(CallGraph, TrustPLT), %% io:format("--- Analyzing callgraph... "), try NewPlt = dialyzer_succ_typings:analyze_callgraph(StrippedCallGraph, TrustPLT, CodeServer), - Analysis#typer_analysis{callgraph = StrippedCallGraph, trust_plt = NewPlt} + Analysis#analysis{callgraph = StrippedCallGraph, trust_plt = NewPlt} catch error:What -> fatal_error(io_lib:format("Analysis failed with message: ~p", @@ -210,18 +209,361 @@ get_external(Exts, Plt) -> lists:foldl(Fun, [], Exts). %%-------------------------------------------------------------------- +%% Showing type information or annotating files with such information. +%%-------------------------------------------------------------------- + +-define(TYPER_ANN_DIR, "typer_ann"). + +-type fun_info() :: {non_neg_integer(), atom(), arity()}. + +-record(info, {records = map__new() :: map(), + functions = [] :: [fun_info()], + types = map__new() :: map(), + edoc = false :: boolean()}). +-record(inc, {map = map__new() :: map(), filter = [] :: files()}). + +-spec show_or_annotate(analysis()) -> 'ok'. + +show_or_annotate(#analysis{mode = Mode, final_files = Files} = Analysis) -> + case Mode of + ?SHOW -> show(Analysis); + ?SHOW_EXPORTED -> show(Analysis); + ?ANNOTATE -> + Fun = fun ({File, Module}) -> + Info = get_final_info(File, Module, Analysis), + write_typed_file(File, Info) + end, + lists:foreach(Fun, Files); + ?ANNOTATE_INC_FILES -> + IncInfo = write_and_collect_inc_info(Analysis), + write_inc_files(IncInfo) + end. + +write_and_collect_inc_info(Analysis) -> + Fun = fun ({File, Module}, Inc) -> + Info = get_final_info(File, Module, Analysis), + write_typed_file(File, Info), + IncFuns = get_functions(File, Analysis), + collect_imported_functions(IncFuns, Info#info.types, Inc) + end, + NewInc = lists:foldl(Fun, #inc{}, Analysis#analysis.final_files), + clean_inc(NewInc). + +write_inc_files(Inc) -> + Fun = + fun (File) -> + Val = map__lookup(File, Inc#inc.map), + %% Val is function with its type info + %% in form [{{Line,F,A},Type}] + Functions = [Key || {Key,_} <- Val], + Val1 = [{{F,A},Type} || {{_Line,F,A},Type} <- Val], + Info = #info{types = map__from_list(Val1), + records = map__new(), + %% Note we need to sort functions here! + functions = lists:keysort(1, Functions)}, + %% io:format("Types ~p\n", [Info#info.types]), + %% io:format("Functions ~p\n", [Info#info.functions]), + %% io:format("Records ~p\n", [Info#info.records]), + write_typed_file(File, Info) + end, + lists:foreach(Fun, dict:fetch_keys(Inc#inc.map)). + +show(Analysis) -> + Fun = fun ({File, Module}) -> + Info = get_final_info(File, Module, Analysis), + show_type_info(File, Info) + end, + lists:foreach(Fun, Analysis#analysis.final_files). + +get_final_info(File, Module, Analysis) -> + Records = get_records(File, Analysis), + Types = get_types(Module, Analysis, Records), + Functions = get_functions(File, Analysis), + Edoc = Analysis#analysis.edoc, + #info{records = Records, functions = Functions, types = Types, edoc = Edoc}. + +collect_imported_functions(Functions, Types, Inc) -> + %% Coming from other sourses, including: + %% FIXME: How to deal with yecc-generated file???? + %% --.yrl (yecc-generated file)??? + %% -- yeccpre.hrl (yecc-generated file)??? + %% -- other cases + Fun = fun ({File, _} = Obj, I) -> + case is_yecc_gen(File, I) of + {true, NewI} -> NewI; + {false, NewI} -> + check_imported_functions(Obj, NewI, Types) + end + end, + lists:foldl(Fun, Inc, Functions). + +-spec is_yecc_gen(file:filename(), #inc{}) -> {boolean(), #inc{}}. + +is_yecc_gen(File, #inc{filter = Fs} = Inc) -> + case lists:member(File, Fs) of + true -> {true, Inc}; + false -> + case filename:extension(File) of + ".yrl" -> + Rootname = filename:rootname(File, ".yrl"), + Obj = Rootname ++ ".erl", + case lists:member(Obj, Fs) of + true -> {true, Inc}; + false -> + NewInc = Inc#inc{filter = [Obj|Fs]}, + {true, NewInc} + end; + _ -> + case filename:basename(File) of + "yeccpre.hrl" -> {true, Inc}; + _ -> {false, Inc} + end + end + end. + +check_imported_functions({File, {Line, F, A}}, Inc, Types) -> + IncMap = Inc#inc.map, + FA = {F, A}, + Type = get_type_info(FA, Types), + case map__lookup(File, IncMap) of + none -> %% File is not added. Add it + Obj = {File,[{FA, {Line, Type}}]}, + NewMap = map__insert(Obj, IncMap), + Inc#inc{map = NewMap}; + Val -> %% File is already in. Check. + case lists:keyfind(FA, 1, Val) of + false -> + %% Function is not in; add it + Obj = {File, Val ++ [{FA, {Line, Type}}]}, + NewMap = map__insert(Obj, IncMap), + Inc#inc{map = NewMap}; + Type -> + %% Function is in and with same type + Inc; + _ -> + %% Function is in but with diff type + inc_warning(FA, File), + Elem = lists:keydelete(FA, 1, Val), + NewMap = case Elem of + [] -> map__remove(File, IncMap); + _ -> map__insert({File, Elem}, IncMap) + end, + Inc#inc{map = NewMap} + end + end. + +inc_warning({F, A}, File) -> + io:format(" ***Warning: Skip function ~p/~p ", [F, A]), + io:format("in file ~p because of inconsistent type\n", [File]). + +clean_inc(Inc) -> + Inc1 = remove_yecc_generated_file(Inc), + normalize_obj(Inc1). + +remove_yecc_generated_file(#inc{filter = Filter} = Inc) -> + Fun = fun (Key, #inc{map = Map} = I) -> + I#inc{map = map__remove(Key, Map)} + end, + lists:foldl(Fun, Inc, Filter). + +normalize_obj(TmpInc) -> + Fun = fun (Key, Val, Inc) -> + NewVal = [{{Line,F,A},Type} || {{F,A},{Line,Type}} <- Val], + map__insert({Key, NewVal}, Inc) + end, + TmpInc#inc{map = map__fold(Fun, map__new(), TmpInc#inc.map)}. + +get_records(File, Analysis) -> + map__lookup(File, Analysis#analysis.record). + +get_types(Module, Analysis, Records) -> + TypeInfoPlt = Analysis#analysis.trust_plt, + TypeInfo = + case dialyzer_plt:lookup_module(TypeInfoPlt, Module) of + none -> []; + {value, List} -> List + end, + CodeServer = Analysis#analysis.code_server, + TypeInfoList = [get_type(I, CodeServer, Records) || I <- TypeInfo], + map__from_list(TypeInfoList). + +get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, Records) -> + case dialyzer_codeserver:lookup_mfa_contract(MFA, CodeServer) of + error -> + {{F, A}, {Range, Arg}}; + {ok, {_FileLine, Contract}} -> + Sig = erl_types:t_fun(Arg, Range), + case dialyzer_contracts:check_contract(Contract, Sig) of + ok -> {{F, A}, {contract, Contract}}; + {error, {extra_range, _, _}} -> + {{F, A}, {contract, Contract}}; + {error, invalid_contract} -> + CString = dialyzer_contracts:contract_to_string(Contract), + SigString = dialyzer_utils:format_sig(Sig, Records), + Msg = io_lib:format("Error in contract of function ~w:~w/~w\n" + "\t The contract is: " ++ CString ++ "\n" ++ + "\t but the inferred signature is: ~s", + [M, F, A, SigString]), + fatal_error(Msg); + {error, ErrorStr} when is_list(ErrorStr) -> % ErrorStr is a string() + Msg = io_lib:format("Error in contract of function ~w:~w/~w: ~s", + [M, F, A, ErrorStr]), + fatal_error(Msg) + end + end. + +get_functions(File, Analysis) -> + case Analysis#analysis.mode of + ?SHOW -> + Funcs = map__lookup(File, Analysis#analysis.func), + Inc_Funcs = map__lookup(File, Analysis#analysis.inc_func), + remove_module_info(Funcs) ++ normalize_incFuncs(Inc_Funcs); + ?SHOW_EXPORTED -> + Ex_Funcs = map__lookup(File, Analysis#analysis.ex_func), + remove_module_info(Ex_Funcs); + ?ANNOTATE -> + Funcs = map__lookup(File, Analysis#analysis.func), + remove_module_info(Funcs); + ?ANNOTATE_INC_FILES -> + map__lookup(File, Analysis#analysis.inc_func) + end. + +normalize_incFuncs(Functions) -> + [FunInfo || {_FileName, FunInfo} <- Functions]. + +-spec remove_module_info([fun_info()]) -> [fun_info()]. + +remove_module_info(FunInfoList) -> + F = fun ({_,module_info,0}) -> false; + ({_,module_info,1}) -> false; + ({Line,F,A}) when is_integer(Line), is_atom(F), is_integer(A) -> true + end, + lists:filter(F, FunInfoList). + +write_typed_file(File, Info) -> + io:format(" Processing file: ~p\n", [File]), + Dir = filename:dirname(File), + RootName = filename:basename(filename:rootname(File)), + Ext = filename:extension(File), + TyperAnnDir = filename:join(Dir, ?TYPER_ANN_DIR), + TmpNewFilename = lists:concat([RootName, ".ann", Ext]), + NewFileName = filename:join(TyperAnnDir, TmpNewFilename), + case file:make_dir(TyperAnnDir) of + {error, Reason} -> + case Reason of + eexist -> %% TypEr dir exists; remove old typer files + ok = file:delete(NewFileName), + write_typed_file(File, Info, NewFileName); + enospc -> + io:format(" Not enough space in ~p\n", [Dir]); + eacces -> + io:format(" No write permission in ~p\n", [Dir]); + _ -> + io:format("Unhandled error ~s when writing ~p\n", [Reason, Dir]), + halt() + end; + ok -> %% Typer dir does NOT exist + write_typed_file(File, Info, NewFileName) + end. + +write_typed_file(File, Info, NewFileName) -> + {ok, Binary} = file:read_file(File), + Chars = binary_to_list(Binary), + write_typed_file(Chars, NewFileName, Info, 1, []), + io:format(" Saved as: ~p\n", [NewFileName]). + +write_typed_file(Chars, File, #info{functions = []}, _LNo, _Acc) -> + ok = file:write_file(File, list_to_binary(Chars), [append]); +write_typed_file([Ch|Chs] = Chars, File, Info, LineNo, Acc) -> + [{Line,F,A}|RestFuncs] = Info#info.functions, + case Line of + 1 -> %% This will happen only for inc files + ok = raw_write(F, A, Info, File, []), + NewInfo = Info#info{functions = RestFuncs}, + NewAcc = [], + write_typed_file(Chars, File, NewInfo, Line, NewAcc); + _ -> + case Ch of + 10 -> + NewLineNo = LineNo + 1, + {NewInfo, NewAcc} = + case NewLineNo of + Line -> + ok = raw_write(F, A, Info, File, [Ch|Acc]), + {Info#info{functions = RestFuncs}, []}; + _ -> + {Info, [Ch|Acc]} + end, + write_typed_file(Chs, File, NewInfo, NewLineNo, NewAcc); + _ -> + write_typed_file(Chs, File, Info, LineNo, [Ch|Acc]) + end + end. + +raw_write(F, A, Info, File, Content) -> + TypeInfo = get_type_string(F, A, Info, file), + ContentList = lists:reverse(Content) ++ TypeInfo ++ "\n", + ContentBin = list_to_binary(ContentList), + file:write_file(File, ContentBin, [append]). + +get_type_string(F, A, Info, Mode) -> + Type = get_type_info({F,A}, Info#info.types), + TypeStr = + case Type of + {contract, C} -> + dialyzer_contracts:contract_to_string(C); + {RetType, ArgType} -> + Sig = erl_types:t_fun(ArgType, RetType), + dialyzer_utils:format_sig(Sig, Info#info.records) + end, + case Info#info.edoc of + false -> + case {Mode, Type} of + {file, {contract, _}} -> ""; + _ -> + Prefix = lists:concat(["-spec ", F]), + lists:concat([Prefix, TypeStr, "."]) + end; + true -> + Prefix = lists:concat(["%% @spec ", F]), + lists:concat([Prefix, TypeStr, "."]) + end. + +show_type_info(File, Info) -> + io:format("\n%% File: ~p\n%% ", [File]), + OutputString = lists:concat(["~.", length(File)+8, "c~n"]), + io:fwrite(OutputString, [$-]), + Fun = fun ({_LineNo, F, A}) -> + TypeInfo = get_type_string(F, A, Info, show), + io:format("~s\n", [TypeInfo]) + end, + lists:foreach(Fun, Info#info.functions). + +get_type_info(Func, Types) -> + case map__lookup(Func, Types) of + none -> + %% Note: Typeinfo of any function should exist in + %% the result offered by dialyzer, otherwise there + %% *must* be something wrong with the analysis + Msg = io_lib:format("No type info for function: ~p\n", [Func]), + fatal_error(Msg); + {contract, _Fun} = C -> C; + {_RetType, _ArgType} = RA -> RA + end. + +%%-------------------------------------------------------------------- %% Processing of command-line options and arguments. %%-------------------------------------------------------------------- --spec process_cl_args() -> {#args{}, #typer_analysis{}}. +-spec process_cl_args() -> {args(), analysis()}. process_cl_args() -> ArgList = init:get_plain_arguments(), %% io:format("Args is ~p\n", [ArgList]), - {Args, Analysis} = analyze_args(ArgList, #args{}, #typer_analysis{}), + {Args, Analysis} = analyze_args(ArgList, #args{}, #analysis{}), %% if the mode has not been set, set it to the default mode (show) - {Args, case Analysis#typer_analysis.mode of - undefined -> Analysis#typer_analysis{mode = ?SHOW}; + {Args, case Analysis#analysis.mode of + undefined -> Analysis#analysis{mode = ?SHOW}; Mode when is_atom(Mode) -> Analysis end}. @@ -292,29 +634,29 @@ analyze_result({trusted, Val}, Args, Analysis) -> NewVal = Args#args.trusted ++ Val, {Args#args{trusted = NewVal}, Analysis}; analyze_result(edoc, Args, Analysis) -> - {Args, Analysis#typer_analysis{edoc = true}}; + {Args, Analysis#analysis{edoc = true}}; %% Get useful information for actual analysis analyze_result({mode, Mode}, Args, Analysis) -> - case Analysis#typer_analysis.mode of - undefined -> {Args, Analysis#typer_analysis{mode = Mode}}; + case Analysis#analysis.mode of + undefined -> {Args, Analysis#analysis{mode = Mode}}; OldMode -> mode_error(OldMode, Mode) end; analyze_result({def, Val}, Args, Analysis) -> - NewVal = Analysis#typer_analysis.macros ++ [Val], - {Args, Analysis#typer_analysis{macros = NewVal}}; + NewVal = Analysis#analysis.macros ++ [Val], + {Args, Analysis#analysis{macros = NewVal}}; analyze_result({inc, Val}, Args, Analysis) -> - NewVal = Analysis#typer_analysis.includes ++ [Val], - {Args, Analysis#typer_analysis{includes = NewVal}}; + NewVal = Analysis#analysis.includes ++ [Val], + {Args, Analysis#analysis{includes = NewVal}}; analyze_result({plt, Plt}, Args, Analysis) -> - {Args, Analysis#typer_analysis{plt = Plt}}; + {Args, Analysis#analysis{plt = Plt}}; analyze_result(no_spec, Args, Analysis) -> - {Args, Analysis#typer_analysis{no_spec = true}}. + {Args, Analysis#analysis{no_spec = true}}. %%-------------------------------------------------------------------- %% File processing. %%-------------------------------------------------------------------- --spec get_all_files(#args{}) -> files(). +-spec get_all_files(args()) -> files(). get_all_files(#args{files = Fs, files_r = Ds}) -> case filter_fd(Fs, Ds, fun test_erl_file_exclude_ann/1) of @@ -445,16 +787,16 @@ collect_info(Analysis) -> NewPlt = try get_dialyzer_plt(Analysis) of DialyzerPlt -> - dialyzer_plt:merge_plts([Analysis#typer_analysis.trust_plt, DialyzerPlt]) + dialyzer_plt:merge_plts([Analysis#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), + Analysis#analysis{trust_plt = NewPlt}, + Analysis#analysis.ana_files), %% Process Remote Types - TmpCServer = NewAnalysis#typer_analysis.code_server, + TmpCServer = NewAnalysis#analysis.code_server, NewCServer = try NewRecords = dialyzer_codeserver:get_temp_records(TmpCServer), @@ -474,12 +816,12 @@ collect_info(Analysis) -> throw:{error, ErrorMsg} -> fatal_error(ErrorMsg) end, - NewAnalysis#typer_analysis{code_server = NewCServer}. + NewAnalysis#analysis{code_server = NewCServer}. collect_one_file_info(File, Analysis) -> - Ds = [{d,Name,Val} || {Name,Val} <- Analysis#typer_analysis.macros], + Ds = [{d,Name,Val} || {Name,Val} <- Analysis#analysis.macros], %% Current directory should also be included in "Includes". - Includes = [filename:dirname(File)|Analysis#typer_analysis.includes], + Includes = [filename:dirname(File)|Analysis#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 @@ -508,14 +850,14 @@ collect_one_file_info(File, Analysis) -> 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, + CS1 = Analysis#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 + case Analysis#analysis.no_spec of true -> CS4; false -> dialyzer_codeserver:store_temp_contracts(Module, SpecInfo, CS4) end, @@ -523,30 +865,29 @@ analyze_core_tree(Core, Records, SpecInfo, ExpTypes, Analysis, File) -> 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, + TmpCG = Analysis#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), + Exported_FuncMap = map__insert({File, Ex_Funcs}, Analysis#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), + FuncMap = map__insert({File, Sorted_Functions}, Analysis#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}. + Analysis#analysis.inc_func), + Final_Files = Analysis#analysis.final_files ++ [{File, Module}], + RecordMap = map__insert({File, Records}, Analysis#analysis.record), + Analysis#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), @@ -573,7 +914,9 @@ analyze_one_function({Var, FunBody} = Function, Acc) -> incFuncAcc = IncFuncAcc, dialyzerObj = NewDialyzerObj}. -get_dialyzer_plt(#typer_analysis{plt = PltFile0}) -> +-spec get_dialyzer_plt(analysis()) -> dialyzer_plt:plt(). + +get_dialyzer_plt(#analysis{plt = PltFile0}) -> PltFile = case PltFile0 =:= none of true -> dialyzer_plt:get_default_plt(); @@ -581,7 +924,6 @@ get_dialyzer_plt(#typer_analysis{plt = PltFile0}) -> end, dialyzer_plt:from_file(PltFile). - %% Exported Types get_exported_types_from_core(Core) -> diff --git a/lib/typer/src/typer.hrl b/lib/typer/src/typer.hrl deleted file mode 100644 index d41bf2c83b..0000000000 --- a/lib/typer/src/typer.hrl +++ /dev/null @@ -1,53 +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% -%% - --define(SHOW, show). --define(SHOW_EXPORTED, show_exported). --define(ANNOTATE, annotate). --define(ANNOTATE_INC_FILES, annotate_inc_files). - --type mode() :: ?SHOW | ?SHOW_EXPORTED | ?ANNOTATE | ?ANNOTATE_INC_FILES. - --record(typer_analysis, - {mode :: mode(), - macros = [] :: [{atom(), term()}], % {macro_name, value} - includes = [] :: [file:filename()], - %% --- for dialyzer --- - code_server = dialyzer_codeserver:new():: dialyzer_codeserver:codeserver(), - callgraph = dialyzer_callgraph:new() :: dialyzer_callgraph:callgraph(), - ana_files = [] :: [file:filename()], % absolute filenames - plt = none :: 'none' | file:filename(), - no_spec = false :: boolean(), - %% --- for typer --- - t_files = [] :: [file:filename()], - %% For choosing between contracts or comments - contracts = true :: 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()}], - ex_func = typer:map__new() :: dict(), - record = typer:map__new() :: dict(), - func = typer:map__new() :: dict(), - inc_func = typer:map__new() :: dict(), - trust_plt = dialyzer_plt:new() :: dialyzer_plt:plt()}). - --record(args, {files = [] :: [file:filename()], - files_r = [] :: [file:filename()], - trusted = [] :: [file:filename()]}). diff --git a/lib/typer/src/typer_annotator.erl b/lib/typer/src/typer_annotator.erl deleted file mode 100644 index 205087407e..0000000000 --- a/lib/typer/src/typer_annotator.erl +++ /dev/null @@ -1,383 +0,0 @@ -%% -*- erlang-indent-level: 2 -*- -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-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% -%% -%%============================================================================ -%% File : typer_annotator.erl -%% Author : Bingwen He <[email protected]> -%% Description : -%% If file 'FILENAME' has been analyzed, then the output of -%% command "diff -B FILENAME.erl typer_ann/FILENAME.ann.erl" -%% should be exactly what TypEr has added, namely type info. -%%============================================================================ - --module(typer_annotator). - --export([annotate/1]). - -%%---------------------------------------------------------------------------- - --include("typer.hrl"). - - %%---------------------------------------------------------------------------- - --define(TYPER_ANN_DIR, "typer_ann"). - --type fun_info() :: {non_neg_integer(), atom(), arity()}. - --record(info, {records = typer:map__new() :: dict(), - functions = [] :: [fun_info()], - types :: dict(), - no_comment_specs = true :: boolean()}). --record(inc, {map = typer:map__new() :: dict(), - filter = [] :: [file:filename()]}). - -%%---------------------------------------------------------------------------- - --spec annotate(#typer_analysis{}) -> 'ok'. - -annotate(#typer_analysis{mode = Mode, final_files = Files} = Analysis) -> - case Mode of - ?SHOW -> show(Analysis); - ?SHOW_EXPORTED -> show(Analysis); - ?ANNOTATE -> - Fun = fun ({File, Module}) -> - Info = get_final_info(File, Module, Analysis), - write_typed_file(File, Info) - end, - lists:foreach(Fun, Files); - ?ANNOTATE_INC_FILES -> - IncInfo = write_and_collect_inc_info(Analysis), - write_inc_files(IncInfo) - end. - -write_and_collect_inc_info(Analysis) -> - Fun = fun ({File, Module}, Inc) -> - Info = get_final_info(File, Module, Analysis), - write_typed_file(File, Info), - IncFuns = get_functions(File, Analysis), - collect_imported_functions(IncFuns, Info#info.types, Inc) - end, - NewInc = lists:foldl(Fun, #inc{}, Analysis#typer_analysis.final_files), - clean_inc(NewInc). - -write_inc_files(Inc) -> - Fun = - fun (File) -> - Val = typer:map__lookup(File,Inc#inc.map), - %% Val is function with its type info - %% in form [{{Line,F,A},Type}] - Functions = [Key || {Key,_} <- Val], - Val1 = [{{F,A},Type} || {{_Line,F,A},Type} <- Val], - Info = #info{types = typer:map__from_list(Val1), - records = typer:map__new(), - %% Note we need to sort functions here! - functions = lists:keysort(1, Functions)}, - %% io:format("Types ~p\n", [Info#info.types]), - %% io:format("Functions ~p\n", [Info#info.functions]), - %% io:format("Records ~p\n", [Info#info.records]), - write_typed_file(File, Info) - end, - lists:foreach(Fun, dict:fetch_keys(Inc#inc.map)). - -show(Analysis) -> - Fun = fun ({File, Module}) -> - Info = get_final_info(File, Module, Analysis), - show_type_info(File, Info) - end, - lists:foreach(Fun, Analysis#typer_analysis.final_files). - -get_final_info(File, Module, Analysis) -> - Records = get_records(File, Analysis), - Types = get_types(Module, Analysis, Records), - Functions = get_functions(File, Analysis), - Bool = Analysis#typer_analysis.contracts, - #info{records = Records, functions = Functions, - types = Types, no_comment_specs = Bool}. - -collect_imported_functions(Functions, Types, Inc) -> - %% Coming from other sourses, including: - %% FIXME: How to deal with yecc-generated file???? - %% --.yrl (yecc-generated file)??? - %% -- yeccpre.hrl (yecc-generated file)??? - %% -- other cases - Fun = fun ({File, _} = Obj, I) -> - case is_yecc_gen(File, I) of - {true, NewI} -> NewI; - {false, NewI} -> - check_imported_functions(Obj, NewI, Types) - end - end, - lists:foldl(Fun, Inc, Functions). - --spec is_yecc_gen(file:filename(), #inc{}) -> {boolean(), #inc{}}. - -is_yecc_gen(File, #inc{filter = Fs} = Inc) -> - case lists:member(File, Fs) of - true -> {true, Inc}; - false -> - case filename:extension(File) of - ".yrl" -> - Rootname = filename:rootname(File, ".yrl"), - Obj = Rootname ++ ".erl", - case lists:member(Obj, Fs) of - true -> {true, Inc}; - false -> - NewInc = Inc#inc{filter = [Obj|Fs]}, - {true, NewInc} - end; - _ -> - case filename:basename(File) of - "yeccpre.hrl" -> {true, Inc}; - _ -> {false, Inc} - end - end - end. - -check_imported_functions({File, {Line, F, A}}, Inc, Types) -> - IncMap = Inc#inc.map, - FA = {F, A}, - Type = get_type_info(FA, Types), - case typer:map__lookup(File, IncMap) of - none -> %% File is not added. Add it - Obj = {File,[{FA, {Line, Type}}]}, - NewMap = typer:map__insert(Obj, IncMap), - Inc#inc{map = NewMap}; - Val -> %% File is already in. Check. - case lists:keyfind(FA, 1, Val) of - false -> - %% Function is not in; add it - Obj = {File, Val ++ [{FA, {Line, Type}}]}, - NewMap = typer:map__insert(Obj, IncMap), - Inc#inc{map = NewMap}; - Type -> - %% Function is in and with same type - Inc; - _ -> - %% Function is in but with diff type - inc_warning(FA, File), - Elem = lists:keydelete(FA, 1, Val), - NewMap = case Elem of - [] -> - typer:map__remove(File, IncMap); - _ -> - typer:map__insert({File, Elem}, IncMap) - end, - Inc#inc{map = NewMap} - end - end. - -inc_warning({F, A}, File) -> - io:format(" ***Warning: Skip function ~p/~p ", [F, A]), - io:format("in file ~p because of inconsistent type\n", [File]). - -clean_inc(Inc) -> - Inc1 = remove_yecc_generated_file(Inc), - normalize_obj(Inc1). - -remove_yecc_generated_file(#inc{filter = Filter} = Inc) -> - Fun = fun (Key, #inc{map = Map} = I) -> - I#inc{map = typer:map__remove(Key, Map)} - end, - lists:foldl(Fun, Inc, Filter). - -normalize_obj(TmpInc) -> - Fun = fun (Key, Val, Inc) -> - NewVal = [{{Line,F,A},Type} || {{F,A},{Line,Type}} <- Val], - typer:map__insert({Key,NewVal}, Inc) - end, - NewMap = typer:map__fold(Fun, typer:map__new(), TmpInc#inc.map), - TmpInc#inc{map = NewMap}. - -get_records(File, Analysis) -> - typer:map__lookup(File, Analysis#typer_analysis.record). - -get_types(Module, Analysis, Records) -> - TypeInfoPlt = Analysis#typer_analysis.trust_plt, - TypeInfo = - case dialyzer_plt:lookup_module(TypeInfoPlt, Module) of - none -> []; - {value, List} -> List - end, - CodeServer = Analysis#typer_analysis.code_server, - TypeInfoList = [get_type(I, CodeServer, Records) || I <- TypeInfo], - typer:map__from_list(TypeInfoList). - -get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, Records) -> - case dialyzer_codeserver:lookup_mfa_contract(MFA, CodeServer) of - error -> - {{F, A}, {Range, Arg}}; - {ok, {_FileLine, Contract}} -> - Sig = erl_types:t_fun(Arg, Range), - case dialyzer_contracts:check_contract(Contract, Sig) of - ok -> {{F, A}, {contract, Contract}}; - {error, {extra_range, _, _}} -> - {{F, A}, {contract, Contract}}; - {error, invalid_contract} -> - CString = dialyzer_contracts:contract_to_string(Contract), - SigString = dialyzer_utils:format_sig(Sig, Records), - Msg = io_lib:format("Error in contract of function ~w:~w/~w\n" - "\t The contract is: " ++ CString ++ "\n" ++ - "\t but the inferred signature is: ~s", - [M, F, A, SigString]), - typer:fatal_error(Msg); - {error, ErrorStr} when is_list(ErrorStr) -> % ErrorStr is a string() - Msg = io_lib:format("Error in contract of function ~w:~w/~w: ~s", - [M, F, A, ErrorStr]), - typer:fatal_error(Msg) - end - end. - -get_functions(File, Analysis) -> - case Analysis#typer_analysis.mode of - ?SHOW -> - Funcs = typer:map__lookup(File, Analysis#typer_analysis.func), - Inc_Funcs = typer:map__lookup(File, Analysis#typer_analysis.inc_func), - remove_module_info(Funcs) ++ normalize_incFuncs(Inc_Funcs); - ?SHOW_EXPORTED -> - Ex_Funcs = typer:map__lookup(File, Analysis#typer_analysis.ex_func), - remove_module_info(Ex_Funcs); - ?ANNOTATE -> - Funcs = typer:map__lookup(File, Analysis#typer_analysis.func), - remove_module_info(Funcs); - ?ANNOTATE_INC_FILES -> - typer:map__lookup(File, Analysis#typer_analysis.inc_func) - end. - -normalize_incFuncs(Functions) -> - [FunInfo || {_FileName, FunInfo} <- Functions]. - --spec remove_module_info([fun_info()]) -> [fun_info()]. - -remove_module_info(FunInfoList) -> - F = fun ({_,module_info,0}) -> false; - ({_,module_info,1}) -> false; - ({Line,F,A}) when is_integer(Line), is_atom(F), is_integer(A) -> true - end, - lists:filter(F, FunInfoList). - -write_typed_file(File, Info) -> - io:format(" Processing file: ~p\n", [File]), - Dir = filename:dirname(File), - RootName = filename:basename(filename:rootname(File)), - Ext = filename:extension(File), - TyperAnnDir = filename:join(Dir, ?TYPER_ANN_DIR), - TmpNewFilename = lists:concat([RootName, ".ann", Ext]), - NewFileName = filename:join(TyperAnnDir, TmpNewFilename), - case file:make_dir(TyperAnnDir) of - {error, Reason} -> - case Reason of - eexist -> %% TypEr dir exists; remove old typer files - ok = file:delete(NewFileName), - write_typed_file(File, Info, NewFileName); - enospc -> - io:format(" Not enough space in ~p\n", [Dir]); - eacces -> - io:format(" No write permission in ~p\n", [Dir]); - _ -> - io:format("Unhandled error ~s when writing ~p\n", [Reason, Dir]), - halt() - end; - ok -> %% Typer dir does NOT exist - write_typed_file(File, Info, NewFileName) - end. - -write_typed_file(File, Info, NewFileName) -> - {ok, Binary} = file:read_file(File), - Chars = binary_to_list(Binary), - write_typed_file(Chars, NewFileName, Info, 1, []), - io:format(" Saved as: ~p\n", [NewFileName]). - -write_typed_file(Chars, File, #info{functions = []}, _LNo, _Acc) -> - ok = file:write_file(File, list_to_binary(Chars), [append]); -write_typed_file([Ch|Chs] = Chars, File, Info, LineNo, Acc) -> - [{Line,F,A}|RestFuncs] = Info#info.functions, - case Line of - 1 -> %% This will happen only for inc files - ok = raw_write(F, A, Info, File, []), - NewInfo = Info#info{functions = RestFuncs}, - NewAcc = [], - write_typed_file(Chars, File, NewInfo, Line, NewAcc); - _ -> - case Ch of - 10 -> - NewLineNo = LineNo + 1, - {NewInfo, NewAcc} = - case NewLineNo of - Line -> - ok = raw_write(F, A, Info, File, [Ch|Acc]), - {Info#info{functions = RestFuncs}, []}; - _ -> - {Info, [Ch|Acc]} - end, - write_typed_file(Chs, File, NewInfo, NewLineNo, NewAcc); - _ -> - write_typed_file(Chs, File, Info, LineNo, [Ch|Acc]) - end - end. - -raw_write(F, A, Info, File, Content) -> - TypeInfo = get_type_string(F, A, Info, file), - ContentList = lists:reverse(Content) ++ TypeInfo ++ "\n", - ContentBin = list_to_binary(ContentList), - file:write_file(File, ContentBin, [append]). - -get_type_string(F, A, Info, Mode) -> - Type = get_type_info({F,A}, Info#info.types), - TypeStr = - case Type of - {contract, C} -> - dialyzer_contracts:contract_to_string(C); - {RetType, ArgType} -> - Sig = erl_types:t_fun(ArgType, RetType), - dialyzer_utils:format_sig(Sig, Info#info.records) - end, - case Info#info.no_comment_specs of - true -> - case {Mode, Type} of - {file, {contract, _}} -> ""; - _ -> - Prefix = lists:concat(["-spec ", F]), - lists:concat([Prefix, TypeStr, "."]) - end; - false -> - Prefix = lists:concat(["%% @spec ", F]), - lists:concat([Prefix, TypeStr, "."]) - end. - -show_type_info(File, Info) -> - io:format("\n%% File: ~p\n%% ", [File]), - OutputString = lists:concat(["~.", length(File)+8, "c~n"]), - io:fwrite(OutputString, [$-]), - Fun = fun ({_LineNo, F, A}) -> - TypeInfo = get_type_string(F, A, Info, show), - io:format("~s\n", [TypeInfo]) - end, - lists:foreach(Fun, Info#info.functions). - -get_type_info(Func, Types) -> - case typer:map__lookup(Func, Types) of - none -> - %% Note: Typeinfo of any function should exist in - %% the result offered by dialyzer, otherwise there - %% *must* be something wrong with the analysis - io:format("No type info for function: ~p\n", [Func]), - halt(); - {contract, _Fun} = C -> C; - {_RetType, _ArgType} = RA -> RA - end. |