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(-) 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