aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/typer/src/Makefile4
-rw-r--r--lib/typer/src/typer.app.src4
-rw-r--r--lib/typer/src/typer.erl188
-rw-r--r--lib/typer/src/typer_info.erl190
4 files changed, 180 insertions, 206 deletions
diff --git a/lib/typer/src/Makefile b/lib/typer/src/Makefile
index f814e943ea..41ed719d1b 100644
--- a/lib/typer/src/Makefile
+++ b/lib/typer/src/Makefile
@@ -45,9 +45,7 @@ DIALYZER_DIR = $(ERL_TOP)/lib/dialyzer
# ----------------------------------------------------
# Target Specs
# ----------------------------------------------------
-MODULES = typer \
- typer_annotator \
- typer_info
+MODULES = typer typer_annotator
HRL_FILES= typer.hrl
ERL_FILES= $(MODULES:%=%.erl)
diff --git a/lib/typer/src/typer.app.src b/lib/typer/src/typer.app.src
index 2109f7fc37..d3923e1953 100644
--- a/lib/typer/src/typer.app.src
+++ b/lib/typer/src/typer.app.src
@@ -4,9 +4,7 @@
[{description, "TYPe annotator for ERlang programs, version %VSN%"},
{vsn, "%VSN%"},
{modules, [typer,
- typer_annotator,
- typer_info,
- typer_options]},
+ typer_annotator]},
{registered, []},
{applications, [compiler, dialyzer, hipe, kernel, stdlib]},
{env, []}]}.
diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl
index 54818553da..01a068a85d 100644
--- a/lib/typer/src/typer.erl
+++ b/lib/typer/src/typer.erl
@@ -31,7 +31,7 @@
-module(typer).
-export([start/0]).
--export([fatal_error/1, compile_error/1]). % for error reporting
+-export([fatal_error/1]). % for error reporting
-export([map__new/0, map__insert/2, map__lookup/2, map__from_list/1, map__remove/2, map__fold/3]).
%%-----------------------------------------------------------------------
@@ -59,8 +59,8 @@
no_spec = false :: boolean(),
%% --- for typer ---
t_files = [] :: files(),
- %% For choosing between contracts or comments
- contracts = true :: boolean(),
+ %% For choosing between specs or edoc @spec comments
+ edoc = false :: boolean(),
%% Files in 'final_files' are compilable with option 'to_pp'; we keep
%% them as {FileName, ModuleName} in case the ModuleName is different
final_files = [] :: [{file:filename(), module()}],
@@ -89,7 +89,7 @@ start() ->
All_Files = get_all_files(Args),
%% io:format("All_Files: ~p\n", [All_Files]),
Analysis3 = Analysis2#typer_analysis{ana_files = All_Files},
- Analysis4 = typer_info:collect(Analysis3),
+ Analysis4 = collect_info(Analysis3),
%% io:format("Final: ~p\n", [Analysis4#typer_analysis.final_files]),
TypeInfo = get_type_info(Analysis4),
typer_annotator:annotate(TypeInfo),
@@ -236,7 +236,7 @@ cl(["-h"|_]) -> help_message();
cl(["--help"|_]) -> help_message();
cl(["-v"|_]) -> version_message();
cl(["--version"|_]) -> version_message();
-cl(["--comments"|Opts]) -> {comments, Opts};
+cl(["--edoc"|Opts]) -> {edoc, Opts};
cl(["--show"|Opts]) -> {{mode, ?SHOW}, Opts};
cl(["--show_exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts};
cl(["--show-exported"|Opts]) -> {{mode, ?SHOW_EXPORTED}, Opts};
@@ -291,8 +291,8 @@ analyze_result({files_r, Val}, Args, Analysis) ->
analyze_result({trusted, Val}, Args, Analysis) ->
NewVal = Args#args.trusted ++ Val,
{Args#args{trusted = NewVal}, Analysis};
-analyze_result(comments, Args, Analysis) ->
- {Args, Analysis#typer_analysis{contracts = false}};
+analyze_result(edoc, Args, Analysis) ->
+ {Args, Analysis#typer_analysis{edoc = true}};
%% Get useful information for actual analysis
analyze_result({mode, Mode}, Args, Analysis) ->
case Analysis#typer_analysis.mode of
@@ -427,6 +427,174 @@ remove_dup(Files) ->
lists:reverse(Reversed_Elems).
%%--------------------------------------------------------------------
+%% Collect information.
+%%--------------------------------------------------------------------
+
+-type func_info() :: {non_neg_integer(), atom(), arity()}.
+-type inc_file_info() :: {file:filename(), func_info()}.
+
+-record(tmpAcc, {file :: file:filename(),
+ module :: atom(),
+ funcAcc = [] :: [func_info()],
+ incFuncAcc = [] :: [inc_file_info()],
+ dialyzerObj = [] :: [{mfa(), {_, _}}]}).
+
+-spec collect_info(analysis()) -> analysis().
+
+collect_info(Analysis) ->
+ NewPlt =
+ try get_dialyzer_plt(Analysis) of
+ DialyzerPlt ->
+ dialyzer_plt:merge_plts([Analysis#typer_analysis.trust_plt, DialyzerPlt])
+ catch
+ throw:{dialyzer_error,_Reason} ->
+ fatal_error("Dialyzer's PLT is missing or is not up-to-date; please (re)create it")
+ end,
+ NewAnalysis = lists:foldl(fun collect_one_file_info/2,
+ Analysis#typer_analysis{trust_plt = NewPlt},
+ Analysis#typer_analysis.ana_files),
+ %% Process Remote Types
+ TmpCServer = NewAnalysis#typer_analysis.code_server,
+ NewCServer =
+ try
+ NewRecords = dialyzer_codeserver:get_temp_records(TmpCServer),
+ NewExpTypes = dialyzer_codeserver:get_temp_exported_types(TmpCServer),
+ OldRecords = dialyzer_plt:get_types(NewPlt),
+ OldExpTypes = dialyzer_plt:get_exported_types(NewPlt),
+ MergedRecords = dialyzer_utils:merge_records(NewRecords, OldRecords),
+ MergedExpTypes = sets:union(NewExpTypes, OldExpTypes),
+ %% io:format("Merged Records ~p",[MergedRecords]),
+ TmpCServer1 = dialyzer_codeserver:set_temp_records(MergedRecords, TmpCServer),
+ TmpCServer2 =
+ dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes,
+ TmpCServer1),
+ TmpCServer3 = dialyzer_utils:process_record_remote_types(TmpCServer2),
+ dialyzer_contracts:process_contract_remote_types(TmpCServer3)
+ catch
+ throw:{error, ErrorMsg} ->
+ fatal_error(ErrorMsg)
+ end,
+ NewAnalysis#typer_analysis{code_server = NewCServer}.
+
+collect_one_file_info(File, Analysis) ->
+ Ds = [{d,Name,Val} || {Name,Val} <- Analysis#typer_analysis.macros],
+ %% Current directory should also be included in "Includes".
+ Includes = [filename:dirname(File)|Analysis#typer_analysis.includes],
+ Is = [{i,Dir} || Dir <- Includes],
+ Options = dialyzer_utils:src_compiler_opts() ++ Is ++ Ds,
+ case dialyzer_utils:get_abstract_code_from_src(File, Options) of
+ {error, Reason} ->
+ %% io:format("File=~p\n,Options=~p\n,Error=~p\n", [File,Options,Reason]),
+ compile_error(Reason);
+ {ok, AbstractCode} ->
+ case dialyzer_utils:get_core_from_abstract_code(AbstractCode, Options) of
+ error -> compile_error(["Could not get core erlang for "++File]);
+ {ok, Core} ->
+ case dialyzer_utils:get_record_and_type_info(AbstractCode) of
+ {error, Reason} -> compile_error([Reason]);
+ {ok, Records} ->
+ Mod = cerl:concrete(cerl:module_name(Core)),
+ case dialyzer_utils:get_spec_info(Mod, AbstractCode, Records) of
+ {error, Reason} -> compile_error([Reason]);
+ {ok, SpecInfo} ->
+ ExpTypes = get_exported_types_from_core(Core),
+ analyze_core_tree(Core, Records, SpecInfo, ExpTypes,
+ Analysis, File)
+ end
+ end
+ end
+ end.
+
+analyze_core_tree(Core, Records, SpecInfo, ExpTypes, Analysis, File) ->
+ Module = cerl:concrete(cerl:module_name(Core)),
+ TmpTree = cerl:from_records(Core),
+ CS1 = Analysis#typer_analysis.code_server,
+ NextLabel = dialyzer_codeserver:get_next_core_label(CS1),
+ {Tree, NewLabel} = cerl_trees:label(TmpTree, NextLabel),
+ CS2 = dialyzer_codeserver:insert(Module, Tree, CS1),
+ CS3 = dialyzer_codeserver:set_next_core_label(NewLabel, CS2),
+ CS4 = dialyzer_codeserver:store_temp_records(Module, Records, CS3),
+ CS5 =
+ case Analysis#typer_analysis.no_spec of
+ true -> CS4;
+ false -> dialyzer_codeserver:store_temp_contracts(Module, SpecInfo, CS4)
+ end,
+ OldExpTypes = dialyzer_codeserver:get_temp_exported_types(CS5),
+ MergedExpTypes = sets:union(ExpTypes, OldExpTypes),
+ CS6 = dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, CS5),
+ Ex_Funcs = [{0,F,A} || {_,_,{F,A}} <- cerl:module_exports(Tree)],
+ TmpCG = Analysis#typer_analysis.callgraph,
+ CG = dialyzer_callgraph:scan_core_tree(Tree, TmpCG),
+ Fun = fun analyze_one_function/2,
+ All_Defs = cerl:module_defs(Tree),
+ Acc = lists:foldl(Fun, #tmpAcc{file=File, module=Module}, All_Defs),
+ Exported_FuncMap = map__insert({File, Ex_Funcs},
+ Analysis#typer_analysis.ex_func),
+ %% NOTE: we must sort all functions in the file which
+ %% originate from this file by *numerical order* of lineNo
+ Sorted_Functions = lists:keysort(1, Acc#tmpAcc.funcAcc),
+ FuncMap = map__insert({File, Sorted_Functions}, Analysis#typer_analysis.func),
+ %% NOTE: However we do not need to sort functions
+ %% which are imported from included files.
+ IncFuncMap = map__insert({File, Acc#tmpAcc.incFuncAcc},
+ Analysis#typer_analysis.inc_func),
+ Final_Files = Analysis#typer_analysis.final_files ++ [{File, Module}],
+ RecordMap = map__insert({File, Records}, Analysis#typer_analysis.record),
+ Analysis#typer_analysis{final_files=Final_Files,
+ callgraph=CG,
+ code_server=CS6,
+ ex_func=Exported_FuncMap,
+ inc_func=IncFuncMap,
+ record=RecordMap,
+ func=FuncMap}.
+
+analyze_one_function({Var, FunBody} = Function, Acc) ->
+ F = cerl:fname_id(Var),
+ A = cerl:fname_arity(Var),
+ TmpDialyzerObj = {{Acc#tmpAcc.module, F, A}, Function},
+ NewDialyzerObj = Acc#tmpAcc.dialyzerObj ++ [TmpDialyzerObj],
+ [_, LineNo, {file, FileName}] = cerl:get_ann(FunBody),
+ BaseName = filename:basename(FileName),
+ FuncInfo = {LineNo, F, A},
+ OriginalName = Acc#tmpAcc.file,
+ {FuncAcc, IncFuncAcc} =
+ case (FileName =:= OriginalName) orelse (BaseName =:= OriginalName) of
+ true -> %% Coming from original file
+ %% io:format("Added function ~p\n", [{LineNo, F, A}]),
+ {Acc#tmpAcc.funcAcc ++ [FuncInfo], Acc#tmpAcc.incFuncAcc};
+ false ->
+ %% Coming from other sourses, including:
+ %% -- .yrl (yecc-generated file)
+ %% -- yeccpre.hrl (yecc-generated file)
+ %% -- other cases
+ {Acc#tmpAcc.funcAcc, Acc#tmpAcc.incFuncAcc ++ [{FileName, FuncInfo}]}
+ end,
+ Acc#tmpAcc{funcAcc = FuncAcc,
+ incFuncAcc = IncFuncAcc,
+ dialyzerObj = NewDialyzerObj}.
+
+get_dialyzer_plt(#typer_analysis{plt = PltFile0}) ->
+ PltFile =
+ case PltFile0 =:= none of
+ true -> dialyzer_plt:get_default_plt();
+ false -> PltFile0
+ end,
+ dialyzer_plt:from_file(PltFile).
+
+
+%% Exported Types
+
+get_exported_types_from_core(Core) ->
+ Attrs = cerl:module_attrs(Core),
+ ExpTypes1 = [cerl:concrete(L2) || {L1, L2} <- Attrs,
+ cerl:is_literal(L1),
+ cerl:is_literal(L2),
+ cerl:concrete(L1) =:= 'export_type'],
+ ExpTypes2 = lists:flatten(ExpTypes1),
+ M = cerl:atom_val(cerl:module_name(Core)),
+ sets:from_list([{M, F, A} || {F, A} <- ExpTypes2]).
+
+%%--------------------------------------------------------------------
%% Utilities for error reporting.
%%--------------------------------------------------------------------
@@ -474,7 +642,7 @@ version_message() ->
-spec help_message() -> no_return().
help_message() ->
- S = " Usage: typer [--help] [--version] [--comments] [--plt PLT]
+ S = " Usage: typer [--help] [--version] [--plt PLT] [--edoc]
[--show | --show-exported | --annotate | --annotate-inc-files]
[-Ddefine]* [-I include_dir]* [-T application]* [-r] file*
@@ -492,8 +660,8 @@ help_message() ->
--annotate-inc-files
Same as --annotate but annotates all -include() files as well as
all .erl files (use this option with caution - has not been tested much)
- --comments
- Prints type information using Edoc comments, not type specs
+ --edoc
+ Prints type information as Edoc @spec comments, not as type specs
--plt PLT
Use the specified dialyzer PLT file rather than the default one
-T file*
diff --git a/lib/typer/src/typer_info.erl b/lib/typer/src/typer_info.erl
deleted file mode 100644
index a568518ffe..0000000000
--- a/lib/typer/src/typer_info.erl
+++ /dev/null
@@ -1,190 +0,0 @@
-%% -*- erlang-indent-level: 2 -*-
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2006-2011. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
--module(typer_info).
-
--export([collect/1]).
-
--type func_info() :: {non_neg_integer(), atom(), arity()}.
--type inc_file_info() :: {file:filename(), func_info()}.
-
--record(tmpAcc, {file :: file:filename(),
- module :: atom(),
- funcAcc=[] :: [func_info()],
- incFuncAcc=[] :: [inc_file_info()],
- dialyzerObj=[] :: [{mfa(), {_, _}}]}).
-
--include("typer.hrl").
-
--spec collect(#typer_analysis{}) -> #typer_analysis{}.
-
-collect(Analysis) ->
- NewPlt =
- try get_dialyzer_plt(Analysis) of
- DialyzerPlt ->
- dialyzer_plt:merge_plts([Analysis#typer_analysis.trust_plt, DialyzerPlt])
- catch
- throw:{dialyzer_error,_Reason} ->
- typer:fatal_error("Dialyzer's PLT is missing or is not up-to-date; please (re)create it")
- end,
- NewAnalysis = lists:foldl(fun collect_one_file_info/2,
- Analysis#typer_analysis{trust_plt = NewPlt},
- Analysis#typer_analysis.ana_files),
- %% Process Remote Types
- TmpCServer = NewAnalysis#typer_analysis.code_server,
- NewCServer =
- try
- NewRecords = dialyzer_codeserver:get_temp_records(TmpCServer),
- NewExpTypes = dialyzer_codeserver:get_temp_exported_types(TmpCServer),
- OldRecords = dialyzer_plt:get_types(NewPlt),
- OldExpTypes = dialyzer_plt:get_exported_types(NewPlt),
- MergedRecords = dialyzer_utils:merge_records(NewRecords, OldRecords),
- MergedExpTypes = sets:union(NewExpTypes, OldExpTypes),
- %% io:format("Merged Records ~p",[MergedRecords]),
- TmpCServer1 = dialyzer_codeserver:set_temp_records(MergedRecords, TmpCServer),
- TmpCServer2 =
- dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes,
- TmpCServer1),
- TmpCServer3 = dialyzer_utils:process_record_remote_types(TmpCServer2),
- dialyzer_contracts:process_contract_remote_types(TmpCServer3)
- catch
- throw:{error, ErrorMsg} ->
- typer:fatal_error(ErrorMsg)
- end,
- NewAnalysis#typer_analysis{code_server = NewCServer}.
-
-collect_one_file_info(File, Analysis) ->
- Ds = [{d,Name,Val} || {Name,Val} <- Analysis#typer_analysis.macros],
- %% Current directory should also be included in "Includes".
- Includes = [filename:dirname(File)|Analysis#typer_analysis.includes],
- Is = [{i,Dir} || Dir <- Includes],
- Options = dialyzer_utils:src_compiler_opts() ++ Is ++ Ds,
- case dialyzer_utils:get_abstract_code_from_src(File, Options) of
- {error, Reason} ->
- %% io:format("File=~p\n,Options=~p\n,Error=~p\n", [File,Options,Reason]),
- typer:compile_error(Reason);
- {ok, AbstractCode} ->
- case dialyzer_utils:get_core_from_abstract_code(AbstractCode, Options) of
- error -> typer:compile_error(["Could not get core erlang for "++File]);
- {ok, Core} ->
- case dialyzer_utils:get_record_and_type_info(AbstractCode) of
- {error, Reason} -> typer:compile_error([Reason]);
- {ok, Records} ->
- Mod = cerl:concrete(cerl:module_name(Core)),
- case dialyzer_utils:get_spec_info(Mod, AbstractCode, Records) of
- {error, Reason} -> typer:compile_error([Reason]);
- {ok, SpecInfo} ->
- ExpTypes = get_exported_types_from_core(Core),
- analyze_core_tree(Core, Records, SpecInfo, ExpTypes,
- Analysis, File)
- end
- end
- end
- end.
-
-analyze_core_tree(Core, Records, SpecInfo, ExpTypes, Analysis, File) ->
- Module = cerl:concrete(cerl:module_name(Core)),
- TmpTree = cerl:from_records(Core),
- CS1 = Analysis#typer_analysis.code_server,
- NextLabel = dialyzer_codeserver:get_next_core_label(CS1),
- {Tree, NewLabel} = cerl_trees:label(TmpTree, NextLabel),
- CS2 = dialyzer_codeserver:insert(Module, Tree, CS1),
- CS3 = dialyzer_codeserver:set_next_core_label(NewLabel, CS2),
- CS4 = dialyzer_codeserver:store_temp_records(Module, Records, CS3),
- CS5 =
- case Analysis#typer_analysis.no_spec of
- true -> CS4;
- false -> dialyzer_codeserver:store_temp_contracts(Module, SpecInfo, CS4)
- end,
- OldExpTypes = dialyzer_codeserver:get_temp_exported_types(CS5),
- MergedExpTypes = sets:union(ExpTypes, OldExpTypes),
- CS6 = dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, CS5),
- Ex_Funcs = [{0,F,A} || {_,_,{F,A}} <- cerl:module_exports(Tree)],
- TmpCG = Analysis#typer_analysis.callgraph,
- CG = dialyzer_callgraph:scan_core_tree(Tree, TmpCG),
- Fun = fun analyze_one_function/2,
- All_Defs = cerl:module_defs(Tree),
- Acc = lists:foldl(Fun, #tmpAcc{file=File, module=Module}, All_Defs),
- Exported_FuncMap = typer:map__insert({File, Ex_Funcs},
- Analysis#typer_analysis.ex_func),
- %% NOTE: we must sort all functions in the file which
- %% originate from this file by *numerical order* of lineNo
- Sorted_Functions = lists:keysort(1, Acc#tmpAcc.funcAcc),
- FuncMap = typer:map__insert({File, Sorted_Functions},
- Analysis#typer_analysis.func),
- %% NOTE: However we do not need to sort functions
- %% which are imported from included files.
- IncFuncMap = typer:map__insert({File, Acc#tmpAcc.incFuncAcc},
- Analysis#typer_analysis.inc_func),
- Final_Files = Analysis#typer_analysis.final_files ++ [{File, Module}],
- RecordMap = typer:map__insert({File, Records}, Analysis#typer_analysis.record),
- Analysis#typer_analysis{final_files=Final_Files,
- callgraph=CG,
- code_server=CS6,
- ex_func=Exported_FuncMap,
- inc_func=IncFuncMap,
- record=RecordMap,
- func=FuncMap}.
-
-analyze_one_function({Var, FunBody} = Function, Acc) ->
- F = cerl:fname_id(Var),
- A = cerl:fname_arity(Var),
- TmpDialyzerObj = {{Acc#tmpAcc.module, F, A}, Function},
- NewDialyzerObj = Acc#tmpAcc.dialyzerObj ++ [TmpDialyzerObj],
- [_, LineNo, {file, FileName}] = cerl:get_ann(FunBody),
- BaseName = filename:basename(FileName),
- FuncInfo = {LineNo, F, A},
- OriginalName = Acc#tmpAcc.file,
- {FuncAcc, IncFuncAcc} =
- case (FileName =:= OriginalName) orelse (BaseName =:= OriginalName) of
- true -> %% Coming from original file
- %% io:format("Added function ~p\n", [{LineNo, F, A}]),
- {Acc#tmpAcc.funcAcc ++ [FuncInfo], Acc#tmpAcc.incFuncAcc};
- false ->
- %% Coming from other sourses, including:
- %% -- .yrl (yecc-generated file)
- %% -- yeccpre.hrl (yecc-generated file)
- %% -- other cases
- {Acc#tmpAcc.funcAcc, Acc#tmpAcc.incFuncAcc ++ [{FileName, FuncInfo}]}
- end,
- Acc#tmpAcc{funcAcc = FuncAcc,
- incFuncAcc = IncFuncAcc,
- dialyzerObj = NewDialyzerObj}.
-
-get_dialyzer_plt(#typer_analysis{plt = PltFile0}) ->
- PltFile =
- case PltFile0 =:= none of
- true -> dialyzer_plt:get_default_plt();
- false -> PltFile0
- end,
- dialyzer_plt:from_file(PltFile).
-
-
-%% Exported Types
-
-get_exported_types_from_core(Core) ->
- Attrs = cerl:module_attrs(Core),
- ExpTypes1 = [cerl:concrete(L2) || {L1, L2} <- Attrs,
- cerl:is_literal(L1),
- cerl:is_literal(L2),
- cerl:concrete(L1) =:= 'export_type'],
- ExpTypes2 = lists:flatten(ExpTypes1),
- M = cerl:atom_val(cerl:module_name(Core)),
- sets:from_list([{M, F, A} || {F, A} <- ExpTypes2]).