aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dialyzer/src/dialyzer_cl_parse.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dialyzer/src/dialyzer_cl_parse.erl')
-rw-r--r--lib/dialyzer/src/dialyzer_cl_parse.erl448
1 files changed, 448 insertions, 0 deletions
diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl
new file mode 100644
index 0000000000..ae466e5c01
--- /dev/null
+++ b/lib/dialyzer/src/dialyzer_cl_parse.erl
@@ -0,0 +1,448 @@
+%% -*- 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(dialyzer_cl_parse).
+
+-export([start/0]).
+-export([collect_args/1]). % used also by typer_options.erl
+
+-include("dialyzer.hrl").
+
+%%-----------------------------------------------------------------------
+
+-type dial_cl_parse_ret() :: {'check_init', #options{}}
+ | {'plt_info', #options{}}
+ | {'cl', #options{}}
+ | {{'gui', 'gs' | 'wx'}, #options{}}
+ | {'error', string()}.
+
+%%-----------------------------------------------------------------------
+
+-spec start() -> dial_cl_parse_ret().
+
+start() ->
+ init(),
+ Args = init:get_plain_arguments(),
+ try
+ cl(Args)
+ catch
+ throw:{dialyzer_cl_parse_error, Msg} -> {error, Msg};
+ _:R ->
+ Msg = io_lib:format("~p\n~p\n", [R, erlang:get_stacktrace()]),
+ {error, lists:flatten(Msg)}
+ end.
+
+cl(["--add_to_plt"|T]) ->
+ put(dialyzer_options_analysis_type, plt_add),
+ cl(T);
+cl(["--apps"|T]) ->
+ T1 = get_lib_dir(T, []),
+ {Args, T2} = collect_args(T1),
+ append_var(dialyzer_options_files_rec, Args),
+ cl(T2);
+cl(["--build_plt"|T]) ->
+ put(dialyzer_options_analysis_type, plt_build),
+ cl(T);
+cl(["--check_plt"|T]) ->
+ put(dialyzer_options_analysis_type, plt_check),
+ cl(T);
+cl(["-n"|T]) ->
+ cl(["--no_check_plt"|T]);
+cl(["--no_check_plt"|T]) ->
+ put(dialyzer_options_check_plt, false),
+ cl(T);
+cl(["--plt_info"|T]) ->
+ put(dialyzer_options_analysis_type, plt_info),
+ cl(T);
+cl(["--get_warnings"|T]) ->
+ put(dialyzer_options_get_warnings, true),
+ cl(T);
+cl(["-D"|_]) ->
+ error("No defines specified after -D");
+cl(["-D"++Define|T]) ->
+ Def = re:split(Define, "=", [{return, list}]),
+ append_defines(Def),
+ cl(T);
+cl(["-h"|_]) ->
+ help_message();
+cl(["--help"|_]) ->
+ help_message();
+cl(["-I"]) ->
+ error("no include directory specified after -I");
+cl(["-I", Dir|T]) ->
+ append_include(Dir),
+ cl(T);
+cl(["-I"++Dir|T]) ->
+ append_include(Dir),
+ cl(T);
+cl(["-c"++_|T]) ->
+ NewTail = command_line(T),
+ cl(NewTail);
+cl(["-r"++_|T0]) ->
+ {Args, T} = collect_args(T0),
+ append_var(dialyzer_options_files_rec, Args),
+ cl(T);
+cl(["--remove_from_plt"|T]) ->
+ put(dialyzer_options_analysis_type, plt_remove),
+ cl(T);
+cl(["--com"++_|T]) ->
+ NewTail = command_line(T),
+ cl(NewTail);
+cl(["--output"]) ->
+ error("No outfile specified");
+cl(["-o"]) ->
+ error("No outfile specified");
+cl(["--output",Output|T]) ->
+ put(dialyzer_output, Output),
+ cl(T);
+cl(["--output_plt"]) ->
+ error("No outfile specified for --output_plt");
+cl(["--output_plt",Output|T]) ->
+ put(dialyzer_output_plt, Output),
+ cl(T);
+cl(["-o", Output|T]) ->
+ put(dialyzer_output, Output),
+ cl(T);
+cl(["-o"++Output|T]) ->
+ put(dialyzer_output, Output),
+ cl(T);
+cl(["--raw"|T]) ->
+ put(dialyzer_output_format, raw),
+ cl(T);
+cl(["-pa", Path|T]) ->
+ case code:add_patha(Path) of
+ true -> cl(T);
+ {error, _} -> error("Bad directory for -pa: "++Path)
+ end;
+cl(["--plt", PLT|T]) ->
+ put(dialyzer_init_plt, PLT),
+ cl(T);
+cl(["--plt"]) ->
+ error("No plt specified for --plt");
+cl(["-q"|T]) ->
+ put(dialyzer_options_report_mode, quiet),
+ cl(T);
+cl(["--quiet"|T]) ->
+ put(dialyzer_options_report_mode, quiet),
+ cl(T);
+cl(["--src"|T]) ->
+ put(dialyzer_options_from, src_code),
+ cl(T);
+cl(["--no_spec"|T]) ->
+ put(dialyzer_options_use_contracts, false),
+ cl(T);
+cl(["-v"|_]) ->
+ io:format("Dialyzer version "++?VSN++"\n"),
+ erlang:halt(?RET_NOTHING_SUSPICIOUS);
+cl(["--version"|_]) ->
+ io:format("Dialyzer version "++?VSN++"\n"),
+ erlang:halt(?RET_NOTHING_SUSPICIOUS);
+cl(["--verbose"|T]) ->
+ put(dialyzer_options_report_mode, verbose),
+ cl(T);
+cl(["-W"|_]) ->
+ error("-W given without warning");
+cl(["-Whelp"|_]) ->
+ help_warnings();
+cl(["-W"++Warn|T]) ->
+ append_var(dialyzer_warnings, [list_to_atom(Warn)]),
+ cl(T);
+cl(["--dump_callgraph"]) ->
+ error("No outfile specified for --dump_callgraph");
+cl(["--dump_callgraph", File|T]) ->
+ put(dialyzer_callgraph_file, File),
+ cl(T);
+cl(["--gui"|T]) ->
+ put(dialyzer_options_mode, {gui, gs}),
+ cl(T);
+cl(["--wx"|T]) ->
+ put(dialyzer_options_mode, {gui, wx}),
+ cl(T);
+cl([H|_] = L) ->
+ case filelib:is_file(H) orelse filelib:is_dir(H) of
+ true ->
+ NewTail = command_line(L),
+ cl(NewTail);
+ false ->
+ error("Unknown option: "++H)
+ end;
+cl([]) ->
+ {RetTag, Opts} =
+ case get(dialyzer_options_analysis_type) =:= plt_info of
+ true ->
+ put(dialyzer_options_analysis_type, plt_check),
+ {plt_info, cl_options()};
+ false ->
+ case get(dialyzer_options_mode) of
+ {gui,_} = GUI -> {GUI, common_options()};
+ cl ->
+ case get(dialyzer_options_analysis_type) =:= plt_check of
+ true -> {check_init, cl_options()};
+ false -> {cl, cl_options()}
+ end
+ end
+ end,
+ case dialyzer_options:build(Opts) of
+ {error, Msg} -> error(Msg);
+ OptsRecord -> {RetTag, OptsRecord}
+ end.
+
+%%-----------------------------------------------------------------------
+
+command_line(T0) ->
+ {Args, T} = collect_args(T0),
+ append_var(dialyzer_options_files, Args),
+ %% if all files specified are ".erl" files, set the 'src' flag automatically
+ case lists:all(fun(F) -> filename:extension(F) =:= ".erl" end, Args) of
+ true -> put(dialyzer_options_from, src_code);
+ false -> ok
+ end,
+ T.
+
+error(Str) ->
+ Msg = lists:flatten(Str),
+ throw({dialyzer_cl_parse_error, Msg}).
+
+init() ->
+ put(dialyzer_options_mode, cl),
+ put(dialyzer_options_files_rec, []),
+ put(dialyzer_options_report_mode, normal),
+ put(dialyzer_warnings, []),
+ DefaultOpts = #options{},
+ put(dialyzer_include, DefaultOpts#options.include_dirs),
+ put(dialyzer_options_defines, DefaultOpts#options.defines),
+ put(dialyzer_options_files, DefaultOpts#options.files),
+ put(dialyzer_output_format, formatted),
+ put(dialyzer_options_check_plt, DefaultOpts#options.check_plt),
+ ok.
+
+append_defines([Def, Val]) ->
+ {ok, Tokens, _} = erl_scan:string(Val++"."),
+ {ok, ErlVal} = erl_parse:parse_term(Tokens),
+ append_var(dialyzer_options_defines, [{list_to_atom(Def), ErlVal}]);
+append_defines([Def]) ->
+ append_var(dialyzer_options_defines, [{list_to_atom(Def), true}]).
+
+append_include(Dir) ->
+ append_var(dialyzer_include, [Dir]).
+
+append_var(Var, List) when is_list(List) ->
+ put(Var, get(Var) ++ List),
+ ok.
+
+%%-----------------------------------------------------------------------
+
+-spec collect_args([string()]) -> {[string()], [string()]}.
+
+collect_args(List) ->
+ collect_args_1(List, []).
+
+collect_args_1(["-"++_|_] = L, Acc) ->
+ {lists:reverse(Acc), L};
+collect_args_1([Arg|T], Acc) ->
+ collect_args_1(T, [Arg|Acc]);
+collect_args_1([], Acc) ->
+ {lists:reverse(Acc), []}.
+
+%%-----------------------------------------------------------------------
+
+cl_options() ->
+ [{files, get(dialyzer_options_files)},
+ {files_rec, get(dialyzer_options_files_rec)},
+ {output_file, get(dialyzer_output)},
+ {output_format, get(dialyzer_output_format)},
+ {analysis_type, get(dialyzer_options_analysis_type)},
+ {get_warnings, get(dialyzer_options_get_warnings)},
+ {callgraph_file, get(dialyzer_callgraph_file)}
+ |common_options()].
+
+common_options() ->
+ [{defines, get(dialyzer_options_defines)},
+ {from, get(dialyzer_options_from)},
+ {include_dirs, get(dialyzer_include)},
+ {init_plt, get(dialyzer_init_plt)},
+ {output_plt, get(dialyzer_output_plt)},
+ {report_mode, get(dialyzer_options_report_mode)},
+ {use_spec, get(dialyzer_options_use_contracts)},
+ {warnings, get(dialyzer_warnings)},
+ {check_plt, get(dialyzer_options_check_plt)}].
+
+%%-----------------------------------------------------------------------
+
+get_lib_dir([H|T], Acc) ->
+ NewElem =
+ case code:lib_dir(list_to_atom(H)) of
+ {error, bad_name} ->
+ case H =:= "erts" of % hack for including erts in an un-installed system
+ true -> filename:join(code:root_dir(), "erts/preloaded/ebin");
+ false -> H
+ end;
+ LibDir -> LibDir ++ "/ebin"
+ end,
+ get_lib_dir(T, [NewElem|Acc]);
+get_lib_dir([], Acc) ->
+ lists:reverse(Acc).
+
+%%-----------------------------------------------------------------------
+
+help_warnings() ->
+ S = warning_options_msg(),
+ io:put_chars(S),
+ erlang:halt(?RET_NOTHING_SUSPICIOUS).
+
+help_message() ->
+ S = "Usage: dialyzer [--help] [--version] [--shell] [--quiet] [--verbose]
+ [-pa dir]* [--plt plt] [-Ddefine]* [-I include_dir]*
+ [--output_plt file] [-Wwarn]* [--src] [--gui | --wx]
+ [-c applications] [-r applications] [-o outfile]
+ [--build_plt] [--add_to_plt] [--remove_from_plt]
+ [--check_plt] [--no_check_plt] [--plt_info] [--get_warnings]
+Options:
+ -c applications (or --command-line applications)
+ Use Dialyzer from the command line (no GUI) to detect defects in the
+ specified applications (directories or .erl or .beam files)
+ -r applications
+ Same as -c only that directories are searched recursively for
+ subdirectories containing .erl or .beam files (depending on the
+ type of analysis)
+ -o outfile (or --output outfile)
+ When using Dialyzer from the command line, send the analysis
+ results to the specified \"outfile\" rather than to stdout
+ --raw
+ When using Dialyzer from the command line, output the raw analysis
+ results (Erlang terms) instead of the formatted result.
+ The raw format is easier to post-process (for instance, to filter
+ warnings or to output HTML pages)
+ --src
+ Override the default, which is to analyze BEAM files, and
+ analyze starting from Erlang source code instead
+ -Dname (or -Dname=value)
+ When analyzing from source, pass the define to Dialyzer (**)
+ -I include_dir
+ When analyzing from source, pass the include_dir to Dialyzer (**)
+ -pa dir
+ Include dir in the path for Erlang (useful when analyzing files
+ that have '-include_lib()' directives)
+ --output_plt file
+ Store the plt at the specified file after building it
+ --plt plt
+ Use the specified plt as the initial plt (if the plt was built
+ during setup the files will be checked for consistency)
+ -Wwarn
+ A family of options which selectively turn on/off warnings
+ (for help on the names of warnings use dialyzer -Whelp)
+ --shell
+ Do not disable the Erlang shell while running the GUI
+ --version (or -v)
+ Prints the Dialyzer version and some more information and exits
+ --help (or -h)
+ Prints this message and exits
+ --quiet (or -q)
+ Makes Dialyzer a bit more quiet
+ --verbose
+ Makes Dialyzer a bit more verbose
+ --build_plt
+ The analysis starts from an empty plt and creates a new one from the
+ files specified with -c and -r. Only works for beam files.
+ Use --plt or --output_plt to override the default plt location.
+ --add_to_plt
+ The plt is extended to also include the files specified with -c and -r.
+ Use --plt to specify wich plt to start from, and --output_plt to
+ specify where to put the plt. Note that the analysis might include
+ files from the plt if they depend on the new files.
+ This option only works with beam files.
+ --remove_from_plt
+ The information from the files specified with -c and -r is removed
+ from the plt. Note that this may cause a re-analysis of the remaining
+ dependent files.
+ --check_plt
+ Checks the plt for consistency and rebuilds it if it is not up-to-date.
+ Actually, this option is of rare use as it is on by default.
+ --no_check_plt (or -n)
+ Skip the plt check when running Dialyzer. Useful when working with
+ installed plts that never change.
+ --plt_info
+ Makes Dialyzer print information about the plt and then quit. The plt
+ can be specified with --plt.
+ --get_warnings
+ Makes Dialyzer emit warnings even when manipulating the plt. Only
+ emits warnings for files that are actually analyzed.
+ --dump_callgraph file
+ Dump the call graph into the specified file whose format is determined
+ by the file name extension. Supported extensions are: raw, dot, and ps.
+ If something else is used as file name extension, default format '.raw'
+ will be used.
+ --gui
+ Use the gs-based GUI.
+ --wx
+ Use the wx-based GUI.
+
+Note:
+ * denotes that multiple occurrences of these options are possible.
+ ** options -D and -I work both from command-line and in the Dialyzer GUI;
+ the syntax of defines and includes is the same as that used by \"erlc\".
+
+" ++ warning_options_msg() ++ "
+The exit status of the command line version is:
+ 0 - No problems were encountered during the analysis and no
+ warnings were emitted.
+ 1 - Problems were encountered during the analysis.
+ 2 - No problems were encountered, but warnings were emitted.
+",
+ io:put_chars(S),
+ erlang:halt(?RET_NOTHING_SUSPICIOUS).
+
+warning_options_msg() ->
+ "Warning options:
+ -Wno_return
+ Suppress warnings for functions that will never return a value.
+ -Wno_unused
+ Suppress warnings for unused functions.
+ -Wno_improper_lists
+ Suppress warnings for construction of improper lists.
+ -Wno_tuple_as_fun
+ Suppress warnings for using tuples instead of funs.
+ -Wno_fun_app
+ Suppress warnings for fun applications that will fail.
+ -Wno_match
+ Suppress warnings for patterns that are unused or cannot match.
+ -Wno_opaque
+ Suppress warnings for violations of opaqueness of data types.
+ -Wunmatched_returns ***
+ Include warnings for function calls which ignore a structured return
+ value or do not match against one of many possible return value(s).
+ -Werror_handling ***
+ Include warnings for functions that only return by means of an exception.
+ -Wrace_conditions ***
+ Include warnings for possible race conditions.
+ -Wunderspecs ***
+ Warn about underspecified functions
+ (those whose -spec is strictly more allowing than the success typing).
+
+The following options are also available but their use is not recommended:
+(they are mostly for Dialyzer developers and internal debugging)
+ -Woverspecs ***
+ Warn about overspecified functions
+ (those whose -spec is strictly less allowing than the success typing).
+ -Wspecdiffs ***
+ Warn when the -spec is different than the success typing.
+
+*** Identifies options that turn on warnings rather than turning them off.
+".