aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/typer/src/Makefile1
-rw-r--r--lib/typer/src/typer.app.src1
-rw-r--r--lib/typer/src/typer.erl113
-rw-r--r--lib/typer/src/typer.hrl8
-rw-r--r--lib/typer/src/typer_annotator.erl60
-rw-r--r--lib/typer/src/typer_info.erl12
-rw-r--r--lib/typer/src/typer_options.erl10
-rw-r--r--lib/typer/src/typer_preprocess.erl8
8 files changed, 138 insertions, 75 deletions
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 <[email protected]>
-%% 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