aboutsummaryrefslogtreecommitdiffstats
path: root/lib/typer
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/typer
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/typer')
-rw-r--r--lib/typer/Makefile43
-rw-r--r--lib/typer/ebin/.gitignore0
-rw-r--r--lib/typer/src/Makefile121
-rw-r--r--lib/typer/src/typer.app.src14
-rw-r--r--lib/typer/src/typer.appup.src1
-rw-r--r--lib/typer/src/typer.erl197
-rw-r--r--lib/typer/src/typer.hrl64
-rw-r--r--lib/typer/src/typer_annotator.erl382
-rw-r--r--lib/typer/src/typer_info.erl162
-rw-r--r--lib/typer/src/typer_map.erl47
-rw-r--r--lib/typer/src/typer_options.erl191
-rw-r--r--lib/typer/src/typer_preprocess.erl154
-rw-r--r--lib/typer/vsn.mk1
13 files changed, 1377 insertions, 0 deletions
diff --git a/lib/typer/Makefile b/lib/typer/Makefile
new file mode 100644
index 0000000000..40a82e9bba
--- /dev/null
+++ b/lib/typer/Makefile
@@ -0,0 +1,43 @@
+#
+# %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%
+#
+#=============================================================================
+#
+# File: lib/typer/Makefile
+# Authors: Bingwen He, Tobias Lindahl, and Kostis Sagonas
+#
+#=============================================================================
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+#
+# Macros
+#
+
+SUB_DIRECTORIES = src
+
+include vsn.mk
+VSN = $(TYPER_VSN)
+
+SPECIAL_TARGETS =
+
+#
+# Default Subdir Targets
+#
+include $(ERL_TOP)/make/otp_subdir.mk
+
diff --git a/lib/typer/ebin/.gitignore b/lib/typer/ebin/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/typer/ebin/.gitignore
diff --git a/lib/typer/src/Makefile b/lib/typer/src/Makefile
new file mode 100644
index 0000000000..9c9ef6156f
--- /dev/null
+++ b/lib/typer/src/Makefile
@@ -0,0 +1,121 @@
+#
+# %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%
+#
+#=============================================================================
+#
+# File: lib/typer/src/Makefile
+# Authors: Kostis Sagonas
+#
+#=============================================================================
+
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(TYPER_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/typer-$(VSN)
+
+# ----------------------------------------------------
+# Orientation information -- find dialyzer's dir
+# ----------------------------------------------------
+DIALYZER_DIR = $(ERL_TOP)/lib/dialyzer
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+MODULES = \
+ typer \
+ typer_annotator \
+ typer_info \
+ typer_map \
+ typer_options \
+ typer_preprocess
+
+HRL_FILES= typer.hrl
+ERL_FILES= $(MODULES:%=%.erl)
+INSTALL_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET)
+TARGET_FILES= $(INSTALL_FILES)
+
+APP_FILE= typer.app
+APP_SRC= $(APP_FILE).src
+APP_TARGET= $(EBIN)/$(APP_FILE)
+
+APPUP_FILE= typer.appup
+APPUP_SRC= $(APPUP_FILE).src
+APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS += +warn_exported_vars +warn_untyped_record +warn_missing_spec
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES)
+
+docs:
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+# ----------------------------------------------------
+# 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
+
+$(APP_TARGET): $(APP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+# ---------------------------------------------------------------------
+# dependencies -- I wish they were somehow automatically generated
+# ---------------------------------------------------------------------
+
+$(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
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(YRL_FILES) \
+ $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(INSTALL_FILES) $(RELSYSDIR)/ebin
+
+release_docs_spec:
diff --git a/lib/typer/src/typer.app.src b/lib/typer/src/typer.app.src
new file mode 100644
index 0000000000..3eb0cbf816
--- /dev/null
+++ b/lib/typer/src/typer.app.src
@@ -0,0 +1,14 @@
+% This is an -*- erlang -*- file.
+
+{application, typer,
+ [{description, "TYPe annotator for ERlang programs, version %VSN%"},
+ {vsn, "%VSN%"},
+ {modules, [typer,
+ typer_annotator,
+ typer_info,
+ typer_map,
+ typer_options,
+ typer_preprocess]},
+ {registered, []},
+ {applications, [compiler, dialyzer, hipe, kernel, stdlib]},
+ {env, []}]}.
diff --git a/lib/typer/src/typer.appup.src b/lib/typer/src/typer.appup.src
new file mode 100644
index 0000000000..54a63833e6
--- /dev/null
+++ b/lib/typer/src/typer.appup.src
@@ -0,0 +1 @@
+{"%VSN%",[],[]}.
diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl
new file mode 100644
index 0000000000..cebe6ea488
--- /dev/null
+++ b/lib/typer/src/typer.erl
@@ -0,0 +1,197 @@
+%% -*- 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%
+%%
+
+%%--------------------------------------------------------------------
+%% File : typer.erl
+%% Author : Bingwen He <[email protected]>
+%% Description : The main driver of the TypEr application
+%%--------------------------------------------------------------------
+
+-module(typer).
+
+-export([start/0]).
+-export([error/1, compile_error/1]). % for error reporting
+
+-include("typer.hrl").
+
+%%--------------------------------------------------------------------
+
+-spec start() -> no_return().
+
+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),
+ Analysis1 = Analysis#typer_analysis{t_files = TrustedFiles},
+ Analysis2 = extract(Analysis1),
+ All_Files = typer_preprocess:get_all_files(Args, analysis),
+ %% io:format("All_Files: ~p\n", [All_Files]),
+ Analysis3 = Analysis2#typer_analysis{ana_files = All_Files},
+ Analysis4 = typer_info:collect(Analysis3),
+ %% io:format("Final: ~p\n", [Analysis4#typer_analysis.final_files]),
+ TypeInfo = get_type_info(Analysis4),
+ typer_annotator:annotate(TypeInfo),
+ %% io:format("\nTyper analysis finished\n"),
+ erlang:halt(0).
+
+%%--------------------------------------------------------------------
+
+-spec extract(#typer_analysis{}) -> #typer_analysis{}.
+
+extract(#typer_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(),
+ Fun =
+ fun(File, CS) ->
+ %% We include one more dir; the one above the one we are trusting
+ %% E.g, for /home/tests/typer_ann/test.ann.erl, we should include
+ %% /home/tests/ rather than /home/tests/typer_ann/
+ AllIncludes = [filename:dirname(filename:dirname(File)) | Includes],
+ Is = [{i, Dir} || Dir <- AllIncludes],
+ CompOpts = dialyzer_utils:src_compiler_opts() ++ Is ++ Ds,
+ case dialyzer_utils:get_abstract_code_from_src(File, CompOpts) of
+ {ok, AbstractCode} ->
+ case dialyzer_utils:get_record_and_type_info(AbstractCode) of
+ {ok, RecDict} ->
+ Mod = list_to_atom(filename:basename(File, ".erl")),
+ case dialyzer_utils:get_spec_info(Mod, AbstractCode, RecDict) of
+ {ok, SpecDict} ->
+ CS1 = dialyzer_codeserver:store_temp_records(Mod, RecDict, CS),
+ dialyzer_codeserver:store_temp_contracts(Mod, SpecDict, CS1);
+ {error, Reason} -> compile_error([Reason])
+ end;
+ {error, Reason} -> compile_error([Reason])
+ end;
+ {error, Reason} -> compile_error(Reason)
+ end
+ end,
+ CodeServer1 = lists:foldl(Fun, CodeServer, TFiles),
+ %% Process remote types
+ NewCodeServer =
+ try
+ NewRecords = dialyzer_codeserver:get_temp_records(CodeServer1),
+ OldRecords = dialyzer_plt:get_types(TrustPLT), % XXX change to the PLT?
+ MergedRecords = dialyzer_utils:merge_records(NewRecords, OldRecords),
+ CodeServer2 = dialyzer_codeserver:set_temp_records(MergedRecords, CodeServer1),
+ CodeServer3 = dialyzer_utils:process_record_remote_types(CodeServer2),
+ dialyzer_contracts:process_contract_remote_types(CodeServer3)
+ catch
+ throw:{error, ErrorMsg} ->
+ compile_error(ErrorMsg)
+ end,
+ %% Create TrustPLT
+ Contracts = dialyzer_codeserver:get_contracts(NewCodeServer),
+ Modules = dict:fetch_keys(Contracts),
+ FoldFun =
+ fun(Module, TmpPlt) ->
+ {ok, ModuleContracts} = dict:find(Module, Contracts),
+ SpecList = [{MFA, Contract}
+ || {MFA, {_FileLine, Contract}} <- dict:to_list(ModuleContracts)],
+ dialyzer_plt:insert_contract_list(TmpPlt, SpecList)
+ end,
+ NewTrustPLT = lists:foldl(FoldFun, TrustPLT, Modules),
+ Analysis#typer_analysis{trust_plt = NewTrustPLT}.
+
+%%--------------------------------------------------------------------
+
+-spec get_type_info(#typer_analysis{}) -> #typer_analysis{}.
+
+get_type_info(#typer_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}
+ catch
+ error:What ->
+ 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]))
+ end.
+
+-spec remove_external(dialyzer_callgraph:callgraph(), dialyzer_plt:plt()) -> dialyzer_callgraph:callgraph().
+
+remove_external(CallGraph, PLT) ->
+ {StrippedCG0, Ext} = dialyzer_callgraph:remove_external(CallGraph),
+ StrippedCG = dialyzer_callgraph:finalize(StrippedCG0),
+ case get_external(Ext, PLT) of
+ [] -> ok;
+ Externals ->
+ msg(io_lib:format(" Unknown functions: ~p\n", [lists:usort(Externals)]))
+ end,
+ StrippedCG.
+
+-spec get_external([{mfa(), mfa()}], dialyzer_plt:plt()) -> [mfa()].
+
+get_external(Exts, Plt) ->
+ Fun = fun ({_From, To = {M, F, A}}, Acc) ->
+ case dialyzer_plt:contains_mfa(Plt, To) of
+ false ->
+ case erl_bif_types:is_known(M, F, A) of
+ true -> Acc;
+ false -> [To|Acc]
+ end;
+ true -> Acc
+ end
+ end,
+ lists:foldl(Fun, [], Exts).
+
+%%--------------------------------------------------------------------
+
+-spec error(string()) -> no_return().
+
+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.
+%%--------------------------------------------------------------------
+
+-spec msg(string()) -> 'ok'.
+
+msg(Msg) ->
+ case os:type() of
+ {unix, _} ->
+ P = open_port({fd, 0, 2}, [out]),
+ port_command(P, Msg),
+ true = port_close(P),
+ ok;
+ _ -> % win32, vxworks
+ io:format("~s", [Msg])
+ end.
+
+%%--------------------------------------------------------------------
diff --git a/lib/typer/src/typer.hrl b/lib/typer/src/typer.hrl
new file mode 100644
index 0000000000..c331dd82db
--- /dev/null
+++ b/lib/typer/src/typer.hrl
@@ -0,0 +1,64 @@
+%% -*- 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%
+%%
+
+-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(), _}], % {macro_name, value}
+ includes = [] :: [string()],
+
+ %% 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(),
+
+ %% Esp for TypEr
+ %% ----------------------
+ t_files = [] :: [string()],
+
+ %% 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
+ final_files = [] :: [{string(), atom()}],
+
+ 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()}).
+
+-record(args,
+ {analyze = [] :: [string()],
+ analyzed_dir_r = [] :: [string()],
+ trust = [] :: [string()]}).
diff --git a/lib/typer/src/typer_annotator.erl b/lib/typer/src/typer_annotator.erl
new file mode 100644
index 0000000000..17eeeb6dfe
--- /dev/null
+++ b/lib/typer/src/typer_annotator.erl
@@ -0,0 +1,382 @@
+%% -*- erlang-indent-level: 2 -*-
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-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%
+%%
+%%============================================================================
+%% File : typer_annotator.erl
+%% Author : Bingwen He <[email protected]>
+%% Description :
+%% If file 'FILENAME' has been analyzed, then the output of
+%% command "diff -B FILENAME.erl typer_ann/FILENAME.ann.erl"
+%% should be exactly what TypEr has added, namely type info.
+%%============================================================================
+
+-module(typer_annotator).
+
+-export([annotate/1]).
+
+%%----------------------------------------------------------------------------
+
+-include("typer.hrl").
+
+%%----------------------------------------------------------------------------
+
+-define(TYPER_ANN_DIR, "typer_ann").
+
+-type func_info() :: {non_neg_integer(), atom(), arity()}.
+
+-record(info, {recMap = typer_map:new() :: dict(),
+ funcs = [] :: [func_info()],
+ typeMap :: dict(),
+ contracts :: boolean()}).
+-record(inc, {map = typer_map:new() :: dict(),
+ filter = [] :: [string()]}).
+
+%%----------------------------------------------------------------------------
+
+-spec annotate(#typer_analysis{}) -> 'ok'.
+
+annotate(Analysis) ->
+ case Analysis#typer_analysis.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, Analysis#typer_analysis.final_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_funcs(IncFuns, Info#info.typeMap, 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{typeMap = typer_map:from_list(Val1),
+ recMap = 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]),
+ 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_only(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),
+ Functions = get_functions(File, Analysis),
+ Contracts = Analysis#typer_analysis.contracts,
+ #info{recMap=RecMap, funcs=Functions, typeMap=TypeMap, contracts=Contracts}.
+
+collect_imported_funcs(Funcs, TypeMap, TmpInc) ->
+ %% 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)
+ end
+ end,
+ lists:foldl(Fun, TmpInc, Funcs).
+
+-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};
+ 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};
+ false ->
+ NewFilter = [Obj|Inc#inc.filter],
+ NewInc = Inc#inc{filter = NewFilter},
+ {yecc_generated, NewInc}
+ end;
+ _ ->
+ case filename:basename(File) of
+ "yeccpre.hrl" -> {yecc_generated, Inc};
+ _ -> {not_yecc, Inc}
+ end
+ end
+ end.
+
+check_imported_funcs({File, {Line, F, A}}, Inc, TypeMap) ->
+ IncMap = Inc#inc.map,
+ FA = {F, A},
+ Type = get_type_info(FA, TypeMap),
+ 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(TmpInc) ->
+ Fun = fun(Key, Inc) ->
+ NewMap = typer_map:remove(Key, Inc#inc.map),
+ Inc#inc{map = NewMap}
+ end,
+ lists:foldl(Fun, TmpInc, TmpInc#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_recMap(File, Analysis) ->
+ typer_map:lookup(File, Analysis#typer_analysis.record).
+
+get_typeMap(Module, Analysis, RecMap) ->
+ 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, RecMap) || I <- TypeInfo],
+ typer_map:from_list(TypeInfoList).
+
+get_type({{M, F, A} = MFA, Range, Arg}, CodeServer, RecMap) ->
+ 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, invalid_contract} ->
+ CString = dialyzer_contracts:contract_to_string(Contract),
+ SigString = dialyzer_utils:format_sig(Sig, RecMap),
+ 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} ->
+ typer:error(
+ io_lib:format("Error in contract of function ~w:~w/~w: ~s",
+ [M, F, A, 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(Funcs) ->
+ [FuncInfo || {_FileName, FuncInfo} <- Funcs].
+
+-spec remove_module_info([func_info()]) -> [func_info()].
+
+remove_module_info(FuncInfoList) ->
+ 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).
+
+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("Unknown error when writing ~p\n", [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{funcs = []}, _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,
+ case Line of
+ 1 -> %% This will happen only for inc files
+ ok = raw_write(F, A, Info, File, []),
+ NewInfo = Info#info{funcs = 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{funcs = 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.typeMap),
+ 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)
+ end,
+ case Info#info.contracts 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_only(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.funcs).
+
+get_type_info(Func, TypeMap) ->
+ case typer_map:lookup(Func, TypeMap) 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.
diff --git a/lib/typer/src/typer_info.erl b/lib/typer/src/typer_info.erl
new file mode 100644
index 0000000000..ea25fa6f68
--- /dev/null
+++ b/lib/typer/src/typer_info.erl
@@ -0,0 +1,162 @@
+%% -*- 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_info).
+
+-export([collect/1]).
+
+-type func_info() :: {non_neg_integer(), atom(), arity()}.
+-type inc_file_info() :: {string(), func_info()}.
+
+-record(tmpAcc, {file :: string(),
+ 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: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),
+ OldRecords = dialyzer_plt:get_types(NewPlt),
+ MergedRecords = dialyzer_utils:merge_records(NewRecords, OldRecords),
+ %% 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)
+ catch
+ throw:{error, ErrorMsg} ->
+ typer: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 = 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)
+ end
+ end
+ end
+ end.
+
+analyze_core_tree(Core, Records, SpecInfo, Analysis, File) ->
+ Module = list_to_atom(filename:basename(File, ".erl")),
+ 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 = dialyzer_codeserver:store_temp_contracts(Module, SpecInfo, CS4),
+ 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=CS5,
+ 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).
diff --git a/lib/typer/src/typer_map.erl b/lib/typer/src/typer_map.erl
new file mode 100644
index 0000000000..bf62dea651
--- /dev/null
+++ b/lib/typer/src/typer_map.erl
@@ -0,0 +1,47 @@
+%% -*- 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).
diff --git a/lib/typer/src/typer_options.erl b/lib/typer/src/typer_options.erl
new file mode 100644
index 0000000000..1e53b1b305
--- /dev/null
+++ b/lib/typer/src/typer_options.erl
@@ -0,0 +1,191 @@
+%% -*- 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%
+%%
+%%===========================================================================
+%% File : typer_options.erl
+%% Author : Bingwen He <[email protected]>
+%% 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",[Args]),
+ {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(["--plt",Plt|Opts]) -> {{plt, Plt}, Opts};
+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),
+ {{def, DefPair}, Opts}
+ end;
+cl(["-I",Dir|Opts]) -> {{inc,Dir}, Opts};
+cl(["-I"++Dir|Opts]) ->
+ case Dir of
+ "" -> typer: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");
+ [_|_] -> {{trust, Files}, RestOpts}
+ end;
+cl(["-r"|Opts]) ->
+ {Files, RestOpts} = dialyzer_cl_parse:collect_args(Opts),
+ {{a_dir_r, Files}, RestOpts};
+cl(["-"++H|_]) -> typer:error("unknown option -"++H);
+cl(Opts) ->
+ {Args, RestOpts} = dialyzer_cl_parse:collect_args(Opts),
+ {{analyze, Args}, 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({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(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}}.
+
+%%--------------------------------------------------------------------
+
+-spec mode_error() -> no_return().
+mode_error() ->
+ typer: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).
diff --git a/lib/typer/src/typer_preprocess.erl b/lib/typer/src/typer_preprocess.erl
new file mode 100644
index 0000000000..7cb0b9932b
--- /dev/null
+++ b/lib/typer/src/typer_preprocess.erl
@@ -0,0 +1,154 @@
+%% -*- 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_preprocess).
+
+-export([get_all_files/2]).
+
+-include("typer.hrl").
+
+%%----------------------------------------------------------------------------
+
+-spec get_all_files(#args{}, 'analysis' | 'trust') -> [string()].
+
+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
+ [] -> 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).
+
+-spec test_erl_file_exclude_ann(string()) -> 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(string()) -> boolean().
+
+test_erl_file(File) ->
+ filename:extension(File) =:= ".erl".
+
+-spec internal_get_all_files([string()], [string()],
+ fun((string()) -> boolean())) -> [string()].
+
+internal_get_all_files(File_Dir, Dir_R, Fun) ->
+ All_File_1 = process_file_and_dir(File_Dir, Fun),
+ All_File_2 = process_dir_recursively(Dir_R, Fun),
+ remove_dup(All_File_1 ++ All_File_2).
+
+-spec process_file_and_dir([string()],
+ fun((string()) -> boolean())) -> [string()].
+
+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)
+ end
+ end,
+ lists:foldl(Fun, [], File_Dir).
+
+-spec process_dir_recursively([string()],
+ fun((string()) -> boolean())) -> [string()].
+
+process_dir_recursively(Dirs, TestFun) ->
+ Fun = fun (Dir, Acc) ->
+ check_dir(Dir, recursive, Acc, TestFun)
+ end,
+ lists:foldl(Fun, [], Dirs).
+
+-spec check_dir(string(),
+ 'non_recursive' | 'recursive',
+ [string()],
+ fun((string()) -> boolean())) -> [string()].
+
+check_dir(Dir, Mode, Acc, Fun) ->
+ case file:list_dir(Dir) of
+ {ok, Files} ->
+ {TmpDirs, TmpFiles} = split_dirs_and_files(Files, Dir),
+ case Mode of
+ non_recursive ->
+ FinalFiles = process_file_and_dir(TmpFiles, Fun),
+ Acc ++ FinalFiles;
+ recursive ->
+ TmpAcc1 = process_file_and_dir(TmpFiles, Fun),
+ TmpAcc2 = process_dir_recursively(TmpDirs, Fun),
+ Acc ++ TmpAcc1 ++ TmpAcc2
+ end;
+ {error, eacces} ->
+ typer:error("no access permission to dir \""++Dir++"\"");
+ {error, enoent} ->
+ typer:error("cannot access "++Dir++": No such file or directory");
+ {error, _Reason} ->
+ typer:error("error involving a use of file:list_dir/1")
+ end.
+
+%% Same order as the input list
+-spec process_file(string(), fun((string()) -> boolean()), string()) -> [string()].
+
+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([string()], string()) -> {[string()], [string()]}.
+
+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([string()]) -> [string()].
+
+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).
diff --git a/lib/typer/vsn.mk b/lib/typer/vsn.mk
new file mode 100644
index 0000000000..9558412375
--- /dev/null
+++ b/lib/typer/vsn.mk
@@ -0,0 +1 @@
+TYPER_VSN = 0.1.7.3