From 28e16ffea3d799690535ee6361db0b4e1a2dead0 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sat, 5 Feb 2011 20:11:55 +0200 Subject: First cut of tidier's cleanup Added ability to receive the set of exported types and report unknown ones. While doing the above, cleaned up types, specs, and record field names. --- lib/typer/src/typer.erl | 39 ++++++++++++++++----- lib/typer/src/typer.hrl | 27 +++++++------- lib/typer/src/typer_info.erl | 48 ++++++++++++++++++------- lib/typer/src/typer_options.erl | 43 +++++++++++------------ lib/typer/src/typer_preprocess.erl | 72 +++++++++++++++++--------------------- 5 files changed, 134 insertions(+), 95 deletions(-) (limited to 'lib') diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index e19614f911..206ce8e797 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -1,20 +1,20 @@ %% -*- erlang-indent-level: 2 -*- %%----------------------------------------------------------------------- %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. -%% +%% +%% 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% %% @@ -26,11 +26,12 @@ -module(typer). -%% Avoid warning for local function error/1 clashing with autoimported BIF. --compile({no_auto_import,[error/1]}). -export([start/0]). -export([error/1, compile_error/1]). % for error reporting +%% Avoid warning for local function error/1 clashing with autoimported BIF. +-compile({no_auto_import, [error/1]}). + -include("typer.hrl"). %%-------------------------------------------------------------------- @@ -143,7 +144,13 @@ remove_external(CallGraph, PLT) -> case get_external(Ext, PLT) of [] -> ok; Externals -> - msg(io_lib:format(" Unknown functions: ~p\n", [lists:usort(Externals)])) + msg(io_lib:format(" Unknown functions: ~p\n", [lists:usort(Externals)])), + ExtTypes = rcv_ext_types(), + case ExtTypes of + [] -> ok; + _ -> + msg(io_lib:format(" Unknown types: ~p\n", [ExtTypes])) + end end, StrippedCG. @@ -197,3 +204,19 @@ msg(Msg) -> end. %%-------------------------------------------------------------------- +%% Handle messages. +%%-------------------------------------------------------------------- + +rcv_ext_types() -> + Self = self(), + Self ! {Self, done}, + rcv_ext_types(Self, []). + +rcv_ext_types(Self, ExtTypes) -> + receive + {Self, ext_types, ExtType} -> + rcv_ext_types(Self, [ExtType|ExtTypes]); + {Self, done} -> lists:usort(ExtTypes) + end. + +%%-------------------------------------------------------------------- diff --git a/lib/typer/src/typer.hrl b/lib/typer/src/typer.hrl index c331dd82db..2e4ec4f894 100644 --- a/lib/typer/src/typer.hrl +++ b/lib/typer/src/typer.hrl @@ -1,20 +1,20 @@ %% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. -%% +%% +%% 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% %% @@ -28,18 +28,18 @@ -record(typer_analysis, {mode :: mode(), macros = [] :: [{atom(), _}], % {macro_name, value} - includes = [] :: [string()], + includes = [] :: [file:filename()], %% Esp for Dialyzer %% ---------------------- code_server = dialyzer_codeserver:new():: dialyzer_codeserver:codeserver(), callgraph = dialyzer_callgraph:new() :: dialyzer_callgraph:callgraph(), - ana_files = [] :: [string()], % absolute filenames - plt = none :: 'none' | string(), + ana_files = [] :: [file:filename()], % absolute filenames + plt = none :: 'none' | file:filename(), %% Esp for TypEr %% ---------------------- - t_files = [] :: [string()], + t_files = [] :: [file:filename()], %% For choosing between contracts or comments contracts = true :: boolean(), @@ -47,7 +47,7 @@ %% Any file in 'final_files' is compilable. %% And we need to keep it as {FileName,ModuleName} %% in case filename does NOT match with moduleName - final_files = [] :: [{string(), atom()}], + final_files = [] :: [{file:filename(), module()}], ex_func = typer_map:new() :: dict(), record = typer_map:new() :: dict(), @@ -58,7 +58,6 @@ inc_func = typer_map:new() :: dict(), trust_plt = dialyzer_plt:new() :: dialyzer_plt:plt()}). --record(args, - {analyze = [] :: [string()], - analyzed_dir_r = [] :: [string()], - trust = [] :: [string()]}). +-record(args, {files = [] :: [file:filename()], + files_r = [] :: [file:filename()], + trusted = [] :: [file:filename()]}). diff --git a/lib/typer/src/typer_info.erl b/lib/typer/src/typer_info.erl index ea25fa6f68..615d2b4796 100644 --- a/lib/typer/src/typer_info.erl +++ b/lib/typer/src/typer_info.erl @@ -1,20 +1,20 @@ %% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. -%% +%% +%% 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% %% @@ -52,12 +52,18 @@ collect(Analysis) -> 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_utils:process_record_remote_types(TmpCServer1), - dialyzer_contracts:process_contract_remote_types(TmpCServer2) + 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:error(ErrorMsg) @@ -80,18 +86,20 @@ collect_one_file_info(File, Analysis) -> {ok, Core} -> case dialyzer_utils:get_record_and_type_info(AbstractCode) of {error, Reason} -> typer:compile_error([Reason]); - {ok, Records} -> + {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) + {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, Analysis, File) -> +analyze_core_tree(Core, Records, SpecInfo, ExpTypes, Analysis, File) -> Module = list_to_atom(filename:basename(File, ".erl")), TmpTree = cerl:from_records(Core), CS1 = Analysis#typer_analysis.code_server, @@ -101,6 +109,9 @@ analyze_core_tree(Core, Records, SpecInfo, Analysis, File) -> 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), + 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), @@ -122,7 +133,7 @@ analyze_core_tree(Core, Records, SpecInfo, Analysis, File) -> RecordMap = typer_map:insert({File, Records}, Analysis#typer_analysis.record), Analysis#typer_analysis{final_files=Final_Files, callgraph=CG, - code_server=CS5, + code_server=CS6, ex_func=Exported_FuncMap, inc_func=IncFuncMap, record=RecordMap, @@ -160,3 +171,16 @@ get_dialyzer_plt(#typer_analysis{plt = PltFile0}) -> 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]). diff --git a/lib/typer/src/typer_options.erl b/lib/typer/src/typer_options.erl index 1e53b1b305..f149c937c7 100644 --- a/lib/typer/src/typer_options.erl +++ b/lib/typer/src/typer_options.erl @@ -1,20 +1,20 @@ %% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. -%% +%% +%% 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% %% %%=========================================================================== @@ -39,7 +39,7 @@ process() -> ArgList = init:get_plain_arguments(), - %% io:format("Args is ~p\n",[Args]), + %% io:format("Args is ~p\n", [ArgList]), {Args, Analysis} = analyze_args(ArgList, #args{}, #typer_analysis{}), %% if the mode has not been set, set it to the default mode (show) {Args, case Analysis#typer_analysis.mode of @@ -73,11 +73,10 @@ cl(["-D"++Def|Opts]) -> case Def of "" -> typer:error("no variable name specified after -D"); _ -> - L = re:split(Def, "=", [{return, list}]), - DefPair = process_def_list(L), + DefPair = process_def_list(re:split(Def, "=", [{return, list}])), {{def, DefPair}, Opts} end; -cl(["-I",Dir|Opts]) -> {{inc,Dir}, Opts}; +cl(["-I",Dir|Opts]) -> {{inc, Dir}, Opts}; cl(["-I"++Dir|Opts]) -> case Dir of "" -> typer:error("no include directory specified after -I"); @@ -87,15 +86,15 @@ cl(["-T"|Opts]) -> {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts), case Files of [] -> typer:error("no file or directory specified after -T"); - [_|_] -> {{trust, Files}, RestOpts} + [_|_] -> {{trusted, Files}, RestOpts} end; cl(["-r"|Opts]) -> {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts), - {{a_dir_r, Files}, RestOpts}; + {{files_r, Files}, RestOpts}; cl(["-"++H|_]) -> typer:error("unknown option -"++H); cl(Opts) -> - {Args, RestOpts} = dialyzer_cl_parse:collect_args(Opts), - {{analyze, Args}, RestOpts}. + {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts), + {{files, Files}, RestOpts}. process_def_list(L) -> case L of @@ -108,15 +107,15 @@ process_def_list(L) -> end. %% Get information about files that the user trusts and wants to analyze -analyze_result({analyze, Val}, Args, Analysis) -> - NewVal = Args#args.analyze ++ Val, - {Args#args{analyze = NewVal}, Analysis}; -analyze_result({a_dir_r, Val}, Args, Analysis) -> - NewVal = Args#args.analyzed_dir_r ++ Val, - {Args#args{analyzed_dir_r = NewVal}, Analysis}; -analyze_result({trust, Val}, Args, Analysis) -> - NewVal = Args#args.trust ++ Val, - {Args#args{trust = NewVal}, Analysis}; +analyze_result({files, Val}, Args, Analysis) -> + NewVal = Args#args.files ++ Val, + {Args#args{files = NewVal}, Analysis}; +analyze_result({files_r, Val}, Args, Analysis) -> + NewVal = Args#args.files_r ++ Val, + {Args#args{files_r = NewVal}, 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}}; %% Get useful information for actual analysis diff --git a/lib/typer/src/typer_preprocess.erl b/lib/typer/src/typer_preprocess.erl index 7cb0b9932b..27660e849e 100644 --- a/lib/typer/src/typer_preprocess.erl +++ b/lib/typer/src/typer_preprocess.erl @@ -1,20 +1,20 @@ %% -*- erlang-indent-level: 2 -*- %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. -%% +%% +%% 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% %% @@ -26,19 +26,17 @@ %%---------------------------------------------------------------------------- --spec get_all_files(#args{}, 'analysis' | 'trust') -> [string()]. +-spec get_all_files(#args{}, 'analysis' | 'trust') -> [file:filename()]. -get_all_files(Args, analysis) -> - case internal_get_all_files(Args#args.analyze, - Args#args.analyzed_dir_r, - fun test_erl_file_exclude_ann/1) of +get_all_files(#args{files=Fs,files_r=Ds}, analysis) -> + case files_and_dirs(Fs, Ds, fun test_erl_file_exclude_ann/1) of [] -> typer:error("no file(s) to analyze"); AllFiles -> AllFiles end; -get_all_files(Args, trust) -> - internal_get_all_files(Args#args.trust, [], fun test_erl_file/1). +get_all_files(#args{trusted=Fs}, trust) -> + files_and_dirs(Fs, [], fun test_erl_file/1). --spec test_erl_file_exclude_ann(string()) -> boolean(). +-spec test_erl_file_exclude_ann(file:filename()) -> boolean(). test_erl_file_exclude_ann(File) -> case filename:extension(File) of @@ -50,57 +48,53 @@ test_erl_file_exclude_ann(File) -> _ -> false end. --spec test_erl_file(string()) -> boolean(). +-spec test_erl_file(file:filename()) -> boolean(). test_erl_file(File) -> filename:extension(File) =:= ".erl". --spec internal_get_all_files([string()], [string()], - fun((string()) -> boolean())) -> [string()]. +-spec files_and_dirs([file:filename()], [file:filename()], + fun((file:filename()) -> boolean())) -> [file:filename()]. -internal_get_all_files(File_Dir, Dir_R, Fun) -> +files_and_dirs(File_Dir, Dir_R, Fun) -> All_File_1 = process_file_and_dir(File_Dir, Fun), - All_File_2 = process_dir_recursively(Dir_R, Fun), + All_File_2 = process_dir_rec(Dir_R, Fun), remove_dup(All_File_1 ++ All_File_2). --spec process_file_and_dir([string()], - fun((string()) -> boolean())) -> [string()]. +-spec process_file_and_dir([file:filename()], + fun((file:filename()) -> boolean())) -> [file:filename()]. process_file_and_dir(File_Dir, TestFun) -> Fun = fun (Elem, Acc) -> case filelib:is_regular(Elem) of true -> process_file(Elem, TestFun, Acc); - false -> check_dir(Elem, non_recursive, Acc, TestFun) + false -> check_dir(Elem, false, Acc, TestFun) end end, lists:foldl(Fun, [], File_Dir). --spec process_dir_recursively([string()], - fun((string()) -> boolean())) -> [string()]. +-spec process_dir_rec([file:filename()], + fun((file:filename()) -> boolean())) -> [file:filename()]. -process_dir_recursively(Dirs, TestFun) -> - Fun = fun (Dir, Acc) -> - check_dir(Dir, recursive, Acc, TestFun) - end, +process_dir_rec(Dirs, TestFun) -> + Fun = fun (Dir, Acc) -> check_dir(Dir, true, Acc, TestFun) end, lists:foldl(Fun, [], Dirs). --spec check_dir(string(), - 'non_recursive' | 'recursive', - [string()], - fun((string()) -> boolean())) -> [string()]. +-spec check_dir(file:filename(), boolean(), [file:filename()], + fun((file:filename()) -> boolean())) -> [file:filename()]. -check_dir(Dir, Mode, Acc, Fun) -> +check_dir(Dir, Recursive, Acc, Fun) -> case file:list_dir(Dir) of {ok, Files} -> {TmpDirs, TmpFiles} = split_dirs_and_files(Files, Dir), - case Mode of - non_recursive -> + case Recursive of + false -> FinalFiles = process_file_and_dir(TmpFiles, Fun), Acc ++ FinalFiles; - recursive -> + true -> TmpAcc1 = process_file_and_dir(TmpFiles, Fun), - TmpAcc2 = process_dir_recursively(TmpDirs, Fun), + TmpAcc2 = process_dir_rec(TmpDirs, Fun), Acc ++ TmpAcc1 ++ TmpAcc2 end; {error, eacces} -> @@ -112,7 +106,7 @@ check_dir(Dir, Mode, Acc, Fun) -> end. %% Same order as the input list --spec process_file(string(), fun((string()) -> boolean()), string()) -> [string()]. +-spec process_file(file:filename(), fun((file:filename()) -> boolean()), [file:filename()]) -> [file:filename()]. process_file(File, TestFun, Acc) -> case TestFun(File) of @@ -121,7 +115,7 @@ process_file(File, TestFun, Acc) -> end. %% Same order as the input list --spec split_dirs_and_files([string()], string()) -> {[string()], [string()]}. +-spec split_dirs_and_files([file:filename()], file:filename()) -> {[file:filename()], [file:filename()]}. split_dirs_and_files(Elems, Dir) -> Test_Fun = @@ -141,7 +135,7 @@ split_dirs_and_files(Elems, Dir) -> %% Removes duplicate filenames but it keeps the order of the input list --spec remove_dup([string()]) -> [string()]. +-spec remove_dup([file:filename()]) -> [file:filename()]. remove_dup(Files) -> Test_Dup = fun (File, Acc) -> -- cgit v1.2.3 From 1937ce923530758629d32dd763300b7e2a2fd707 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sat, 5 Feb 2011 21:56:24 +0200 Subject: Replace some string() with file:filename() --- lib/typer/src/typer_info.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/typer/src/typer_info.erl b/lib/typer/src/typer_info.erl index 615d2b4796..7fc1ba8ad0 100644 --- a/lib/typer/src/typer_info.erl +++ b/lib/typer/src/typer_info.erl @@ -23,9 +23,9 @@ -export([collect/1]). -type func_info() :: {non_neg_integer(), atom(), arity()}. --type inc_file_info() :: {string(), func_info()}. +-type inc_file_info() :: {file:filename(), func_info()}. --record(tmpAcc, {file :: string(), +-record(tmpAcc, {file :: file:filename(), module :: atom(), funcAcc=[] :: [func_info()], incFuncAcc=[] :: [inc_file_info()], -- cgit v1.2.3 From 4b5447031fc8478a8c725b97ee1fb8d55365619a Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Sat, 5 Feb 2011 13:25:45 +0200 Subject: Add '--no_spec' option to Typer When run with '--no_spec', Typer will hide from Dialyzer any specs present in the files under analysis. --- lib/typer/src/typer.hrl | 1 + lib/typer/src/typer_info.erl | 6 +++++- lib/typer/src/typer_options.erl | 6 +++++- 3 files changed, 11 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/typer/src/typer.hrl b/lib/typer/src/typer.hrl index 2e4ec4f894..eb3ba5f9c1 100644 --- a/lib/typer/src/typer.hrl +++ b/lib/typer/src/typer.hrl @@ -36,6 +36,7 @@ callgraph = dialyzer_callgraph:new() :: dialyzer_callgraph:callgraph(), ana_files = [] :: [file:filename()], % absolute filenames plt = none :: 'none' | file:filename(), + no_spec = false :: boolean(), %% Esp for TypEr %% ---------------------- diff --git a/lib/typer/src/typer_info.erl b/lib/typer/src/typer_info.erl index 7fc1ba8ad0..df0b4448f3 100644 --- a/lib/typer/src/typer_info.erl +++ b/lib/typer/src/typer_info.erl @@ -108,7 +108,11 @@ analyze_core_tree(Core, Records, SpecInfo, ExpTypes, Analysis, File) -> 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), + 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), diff --git a/lib/typer/src/typer_options.erl b/lib/typer/src/typer_options.erl index f149c937c7..c0f4260f19 100644 --- a/lib/typer/src/typer_options.erl +++ b/lib/typer/src/typer_options.erl @@ -60,6 +60,7 @@ analyze_args(ArgList, Args, Analysis) -> cl(["-h"|_]) -> help_message(); cl(["--help"|_]) -> help_message(); +cl(["--no_spec"|Opts]) -> {no_spec, Opts}; cl(["-v"|_]) -> version_message(); cl(["--version"|_]) -> version_message(); cl(["--comments"|Opts]) -> {comments, Opts}; @@ -131,7 +132,10 @@ analyze_result({inc, Val}, Args, Analysis) -> NewVal = Analysis#typer_analysis.includes ++ [Val], {Args, Analysis#typer_analysis{includes = NewVal}}; analyze_result({plt, Plt}, Args, Analysis) -> - {Args, Analysis#typer_analysis{plt = Plt}}. + {Args, Analysis#typer_analysis{plt = Plt}}; +analyze_result(no_spec, Args, Analysis) -> + {Args, Analysis#typer_analysis{no_spec = true}}. + %%-------------------------------------------------------------------- -- cgit v1.2.3 From d6fa4fd84f7c159ebd19539feff94673bb35650b Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 6 Feb 2011 11:57:34 +0200 Subject: Up version to reflect major rewrite --- lib/typer/vsn.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/typer/vsn.mk b/lib/typer/vsn.mk index 7f4aabb335..83c37e307b 100644 --- a/lib/typer/vsn.mk +++ b/lib/typer/vsn.mk @@ -1 +1 @@ -TYPER_VSN = 0.1.7.5 +TYPER_VSN = 0.2 -- cgit v1.2.3 From 7aabed32dd2eb7c89b061c53639a082c09bf693a Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 6 Feb 2011 12:25:05 +0200 Subject: Clean up of comments --- lib/typer/src/typer.hrl | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/typer/src/typer.hrl b/lib/typer/src/typer.hrl index eb3ba5f9c1..f08668a2ac 100644 --- a/lib/typer/src/typer.hrl +++ b/lib/typer/src/typer.hrl @@ -27,34 +27,23 @@ -record(typer_analysis, {mode :: mode(), - macros = [] :: [{atom(), _}], % {macro_name, value} + macros = [] :: [{atom(), term()}], % {macro_name, value} includes = [] :: [file:filename()], - - %% Esp for Dialyzer - %% ---------------------- + %% --- 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(), - - %% Esp for TypEr - %% ---------------------- + %% --- for typer --- t_files = [] :: [file:filename()], - %% For choosing between contracts or comments contracts = true :: boolean(), - - %% Any file in 'final_files' is compilable. - %% And we need to keep it as {FileName,ModuleName} - %% in case filename does NOT match with moduleName + %% 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(), - - %% Functions: the line number of the function - %% should be kept as well func = typer_map:new() :: dict(), inc_func = typer_map:new() :: dict(), trust_plt = dialyzer_plt:new() :: dialyzer_plt:plt()}). -- cgit v1.2.3 From 8522aaffd9ff146492808ae2a5be9ef58e4e8b90 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 6 Feb 2011 12:26:21 +0200 Subject: Moved option to its proper position --- lib/typer/src/typer_options.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/typer/src/typer_options.erl b/lib/typer/src/typer_options.erl index c0f4260f19..9545c7334b 100644 --- a/lib/typer/src/typer_options.erl +++ b/lib/typer/src/typer_options.erl @@ -60,7 +60,6 @@ analyze_args(ArgList, Args, Analysis) -> cl(["-h"|_]) -> help_message(); cl(["--help"|_]) -> help_message(); -cl(["--no_spec"|Opts]) -> {no_spec, Opts}; cl(["-v"|_]) -> version_message(); cl(["--version"|_]) -> version_message(); cl(["--comments"|Opts]) -> {comments, Opts}; @@ -69,6 +68,7 @@ cl(["--show_exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts}; cl(["--show-exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts}; cl(["--annotate"|Opts]) -> {{mode, ?ANNOTATE}, Opts}; cl(["--annotate-inc-files"|Opts]) -> {{mode, ?ANNOTATE_INC_FILES}, Opts}; +cl(["--no_spec"|Opts]) -> {no_spec, Opts}; cl(["--plt",Plt|Opts]) -> {{plt, Plt}, Opts}; cl(["-D"++Def|Opts]) -> case Def of -- cgit v1.2.3 From ee9ff644f20818c660cc66d5f155283b91214574 Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Sun, 6 Feb 2011 12:28:12 +0200 Subject: Fix Typer's hanging when module name doesn't match filename --- lib/typer/src/typer_info.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/typer/src/typer_info.erl b/lib/typer/src/typer_info.erl index df0b4448f3..1387245064 100644 --- a/lib/typer/src/typer_info.erl +++ b/lib/typer/src/typer_info.erl @@ -87,7 +87,7 @@ collect_one_file_info(File, Analysis) -> 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")), + 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} -> @@ -100,7 +100,7 @@ collect_one_file_info(File, Analysis) -> end. analyze_core_tree(Core, Records, SpecInfo, ExpTypes, Analysis, File) -> - Module = list_to_atom(filename:basename(File, ".erl")), + 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), -- cgit v1.2.3 From e906d3c423425139081d5c1e3683815d6ddcdceb Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 6 Feb 2011 15:02:23 +0200 Subject: Various cleanups --- lib/typer/src/typer_annotator.erl | 157 +++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 79 deletions(-) (limited to 'lib') diff --git a/lib/typer/src/typer_annotator.erl b/lib/typer/src/typer_annotator.erl index 68a8f03a5c..8904867d3e 100644 --- a/lib/typer/src/typer_annotator.erl +++ b/lib/typer/src/typer_annotator.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% 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 @@ -34,46 +34,46 @@ -include("typer.hrl"). -%%---------------------------------------------------------------------------- + %%---------------------------------------------------------------------------- -define(TYPER_ANN_DIR, "typer_ann"). --type func_info() :: {non_neg_integer(), atom(), arity()}. +-type fun_info() :: {non_neg_integer(), atom(), arity()}. --record(info, {recMap = typer_map:new() :: dict(), - funcs = [] :: [func_info()], - typeMap :: dict(), - contracts :: boolean()}). +-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 = [] :: [string()]}). + filter = [] :: [file:filename()]}). %%---------------------------------------------------------------------------- -spec annotate(#typer_analysis{}) -> 'ok'. -annotate(Analysis) -> - case Analysis#typer_analysis.mode of +annotate(#typer_analysis{mode = Mode, final_files = Files} = Analysis) -> + case Mode of ?SHOW -> show(Analysis); ?SHOW_EXPORTED -> show(Analysis); ?ANNOTATE -> - Fun = fun({File, Module}) -> + Fun = fun ({File, Module}) -> Info = get_final_info(File, Module, Analysis), write_typed_file(File, Info) end, - lists:foreach(Fun, Analysis#typer_analysis.final_files); + 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) -> + Fun = fun ({File, Module}, Inc) -> Info = get_final_info(File, Module, Analysis), write_typed_file(File, Info), IncFuns = get_functions(File, Analysis), - collect_imported_funcs(IncFuns, Info#info.typeMap, Inc) + collect_imported_functions(IncFuns, Info#info.types, Inc) end, - NewInc = lists:foldl(Fun,#inc{}, Analysis#typer_analysis.final_files), + NewInc = lists:foldl(Fun, #inc{}, Analysis#typer_analysis.final_files), clean_inc(NewInc). write_inc_files(Inc) -> @@ -84,75 +84,75 @@ write_inc_files(Inc) -> %% in form [{{Line,F,A},Type}] Functions = [Key || {Key,_} <- Val], Val1 = [{{F,A},Type} || {{_Line,F,A},Type} <- Val], - Info = #info{typeMap = typer_map:from_list(Val1), - recMap = typer_map:new(), + Info = #info{types = typer_map:from_list(Val1), + records = typer_map:new(), %% Note we need to sort functions here! - funcs = lists:keysort(1, Functions)}, - %% io:format("TypeMap ~p\n", [Info#info.typeMap]), - %% io:format("Funcs ~p\n", [Info#info.funcs]), - %% io:format("RecMap ~p\n", [Info#info.recMap]), + 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}) -> + Fun = fun ({File, Module}) -> Info = get_final_info(File, Module, Analysis), - show_type_info_only(File, Info) + show_type_info(File, Info) end, lists:foreach(Fun, Analysis#typer_analysis.final_files). get_final_info(File, Module, Analysis) -> - RecMap = get_recMap(File, Analysis), - TypeMap = get_typeMap(Module, Analysis,RecMap), + Records = get_records(File, Analysis), + Types = get_types(Module, Analysis, Records), Functions = get_functions(File, Analysis), - Contracts = Analysis#typer_analysis.contracts, - #info{recMap=RecMap, funcs=Functions, typeMap=TypeMap, contracts=Contracts}. + Bool = Analysis#typer_analysis.contracts, + #info{records = Records, functions = Functions, + types = Types, no_comment_specs = Bool}. -collect_imported_funcs(Funcs, TypeMap, TmpInc) -> +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, Inc) -> - case is_yecc_file(File, Inc) of - {yecc_generated, NewInc} -> NewInc; - {not_yecc, NewInc} -> - check_imported_funcs(Obj, NewInc, TypeMap) + 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, TmpInc, Funcs). + lists:foldl(Fun, Inc, Functions). --spec is_yecc_file(string(), #inc{}) -> {'not_yecc', #inc{}} - | {'yecc_generated', #inc{}}. -is_yecc_file(File, Inc) -> - case lists:member(File, Inc#inc.filter) of - true -> {yecc_generated, Inc}; +-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, Inc#inc.filter) of - true -> {yecc_generated, Inc}; + case lists:member(Obj, Fs) of + true -> {true, Inc}; false -> - NewFilter = [Obj|Inc#inc.filter], - NewInc = Inc#inc{filter = NewFilter}, - {yecc_generated, NewInc} + NewInc = Inc#inc{filter = [Obj|Fs]}, + {true, NewInc} end; _ -> case filename:basename(File) of - "yeccpre.hrl" -> {yecc_generated, Inc}; - _ -> {not_yecc, Inc} + "yeccpre.hrl" -> {true, Inc}; + _ -> {false, Inc} end end end. -check_imported_funcs({File, {Line, F, A}}, Inc, TypeMap) -> +check_imported_functions({File, {Line, F, A}}, Inc, Types) -> IncMap = Inc#inc.map, FA = {F, A}, - Type = get_type_info(FA, TypeMap), + 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}}]}, @@ -190,25 +190,24 @@ clean_inc(Inc) -> Inc1 = remove_yecc_generated_file(Inc), normalize_obj(Inc1). -remove_yecc_generated_file(TmpInc) -> - Fun = fun(Key, Inc) -> - NewMap = typer_map:remove(Key, Inc#inc.map), - Inc#inc{map = NewMap} +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, TmpInc, TmpInc#inc.filter). - + lists:foldl(Fun, Inc, Filter). + normalize_obj(TmpInc) -> - Fun = fun(Key, Val, Inc) -> + 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_recMap(File, Analysis) -> +get_records(File, Analysis) -> typer_map:lookup(File, Analysis#typer_analysis.record). -get_typeMap(Module, Analysis, RecMap) -> +get_types(Module, Analysis, Records) -> TypeInfoPlt = Analysis#typer_analysis.trust_plt, TypeInfo = case dialyzer_plt:lookup_module(TypeInfoPlt, Module) of @@ -216,10 +215,10 @@ get_typeMap(Module, Analysis, RecMap) -> {value, List} -> List end, CodeServer = Analysis#typer_analysis.code_server, - TypeInfoList = [get_type(I, CodeServer, RecMap) || I <- TypeInfo], + TypeInfoList = [get_type(I, CodeServer, Records) || I <- TypeInfo], typer_map:from_list(TypeInfoList). -get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, RecMap) -> +get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, Records) -> case dialyzer_codeserver:lookup_mfa_contract(MFA, CodeServer) of error -> {{F, A}, {Range, Arg}}; @@ -231,7 +230,7 @@ get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, RecMap) -> {{F, A}, {contract, Contract}}; {error, invalid_contract} -> CString = dialyzer_contracts:contract_to_string(Contract), - SigString = dialyzer_utils:format_sig(Sig, RecMap), + SigString = dialyzer_utils:format_sig(Sig, Records), typer:error( io_lib:format("Error in contract of function ~w:~w/~w\n" "\t The contract is: " ++ CString ++ "\n" ++ @@ -260,17 +259,17 @@ get_functions(File, Analysis) -> typer_map:lookup(File, Analysis#typer_analysis.inc_func) end. -normalize_incFuncs(Funcs) -> - [FuncInfo || {_FileName, FuncInfo} <- Funcs]. +normalize_incFuncs(Functions) -> + [FunInfo || {_FileName, FunInfo} <- Functions]. --spec remove_module_info([func_info()]) -> [func_info()]. +-spec remove_module_info([fun_info()]) -> [fun_info()]. -remove_module_info(FuncInfoList) -> +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, FuncInfoList). + lists:filter(F, FunInfoList). write_typed_file(File, Info) -> io:format(" Processing file: ~p\n", [File]), @@ -278,7 +277,7 @@ write_typed_file(File, Info) -> RootName = filename:basename(filename:rootname(File)), Ext = filename:extension(File), TyperAnnDir = filename:join(Dir, ?TYPER_ANN_DIR), - TmpNewFilename = lists:concat([RootName,".ann",Ext]), + TmpNewFilename = lists:concat([RootName, ".ann", Ext]), NewFileName = filename:join(TyperAnnDir, TmpNewFilename), case file:make_dir(TyperAnnDir) of {error, Reason} -> @@ -291,7 +290,7 @@ write_typed_file(File, Info) -> eacces -> io:format(" No write permission in ~p\n", [Dir]); _ -> - io:format("Unknown error when writing ~p\n", [Dir]), + io:format("Unhandled error ~s when writing ~p\n", [Reason, Dir]), halt() end; ok -> %% Typer dir does NOT exist @@ -304,14 +303,14 @@ write_typed_file(File, Info, NewFileName) -> write_typed_file(Chars, NewFileName, Info, 1, []), io:format(" Saved as: ~p\n", [NewFileName]). -write_typed_file(Chars, File, #info{funcs = []}, _LNo, _Acc) -> +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.funcs, + [{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{funcs = RestFuncs}, + NewInfo = Info#info{functions = RestFuncs}, NewAcc = [], write_typed_file(Chars, File, NewInfo, Line, NewAcc); _ -> @@ -322,7 +321,7 @@ write_typed_file([Ch|Chs] = Chars, File, Info, LineNo, Acc) -> case NewLineNo of Line -> ok = raw_write(F, A, Info, File, [Ch|Acc]), - {Info#info{funcs = RestFuncs}, []}; + {Info#info{functions = RestFuncs}, []}; _ -> {Info, [Ch|Acc]} end, @@ -339,16 +338,16 @@ raw_write(F, A, Info, File, Content) -> file:write_file(File, ContentBin, [append]). get_type_string(F, A, Info, Mode) -> - Type = get_type_info({F,A}, Info#info.typeMap), + Type = get_type_info({F,A}, Info#info.types), TypeStr = case Type of {contract, C} -> dialyzer_contracts:contract_to_string(C); {RetType, ArgType} -> - dialyzer_utils:format_sig(erl_types:t_fun(ArgType, RetType), - Info#info.recMap) + Sig = erl_types:t_fun(ArgType, RetType), + dialyzer_utils:format_sig(Sig, Info#info.records) end, - case Info#info.contracts of + case Info#info.no_comment_specs of true -> case {Mode, Type} of {file, {contract, _}} -> ""; @@ -361,7 +360,7 @@ get_type_string(F, A, Info, Mode) -> lists:concat([Prefix, TypeStr, "."]) end. -show_type_info_only(File, Info) -> +show_type_info(File, Info) -> io:format("\n%% File: ~p\n%% ", [File]), OutputString = lists:concat(["~.", length(File)+8, "c~n"]), io:fwrite(OutputString, [$-]), @@ -369,10 +368,10 @@ show_type_info_only(File, Info) -> TypeInfo = get_type_string(F, A, Info, show), io:format("~s\n", [TypeInfo]) end, - lists:foreach(Fun, Info#info.funcs). + lists:foreach(Fun, Info#info.functions). -get_type_info(Func, TypeMap) -> - case typer_map:lookup(Func, TypeMap) of +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 -- cgit v1.2.3 From 80407de34665df19cc8c34fb361ae179d1e3bb70 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 6 Feb 2011 16:11:00 +0200 Subject: Delete typer_map.erl file --- lib/typer/src/Makefile | 1 - lib/typer/src/typer.app.src | 1 - lib/typer/src/typer.erl | 113 +++++++++++++++++++++++++++++-------- lib/typer/src/typer.hrl | 8 +-- lib/typer/src/typer_annotator.erl | 60 ++++++++++---------- lib/typer/src/typer_info.erl | 12 ++-- lib/typer/src/typer_options.erl | 10 ++-- lib/typer/src/typer_preprocess.erl | 8 +-- 8 files changed, 138 insertions(+), 75 deletions(-) (limited to 'lib') diff --git a/lib/typer/src/Makefile b/lib/typer/src/Makefile index 9c9ef6156f..f367d5980a 100644 --- a/lib/typer/src/Makefile +++ b/lib/typer/src/Makefile @@ -49,7 +49,6 @@ MODULES = \ typer \ typer_annotator \ typer_info \ - typer_map \ typer_options \ typer_preprocess diff --git a/lib/typer/src/typer.app.src b/lib/typer/src/typer.app.src index 3eb0cbf816..f7c3ff867f 100644 --- a/lib/typer/src/typer.app.src +++ b/lib/typer/src/typer.app.src @@ -6,7 +6,6 @@ {modules, [typer, typer_annotator, typer_info, - typer_map, typer_options, typer_preprocess]}, {registered, []}, diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index 206ce8e797..c1406cdbbe 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -18,21 +18,60 @@ %% %CopyrightEnd% %% -%%-------------------------------------------------------------------- +%%----------------------------------------------------------------------- %% File : typer.erl -%% Author : Bingwen He -%% Description : The main driver of the TypEr application -%%-------------------------------------------------------------------- +%% Author(s) : The first version of typer was written by Bingwen He +%% with guidance from Kostis Sagonas and Tobias Lindahl. +%% Since June 2008 typer is maintained by Kostis Sagonas. +%% Description : An Erlang/OTP application that shows type information +%% for Erlang modules to the user. Additionally, it can +%% annotates the code of files with such type information. +%%----------------------------------------------------------------------- -module(typer). -export([start/0]). --export([error/1, compile_error/1]). % for error reporting +-export([fatal_error/1, compile_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]). + +%%----------------------------------------------------------------------- -%% Avoid warning for local function error/1 clashing with autoimported BIF. --compile({no_auto_import, [error/1]}). +-define(SHOW, show). +-define(SHOW_EXPORTED, show_exported). +-define(ANNOTATE, annotate). +-define(ANNOTATE_INC_FILES, annotate_inc_files). --include("typer.hrl"). +-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 = map__new() :: map(), + record = map__new() :: map(), + func = map__new() :: map(), + inc_func = map__new() :: map(), + trust_plt = dialyzer_plt:new() :: dialyzer_plt:plt()}). +-type analysis() :: #typer_analysis{}. + +-record(args, {files = [] :: [file:filename()], + files_r = [] :: [file:filename()], + trusted = [] :: [file:filename()]}). %%-------------------------------------------------------------------- @@ -57,7 +96,7 @@ start() -> %%-------------------------------------------------------------------- --spec extract(#typer_analysis{}) -> #typer_analysis{}. +-spec extract(analysis()) -> analysis(). extract(#typer_analysis{macros = Macros, includes = Includes, t_files = TFiles, trust_plt = TrustPLT} = Analysis) -> @@ -117,7 +156,7 @@ extract(#typer_analysis{macros = Macros, includes = Includes, %%-------------------------------------------------------------------- --spec get_type_info(#typer_analysis{}) -> #typer_analysis{}. +-spec get_type_info(analysis()) -> analysis(). get_type_info(#typer_analysis{callgraph = CallGraph, trust_plt = TrustPLT, @@ -130,10 +169,10 @@ get_type_info(#typer_analysis{callgraph = CallGraph, Analysis#typer_analysis{callgraph = StrippedCallGraph, trust_plt = NewPlt} catch error:What -> - error(io_lib:format("Analysis failed with message: ~p", - [{What, erlang:get_stacktrace()}])); + fatal_error(io_lib:format("Analysis failed with message: ~p", + [{What, erlang:get_stacktrace()}])); throw:{dialyzer_succ_typing_error, Msg} -> - error(io_lib:format("Analysis failed with message: ~s", [Msg])) + fatal_error(io_lib:format("Analysis failed with message: ~s", [Msg])) end. -spec remove_external(dialyzer_callgraph:callgraph(), dialyzer_plt:plt()) -> dialyzer_callgraph:callgraph(). @@ -170,31 +209,27 @@ get_external(Exts, Plt) -> lists:foldl(Fun, [], Exts). %%-------------------------------------------------------------------- +%% Utilities for error reporting. +%%-------------------------------------------------------------------- --spec error(string()) -> no_return(). +-spec fatal_error(string()) -> no_return(). -error(Slogan) -> +fatal_error(Slogan) -> msg(io_lib:format("typer: ~s\n", [Slogan])), erlang:halt(1). -%%-------------------------------------------------------------------- - -spec compile_error([string()]) -> no_return(). compile_error(Reason) -> JoinedString = lists:flatten([X ++ "\n" || X <- Reason]), Msg = "Analysis failed with error report:\n" ++ JoinedString, - error(Msg). - -%%-------------------------------------------------------------------- -%% Outputs a message on 'stderr', if possible. -%%-------------------------------------------------------------------- + fatal_error(Msg). -spec msg(string()) -> 'ok'. msg(Msg) -> case os:type() of - {unix, _} -> + {unix, _} -> % Output a message on 'stderr', if possible P = open_port({fd, 0, 2}, [out]), port_command(P, Msg), true = port_close(P), @@ -216,7 +251,37 @@ rcv_ext_types(Self, ExtTypes) -> receive {Self, ext_types, ExtType} -> rcv_ext_types(Self, [ExtType|ExtTypes]); - {Self, done} -> lists:usort(ExtTypes) + {Self, done} -> + lists:usort(ExtTypes) end. %%-------------------------------------------------------------------- +%% A convenient abstraction of a Key-Value mapping data structure +%%-------------------------------------------------------------------- + +-type map() :: dict(). + +-spec map__new() -> map(). +map__new() -> + dict:new(). + +-spec map__insert({term(), term()}, map()) -> map(). +map__insert(Object, Map) -> + {Key, Value} = Object, + dict:store(Key, Value, Map). + +-spec map__lookup(term(), map()) -> term(). +map__lookup(Key, Map) -> + try dict:fetch(Key, Map) catch error:_ -> none end. + +-spec map__from_list([{term(), term()}]) -> map(). +map__from_list(List) -> + dict:from_list(List). + +-spec map__remove(term(), map()) -> map(). +map__remove(Key, Dict) -> + dict:erase(Key, Dict). + +-spec map__fold(fun((term(), term(), term()) -> term()), term(), map()) -> term(). +map__fold(Fun, Acc0, Dict) -> + dict:fold(Fun, Acc0, Dict). diff --git a/lib/typer/src/typer.hrl b/lib/typer/src/typer.hrl index f08668a2ac..d41bf2c83b 100644 --- a/lib/typer/src/typer.hrl +++ b/lib/typer/src/typer.hrl @@ -42,10 +42,10 @@ %% 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(), + 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()], diff --git a/lib/typer/src/typer_annotator.erl b/lib/typer/src/typer_annotator.erl index 8904867d3e..205087407e 100644 --- a/lib/typer/src/typer_annotator.erl +++ b/lib/typer/src/typer_annotator.erl @@ -40,11 +40,11 @@ -type fun_info() :: {non_neg_integer(), atom(), arity()}. --record(info, {records = typer_map:new() :: dict(), +-record(info, {records = typer:map__new() :: dict(), functions = [] :: [fun_info()], types :: dict(), no_comment_specs = true :: boolean()}). --record(inc, {map = typer_map:new() :: dict(), +-record(inc, {map = typer:map__new() :: dict(), filter = [] :: [file:filename()]}). %%---------------------------------------------------------------------------- @@ -79,13 +79,13 @@ write_and_collect_inc_info(Analysis) -> write_inc_files(Inc) -> Fun = fun (File) -> - Val = typer_map:lookup(File,Inc#inc.map), + 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(), + 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]), @@ -153,17 +153,17 @@ 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 + 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), + 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), + NewMap = typer:map__insert(Obj, IncMap), Inc#inc{map = NewMap}; Type -> %% Function is in and with same type @@ -174,9 +174,9 @@ check_imported_functions({File, {Line, F, A}}, Inc, Types) -> Elem = lists:keydelete(FA, 1, Val), NewMap = case Elem of [] -> - typer_map:remove(File, IncMap); + typer:map__remove(File, IncMap); _ -> - typer_map:insert({File, Elem}, IncMap) + typer:map__insert({File, Elem}, IncMap) end, Inc#inc{map = NewMap} end @@ -192,20 +192,20 @@ clean_inc(Inc) -> remove_yecc_generated_file(#inc{filter = Filter} = Inc) -> Fun = fun (Key, #inc{map = Map} = I) -> - I#inc{map = typer_map:remove(Key, Map)} + 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) + typer:map__insert({Key,NewVal}, Inc) end, - NewMap = typer_map:fold(Fun, typer_map:new(), TmpInc#inc.map), + 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). + typer:map__lookup(File, Analysis#typer_analysis.record). get_types(Module, Analysis, Records) -> TypeInfoPlt = Analysis#typer_analysis.trust_plt, @@ -216,7 +216,7 @@ get_types(Module, Analysis, Records) -> end, CodeServer = Analysis#typer_analysis.code_server, TypeInfoList = [get_type(I, CodeServer, Records) || I <- TypeInfo], - typer_map:from_list(TypeInfoList). + typer:map__from_list(TypeInfoList). get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, Records) -> case dialyzer_codeserver:lookup_mfa_contract(MFA, CodeServer) of @@ -231,32 +231,32 @@ get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, Records) -> {error, invalid_contract} -> CString = dialyzer_contracts:contract_to_string(Contract), SigString = dialyzer_utils:format_sig(Sig, Records), - typer:error( - 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])); - {error, Msg} when is_list(Msg) -> % Msg is a string() - typer:error( - io_lib:format("Error in contract of function ~w:~w/~w: ~s", - [M, F, A, Msg])) + 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), + 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), + 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), + 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) + typer:map__lookup(File, Analysis#typer_analysis.inc_func) end. normalize_incFuncs(Functions) -> @@ -371,7 +371,7 @@ show_type_info(File, Info) -> lists:foreach(Fun, Info#info.functions). get_type_info(Func, Types) -> - case typer_map:lookup(Func, Types) of + case typer:map__lookup(Func, Types) of none -> %% Note: Typeinfo of any function should exist in %% the result offered by dialyzer, otherwise there diff --git a/lib/typer/src/typer_info.erl b/lib/typer/src/typer_info.erl index 1387245064..a568518ffe 100644 --- a/lib/typer/src/typer_info.erl +++ b/lib/typer/src/typer_info.erl @@ -42,7 +42,7 @@ collect(Analysis) -> 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") + 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}, @@ -66,7 +66,7 @@ collect(Analysis) -> dialyzer_contracts:process_contract_remote_types(TmpCServer3) catch throw:{error, ErrorMsg} -> - typer:error(ErrorMsg) + typer:fatal_error(ErrorMsg) end, NewAnalysis#typer_analysis{code_server = NewCServer}. @@ -122,19 +122,19 @@ analyze_core_tree(Core, Records, SpecInfo, ExpTypes, Analysis, File) -> 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}, + 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}, + 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}, + 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), + RecordMap = typer:map__insert({File, Records}, Analysis#typer_analysis.record), Analysis#typer_analysis{final_files=Final_Files, callgraph=CG, code_server=CS6, diff --git a/lib/typer/src/typer_options.erl b/lib/typer/src/typer_options.erl index 9545c7334b..b041052cd2 100644 --- a/lib/typer/src/typer_options.erl +++ b/lib/typer/src/typer_options.erl @@ -72,7 +72,7 @@ cl(["--no_spec"|Opts]) -> {no_spec, Opts}; cl(["--plt",Plt|Opts]) -> {{plt, Plt}, Opts}; cl(["-D"++Def|Opts]) -> case Def of - "" -> typer:error("no variable name specified after -D"); + "" -> typer:fatal_error("no variable name specified after -D"); _ -> DefPair = process_def_list(re:split(Def, "=", [{return, list}])), {{def, DefPair}, Opts} @@ -80,19 +80,19 @@ cl(["-D"++Def|Opts]) -> cl(["-I",Dir|Opts]) -> {{inc, Dir}, Opts}; cl(["-I"++Dir|Opts]) -> case Dir of - "" -> typer:error("no include directory specified after -I"); + "" -> typer:fatal_error("no include directory specified after -I"); _ -> {{inc, Dir}, Opts} end; cl(["-T"|Opts]) -> {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts), case Files of - [] -> typer:error("no file or directory specified after -T"); + [] -> typer:fatal_error("no file or directory specified after -T"); [_|_] -> {{trusted, Files}, RestOpts} end; cl(["-r"|Opts]) -> {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts), {{files_r, Files}, RestOpts}; -cl(["-"++H|_]) -> typer:error("unknown option -"++H); +cl(["-"++H|_]) -> typer:fatal_error("unknown option -"++H); cl(Opts) -> {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts), {{files, Files}, RestOpts}. @@ -141,7 +141,7 @@ analyze_result(no_spec, Args, Analysis) -> -spec mode_error() -> no_return(). mode_error() -> - typer:error("can not do \"show\", \"show-exported\", \"annotate\", and \"annotate-inc-files\" at the same time"). + typer:fatal_error("can not do \"show\", \"show-exported\", \"annotate\", and \"annotate-inc-files\" at the same time"). -spec version_message() -> no_return(). version_message() -> diff --git a/lib/typer/src/typer_preprocess.erl b/lib/typer/src/typer_preprocess.erl index 27660e849e..3366704bad 100644 --- a/lib/typer/src/typer_preprocess.erl +++ b/lib/typer/src/typer_preprocess.erl @@ -30,7 +30,7 @@ get_all_files(#args{files=Fs,files_r=Ds}, analysis) -> case files_and_dirs(Fs, Ds, fun test_erl_file_exclude_ann/1) of - [] -> typer:error("no file(s) to analyze"); + [] -> typer:fatal_error("no file(s) to analyze"); AllFiles -> AllFiles end; get_all_files(#args{trusted=Fs}, trust) -> @@ -98,11 +98,11 @@ check_dir(Dir, Recursive, Acc, Fun) -> Acc ++ TmpAcc1 ++ TmpAcc2 end; {error, eacces} -> - typer:error("no access permission to dir \""++Dir++"\""); + typer:fatal_error("no access permission to dir \""++Dir++"\""); {error, enoent} -> - typer:error("cannot access "++Dir++": No such file or directory"); + typer:fatal_error("cannot access "++Dir++": No such file or directory"); {error, _Reason} -> - typer:error("error involving a use of file:list_dir/1") + typer:fatal_error("error involving a use of file:list_dir/1") end. %% Same order as the input list -- cgit v1.2.3 From 1f909eefa0e76bd219dbf933be0785d614057450 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 6 Feb 2011 16:12:12 +0200 Subject: Delete typer_map.erl file, really this time --- lib/typer/src/typer_map.erl | 47 --------------------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 lib/typer/src/typer_map.erl (limited to 'lib') diff --git a/lib/typer/src/typer_map.erl b/lib/typer/src/typer_map.erl deleted file mode 100644 index bf62dea651..0000000000 --- a/lib/typer/src/typer_map.erl +++ /dev/null @@ -1,47 +0,0 @@ -%% -*- 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_map). - --export([new/0, insert/2, lookup/2, from_list/1, remove/2, fold/3]). - --spec new() -> dict(). -new() -> - dict:new(). - --spec insert({term(), term()}, dict()) -> dict(). -insert(Object, Dict) -> - {Key, Value} = Object, - dict:store(Key, Value, Dict). - --spec lookup(term(), dict()) -> any(). -lookup(Key, Dict) -> - try dict:fetch(Key, Dict) catch error:_ -> none end. - --spec from_list([{term(), term()}]) -> dict(). -from_list(List) -> - dict:from_list(List). - --spec remove(term(), dict()) -> dict(). -remove(Key, Dict) -> - dict:erase(Key, Dict). - --spec fold(fun((term(), term(), term()) -> term()), term(), dict()) -> term(). -fold(Fun, Acc0, Dict) -> - dict:fold(Fun, Acc0, Dict). -- cgit v1.2.3 From 3562e3b44c2ad2df81eb192a2f69d939f73b9232 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 6 Feb 2011 16:43:13 +0200 Subject: Remove typer_preprocess.erl file --- lib/typer/src/Makefile | 6 +- lib/typer/src/typer.app.src | 3 +- lib/typer/src/typer.erl | 139 ++++++++++++++++++++++++++++++++-- lib/typer/src/typer_preprocess.erl | 148 ------------------------------------- 4 files changed, 134 insertions(+), 162 deletions(-) delete mode 100644 lib/typer/src/typer_preprocess.erl (limited to 'lib') diff --git a/lib/typer/src/Makefile b/lib/typer/src/Makefile index f367d5980a..76cd557cc8 100644 --- a/lib/typer/src/Makefile +++ b/lib/typer/src/Makefile @@ -45,12 +45,10 @@ DIALYZER_DIR = $(ERL_TOP)/lib/dialyzer # ---------------------------------------------------- # Target Specs # ---------------------------------------------------- -MODULES = \ - typer \ +MODULES = typer \ typer_annotator \ typer_info \ - typer_options \ - typer_preprocess + typer_options 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 f7c3ff867f..2109f7fc37 100644 --- a/lib/typer/src/typer.app.src +++ b/lib/typer/src/typer.app.src @@ -6,8 +6,7 @@ {modules, [typer, typer_annotator, typer_info, - typer_options, - typer_preprocess]}, + typer_options]}, {registered, []}, {applications, [compiler, dialyzer, hipe, kernel, stdlib]}, {env, []}]}. diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index c1406cdbbe..3f1302ecc9 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -45,18 +45,20 @@ %%----------------------------------------------------------------------- +-type files() :: [file:filename()]. + -record(typer_analysis, {mode :: mode(), macros = [] :: [{atom(), term()}], % {macro_name, value} - includes = [] :: [file:filename()], + includes = [] :: files(), %% --- for dialyzer --- code_server = dialyzer_codeserver:new():: dialyzer_codeserver:codeserver(), callgraph = dialyzer_callgraph:new() :: dialyzer_callgraph:callgraph(), - ana_files = [] :: [file:filename()], % absolute filenames + ana_files = [] :: files(), % absolute names plt = none :: 'none' | file:filename(), no_spec = false :: boolean(), %% --- for typer --- - t_files = [] :: [file:filename()], + t_files = [] :: files(), %% For choosing between contracts or comments contracts = true :: boolean(), %% Files in 'final_files' are compilable with option 'to_pp'; we keep @@ -69,9 +71,9 @@ trust_plt = dialyzer_plt:new() :: dialyzer_plt:plt()}). -type analysis() :: #typer_analysis{}. --record(args, {files = [] :: [file:filename()], - files_r = [] :: [file:filename()], - trusted = [] :: [file:filename()]}). +-record(args, {files = [] :: files(), + files_r = [] :: files(), + trusted = [] :: files()}). %%-------------------------------------------------------------------- @@ -81,10 +83,10 @@ start() -> {Args, Analysis} = typer_options:process(), %% io:format("Args: ~p\n", [Args]), %% io:format("Analysis: ~p\n", [Analysis]), - TrustedFiles = typer_preprocess:get_all_files(Args, trust), + TrustedFiles = filter_fd(Args#args.trusted, [], fun is_erl_file/1), Analysis1 = Analysis#typer_analysis{t_files = TrustedFiles}, Analysis2 = extract(Analysis1), - All_Files = typer_preprocess:get_all_files(Args, analysis), + 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), @@ -208,6 +210,127 @@ get_external(Exts, Plt) -> end, lists:foldl(Fun, [], Exts). +%%-------------------------------------------------------------------- +%% File processing. +%%-------------------------------------------------------------------- + +-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 + [] -> fatal_error("no file(s) to analyze"); + AllFiles -> AllFiles + end. + +-spec test_erl_file_exclude_ann(file:filename()) -> boolean(). + +test_erl_file_exclude_ann(File) -> + case is_erl_file(File) of + true -> %% Exclude files ending with ".ann.erl" + case re:run(File, "[\.]ann[\.]erl$") of + {match, _} -> false; + nomatch -> true + end; + false -> false + end. + +-spec is_erl_file(file:filename()) -> boolean(). + +is_erl_file(File) -> + filename:extension(File) =:= ".erl". + +-type test_file_fun() :: fun((file:filename()) -> boolean()). + +-spec filter_fd(files(), files(), test_file_fun()) -> files(). + +filter_fd(File_Dir, Dir_R, Fun) -> + All_File_1 = process_file_and_dir(File_Dir, Fun), + All_File_2 = process_dir_rec(Dir_R, Fun), + remove_dup(All_File_1 ++ All_File_2). + +-spec process_file_and_dir(files(), test_file_fun()) -> files(). + +process_file_and_dir(File_Dir, TestFun) -> + Fun = + fun (Elem, Acc) -> + case filelib:is_regular(Elem) of + true -> process_file(Elem, TestFun, Acc); + false -> check_dir(Elem, false, Acc, TestFun) + end + end, + lists:foldl(Fun, [], File_Dir). + +-spec process_dir_rec(files(), test_file_fun()) -> files(). + +process_dir_rec(Dirs, TestFun) -> + Fun = fun (Dir, Acc) -> check_dir(Dir, true, Acc, TestFun) end, + lists:foldl(Fun, [], Dirs). + +-spec check_dir(file:filename(), boolean(), files(), test_file_fun()) -> files(). + +check_dir(Dir, Recursive, Acc, Fun) -> + case file:list_dir(Dir) of + {ok, Files} -> + {TmpDirs, TmpFiles} = split_dirs_and_files(Files, Dir), + case Recursive of + false -> + FinalFiles = process_file_and_dir(TmpFiles, Fun), + Acc ++ FinalFiles; + true -> + TmpAcc1 = process_file_and_dir(TmpFiles, Fun), + TmpAcc2 = process_dir_rec(TmpDirs, Fun), + Acc ++ TmpAcc1 ++ TmpAcc2 + end; + {error, eacces} -> + fatal_error("no access permission to dir \""++Dir++"\""); + {error, enoent} -> + fatal_error("cannot access "++Dir++": No such file or directory"); + {error, _Reason} -> + fatal_error("error involving a use of file:list_dir/1") + end. + +%% Same order as the input list +-spec process_file(file:filename(), test_file_fun(), files()) -> files(). + +process_file(File, TestFun, Acc) -> + case TestFun(File) of + true -> Acc ++ [File]; + false -> Acc + end. + +%% Same order as the input list +-spec split_dirs_and_files(files(), file:filename()) -> {files(), files()}. + +split_dirs_and_files(Elems, Dir) -> + Test_Fun = + fun (Elem, {DirAcc, FileAcc}) -> + File = filename:join(Dir, Elem), + case filelib:is_regular(File) of + false -> {[File|DirAcc], FileAcc}; + true -> {DirAcc, [File|FileAcc]} + end + end, + {Dirs, Files} = lists:foldl(Test_Fun, {[], []}, Elems), + {lists:reverse(Dirs), lists:reverse(Files)}. + +%%----------------------------------------------------------------------- +%% Utilities +%%----------------------------------------------------------------------- + +%% Removes duplicate filenames but it keeps the order of the input list + +-spec remove_dup(files()) -> files(). + +remove_dup(Files) -> + Test_Dup = fun (File, Acc) -> + case lists:member(File, Acc) of + true -> Acc; + false -> [File|Acc] + end + end, + Reversed_Elems = lists:foldl(Test_Dup, [], Files), + lists:reverse(Reversed_Elems). + %%-------------------------------------------------------------------- %% Utilities for error reporting. %%-------------------------------------------------------------------- diff --git a/lib/typer/src/typer_preprocess.erl b/lib/typer/src/typer_preprocess.erl deleted file mode 100644 index 3366704bad..0000000000 --- a/lib/typer/src/typer_preprocess.erl +++ /dev/null @@ -1,148 +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_preprocess). - --export([get_all_files/2]). - --include("typer.hrl"). - -%%---------------------------------------------------------------------------- - --spec get_all_files(#args{}, 'analysis' | 'trust') -> [file:filename()]. - -get_all_files(#args{files=Fs,files_r=Ds}, analysis) -> - case files_and_dirs(Fs, Ds, fun test_erl_file_exclude_ann/1) of - [] -> typer:fatal_error("no file(s) to analyze"); - AllFiles -> AllFiles - end; -get_all_files(#args{trusted=Fs}, trust) -> - files_and_dirs(Fs, [], fun test_erl_file/1). - --spec test_erl_file_exclude_ann(file:filename()) -> boolean(). - -test_erl_file_exclude_ann(File) -> - case filename:extension(File) of - ".erl" -> %% Exclude files ending with ".ann.erl" - case re:run(File, "[\.]ann[\.]erl$") of - {match, _} -> false; - nomatch -> true - end; - _ -> false - end. - --spec test_erl_file(file:filename()) -> boolean(). - -test_erl_file(File) -> - filename:extension(File) =:= ".erl". - --spec files_and_dirs([file:filename()], [file:filename()], - fun((file:filename()) -> boolean())) -> [file:filename()]. - -files_and_dirs(File_Dir, Dir_R, Fun) -> - All_File_1 = process_file_and_dir(File_Dir, Fun), - All_File_2 = process_dir_rec(Dir_R, Fun), - remove_dup(All_File_1 ++ All_File_2). - --spec process_file_and_dir([file:filename()], - fun((file:filename()) -> boolean())) -> [file:filename()]. - -process_file_and_dir(File_Dir, TestFun) -> - Fun = - fun (Elem, Acc) -> - case filelib:is_regular(Elem) of - true -> process_file(Elem, TestFun, Acc); - false -> check_dir(Elem, false, Acc, TestFun) - end - end, - lists:foldl(Fun, [], File_Dir). - --spec process_dir_rec([file:filename()], - fun((file:filename()) -> boolean())) -> [file:filename()]. - -process_dir_rec(Dirs, TestFun) -> - Fun = fun (Dir, Acc) -> check_dir(Dir, true, Acc, TestFun) end, - lists:foldl(Fun, [], Dirs). - --spec check_dir(file:filename(), boolean(), [file:filename()], - fun((file:filename()) -> boolean())) -> [file:filename()]. - -check_dir(Dir, Recursive, Acc, Fun) -> - case file:list_dir(Dir) of - {ok, Files} -> - {TmpDirs, TmpFiles} = split_dirs_and_files(Files, Dir), - case Recursive of - false -> - FinalFiles = process_file_and_dir(TmpFiles, Fun), - Acc ++ FinalFiles; - true -> - TmpAcc1 = process_file_and_dir(TmpFiles, Fun), - TmpAcc2 = process_dir_rec(TmpDirs, Fun), - Acc ++ TmpAcc1 ++ TmpAcc2 - end; - {error, eacces} -> - typer:fatal_error("no access permission to dir \""++Dir++"\""); - {error, enoent} -> - typer:fatal_error("cannot access "++Dir++": No such file or directory"); - {error, _Reason} -> - typer:fatal_error("error involving a use of file:list_dir/1") - end. - -%% Same order as the input list --spec process_file(file:filename(), fun((file:filename()) -> boolean()), [file:filename()]) -> [file:filename()]. - -process_file(File, TestFun, Acc) -> - case TestFun(File) of - true -> Acc ++ [File]; - false -> Acc - end. - -%% Same order as the input list --spec split_dirs_and_files([file:filename()], file:filename()) -> {[file:filename()], [file:filename()]}. - -split_dirs_and_files(Elems, Dir) -> - Test_Fun = - fun (Elem, {DirAcc, FileAcc}) -> - File = filename:join(Dir, Elem), - case filelib:is_regular(File) of - false -> {[File|DirAcc], FileAcc}; - true -> {DirAcc, [File|FileAcc]} - end - end, - {Dirs, Files} = lists:foldl(Test_Fun, {[], []}, Elems), - {lists:reverse(Dirs), lists:reverse(Files)}. - -%%----------------------------------------------------------------------- -%% Utilities -%%----------------------------------------------------------------------- - -%% Removes duplicate filenames but it keeps the order of the input list - --spec remove_dup([file:filename()]) -> [file:filename()]. - -remove_dup(Files) -> - Test_Dup = fun (File, Acc) -> - case lists:member(File, Acc) of - true -> Acc; - false -> [File|Acc] - end - end, - Reversed_Elems = lists:foldl(Test_Dup, [], Files), - lists:reverse(Reversed_Elems). -- cgit v1.2.3 From 0e14138a018ed824a9e96be02019627bdd188ef4 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 6 Feb 2011 17:26:07 +0200 Subject: Remove typer_options.erl file --- lib/typer/src/Makefile | 9 +- lib/typer/src/typer.erl | 176 +++++++++++++++++++++++++++++++++--- lib/typer/src/typer_options.erl | 194 ---------------------------------------- 3 files changed, 169 insertions(+), 210 deletions(-) delete mode 100644 lib/typer/src/typer_options.erl (limited to 'lib') diff --git a/lib/typer/src/Makefile b/lib/typer/src/Makefile index 76cd557cc8..f814e943ea 100644 --- a/lib/typer/src/Makefile +++ b/lib/typer/src/Makefile @@ -47,8 +47,7 @@ DIALYZER_DIR = $(ERL_TOP)/lib/dialyzer # ---------------------------------------------------- MODULES = typer \ typer_annotator \ - typer_info \ - typer_options + typer_info HRL_FILES= typer.hrl ERL_FILES= $(MODULES:%=%.erl) @@ -84,8 +83,8 @@ clean: # Special Build Targets # ---------------------------------------------------- -$(EBIN)/typer_options.$(EMULATOR): typer_options.erl ../vsn.mk Makefile - erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) typer_options.erl +$(EBIN)/typer.$(EMULATOR): typer.erl ../vsn.mk Makefile + erlc -W $(ERL_COMPILE_FLAGS) -DVSN="\"v$(VSN)\"" -o$(EBIN) typer.erl $(APP_TARGET): $(APP_SRC) ../vsn.mk sed -e 's;%VSN%;$(VSN);' $< > $@ @@ -100,8 +99,6 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk $(EBIN)/typer.beam: typer.hrl $(EBIN)/typer_annotator.beam: typer.hrl $(EBIN)/typer_info.beam: typer.hrl -$(EBIN)/typer_options.beam: typer.hrl -$(EBIN)/typer_preprocess.beam: typer.hrl # ---------------------------------------------------- # Release Target diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index 3f1302ecc9..54818553da 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -80,7 +80,7 @@ -spec start() -> no_return(). start() -> - {Args, Analysis} = typer_options:process(), + {Args, Analysis} = process_cl_args(), %% io:format("Args: ~p\n", [Args]), %% io:format("Analysis: ~p\n", [Analysis]), TrustedFiles = filter_fd(Args#args.trusted, [], fun is_erl_file/1), @@ -189,8 +189,7 @@ remove_external(CallGraph, PLT) -> ExtTypes = rcv_ext_types(), case ExtTypes of [] -> ok; - _ -> - msg(io_lib:format(" Unknown types: ~p\n", [ExtTypes])) + _ -> msg(io_lib:format(" Unknown types: ~p\n", [ExtTypes])) end end, StrippedCG. @@ -210,13 +209,114 @@ get_external(Exts, Plt) -> end, lists:foldl(Fun, [], Exts). +%%-------------------------------------------------------------------- +%% Processing of command-line options and arguments. +%%-------------------------------------------------------------------- + +-spec process_cl_args() -> {#args{}, #typer_analysis{}}. + +process_cl_args() -> + ArgList = init:get_plain_arguments(), + %% io:format("Args is ~p\n", [ArgList]), + {Args, Analysis} = analyze_args(ArgList, #args{}, #typer_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}; + Mode when is_atom(Mode) -> Analysis + end}. + +analyze_args([], Args, Analysis) -> + {Args, Analysis}; +analyze_args(ArgList, Args, Analysis) -> + {Result, Rest} = cl(ArgList), + {NewArgs, NewAnalysis} = analyze_result(Result, Args, Analysis), + analyze_args(Rest, NewArgs, NewAnalysis). + +cl(["-h"|_]) -> help_message(); +cl(["--help"|_]) -> help_message(); +cl(["-v"|_]) -> version_message(); +cl(["--version"|_]) -> version_message(); +cl(["--comments"|Opts]) -> {comments, Opts}; +cl(["--show"|Opts]) -> {{mode, ?SHOW}, Opts}; +cl(["--show_exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts}; +cl(["--show-exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts}; +cl(["--annotate"|Opts]) -> {{mode, ?ANNOTATE}, Opts}; +cl(["--annotate-inc-files"|Opts]) -> {{mode, ?ANNOTATE_INC_FILES}, Opts}; +cl(["--no_spec"|Opts]) -> {no_spec, Opts}; +cl(["--plt",Plt|Opts]) -> {{plt, Plt}, Opts}; +cl(["-D"++Def|Opts]) -> + case Def of + "" -> fatal_error("no variable name specified after -D"); + _ -> + DefPair = process_def_list(re:split(Def, "=", [{return, list}])), + {{def, DefPair}, Opts} + end; +cl(["-I",Dir|Opts]) -> {{inc, Dir}, Opts}; +cl(["-I"++Dir|Opts]) -> + case Dir of + "" -> fatal_error("no include directory specified after -I"); + _ -> {{inc, Dir}, Opts} + end; +cl(["-T"|Opts]) -> + {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts), + case Files of + [] -> fatal_error("no file or directory specified after -T"); + [_|_] -> {{trusted, Files}, RestOpts} + end; +cl(["-r"|Opts]) -> + {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts), + {{files_r, Files}, RestOpts}; +cl(["-"++H|_]) -> fatal_error("unknown option -"++H); +cl(Opts) -> + {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts), + {{files, Files}, RestOpts}. + +process_def_list(L) -> + case L of + [Name, Value] -> + {ok, Tokens, _} = erl_scan:string(Value ++ "."), + {ok, ErlValue} = erl_parse:parse_term(Tokens), + {list_to_atom(Name), ErlValue}; + [Name] -> + {list_to_atom(Name), true} + end. + +%% Get information about files that the user trusts and wants to analyze +analyze_result({files, Val}, Args, Analysis) -> + NewVal = Args#args.files ++ Val, + {Args#args{files = NewVal}, Analysis}; +analyze_result({files_r, Val}, Args, Analysis) -> + NewVal = Args#args.files_r ++ Val, + {Args#args{files_r = NewVal}, 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}}; +%% 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}}; + OldMode -> mode_error(OldMode, Mode) + end; +analyze_result({def, Val}, Args, Analysis) -> + NewVal = Analysis#typer_analysis.macros ++ [Val], + {Args, Analysis#typer_analysis{macros = NewVal}}; +analyze_result({inc, Val}, Args, Analysis) -> + NewVal = Analysis#typer_analysis.includes ++ [Val], + {Args, Analysis#typer_analysis{includes = NewVal}}; +analyze_result({plt, Plt}, Args, Analysis) -> + {Args, Analysis#typer_analysis{plt = Plt}}; +analyze_result(no_spec, Args, Analysis) -> + {Args, Analysis#typer_analysis{no_spec = true}}. + %%-------------------------------------------------------------------- %% File processing. %%-------------------------------------------------------------------- -spec get_all_files(#args{}) -> files(). -get_all_files(#args{files = Fs,files_r = Ds}) -> +get_all_files(#args{files = Fs, files_r = Ds}) -> case filter_fd(Fs, Ds, fun test_erl_file_exclude_ann/1) of [] -> fatal_error("no file(s) to analyze"); AllFiles -> AllFiles @@ -313,12 +413,7 @@ split_dirs_and_files(Elems, Dir) -> {Dirs, Files} = lists:foldl(Test_Fun, {[], []}, Elems), {lists:reverse(Dirs), lists:reverse(Files)}. -%%----------------------------------------------------------------------- -%% Utilities -%%----------------------------------------------------------------------- - -%% Removes duplicate filenames but it keeps the order of the input list - +%% Removes duplicate filenames but keeps the order of the input list -spec remove_dup(files()) -> files(). remove_dup(Files) -> @@ -341,6 +436,13 @@ fatal_error(Slogan) -> msg(io_lib:format("typer: ~s\n", [Slogan])), erlang:halt(1). +-spec mode_error(mode(), mode()) -> no_return(). +mode_error(OldMode, NewMode) -> + Msg = io_lib:format("Mode was previously set to '~s'; " + "can not set it to '~s' now", + [OldMode, NewMode]), + fatal_error(Msg). + -spec compile_error([string()]) -> no_return(). compile_error(Reason) -> @@ -361,6 +463,60 @@ msg(Msg) -> io:format("~s", [Msg]) end. +%%-------------------------------------------------------------------- +%% Version and help messages. +%%-------------------------------------------------------------------- + +-spec version_message() -> no_return(). +version_message() -> + io:format("TypEr version "++?VSN++"\n"), + erlang:halt(0). + +-spec help_message() -> no_return(). +help_message() -> + S = " Usage: typer [--help] [--version] [--comments] [--plt PLT] + [--show | --show-exported | --annotate | --annotate-inc-files] + [-Ddefine]* [-I include_dir]* [-T application]* [-r] file* + + Options: + -r dir* + search directories recursively for .erl files below them + --show + Prints type specifications for all functions on stdout. + (this is the default behaviour; this option is not really needed) + --show-exported (or --show_exported) + Same as --show, but prints specifications for exported functions only + Specs are displayed sorted alphabetically on the function's name + --annotate + Annotates the specified files with type specifications + --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 + --plt PLT + Use the specified dialyzer PLT file rather than the default one + -T file* + The specified file(s) already contain type specifications and these + are to be trusted in order to print specs for the rest of the files + (Multiple files or dirs, separated by spaces, can be specified.) + -Dname (or -Dname=value) + pass the defined name(s) to TypEr + (The syntax of defines is the same as that used by \"erlc\".) + -I include_dir + pass the include_dir to TypEr + (The syntax of includes is the same as that used by \"erlc\".) + --version (or -v) + prints the Typer version and exits + --help (or -h) + prints this message and exits + + Note: + * denotes that multiple occurrences of these options are possible. +", + io:put_chars(S), + erlang:halt(0). + %%-------------------------------------------------------------------- %% Handle messages. %%-------------------------------------------------------------------- diff --git a/lib/typer/src/typer_options.erl b/lib/typer/src/typer_options.erl deleted file mode 100644 index b041052cd2..0000000000 --- a/lib/typer/src/typer_options.erl +++ /dev/null @@ -1,194 +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% -%% -%%=========================================================================== -%% File : typer_options.erl -%% Author : Bingwen He -%% Description : Handles all command-line options given to TypEr -%%=========================================================================== - --module(typer_options). - --export([process/0]). - -%%--------------------------------------------------------------------------- - --include("typer.hrl"). - -%%--------------------------------------------------------------------------- -%% Exported functions -%%--------------------------------------------------------------------------- - --spec process() -> {#args{}, #typer_analysis{}}. - -process() -> - ArgList = init:get_plain_arguments(), - %% io:format("Args is ~p\n", [ArgList]), - {Args, Analysis} = analyze_args(ArgList, #args{}, #typer_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}; - Mode when is_atom(Mode) -> Analysis - end}. - -%%--------------------------------------------------------------------------- -%% Internal functions -%%--------------------------------------------------------------------------- - -analyze_args([], Args, Analysis) -> - {Args, Analysis}; -analyze_args(ArgList, Args, Analysis) -> - {Result, Rest} = cl(ArgList), - {NewArgs, NewAnalysis} = analyze_result(Result, Args, Analysis), - analyze_args(Rest, NewArgs, NewAnalysis). - -cl(["-h"|_]) -> help_message(); -cl(["--help"|_]) -> help_message(); -cl(["-v"|_]) -> version_message(); -cl(["--version"|_]) -> version_message(); -cl(["--comments"|Opts]) -> {comments, Opts}; -cl(["--show"|Opts]) -> {{mode, ?SHOW}, Opts}; -cl(["--show_exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts}; -cl(["--show-exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts}; -cl(["--annotate"|Opts]) -> {{mode, ?ANNOTATE}, Opts}; -cl(["--annotate-inc-files"|Opts]) -> {{mode, ?ANNOTATE_INC_FILES}, Opts}; -cl(["--no_spec"|Opts]) -> {no_spec, Opts}; -cl(["--plt",Plt|Opts]) -> {{plt, Plt}, Opts}; -cl(["-D"++Def|Opts]) -> - case Def of - "" -> typer:fatal_error("no variable name specified after -D"); - _ -> - DefPair = process_def_list(re:split(Def, "=", [{return, list}])), - {{def, DefPair}, Opts} - end; -cl(["-I",Dir|Opts]) -> {{inc, Dir}, Opts}; -cl(["-I"++Dir|Opts]) -> - case Dir of - "" -> typer:fatal_error("no include directory specified after -I"); - _ -> {{inc, Dir}, Opts} - end; -cl(["-T"|Opts]) -> - {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts), - case Files of - [] -> typer:fatal_error("no file or directory specified after -T"); - [_|_] -> {{trusted, Files}, RestOpts} - end; -cl(["-r"|Opts]) -> - {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts), - {{files_r, Files}, RestOpts}; -cl(["-"++H|_]) -> typer:fatal_error("unknown option -"++H); -cl(Opts) -> - {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts), - {{files, Files}, RestOpts}. - -process_def_list(L) -> - case L of - [Name, Value] -> - {ok, Tokens, _} = erl_scan:string(Value ++ "."), - {ok, ErlValue} = erl_parse:parse_term(Tokens), - {list_to_atom(Name), ErlValue}; - [Name] -> - {list_to_atom(Name), true} - end. - -%% Get information about files that the user trusts and wants to analyze -analyze_result({files, Val}, Args, Analysis) -> - NewVal = Args#args.files ++ Val, - {Args#args{files = NewVal}, Analysis}; -analyze_result({files_r, Val}, Args, Analysis) -> - NewVal = Args#args.files_r ++ Val, - {Args#args{files_r = NewVal}, 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}}; -%% Get useful information for actual analysis -analyze_result({mode, Val}, Args, Analysis) -> - case Analysis#typer_analysis.mode of - undefined -> {Args, Analysis#typer_analysis{mode = Val}}; - _ -> mode_error() - end; -analyze_result({def, Val}, Args, Analysis) -> - NewVal = Analysis#typer_analysis.macros ++ [Val], - {Args, Analysis#typer_analysis{macros = NewVal}}; -analyze_result({inc, Val}, Args, Analysis) -> - NewVal = Analysis#typer_analysis.includes ++ [Val], - {Args, Analysis#typer_analysis{includes = NewVal}}; -analyze_result({plt, Plt}, Args, Analysis) -> - {Args, Analysis#typer_analysis{plt = Plt}}; -analyze_result(no_spec, Args, Analysis) -> - {Args, Analysis#typer_analysis{no_spec = true}}. - - -%%-------------------------------------------------------------------- - --spec mode_error() -> no_return(). -mode_error() -> - typer:fatal_error("can not do \"show\", \"show-exported\", \"annotate\", and \"annotate-inc-files\" at the same time"). - --spec version_message() -> no_return(). -version_message() -> - io:format("TypEr version "++?VSN++"\n"), - erlang:halt(0). - --spec help_message() -> no_return(). -help_message() -> - S = " Usage: typer [--help] [--version] [--comments] [--plt PLT] - [--show | --show-exported | --annotate | --annotate-inc-files] - [-Ddefine]* [-I include_dir]* [-T application]* [-r] file* - - Options: - -r dir* - search directories recursively for .erl files below them - --show - Prints type specifications for all functions on stdout. - (this is the default behaviour; this option is not really needed) - --show-exported (or --show_exported) - Same as --show, but prints specifications for exported functions only - Specs are displayed sorted alphabetically on the function's name - --annotate - Annotates the specified files with type specifications - --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 - --plt PLT - Use the specified dialyzer PLT file rather than the default one - -T file* - The specified file(s) already contain type specifications and these - are to be trusted in order to print specs for the rest of the files - (Multiple files or dirs, separated by spaces, can be specified.) - -Dname (or -Dname=value) - pass the defined name(s) to TypEr - (The syntax of defines is the same as that used by \"erlc\".) - -I include_dir - pass the include_dir to TypEr - (The syntax of includes is the same as that used by \"erlc\".) - --version (or -v) - prints the Typer version and exits - --help (or -h) - prints this message and exits - - Note: - * denotes that multiple occurrences of these options are possible. -", - io:put_chars(S), - erlang:halt(0). -- cgit v1.2.3 From b5bfd43fe718ec020f99238c561fdba6a7123c2c Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 6 Feb 2011 17:51:22 +0200 Subject: Remove typer_info.erl file --- lib/typer/src/Makefile | 4 +- lib/typer/src/typer.app.src | 4 +- lib/typer/src/typer.erl | 188 +++++++++++++++++++++++++++++++++++++++--- lib/typer/src/typer_info.erl | 190 ------------------------------------------- 4 files changed, 180 insertions(+), 206 deletions(-) delete mode 100644 lib/typer/src/typer_info.erl (limited to 'lib') 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 @@ -426,6 +426,174 @@ remove_dup(Files) -> Reversed_Elems = lists:foldl(Test_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]). -- cgit v1.2.3 From a3e7c43db2b7774a5bd528da3adb453d3a6dcfa7 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 6 Feb 2011 18:19:46 +0200 Subject: Remove typer_annotator.erl and typer.hrl --- lib/typer/src/Makefile | 9 +- lib/typer/src/typer.app.src | 3 +- lib/typer/src/typer.erl | 450 +++++++++++++++++++++++++++++++++----- lib/typer/src/typer.hrl | 53 ----- lib/typer/src/typer_annotator.erl | 383 -------------------------------- 5 files changed, 400 insertions(+), 498 deletions(-) delete mode 100644 lib/typer/src/typer.hrl delete mode 100644 lib/typer/src/typer_annotator.erl (limited to 'lib') 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", @@ -209,19 +208,362 @@ get_external(Exts, Plt) -> end, 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 -%% 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. -- cgit v1.2.3 From df725fd56bc5917d94256af5f3eeba2a7e8a9b35 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 6 Feb 2011 19:14:52 +0200 Subject: Cleanup the analysis record --- lib/typer/src/typer.erl | 76 +++++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 37 deletions(-) (limited to 'lib') diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index 0393076c1f..86efca6507 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -46,27 +46,24 @@ -type files() :: [file:filename()]. -record(analysis, - {mode :: mode(), - macros = [] :: [{atom(), term()}], % {macro_name, value} - includes = [] :: files(), - %% --- for dialyzer --- + {mode :: mode(), + macros = [] :: [{atom(), term()}], % {macro_name, value} + includes = [] :: files(), code_server = dialyzer_codeserver:new():: dialyzer_codeserver:codeserver(), callgraph = dialyzer_callgraph:new() :: dialyzer_callgraph:callgraph(), - ana_files = [] :: files(), % absolute names - plt = none :: 'none' | file:filename(), - no_spec = false :: boolean(), - %% --- for typer --- - t_files = [] :: files(), + files = [] :: files(), % absolute names + plt = none :: 'none' | file:filename(), + no_spec = false :: 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()}], - ex_func = map__new() :: map(), - record = map__new() :: map(), - func = map__new() :: map(), - inc_func = map__new() :: map(), - trust_plt = dialyzer_plt:new() :: dialyzer_plt:plt()}). + edoc = false :: boolean(), + %% Files in 'fms' are compilable with option 'to_pp'; we keep them + %% as {FileName, ModuleName} in case the ModuleName is different + fms = [] :: [{file:filename(), module()}], + ex_func = map__new() :: map(), + record = map__new() :: map(), + func = map__new() :: map(), + inc_func = map__new() :: map(), + trust_plt = dialyzer_plt:new() :: dialyzer_plt:plt()}). -type analysis() :: #analysis{}. -record(args, {files = [] :: files(), @@ -83,13 +80,12 @@ 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#analysis{t_files = TrustedFiles}, - Analysis2 = extract(Analysis1), + Analysis2 = extract(Analysis, TrustedFiles), All_Files = get_all_files(Args), %% io:format("All_Files: ~p\n", [All_Files]), - Analysis3 = Analysis2#analysis{ana_files = All_Files}, + Analysis3 = Analysis2#analysis{files = All_Files}, Analysis4 = collect_info(Analysis3), - %% io:format("Final: ~p\n", [Analysis4#analysis.final_files]), + %% io:format("Final: ~p\n", [Analysis4#analysis.fms]), TypeInfo = get_type_info(Analysis4), show_or_annotate(TypeInfo), %% io:format("\nTyper analysis finished\n"), @@ -97,10 +93,11 @@ start() -> %%-------------------------------------------------------------------- --spec extract(analysis()) -> analysis(). +-spec extract(analysis(), files()) -> analysis(). -extract(#analysis{macros = Macros, includes = Includes, - t_files = TFiles, trust_plt = TrustPLT} = Analysis) -> +extract(#analysis{macros = Macros, + includes = Includes, + trust_plt = TrustPLT} = Analysis, TrustedFiles) -> %% io:format("--- Extracting trusted typer_info... "), Ds = [{d, Name, Value} || {Name, Value} <- Macros], CodeServer = dialyzer_codeserver:new(), @@ -128,7 +125,7 @@ extract(#analysis{macros = Macros, includes = Includes, {error, Reason} -> compile_error(Reason) end end, - CodeServer1 = lists:foldl(Fun, CodeServer, TFiles), + CodeServer1 = lists:foldl(Fun, CodeServer, TrustedFiles), %% Process remote types NewCodeServer = try @@ -224,7 +221,7 @@ get_external(Exts, Plt) -> -spec show_or_annotate(analysis()) -> 'ok'. -show_or_annotate(#analysis{mode = Mode, final_files = Files} = Analysis) -> +show_or_annotate(#analysis{mode = Mode, fms = Files} = Analysis) -> case Mode of ?SHOW -> show(Analysis); ?SHOW_EXPORTED -> show(Analysis); @@ -246,7 +243,7 @@ write_and_collect_inc_info(Analysis) -> IncFuns = get_functions(File, Analysis), collect_imported_functions(IncFuns, Info#info.types, Inc) end, - NewInc = lists:foldl(Fun, #inc{}, Analysis#analysis.final_files), + NewInc = lists:foldl(Fun, #inc{}, Analysis#analysis.fms), clean_inc(NewInc). write_inc_files(Inc) -> @@ -273,7 +270,7 @@ show(Analysis) -> Info = get_final_info(File, Module, Analysis), show_type_info(File, Info) end, - lists:foreach(Fun, Analysis#analysis.final_files). + lists:foreach(Fun, Analysis#analysis.fms). get_final_info(File, Module, Analysis) -> Records = get_records(File, Analysis), @@ -455,12 +452,15 @@ write_typed_file(File, Info) -> ok = file:delete(NewFileName), write_typed_file(File, Info, NewFileName); enospc -> - io:format(" Not enough space in ~p\n", [Dir]); + Msg = io_lib:format("Not enough space in ~p\n", [Dir]), + fatal_error(Msg); eacces -> - io:format(" No write permission in ~p\n", [Dir]); + Msg = io:format("No write permission in ~p\n", [Dir]), + fatal_error(Msg); _ -> - io:format("Unhandled error ~s when writing ~p\n", [Reason, Dir]), - halt() + Msg = io_lib:format("Unhandled error ~s when writing ~p\n", + [Reason, Dir]), + fatal_error(Msg) end; ok -> %% Typer dir does NOT exist write_typed_file(File, Info, NewFileName) @@ -772,7 +772,8 @@ remove_dup(Files) -> %% Collect information. %%-------------------------------------------------------------------- --type func_info() :: {non_neg_integer(), atom(), arity()}. +-type line() :: non_neg_integer(). +-type func_info() :: {line(), atom(), arity()}. -type inc_file_info() :: {file:filename(), func_info()}. -record(tmpAcc, {file :: file:filename(), @@ -794,7 +795,7 @@ collect_info(Analysis) -> end, NewAnalysis = lists:foldl(fun collect_one_file_info/2, Analysis#analysis{trust_plt = NewPlt}, - Analysis#analysis.ana_files), + Analysis#analysis.files), %% Process Remote Types TmpCServer = NewAnalysis#analysis.code_server, NewCServer = @@ -879,9 +880,9 @@ analyze_core_tree(Core, Records, SpecInfo, ExpTypes, Analysis, File) -> %% which are imported from included files. IncFuncMap = map__insert({File, Acc#tmpAcc.incFuncAcc}, Analysis#analysis.inc_func), - Final_Files = Analysis#analysis.final_files ++ [{File, Module}], + FMs = Analysis#analysis.fms ++ [{File, Module}], RecordMap = map__insert({File, Records}, Analysis#analysis.record), - Analysis#analysis{final_files = Final_Files, + Analysis#analysis{fms = FMs, callgraph = CG, code_server = CS6, ex_func = Exported_FuncMap, @@ -947,6 +948,7 @@ fatal_error(Slogan) -> erlang:halt(1). -spec mode_error(mode(), mode()) -> no_return(). + mode_error(OldMode, NewMode) -> Msg = io_lib:format("Mode was previously set to '~s'; " "can not set it to '~s' now", -- cgit v1.2.3 From fde7e1e302d78cdf7d6554804ca785bf637fcd0a Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 6 Feb 2011 19:43:06 +0200 Subject: Type cleanups and simplifications --- lib/typer/src/typer.erl | 69 ++++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 33 deletions(-) (limited to 'lib') diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index 86efca6507..f6b6de6261 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -24,8 +24,8 @@ %% with guidance from Kostis Sagonas and Tobias Lindahl. %% Since June 2008 typer is maintained by Kostis Sagonas. %% Description : An Erlang/OTP application that shows type information -%% for Erlang modules to the user. Additionally, it can -%% annotates the code of files with such type information. +%% for Erlang modules to the user. Additionally, it can +%% annotate the code of files with such type information. %%----------------------------------------------------------------------- -module(typer). @@ -43,27 +43,30 @@ %%----------------------------------------------------------------------- --type files() :: [file:filename()]. +-type files() :: [file:filename()]. +-type callgraph() :: dialyzer_callgraph:callgraph(). +-type codeserver() :: dialyzer_codeserver:codeserver(). +-type plt() :: dialyzer_plt:plt(). -record(analysis, - {mode :: mode(), - macros = [] :: [{atom(), term()}], % {macro_name, value} - includes = [] :: files(), - code_server = dialyzer_codeserver:new():: dialyzer_codeserver:codeserver(), - callgraph = dialyzer_callgraph:new() :: dialyzer_callgraph:callgraph(), - files = [] :: files(), % absolute names - plt = none :: 'none' | file:filename(), - no_spec = false :: boolean(), + {mode :: mode() | 'undefined', + macros = [] :: [{atom(), term()}], + includes = [] :: files(), + codeserver = dialyzer_codeserver:new():: codeserver(), + callgraph = dialyzer_callgraph:new() :: callgraph(), + files = [] :: files(), % absolute names + plt = none :: 'none' | file:filename(), + no_spec = false :: boolean(), %% For choosing between specs or edoc @spec comments - edoc = false :: boolean(), + edoc = false :: boolean(), %% Files in 'fms' are compilable with option 'to_pp'; we keep them %% as {FileName, ModuleName} in case the ModuleName is different - fms = [] :: [{file:filename(), module()}], - ex_func = map__new() :: map(), - record = map__new() :: map(), - func = map__new() :: map(), - inc_func = map__new() :: map(), - trust_plt = dialyzer_plt:new() :: dialyzer_plt:plt()}). + fms = [] :: [{file:filename(), module()}], + ex_func = map__new() :: map(), + record = map__new() :: map(), + func = map__new() :: map(), + inc_func = map__new() :: map(), + trust_plt = dialyzer_plt:new() :: plt()}). -type analysis() :: #analysis{}. -record(args, {files = [] :: files(), @@ -158,7 +161,7 @@ extract(#analysis{macros = Macros, get_type_info(#analysis{callgraph = CallGraph, trust_plt = TrustPLT, - code_server = CodeServer} = Analysis) -> + codeserver = CodeServer} = Analysis) -> StrippedCallGraph = remove_external(CallGraph, TrustPLT), %% io:format("--- Analyzing callgraph... "), try @@ -173,7 +176,7 @@ get_type_info(#analysis{callgraph = CallGraph, fatal_error(io_lib:format("Analysis failed with message: ~s", [Msg])) end. --spec remove_external(dialyzer_callgraph:callgraph(), dialyzer_plt:plt()) -> dialyzer_callgraph:callgraph(). +-spec remove_external(callgraph(), plt()) -> callgraph(). remove_external(CallGraph, PLT) -> {StrippedCG0, Ext} = dialyzer_callgraph:remove_external(CallGraph), @@ -190,7 +193,7 @@ remove_external(CallGraph, PLT) -> end, StrippedCG. --spec get_external([{mfa(), mfa()}], dialyzer_plt:plt()) -> [mfa()]. +-spec get_external([{mfa(), mfa()}], plt()) -> [mfa()]. get_external(Exts, Plt) -> Fun = fun ({_From, To = {M, F, A}}, Acc) -> @@ -211,13 +214,15 @@ get_external(Exts, Plt) -> -define(TYPER_ANN_DIR, "typer_ann"). --type fun_info() :: {non_neg_integer(), atom(), arity()}. +-type line() :: non_neg_integer(). +-type func_info() :: {line(), atom(), arity()}. -record(info, {records = map__new() :: map(), - functions = [] :: [fun_info()], + functions = [] :: [func_info()], types = map__new() :: map(), edoc = false :: boolean()}). -record(inc, {map = map__new() :: map(), filter = [] :: files()}). +-type inc() :: #inc{}. -spec show_or_annotate(analysis()) -> 'ok'. @@ -252,7 +257,7 @@ write_inc_files(Inc) -> Val = map__lookup(File, Inc#inc.map), %% Val is function with its type info %% in form [{{Line,F,A},Type}] - Functions = [Key || {Key,_} <- Val], + Functions = [Key || {Key, _} <- Val], Val1 = [{{F,A},Type} || {{_Line,F,A},Type} <- Val], Info = #info{types = map__from_list(Val1), records = map__new(), @@ -294,7 +299,7 @@ collect_imported_functions(Functions, Types, Inc) -> end, lists:foldl(Fun, Inc, Functions). --spec is_yecc_gen(file:filename(), #inc{}) -> {boolean(), #inc{}}. +-spec is_yecc_gen(file:filename(), inc()) -> {boolean(), inc()}. is_yecc_gen(File, #inc{filter = Fs} = Inc) -> case lists:member(File, Fs) of @@ -380,7 +385,7 @@ get_types(Module, Analysis, Records) -> none -> []; {value, List} -> List end, - CodeServer = Analysis#analysis.code_server, + CodeServer = Analysis#analysis.codeserver, TypeInfoList = [get_type(I, CodeServer, Records) || I <- TypeInfo], map__from_list(TypeInfoList). @@ -772,8 +777,6 @@ remove_dup(Files) -> %% Collect information. %%-------------------------------------------------------------------- --type line() :: non_neg_integer(). --type func_info() :: {line(), atom(), arity()}. -type inc_file_info() :: {file:filename(), func_info()}. -record(tmpAcc, {file :: file:filename(), @@ -797,7 +800,7 @@ collect_info(Analysis) -> Analysis#analysis{trust_plt = NewPlt}, Analysis#analysis.files), %% Process Remote Types - TmpCServer = NewAnalysis#analysis.code_server, + TmpCServer = NewAnalysis#analysis.codeserver, NewCServer = try NewRecords = dialyzer_codeserver:get_temp_records(TmpCServer), @@ -817,7 +820,7 @@ collect_info(Analysis) -> throw:{error, ErrorMsg} -> fatal_error(ErrorMsg) end, - NewAnalysis#analysis{code_server = NewCServer}. + NewAnalysis#analysis{codeserver = NewCServer}. collect_one_file_info(File, Analysis) -> Ds = [{d,Name,Val} || {Name,Val} <- Analysis#analysis.macros], @@ -851,7 +854,7 @@ 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#analysis.code_server, + CS1 = Analysis#analysis.codeserver, NextLabel = dialyzer_codeserver:get_next_core_label(CS1), {Tree, NewLabel} = cerl_trees:label(TmpTree, NextLabel), CS2 = dialyzer_codeserver:insert(Module, Tree, CS1), @@ -884,7 +887,7 @@ analyze_core_tree(Core, Records, SpecInfo, ExpTypes, Analysis, File) -> RecordMap = map__insert({File, Records}, Analysis#analysis.record), Analysis#analysis{fms = FMs, callgraph = CG, - code_server = CS6, + codeserver = CS6, ex_func = Exported_FuncMap, inc_func = IncFuncMap, record = RecordMap, @@ -915,7 +918,7 @@ analyze_one_function({Var, FunBody} = Function, Acc) -> incFuncAcc = IncFuncAcc, dialyzerObj = NewDialyzerObj}. --spec get_dialyzer_plt(analysis()) -> dialyzer_plt:plt(). +-spec get_dialyzer_plt(analysis()) -> plt(). get_dialyzer_plt(#analysis{plt = PltFile0}) -> PltFile = -- cgit v1.2.3 From 28af8292387864f1d8467ddd6d8d6bb343e6851b Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 6 Feb 2011 23:46:15 +0200 Subject: Fix a type error and do some further cleanup --- lib/typer/src/typer.erl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index f6b6de6261..0e91e795af 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -433,7 +433,7 @@ get_functions(File, Analysis) -> normalize_incFuncs(Functions) -> [FunInfo || {_FileName, FunInfo} <- Functions]. --spec remove_module_info([fun_info()]) -> [fun_info()]. +-spec remove_module_info([func_info()]) -> [func_info()]. remove_module_info(FunInfoList) -> F = fun ({_,module_info,0}) -> false; @@ -873,15 +873,14 @@ analyze_core_tree(Core, Records, SpecInfo, ExpTypes, Analysis, File) -> 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), + Acc = lists:foldl(Fun, #tmpAcc{file = File, module = Module}, All_Defs), Exported_FuncMap = map__insert({File, Ex_Funcs}, Analysis#analysis.ex_func), - %% NOTE: we must sort all functions in the file which + %% 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#analysis.func), - %% NOTE: However we do not need to sort functions - %% which are imported from included files. - IncFuncMap = map__insert({File, Acc#tmpAcc.incFuncAcc}, + %% we do not need to sort functions which are imported from included files + IncFuncMap = map__insert({File, Acc#tmpAcc.incFuncAcc}, Analysis#analysis.inc_func), FMs = Analysis#analysis.fms ++ [{File, Module}], RecordMap = map__insert({File, Records}, Analysis#analysis.record), @@ -983,13 +982,15 @@ msg(Msg) -> %%-------------------------------------------------------------------- -spec version_message() -> no_return(). + version_message() -> io:format("TypEr version "++?VSN++"\n"), erlang:halt(0). -spec help_message() -> no_return(). + help_message() -> - S = " Usage: typer [--help] [--version] [--plt PLT] [--edoc] + S = <<" Usage: typer [--help] [--version] [--plt PLT] [--edoc] [--show | --show-exported | --annotate | --annotate-inc-files] [-Ddefine]* [-I include_dir]* [-T application]* [-r] file* @@ -1028,7 +1029,7 @@ help_message() -> Note: * denotes that multiple occurrences of these options are possible. -", +">>, io:put_chars(S), erlang:halt(0). -- cgit v1.2.3 From d8aca1e414c1c39be7fe8292762fbd6a370b37c6 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Tue, 8 Feb 2011 13:14:31 +0200 Subject: Fix crash in oveloaded contracts with overlapping domains Typer should not crash miserably when processing a user-specified overloaded contract with overlapping types in its arguments. --- lib/typer/src/typer.erl | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index 0e91e795af..05cc873c2c 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -399,6 +399,8 @@ get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, Records) -> ok -> {{F, A}, {contract, Contract}}; {error, {extra_range, _, _}} -> {{F, A}, {contract, Contract}}; + {error, {overlapping_contract, []}} -> + {{F, A}, {contract, Contract}}; {error, invalid_contract} -> CString = dialyzer_contracts:contract_to_string(Contract), SigString = dialyzer_utils:format_sig(Sig, Records), -- cgit v1.2.3 From 405342e5adac19e4522bff90ffd4bda39f742c9a Mon Sep 17 00:00:00 2001 From: Stavros Aronis Date: Tue, 8 Feb 2011 16:58:46 +0200 Subject: Add '--show_success_typings' option With '--show_success_typings' Typer will print/use the final success typings from Dialyzer and ignore/overwrite any existing contracts. --- lib/typer/src/typer.erl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index 05cc873c2c..a194770182 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -57,6 +57,7 @@ files = [] :: files(), % absolute names plt = none :: 'none' | file:filename(), no_spec = false :: boolean(), + show_succ = false :: boolean(), %% For choosing between specs or edoc @spec comments edoc = false :: boolean(), %% Files in 'fms' are compilable with option 'to_pp'; we keep them @@ -386,9 +387,18 @@ get_types(Module, Analysis, Records) -> {value, List} -> List end, CodeServer = Analysis#analysis.codeserver, - TypeInfoList = [get_type(I, CodeServer, Records) || I <- TypeInfo], + TypeInfoList = + case Analysis#analysis.show_succ of + true -> + [convert_type_info(I) || I <- TypeInfo]; + false -> + [get_type(I, CodeServer, Records) || I <- TypeInfo] + end, map__from_list(TypeInfoList). +convert_type_info({{_M, F, A}, Range, Arg}) -> + {{F, A}, {Range, Arg}}. + get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, Records) -> case dialyzer_codeserver:lookup_mfa_contract(MFA, CodeServer) of error -> @@ -589,6 +599,7 @@ 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}; +cl(["--show-success-typings"|Opts]) -> {show_succ, Opts}; cl(["--annotate"|Opts]) -> {{mode, ?ANNOTATE}, Opts}; cl(["--annotate-inc-files"|Opts]) -> {{mode, ?ANNOTATE_INC_FILES}, Opts}; cl(["--no_spec"|Opts]) -> {no_spec, Opts}; @@ -656,6 +667,8 @@ analyze_result({inc, Val}, Args, Analysis) -> {Args, Analysis#analysis{includes = NewVal}}; analyze_result({plt, Plt}, Args, Analysis) -> {Args, Analysis#analysis{plt = Plt}}; +analyze_result(show_succ, Args, Analysis) -> + {Args, Analysis#analysis{show_succ = true}}; analyze_result(no_spec, Args, Analysis) -> {Args, Analysis#analysis{no_spec = true}}. -- cgit v1.2.3 From 179fff827985bc314f2a4cc953d66cdfebb05a57 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Thu, 10 Feb 2011 10:17:37 +0200 Subject: Allow for --show_success_typings spelling also --- lib/typer/src/typer.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index a194770182..8955ebe4aa 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -599,6 +599,7 @@ 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}; +cl(["--show_success_typings"|Opts]) -> {show_succ, Opts}; cl(["--show-success-typings"|Opts]) -> {show_succ, Opts}; cl(["--annotate"|Opts]) -> {{mode, ?ANNOTATE}, Opts}; cl(["--annotate-inc-files"|Opts]) -> {{mode, ?ANNOTATE_INC_FILES}, Opts}; -- cgit v1.2.3 From 4d7ada26e135a633b469ce6250b47d4210472a2c Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Thu, 10 Feb 2011 10:40:28 +0200 Subject: Strengthen some specs --- lib/typer/src/typer.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index 8955ebe4aa..fc8caa4f21 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -216,6 +216,7 @@ get_external(Exts, Plt) -> -define(TYPER_ANN_DIR, "typer_ann"). -type line() :: non_neg_integer(). +-type fa() :: {atom(), arity()}. -type func_info() :: {line(), atom(), arity()}. -record(info, {records = map__new() :: map(), @@ -677,7 +678,7 @@ analyze_result(no_spec, Args, Analysis) -> %% File processing. %%-------------------------------------------------------------------- --spec get_all_files(args()) -> files(). +-spec get_all_files(args()) -> [file:filename(),...]. get_all_files(#args{files = Fs, files_r = Ds}) -> case filter_fd(Fs, Ds, fun test_erl_file_exclude_ann/1) of @@ -1068,6 +1069,7 @@ rcv_ext_types(Self, ExtTypes) -> %%-------------------------------------------------------------------- %% A convenient abstraction of a Key-Value mapping data structure +%% specialized for the uses in this module %%-------------------------------------------------------------------- -type map() :: dict(). @@ -1085,7 +1087,7 @@ map__insert(Object, Map) -> map__lookup(Key, Map) -> try dict:fetch(Key, Map) catch error:_ -> none end. --spec map__from_list([{term(), term()}]) -> map(). +-spec map__from_list([{fa(), term()}]) -> map(). map__from_list(List) -> dict:from_list(List). @@ -1093,6 +1095,6 @@ map__from_list(List) -> map__remove(Key, Dict) -> dict:erase(Key, Dict). --spec map__fold(fun((term(), term(), term()) -> term()), term(), map()) -> term(). +-spec map__fold(fun((term(), term(), term()) -> map()), map(), map()) -> map(). map__fold(Fun, Acc0, Dict) -> dict:fold(Fun, Acc0, Dict). -- cgit v1.2.3 From 3752c716606ad74b2affcb19d41f18b217ee7956 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Wed, 9 Mar 2011 09:26:17 +0200 Subject: Add release notes and up version --- lib/typer/RELEASE_NOTES | 22 ++++++++++++++++++++++ lib/typer/vsn.mk | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 lib/typer/RELEASE_NOTES (limited to 'lib') diff --git a/lib/typer/RELEASE_NOTES b/lib/typer/RELEASE_NOTES new file mode 100644 index 0000000000..d91a815ee9 --- /dev/null +++ b/lib/typer/RELEASE_NOTES @@ -0,0 +1,22 @@ +============================================================================== + Major features, additions and changes between Typer versions + (in reversed chronological order) +============================================================================== + +Version 0.9 (in Erlang/OTP R14B02) +---------------------------------- + - Major rewrite; all code has been cleaned up and placed in one file. + The only reason why this is not version 1.0 yet is that there is no proper + documentation for typer which can be displayed in the www.erlang.org site. + - Added ability to receive the set of exported types and report unknown ones. + - Better handling of overloaded contracts; especially erroneous ones on which + typer does not crash anymore. + - Fixed problem that caused typer to hang when given a file whose module name + did not correspond to the file name. + - Added two undocumented options that may come very handy when trying to + understand why typer reports some particular set of types for the functions + in a module. These options are mainly for typer developers at this point, + but may become documented in some future version. + +Older versions +-------------- diff --git a/lib/typer/vsn.mk b/lib/typer/vsn.mk index 83c37e307b..51561939ac 100644 --- a/lib/typer/vsn.mk +++ b/lib/typer/vsn.mk @@ -1 +1 @@ -TYPER_VSN = 0.2 +TYPER_VSN = 0.9 -- cgit v1.2.3