From 84adefa331c4159d432d22840663c38f155cd4c1 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Fri, 20 Nov 2009 14:54:40 +0000 Subject: The R13B03 release. --- lib/typer/src/typer.erl | 197 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 lib/typer/src/typer.erl (limited to 'lib/typer/src/typer.erl') 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 +%% 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. + +%%-------------------------------------------------------------------- -- cgit v1.2.3