summaryrefslogtreecommitdiff
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.erl849
1 files changed, 303 insertions, 546 deletions
diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl
index bb792bb15a..2babea0073 100644
--- a/lib/dialyzer/src/dialyzer_cl_parse.erl
+++ b/lib/dialyzer/src/dialyzer_cl_parse.erl
@@ -1,5 +1,3 @@
-%% -*- erlang-indent-level: 2 -*-
-%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
@@ -14,8 +12,7 @@
-module(dialyzer_cl_parse).
--export([start/0, get_lib_dir/1]).
--export([collect_args/1]). % used also by typer
+-export([start/0]).
-include("dialyzer.hrl").
@@ -27,564 +24,298 @@
| {'gui', #options{}}
| {'error', string()}.
--type deep_string() :: string() | [deep_string()].
-
-%%-----------------------------------------------------------------------
-
-spec start() -> dial_cl_parse_ret().
-
start() ->
- init(),
- Args = init:get_plain_arguments(),
- try
- Ret = cl(Args),
- Ret
- catch
- throw:{dialyzer_cl_parse_error, Msg} -> {error, Msg};
- _:R:S ->
- Msg = io_lib:format("~tp\n~tp\n", [R, S]),
- {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(["--warning_apps"|T]) ->
- T1 = get_lib_dir(T),
- {Args, T2} = collect_args(T1),
- append_var(dialyzer_options_warning_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(["-nn"|T]) ->
- %% Ignored since Erlang/OTP 24.0.
- cl(T);
-cl(["--no_native"|T]) ->
- %% Ignored since Erlang/OTP 24.0.
- cl(T);
-cl(["--no_native_cache"|T]) ->
- %% Ignored since Erlang/OTP 24.0.
- 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"|_]) ->
- cl_error("No defines specified after -D");
-cl(["-D"++Define|T]) ->
- Def = re:split(Define, "=", [{return, list}, unicode]),
- append_defines(Def),
- cl(T);
-cl(["-h"|_]) ->
- help_message();
-cl(["--help"|_]) ->
- help_message();
-cl(["-I"]) ->
- cl_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(["--input_list_file"]) ->
- cl_error("No input list file specified");
-cl(["--input_list_file",File|L]) ->
- read_input_list_file(File),
- cl(L);
-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(["--incremental"|T]) ->
- put(dialyzer_options_analysis_type, incremental),
- cl(T);
-cl(["--com"++_|T]) ->
- NewTail = command_line(T),
- cl(NewTail);
-cl(["--output"]) ->
- cl_error("No outfile specified");
-cl(["-o"]) ->
- cl_error("No outfile specified");
-cl(["--output",Output|T]) ->
- put(dialyzer_output, Output),
- cl(T);
-cl(["--metrics_file",MetricsFile|T]) ->
- put(dialyzer_metrics, MetricsFile),
- cl(T);
-cl(["--module_lookup_file",ModuleLookupFile|T]) ->
- put(dialyzer_module_lookup, ModuleLookupFile),
- cl(T);
-cl(["--output_plt"]) ->
- cl_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(["--fullpath"|T]) ->
- put(dialyzer_filename_opt, fullpath),
- cl(T);
-cl(["--no_indentation"|T]) ->
- put(dialyzer_indent_opt, false),
- cl(T);
-cl(["-pa", Path|T]) ->
- case code:add_patha(Path) of
- true -> cl(T);
- {error, _} -> cl_error("Bad directory for -pa: " ++ Path)
- end;
-cl(["--plt"]) ->
- error("No plt specified for --plt");
-cl(["--plt", PLT|T]) ->
- put(dialyzer_init_plts, [PLT]),
- cl(T);
-cl(["--plts"]) ->
- error("No plts specified for --plts");
-cl(["--plts"|T]) ->
- {PLTs, NewT} = get_plts(T, []),
- put(dialyzer_init_plts, PLTs),
- cl(NewT);
-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(["--statistics"|T]) ->
- put(dialyzer_timing, true),
- cl(T);
-cl(["--resources"|T]) ->
- put(dialyzer_options_report_mode, quiet),
- put(dialyzer_timing, debug),
- 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"|_]) ->
- cl_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"]) ->
- cl_error("No outfile specified for --dump_callgraph");
-cl(["--dump_callgraph", File|T]) ->
- put(dialyzer_callgraph_file, File),
- cl(T);
-cl(["--dump_full_dependencies_graph"]) ->
- cl_error("No outfile specified for --dump_full_dependencies_graph");
-cl(["--dump_full_dependencies_graph", File|T]) ->
- put(dialyzer_mod_deps_file, File),
- cl(T);
-cl(["--gui"|T]) ->
- put(dialyzer_options_mode, gui),
- cl(T);
-cl(["--error_location", LineOrColumn|T]) ->
- put(dialyzer_error_location_opt, list_to_atom(LineOrColumn)),
- cl(T);
-cl(["--solver", Solver|T]) -> % not documented
- append_var(dialyzer_solvers, [list_to_atom(Solver)]),
- cl(T);
-cl([H|_] = L) ->
- case filelib:is_file(H) orelse filelib:is_dir(H) of
- true ->
- NewTail = command_line(L),
- cl(NewTail);
- false ->
- cl_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, 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} -> cl_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.
-
-read_input_list_file(File) ->
- case file:read_file(File) of
- {ok,Bin} ->
- Files = binary:split(Bin, <<"\n">>, [trim_all,global]),
- NewFiles = [binary_to_list(string:trim(F)) || F <- Files],
- append_var(dialyzer_options_files, NewFiles);
- {error,Reason} ->
- cl_error(io_lib:format("Reading of ~s failed: ~s", [File,file:format_error(Reason)]))
- end.
-
--spec cl_error(deep_string()) -> no_return().
-
-cl_error(Str) ->
- Msg = lists:flatten(Str),
- throw({dialyzer_cl_parse_error, Msg}).
-
-init() ->
- %% By not initializing every option, the modified options can be
- %% found. If every option were to be returned by cl_options() and
- %% common_options(), then the environment variables (currently only
- %% ERL_COMPILER_OPTIONS) would be overwritten by default values.
- put(dialyzer_options_mode, cl),
- put(dialyzer_options_files_rec, []),
- put(dialyzer_options_warning_files_rec, []),
- put(dialyzer_options_report_mode, normal),
- put(dialyzer_warnings, []),
- 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) ->
- case get(Var) of
- undefined ->
- put(Var, List);
- L ->
- put(Var, L ++ List)
- end,
- 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), []}.
+ Args = init:get_plain_arguments(),
+ try argparse:parse(Args, cli(), #{progname => dialyzer}) of
+ {ok, ArgMap, _, _} ->
+ {Command, Opts} = postprocess_side_effects(ArgMap),
+ case dialyzer_options:build(maps:to_list(Opts)) of
+ {error, Msg2} ->
+ {error, Msg2};
+ OptsRecord ->
+ {Command, OptsRecord}
+ end;
+ {error, Error} ->
+ {error, argparse:format_error(Error)}
+ catch
+ throw:{dialyzer_cl_parse_error, Msg} ->
+ {error, Msg};
+ _:R:S ->
+ Msg = io_lib:format("~tp\n~tp\n", [R, S]),
+ {error, lists:flatten(Msg)}
+ end.
%%-----------------------------------------------------------------------
-cl_options() ->
- OptsList = [{files, dialyzer_options_files},
- {files_rec, dialyzer_options_files_rec},
- {warning_files_rec, dialyzer_options_warning_files_rec},
- {output_file, dialyzer_output},
- {metrics_file, dialyzer_metrics},
- {module_lookup_file, dialyzer_module_lookup},
- {output_format, dialyzer_output_format},
- {filename_opt, dialyzer_filename_opt},
- {indent_opt, dialyzer_indent_opt},
- {analysis_type, dialyzer_options_analysis_type},
- {get_warnings, dialyzer_options_get_warnings},
- {timing, dialyzer_timing},
- {callgraph_file, dialyzer_callgraph_file},
- {mod_deps_file, dialyzer_mod_deps_file}],
- get_options(OptsList) ++ common_options().
-
-common_options() ->
- OptsList = [{defines, dialyzer_options_defines},
- {from, dialyzer_options_from},
- {include_dirs, dialyzer_include},
- {plts, dialyzer_init_plts},
- {output_plt, dialyzer_output_plt},
- {report_mode, dialyzer_options_report_mode},
- {use_spec, dialyzer_options_use_contracts},
- {warnings, dialyzer_warnings},
- {check_plt, dialyzer_options_check_plt},
- {solvers, dialyzer_solvers}],
- get_options(OptsList).
-
-get_options(TagOptionList) ->
- lists:append([get_opt(Tag, Opt) || {Tag, Opt} <- TagOptionList]).
-
-get_opt(Tag, Opt) ->
- case get(Opt) of
- undefined ->
- [];
- V ->
- [{Tag, V}]
- end.
-
-%%-----------------------------------------------------------------------
-
--spec get_lib_dir([string()]) -> [string()].
-
-get_lib_dir(Apps) ->
- get_lib_dir(Apps, []).
-
-get_lib_dir([H|T], Acc) ->
- NewElem =
- case code:lib_dir(list_to_atom(H)) of
- {error, bad_name} -> H;
- LibDir when H =:= "erts" -> % hack for including erts in an un-installed system
- EbinDir = filename:join([LibDir,"ebin"]),
- case file:read_file_info(EbinDir) of
- {error,enoent} ->
- filename:join([LibDir,"preloaded","ebin"]);
- _ ->
- EbinDir
- end;
- LibDir -> filename:join(LibDir,"ebin")
- end,
- get_lib_dir(T, [NewElem|Acc]);
-get_lib_dir([], Acc) ->
- lists:reverse(Acc).
-
-%%-----------------------------------------------------------------------
-
-get_plts(["--"|T], Acc) -> {lists:reverse(Acc), T};
-get_plts(["-"++_Opt = H|T], Acc) -> {lists:reverse(Acc), [H|T]};
-get_plts([H|T], Acc) -> get_plts(T, [H|Acc]);
-get_plts([], Acc) -> {lists:reverse(Acc), []}.
-
-%%-----------------------------------------------------------------------
-
--spec help_warnings() -> no_return().
-
-help_warnings() ->
- S = warning_options_msg(),
- io:put_chars(S),
- erlang:halt(?RET_NOTHING_SUSPICIOUS).
-
--spec help_message() -> no_return().
-
-help_message() ->
- S = "Usage: dialyzer [--add_to_plt] [--apps applications] [--build_plt]
- [--check_plt] [-Ddefine]* [-Dname]* [--dump_callgraph file]
- [--error_location flag] [files_or_dirs] [--fullpath]
- [--get_warnings] [--gui] [--help] [-I include_dir]*
- [--incremental] [--metrics_file] [--no_check_plt] [--no_indentation] [--no_spec]
- [-o outfile] [--output_plt file] [-pa dir]* [--plt plt] [--plt_info]
- [--plts plt*] [--quiet] [-r dirs] [--raw] [--remove_from_plt]
- [--shell] [--src] [--statistics] [--verbose] [--version]
- [--warning_apps] [-Wwarn]*
-
-Options:
- files_or_dirs (for backwards compatibility also as: -c files_or_dirs)
- Use Dialyzer from the command line to detect defects in the
- specified files or directories containing .erl or .beam files,
- depending on the type of the analysis.
- -r dirs
- Same as the previous but the specified directories are searched
- recursively for subdirectories containing .erl or .beam files in
- them, depending on the type of analysis.
- --input_list_file file
- Specify the name of a file that contains the names of the files
- to be analyzed (one file name per line).
- --apps applications
- Option typically used when building or modifying a plt as in:
- dialyzer --build_plt --apps erts kernel stdlib mnesia ...
- to conveniently refer to library applications corresponding to the
- Erlang/OTP installation. However, the option is general and can also
- be used during analysis in order to refer to Erlang/OTP applications.
- In addition, file or directory names can also be included, as in:
- dialyzer --apps inets ssl ./ebin ../other_lib/ebin/my_module.beam
- --warning_apps applications
- By default, warnings will be reported to all applications given by
- --apps. However, if --warning_apps is used, only those applications
- given to --warning_apps will have warnings reported. All applications
- given by --apps, but not --warning_apps, will be analysed to provide
- context to the analysis, but warnings will not be reported for them.
- For example, you may want to include libraries you depend on in the
- analysis with --apps so discrepancies in their usage can be found,
- but only include your own code with --warning_apps so that
- discrepancies are only reported in code that you own.
- -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).
- --plts plt*
- Merge the specified plts to create the initial plt -- requires
- that the plts are disjoint (i.e., do not have any module
- appearing in more than one plt).
- The plts are created in the usual way:
- dialyzer --build_plt --output_plt plt_1 files_to_include
- ...
- dialyzer --build_plt --output_plt plt_n files_to_include
- and then can be used in either of the following ways:
- dialyzer files_to_analyze --plts plt_1 ... plt_n
- or:
- dialyzer --plts plt_1 ... plt_n -- files_to_analyze
- (Note the -- delimiter in the second case)
- -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)
- Print the Dialyzer version and some more information and exit.
- --help (or -h)
- Print this message and exit.
- --quiet (or -q)
- Make Dialyzer a bit more quiet.
- --verbose
- Make Dialyzer a bit more verbose.
- --statistics
- Prints information about the progress of execution (analysis phases,
- time spent in each and size of the relative input).
- --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(s) 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(s) to specify which 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
- Check the plt for consistency and rebuild 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.
- --incremental
- The analysis starts from an existing incremental PLT, or builds one from
- scratch if one doesn't exist, and runs the minimal amount of additional
- analysis to report all issues in the given set of apps. Notably, incremental
- PLT files are not compatible with \"classic\" PLT files, and vice versa.
- The initial incremental PLT will be updated unless an alternative output
- incremental PLT is given.
- --plt_info
- Make Dialyzer print information about the plt and then quit. The plt
- can be specified with --plt(s).
- --get_warnings
- Make Dialyzer emit warnings even when manipulating the plt. Warnings
- are only emitted 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.
- --dump_full_dependencies_graph file
- Dump the full dependency graph (i.e. dependencies induced by function
- calls, usages of types in specs, behaviour implementations, etc.) into
- the specified file whose format is determined by the file name
- extension. Supported extensions are: dot and ps.
- --metrics_file file
- Write metrics about Dialyzer's incrementality (for example, total number of
- modules considered, how many modules were changed since the PLT was
- last updated, how many modules needed to be analyzed) to a file. This
- can be useful for tracking and debugging Dialyzer's incrementality.
- --error_location column | line
- Use a pair {Line, Column} or an integer Line to pinpoint the location
- of warnings. The default is to use a pair {Line, Column}. When
- formatted, the line and the column are separated by a colon.
- --fullpath
- Display the full path names of files for which warnings are emitted.
- --no_indentation
- Do not indent contracts and success typings. Note that this option has
- no effect when combined with the --raw option.
- --no_spec
- Ignore functions specs. This is useful for debugging when one suspects
- that some specs are incorrect.
- --gui
- Use the GUI.
-
+parse_app(AppOrDir) ->
+ case code:lib_dir(list_to_atom(AppOrDir)) of
+ {error, bad_name} -> AppOrDir;
+ LibDir when AppOrDir =:= "erts" -> % hack for including erts in an un-installed system
+ EbinDir = filename:join([LibDir, "ebin"]),
+ case file:read_file_info(EbinDir) of
+ {error, enoent} ->
+ filename:join([LibDir, "preloaded", "ebin"]);
+ _ ->
+ EbinDir
+ end;
+ LibDir -> filename:join(LibDir, "ebin")
+ end.
+
+parse_input_list(File) ->
+ case file:read_file(File) of
+ {ok, Bin} ->
+ Files = binary:split(Bin, <<"\n">>, [trim_all, global]),
+ [binary_to_list(string:trim(F)) || F <- Files];
+ {error, Reason} ->
+ cl_error(io_lib:format("Reading of ~s failed: ~s", [File, file:format_error(Reason)]))
+ end.
+
+parse_define(Arg) ->
+ case re:split(Arg, "=", [{return, list}, unicode]) of
+ [Def, Val] ->
+ {ok, Tokens, _} = erl_scan:string(Val++"."),
+ {ok, ErlVal} = erl_parse:parse_term(Tokens),
+ {list_to_atom(Def), ErlVal};
+ [Def] ->
+ {list_to_atom(Def), true}
+ end.
+
+cli() ->
+ #{
+ arguments => [
+ #{name => files, action => extend, nargs => list, required => false,
+ help => <<"Use Dialyzer from the command line to detect defects in the "
+ "specified files or directories containing .erl or .beam files, "
+ "depending on the type of the analysis.">>},
+ #{name => files, short => $c, long => "-com", action => extend, nargs => list,
+ help => <<"Same as files, specifies files to run the analysis on (left for compatibility)">>},
+ #{name => files_rec, short => $r, action => extend, nargs => list,
+ help => <<"Search the specified directories "
+ "recursively for subdirectories containing .erl or .beam files in "
+ "them, depending on the type of analysis.">>},
+ #{name => files, long => "-input_list_file", type => {custom, fun parse_input_list/1},
+ action => extend,
+ help => <<"Specify the name of a file that contains the names of the files "
+ "to be analyzed (one file name per line).">>},
+ #{name => files_rec, long => "-apps", type => {custom, fun parse_app/1},
+ nargs => list, action => extend,
+ help => <<"Option typically used when building or modifying a plt as in: \n"
+ "dialyzer --build_plt --apps erts kernel stdlib mnesia ... \n"
+ "to conveniently refer to library applications corresponding to the "
+ "Erlang/OTP installation. However, the option is general and can also "
+ "be used during analysis in order to refer to Erlang/OTP applications. "
+ "In addition, file or directory names can also be included, as in: \n"
+ "dialyzer --apps inets ssl ./ebin ../other_lib/ebin/my_module.beam">>},
+
+ #{name => output_file, short => $o, long => "--output",
+ help => <<"When using Dialyzer from the command line, send the analysis "
+ "results to the specified outfile rather than to stdout.">>},
+ #{name => output_format, long => "-raw", type => boolean, action => {store, raw},
+ help => <<"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).">>},
+ #{name => from, long => "-src", type => boolean, action => {store, src_code},
+ help => <<"Override the default, which is to analyze BEAM files, and "
+ "analyze starting from Erlang source code instead.">>},
+ #{name => defines, short=>$D, type => {custom, fun parse_define/1}, action => append,
+ help => <<"When analyzing from source, pass the define to Dialyzer. (**)">>},
+ #{name => include_dirs, short=>$I, action => append,
+ help => <<"When analyzing from source, pass the include_dir to Dialyzer. (**)">>},
+ #{name => pa, long => "pa", action => append,
+ help => <<"Include dir in the path for Erlang (useful when analyzing files "
+ "that have '-include_lib()' directives).">>},
+ #{name => output_plt, long => "-output_plt",
+ help => <<"Store the plt at the specified file after building it.">>},
+ #{name => plts, long => "-plt", nargs => 1,
+ help => <<"Use the specified plt as the initial plt (if the plt was built "
+ "during setup the files will be checked for consistency).">>},
+ #{name => plts, long => "-plts", nargs => nonempty_list,
+ help => <<"Merge the specified plts to create the initial plt -- requires "
+ "that the plts are disjoint (i.e., do not have any module "
+ "appearing in more than one plt). "
+ "The plts are created in the usual way: \n"
+ " dialyzer --build_plt --output_plt plt_1 files_to_include "
+ " ... \n"
+ " dialyzer --build_plt --output_plt plt_n files_to_include "
+ "and then can be used in either of the following ways: \n"
+ " dialyzer files_to_analyze --plts plt_1 ... plt_n \n"
+ "or: \n"
+ " dialyzer --plts plt_1 ... plt_n -- files_to_analyze \n"
+ "(Note the -- delimiter in the second case)">>},
+ #{name => warnings, short => $W, action => append, type => {atom, [error_handling,
+ no_behaviours, no_contracts, no_fail_call, no_fun_app, no_improper_lists,
+ no_match, no_missing_calls, no_opaque, no_return, no_undefined_callbacks,
+ no_underspecs, no_unknown, no_unused, underspecs, unknown, unmatched_returns,
+ overspecs, specdiffs, extra_return, no_extra_return, missing_return, no_missing_return]},
+ help => {<<"[-Wwarn]*">>, [<<"A family of options which selectively turn on/off warnings">>]}},
+ #{name => shell, long => "-shell", type => boolean,
+ help => <<"Do not disable the Erlang shell while running the GUI.">>},
+ #{name => version, short => $v, long => "-version", type => boolean,
+ help => <<"Print the Dialyzer version and some more information and exit.">>},
+ #{name => help, short => $h, long => "-help", type => boolean,
+ help => <<"Print this message and exit.">>},
+ #{name => report_mode, short => $q, long => "-quiet", type => boolean, action => {store, quiet},
+ default => normal, help => <<"Make Dialyzer a bit more quiet.">>},
+ #{name => report_mode, long => "-verbose", type => boolean, action => {store, verbose},
+ help => <<"Make Dialyzer a bit more verbose.">>},
+ #{name => timing, long => "-statistics", type => boolean,
+ help => <<"Prints information about the progress of execution (analysis phases, "
+ "time spent in each and size of the relative input).">>},
+ #{name => analysis_type, long => "-build_plt", type => boolean, action => {store, plt_build},
+ help => <<"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(s) or --output_plt to override the default plt location.">>},
+ #{name => analysis_type, long=> "-add_to_plt", type => boolean, action => {store, plt_add},
+ help => <<"The plt is extended to also include the files specified with -c and -r. "
+ "Use --plt(s) to specify which 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.">>},
+ #{name => analysis_type, long => "-remove_from_plt", type => boolean, action => {store, plt_remove},
+ help => <<"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.">>},
+ #{name => analysis_type, long => "-check_plt", type => boolean, action => {store, plt_check},
+ help => <<"Check the plt for consistency and rebuild it if it is not up-to-date. "
+ "Actually, this option is of rare use as it is on by default.">>},
+ #{name => check_plt, long => "-no_check_plt", short => $n, type => boolean, action => {store, false},
+ help => <<"Skip the plt check when running Dialyzer. Useful when working with "
+ "installed plts that never change.">>},
+ #{name => analysis_type, long => "-incremental", type => boolean, action => {store, incremental},
+ help => <<"The analysis starts from an existing incremental PLT, or builds one from "
+ "scratch if one doesn't exist, and runs the minimal amount of additional "
+ "analysis to report all issues in the given set of apps. Notably, incremental "
+ "PLT files are not compatible with \"classic\" PLT files, and vice versa. "
+ "The initial incremental PLT will be updated unless an alternative output "
+ "incremental PLT is given.">>},
+ #{name => analysis_type, long => "-plt_info", type => boolean, action => {store, plt_info},
+ help => <<"Make Dialyzer print information about the plt and then quit. The plt "
+ "can be specified with --plt(s).">>},
+ #{name => get_warnings, long => "-get_warnings", type => boolean,
+ help => <<"Make Dialyzer emit warnings even when manipulating the plt. Warnings "
+ "are only emitted for files that are actually analyzed.">>},
+ #{name => callgraph_file, long => "-dump_callgraph",
+ help => <<"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.">>},
+ #{name => mod_deps_file, long => "-dump_full_dependencies_graph",
+ help => <<"Dump the full dependency graph (i.e. dependencies induced by function "
+ "calls, usages of types in specs, behaviour implementations, etc.) into "
+ "the specified file whose format is determined by the file name "
+ "extension. Supported extensions are: dot and ps.">>},
+ #{name => error_location, long => "-error_location", type => {atom, [column, line]},
+ help => <<"Use a pair {Line, Column} or an integer Line to pinpoint the location "
+ "of warnings. The default is to use a pair {Line, Column}. When "
+ "formatted, the line and the column are separated by a colon.">>},
+ #{name => filename_opt, long => "-fullpath", type => boolean, action => {store, fullpath},
+ help => <<"Display the full path names of files for which warnings are emitted.">>},
+ #{name => indent_opt, long => "-no_indentation", type => boolean, action => {store, false},
+ help => <<"Do not indent contracts and success typings. Note that this option has "
+ "no effect when combined with the --raw option.">>},
+ #{name => gui, long => "-gui", type => boolean,
+ help => <<"Use the GUI.">>},
+ #{name => metrics_file, long => "-metrics_file",
+ help => <<"Write metrics about Dialyzer's incrementality (for example, total number of "
+ "modules considered, how many modules were changed since the PLT was "
+ "last updated, how many modules needed to be analyzed) to a file. This "
+ "can be useful for tracking and debugging Dialyzer's incrementality.">>},
+ #{name => warning_files_rec, long => "-warning_apps", type => {custom, fun parse_app/1},
+ nargs => list, action => extend,
+ help => <<"By default, warnings will be reported to all applications given by "
+ "--apps. However, if --warning_apps is used, only those applications "
+ "given to --warning_apps will have warnings reported. All applications "
+ "given by --apps, but not --warning_apps, will be analysed to provide "
+ "context to the analysis, but warnings will not be reported for them. "
+ "For example, you may want to include libraries you depend on in the "
+ "analysis with --apps so discrepancies in their usage can be found, "
+ "but only include your own code with --warning_apps so that "
+ "discrepancies are only reported in code that you own.">>},
+
+ %% Intentionally undocumented options
+ #{name => solvers, long => "-solver", type => {atom, [v1, v2]}, action => append,
+ help => hidden},
+ #{name => timing, long => "-resources", type => boolean, action => {store, debug},
+ help => hidden},
+
+ %% next definition is necessary to ignore '--' left for compatibility reasons
+ #{name => shell, short => $-, type => boolean, help => hidden}
+ ],
+
+ help => [<<"Usage: ">>, usage, <<"\n\nOptions:\n">>,
+ arguments, options, "
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() ++ "
+" ++ configuration_file_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).
+
+"]
+ }.
+
+postprocess_side_effects(ArgMap) when is_map_key(version, ArgMap) ->
+ %% Version handling
+ io:format("Dialyzer version " ++ ?VSN ++ "\n"),
+ erlang:halt(?RET_NOTHING_SUSPICIOUS);
+
+postprocess_side_effects(ArgMap) when is_map_key(help, ArgMap) ->
+ %% Help message
+ io:format(argparse:help(cli(), #{progname => dialyzer})),
+ erlang:halt(?RET_NOTHING_SUSPICIOUS);
+
+postprocess_side_effects(ArgMap) when is_map_key(pa, ArgMap) ->
+ %% Code path side effect
+ [code:add_patha(Path) =/= true andalso cl_error("Bad directory for -pa: " ++ Path) ||
+ Path <- map_get(pa, ArgMap)],
+ postprocess_side_effects(maps:remove(pa, ArgMap));
+
+postprocess_side_effects(ArgMap) when is_map_key(shell, ArgMap) ->
+ %% --shell option is processed by C executable (left here only for help/usage)
+ postprocess_side_effects(maps:remove(shell, ArgMap));
+
+postprocess_side_effects(ArgMap) ->
+ %% if all files specified are ".erl" files, set the 'src' flag automatically
+ %% it is compatibility behaviour, potentially incorrect, because it does not take
+ %% directories (rec_files) into account
+ ArgMap1 =
+ case (is_map_key(files, ArgMap) andalso
+ lists:all(fun(F) -> filename:extension(F) =:= ".erl" end, maps:get(files, ArgMap))) of
+ true ->
+ ArgMap#{from => src_code};
+ false ->
+ ArgMap
+ end,
+
+ %% Run mode (command) is defined by the flag combination
+ case maps:get(analysis_type, ArgMap1, undefined) of
+ plt_info ->
+ %% plt_info is plt_check analysis type
+ {plt_info, ArgMap1#{analysis_type => plt_check}};
+ plt_check ->
+ %% plt_check is a hidden "check_init" command
+ {check_init, ArgMap1};
+ _ when map_get(gui, ArgMap1) ->
+ %% filter out command-line only arguments
+ Allowed = [defines, from, include_dirs, plts, output_plt, report_mode,
+ use_spec, warnings, check_plt, solvers],
+ {gui, maps:with(Allowed, ArgMap1)};
+ _ ->
+ {cl, ArgMap1}
+ end.
+
+cl_error(Str) ->
+ Msg = lists:flatten(Str),
+ throw({dialyzer_cl_parse_error, Msg}).
warning_options_msg() ->
"Warning options:
@@ -654,3 +385,29 @@ They are primarily intended to be used with the -dialyzer attribute:
-Wno_missing_return
Suppress warnings about functions that return values that are not part of the specification.
".
+
+configuration_file_msg() ->
+ "Configuration file:
+ Dialyzer's configuration file may also be used to augment the default
+ options and those given directly to the Dialyzer command. It is commonly
+ used to avoid repeating options which would otherwise need to be given
+ explicitly to Dialyzer on every invocation.
+
+ The location of the configuration file can be set via the
+ DIALYZER_CONFIG environment variable, and defaults to
+ within the user_config location given by filename:basedir/3.
+
+ On your system, the location is currently configured as:
+ " ++ dialyzer_options:get_default_config_filename() ++
+ "
+
+ An example configuration file's contents might be:
+
+ {incremental,
+ {default_apps,[stdlib,kernel,erts]},
+ {default_warning_apps,[stdlib]}
+ }.
+ {warnings, [no_improper_lists]}.
+ {add_pathsa,[\"/users/samwise/potatoes/ebin\"]}.
+ {add_pathsz,[\"/users/smeagol/fish/ebin\"]}.
+".