aboutsummaryrefslogtreecommitdiffstats
path: root/lib/typer/src/typer.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/typer/src/typer.erl')
-rw-r--r--lib/typer/src/typer.erl197
1 files changed, 197 insertions, 0 deletions
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.
+
+%%--------------------------------------------------------------------