dialyzer_cl.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 407 行

ERL
407
字号
%% -*- erlang-indent-level: 2 -*-%%-------------------------------------------------------------------%% ``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 via the world wide web 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.%% %% Copyright 2006, Tobias Lindahl and Kostis Sagonas%% %%     $Id$%%%%%-------------------------------------------------------------------%%% File    : dialyzer_cl.erl%%% Authors : Tobias Lindahl <tobiasl@csd.uu.se>%%%           Kostis Sagonas <kostis@it.uu.se>%%% Description : The command line interface for the Dialyzer tool.%%%%%% Created : 27 Apr 2004 by Tobias Lindahl <tobiasl@csd.uu.se>%%%--------------------------------------------------------------------module(dialyzer_cl).-export([start/1, check_init_plt/2]).-include("dialyzer.hrl").-record(cl_state, {backend_pid,		   init_plt,		   legal_warnings=[],		   nof_warnings=0::integer(),		   output=standard_io,		   output_plt,		   quiet::bool(),		   return_status=?RET_NOTHING_SUSPICIOUS::integer(),		   stored_errors=do_not_store,		   stored_warnings=do_not_store,		   user_plt		  }).start(#options{} = DialyzerOptions) ->  process_flag(trap_exit, true),  StoreWarningsAndErrors =    case DialyzerOptions#options.erlang_mode of      true -> [];      false -> do_not_store    end,  State = new_state(DialyzerOptions#options.init_plt),  NewState1 = init_output(State, DialyzerOptions),    NewState2 =     NewState1#cl_state{legal_warnings=DialyzerOptions#options.legal_warnings,		       output_plt=DialyzerOptions#options.output_plt,		       stored_errors=StoreWarningsAndErrors,		       stored_warnings=StoreWarningsAndErrors,		       quiet=DialyzerOptions#options.quiet},  InitAnalysis = build_analysis_record(NewState2, DialyzerOptions),  NewState3 = run_analysis(NewState2, InitAnalysis),  cl_loop(NewState3).check_init_plt(Opts, Force) ->  process_flag(trap_exit, true),  Quiet = Opts#options.quiet,  case dialyzer_plt:check_init_plt(Opts#options.init_plt) of    {fail, MD5, DiffMd5, Libs, InitPlt} ->            if Quiet -> ok;	 true  -> 	  io:format(" no\n", []),	  case Quiet =:= verbose of	    true -> print_md5_diff(DiffMd5);	    false -> ok	  end      end,      case (not Force) andalso check_if_installed() of	true -> 	  Msg = "    The initial PLT is not up-to-date.\n"	    "    Since Dialyzer is installed no new PLT will be built.\n"	    "    To force the rebuilding use the option --check_init_plt\n",	  error(Msg);	false ->	  Msg = "    Creating initial PLT"	    " (will take several minutes; please be patient)\n",	  io:format("~s", [Msg]),	  InclDirs = Opts#options.include_dirs,	  {T1, _} = statistics(wall_clock),	  Ret =	    case create_init_plt(MD5, Libs, InitPlt, InclDirs) of	      ?RET_INTERNAL_ERROR ->		error("Problems during consistency check of initial PLT");	      ?RET_NOTHING_SUSPICIOUS -> ?RET_NOTHING_SUSPICIOUS;	      ?RET_DISCREPANCIES_FOUND -> ?RET_NOTHING_SUSPICIOUS	    end,	  {T2, _} = statistics(wall_clock),	  if Quiet -> ok;	     true -> print_elapsed_time(T1, T2)	  end,	  Ret      end;    {ok, _InitPlt} ->      if Quiet -> ok;	 true  -> io:format(" yes\n")      end,      ?RET_NOTHING_SUSPICIOUS;    {error, Msg} ->      if Quiet -> ok;	 true  -> io:format(" no\n")      end,      error(Msg)  end.print_elapsed_time(T1, T2) ->  ElapsedTime = T2 - T1,  Mins = ElapsedTime div 60000,  Secs = (ElapsedTime rem 60000) / 1000,  io:format("  Done building PLT in ~wm~.2fs\n", [Mins, Secs]).print_md5_diff(none) ->  io:format("    Could not find the old PLT information\n", []);print_md5_diff(List) ->  io:format("    The PLT information is not up to date:\n", []),  case [Mod || {new, Mod} <- List] of    [] -> ok;    NewMods -> io:format("    New modules: ~p\n", [NewMods])  end,  case [Mod || {removed, Mod} <- List] of    [] -> ok;    RemovedMods -> io:format("    Removed modules: ~p\n", [RemovedMods])  end,  case [Mod || {diff, Mod} <- List] of    [] -> ok;    ChangedMods -> io:format("    Changed modules: ~p\n", [ChangedMods])  end.check_if_installed() ->  case filename:basename(code:lib_dir(dialyzer)) of    "dialyzer" -> false;    "dialyzer-" ++ _Version -> true  end.  create_init_plt(MD5, Libs, InitPlt, IncludeDirs) ->    hipe_compile(),  State = new_state_no_init(),  State1 = State#cl_state{output_plt=InitPlt},  Files = [filename:join(code:lib_dir(Lib), "ebin")|| Lib <- Libs],  Analysis = #analysis{files=Files, 		       init_plt=dialyzer_plt:new(dialyzer_empty_plt),		       include_dirs=IncludeDirs,		       plt_info={MD5, Libs},		       start_from=byte_code,		       type=plt_build,		       user_plt=State1#cl_state.user_plt,		       supress_inline=true},  cl_loop(run_analysis(State1, Analysis)).hipe_compile() ->  case erlang:system_info(hipe_architecture) of    undefined -> ok;    ultrasparc -> ok;    _ ->      {ok, lists}                       = hipe:c(lists),      {ok, dict}                        = hipe:c(dict),      {ok, dialyzer_succ_typings}       = hipe:c(dialyzer_succ_typings),      {ok, dialyzer_analysis_callgraph} = hipe:c(dialyzer_analysis_callgraph),      {ok, dialyzer_typesig}            = hipe:c(dialyzer_typesig),      {ok, dialyzer_dataflow}           = hipe:c(dialyzer_dataflow),      {ok, dialyzer_codeserver}         = hipe:c(dialyzer_codeserver),      {ok, erl_types}                   = hipe:c(erl_types),      {ok, erl_bif_types}               = hipe:c(erl_bif_types),      {ok, cerl}                        = hipe:c(cerl, [no_concurrent_comp]),      ok  end.new_state(InitPlt) ->  NewInitPlt = dialyzer_plt:from_file(dialyzer_init_plt, InitPlt),  new_state1(NewInitPlt).new_state_no_init() ->  new_state1(none).new_state1(InitPlt) ->  UserPLT = dialyzer_plt:new(dialyzer_user_plt),  #cl_state{user_plt=UserPLT, init_plt=InitPlt}.init_output(State, DialyzerOptions) ->  case DialyzerOptions#options.output_file of    "" ->      State;    OutputFile ->      case file:open(OutputFile, [write]) of	{ok, File} ->	  State#cl_state{output=File};	{error, Reason} ->	  Msg = io_lib:format("Could not open output file ~p, Reason: ~p\n",			      [OutputFile, Reason]),	  error(State, lists:flatten(Msg))      end  end.maybe_close_output_file(State) ->  case State#cl_state.output of    standard_io -> ok;    File -> file:close(File)  end.%% ----------------------------------------------------------------%%%%  Main Loop%%cl_loop(State) ->  BackendPid = State#cl_state.backend_pid,  receive    {BackendPid, log, _LogMsg} ->      %% io:format(State#cl_state.output ,"Log: ~s", [_LogMsg]),      cl_loop(State);    {BackendPid, warnings, Warnings} ->      NewState = store_warnings(State, Warnings),      cl_loop(NewState);    {BackendPid, error, Msg} ->      State1 = store_error(State, Msg),      cl_loop(State1#cl_state{return_status=?RET_INTERNAL_ERROR});    {BackendPid, done} ->      return_value(State);    {BackendPid, ext_calls, ExtCalls} ->      Msg = io_lib:format("Unknown functions: ~p\n", [ExtCalls]),      NewState = print_ext_calls(State, Msg),      cl_loop(NewState);    {'EXIT', BackendPid, {error, Reason}} ->      Msg = failed_anal_msg(Reason),      error(State, Msg);    {'EXIT', BackendPid, Reason} when Reason =/= 'normal' ->      Msg = failed_anal_msg(Reason),      maybe_close_output_file(State),      error(State, Msg);    _Other ->      %% io:format("Received ~p\n", [_Other]),      cl_loop(State)  end.failed_anal_msg(Reason) ->  io_lib:format("Analysis failed with error report:\n\t~P", [Reason, 12]).print_ext_calls(State = #cl_state{quiet=true}, _Msg) ->  State;print_ext_calls(State = #cl_state{}, Msg) ->  print_warning_string(State, Msg),  State.store_error(State = #cl_state{stored_errors=StoredErrors}, Msg) ->  NewStoredErrors =    case StoredErrors =:= do_not_store of      true ->	error(State, Msg);      false ->	StoredErrors ++ [Msg]    end,  State#cl_state{stored_errors=NewStoredErrors}.store_warnings(State = #cl_state{nof_warnings=NofOldWarnings,				 stored_warnings=StoredWarnings}, Warnings) ->  NewStoredWarnings =    case StoredWarnings =:= do_not_store of      true ->	WarningString = format_warnings(Warnings),	print_warning_string(State, WarningString),	StoredWarnings;      false ->	StoredWarnings ++ Warnings    end,  NofNewWarning = length(Warnings),  State#cl_state{nof_warnings=NofOldWarnings+NofNewWarning, 		 stored_warnings=NewStoredWarnings}.format_warnings(Warnings) ->  MapFun =    fun({{M, F, A} = MFA, Msg}) when is_atom(M), is_atom(F), is_integer(A)-> 	io_lib:format("~w: ~s", [MFA, Msg]);       ({{File, Line}, Msg}) when is_list(File), is_integer(Line) ->	BaseName = filename:basename(File),	io_lib:format("~s:~w: ~s", [BaseName, Line, Msg])    end,  lists:flatten(lists:map(MapFun, lists:keysort(1, Warnings))).print_warning_string(#cl_state{nof_warnings=NofWarn, output=Output}, String) ->  case NofWarn of    0 -> io:format(Output, "\n", []); %% warnings are just starting to appear    _ -> ok  end,  io:format(Output, "~s", [String]).error(Msg) ->  throw({dialyzer_error, Msg}).error(State, Msg) ->  case State#cl_state.output of    standard_io -> ok;    Outfile -> io:format(Outfile, "\n~s\n", [Msg])  end,  throw({dialyzer_error, Msg}).return_value(State = #cl_state{nof_warnings=NofWarnings, output_plt=OutputPlt,			       stored_errors=StoredErrors,			       stored_warnings=StoredWarnings,			       user_plt=UserPlt, init_plt=InitPlt}) ->  maybe_close_output_file(State),  RetValue =    case State#cl_state.return_status of      ?RET_INTERNAL_ERROR -> ?RET_INTERNAL_ERROR;      _ ->      	case OutputPlt =:= undefined of	  true -> ok;	  false ->	    dialyzer_plt:merge_and_write_file([InitPlt, UserPlt], OutputPlt)	end,	if NofWarnings =:= 0 -> ?RET_NOTHING_SUSPICIOUS;	   true              -> ?RET_DISCREPANCIES_FOUND	end    end,  case StoredWarnings =:= do_not_store of    true ->      %% Assert      do_not_store = StoredErrors,      RetValue;    false ->      %% Assert      false = StoredErrors =:= do_not_store,      {RetValue, StoredWarnings, StoredErrors}  end.%% ----------------------------------------------------------------%%%%  Run the analysis%%build_analysis_record(State, DialyzerOptions) ->  PLT = State#cl_state.user_plt,  From = DialyzerOptions#options.from,  IncludeDirs = DialyzerOptions#options.include_dirs,  Defines = DialyzerOptions#options.defines,  SupressInline = DialyzerOptions#options.supress_inline,  Files0 = ordsets:from_list(DialyzerOptions#options.files),  Files1 = ordsets:from_list(lists:concat([filelib:wildcard(F) 					   || F <- Files0])),  Files2 = add_files_rec(DialyzerOptions#options.files_rec, From),    Files = ordsets:union(Files1, Files2),  AnalType = DialyzerOptions#options.analysis_type,  InitPlt = State#cl_state.init_plt,  #analysis{type=AnalType,	    defines=Defines,	    include_dirs=IncludeDirs, init_plt=InitPlt, user_plt=PLT, 	    files=Files, start_from=From, supress_inline=SupressInline}.add_files_rec(Files, From) ->  Files1 = ordsets:from_list(Files),   Dirs1 = ordsets:filter(fun(X) -> filelib:is_dir(X) end, Files1),    Dirs2 = ordsets:union(Dirs1, all_subdirs(Dirs1)),    FinalFiles = ordsets:union(Files1, Dirs2),  case From of    byte_code -> filter_files(FinalFiles, ".beam");    src_code -> filter_files(FinalFiles, ".erl")  end.all_subdirs(Dirs) ->  all_subdirs(Dirs, []).all_subdirs([Dir|T], Acc) ->  {ok, Files} = file:list_dir(Dir),  SubDirs = lists:zf(fun(F) ->                       SubDir = filename:join(Dir, F),                       case filelib:is_dir(SubDir) of                         true -> {true, SubDir};                         false -> false                       end		     end, Files),  NewAcc = ordsets:union(ordsets:from_list(SubDirs), Acc),  all_subdirs(T++SubDirs, NewAcc);all_subdirs([], Acc) ->  Acc.filter_files(Files, Extension) ->  Fun = fun(X) -> 	    filename:extension(X) =:= Extension	      orelse 		(filelib:is_dir(X) andalso contains_files(X, Extension))	end,  lists:filter(Fun, Files).contains_files(Dir, Extension) ->  {ok, Files} = file:list_dir(Dir),  lists:any(fun(X) -> filename:extension(X) =:= Extension end, Files).run_analysis(State, Analysis) ->  Self = self(),  LegalWarnings = State#cl_state.legal_warnings,  Fun = fun() -> 	    dialyzer_analysis_callgraph:start(Self, LegalWarnings, Analysis)	end,  BackendPid = spawn_link(Fun),  State#cl_state{backend_pid=BackendPid}.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?