summaryrefslogtreecommitdiff
path: root/lib/dialyzer/src/dialyzer_options.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dialyzer/src/dialyzer_options.erl')
-rw-r--r--lib/dialyzer/src/dialyzer_options.erl247
1 files changed, 219 insertions, 28 deletions
diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl
index 5a56e0b0cb..de35f5f204 100644
--- a/lib/dialyzer/src/dialyzer_options.erl
+++ b/lib/dialyzer/src/dialyzer_options.erl
@@ -18,7 +18,7 @@
-module(dialyzer_options).
--export([build/1, build_warnings/2]).
+-export([build/1, build_warnings/2, get_default_config_filename/0]).
-include("dialyzer.hrl").
@@ -32,26 +32,28 @@
build(Opts) ->
DefaultWarns = [?WARN_RETURN_NO_RETURN,
- ?WARN_NOT_CALLED,
- ?WARN_NON_PROPER_LIST,
- ?WARN_FUN_APP,
- ?WARN_MATCHING,
- ?WARN_OPAQUE,
- ?WARN_CALLGRAPH,
- ?WARN_FAILING_CALL,
- ?WARN_BIN_CONSTRUCTION,
- ?WARN_MAP_CONSTRUCTION,
- ?WARN_CONTRACT_RANGE,
- ?WARN_CONTRACT_TYPES,
- ?WARN_CONTRACT_SYNTAX,
- ?WARN_BEHAVIOUR,
- ?WARN_UNDEFINED_CALLBACK],
+ ?WARN_NOT_CALLED,
+ ?WARN_NON_PROPER_LIST,
+ ?WARN_FUN_APP,
+ ?WARN_MATCHING,
+ ?WARN_OPAQUE,
+ ?WARN_CALLGRAPH,
+ ?WARN_FAILING_CALL,
+ ?WARN_BIN_CONSTRUCTION,
+ ?WARN_MAP_CONSTRUCTION,
+ ?WARN_CONTRACT_RANGE,
+ ?WARN_CONTRACT_TYPES,
+ ?WARN_CONTRACT_SYNTAX,
+ ?WARN_BEHAVIOUR,
+ ?WARN_UNDEFINED_CALLBACK,
+ ?WARN_UNKNOWN],
DefaultWarns1 = ordsets:from_list(DefaultWarns),
- InitPlt = dialyzer_plt:get_default_plt(),
- DefaultOpts = #options{},
- DefaultOpts1 = DefaultOpts#options{legal_warnings = DefaultWarns1,
- init_plts = [InitPlt]},
try
+ WarningsFromConfig = proplists:get_value(warnings, get_config(), []),
+ update_path_from_config(),
+ DefaultWarns2 = build_warnings(WarningsFromConfig, DefaultWarns1),
+ DefaultOpts = #options{},
+ DefaultOpts1 = DefaultOpts#options{legal_warnings = DefaultWarns2},
Opts1 = preprocess_opts(Opts),
Env = env_default_opts(),
ErrLoc = proplists:get_value(error_location, Env, ?ERROR_LOCATION),
@@ -62,6 +64,31 @@ build(Opts) ->
throw:{dialyzer_options_error, Msg} -> {error, Msg}
end.
+update_path_from_config() ->
+ Config = get_config(),
+ PAs = proplists:get_value(add_pathsa, Config, []),
+ PZs = proplists:get_value(add_pathsz, Config, []),
+ case is_list(PAs) of
+ true -> ok;
+ false -> bad_option("Bad list of paths in config", {add_pathsa, PAs})
+ end,
+ case is_list(PZs) of
+ true -> ok;
+ false -> bad_option("Bad list of paths in config", {add_pathsz, PZs})
+ end,
+ %% Add paths one-by-one so that we can report issues
+ %% if any path is invalid
+ %% (code:add_pathsa/1 and code:add_pathsz/1 always return ok)
+ [ case code:add_patha(PA) of
+ true -> ok;
+ {error, _} -> bad_option("Failed to add path from config", {add_patha, PA})
+ end || PA <- PAs ],
+ [ case code:add_pathz(PZ) of
+ true -> ok;
+ {error, _} -> bad_option("Failed to add path from config", {add_pathz, PZ})
+ end || PZ <- PZs ],
+ ok.
+
preprocess_opts([]) -> [];
preprocess_opts([{init_plt, File}|Opts]) ->
[{plts, [File]}|preprocess_opts(Opts)];
@@ -69,14 +96,45 @@ preprocess_opts([Opt|Opts]) ->
[Opt|preprocess_opts(Opts)].
postprocess_opts(Opts = #options{}) ->
- check_file_existence(Opts),
- Opts1 = check_output_plt(Opts),
- adapt_get_warnings(Opts1).
+ Opts1 =
+ case {Opts#options.init_plts, Opts#options.analysis_type} of
+ {[],incremental} -> Opts#options{init_plts=[dialyzer_iplt:get_default_iplt_filename()]};
+ {[],_} -> Opts#options{init_plts=[dialyzer_cplt:get_default_cplt_filename()]};
+ {[_|_],_} -> Opts
+ end,
+ check_file_existence(Opts1),
+ check_metrics_file_validity(Opts1),
+ check_module_lookup_file_validity(Opts1),
+ Opts2 = check_output_plt(Opts1),
+ check_init_plt_kind(Opts2),
+ Opts3 = manage_default_incremental_apps(Opts2),
+ adapt_get_warnings(Opts3).
+
+check_metrics_file_validity(#options{analysis_type = incremental, metrics_file = none}) ->
+ ok;
+check_metrics_file_validity(#options{analysis_type = incremental, metrics_file = FileName}) ->
+ assert_filename(FileName);
+check_metrics_file_validity(#options{analysis_type = _NotIncremental, metrics_file = none}) ->
+ ok;
+check_metrics_file_validity(#options{analysis_type = _NotIncremental, metrics_file = FileName}) ->
+ bad_option("A metrics filename may only be given when in incremental mode", {metrics_file, FileName}).
+
+check_module_lookup_file_validity(#options{analysis_type = incremental, module_lookup_file = none}) ->
+ ok;
+check_module_lookup_file_validity(#options{analysis_type = incremental, module_lookup_file = FileName}) ->
+ assert_filename(FileName);
+check_module_lookup_file_validity(#options{analysis_type = _NotIncremental, module_lookup_file = none}) ->
+ ok;
+check_module_lookup_file_validity(#options{analysis_type = _NotIncremental, module_lookup_file = FileName}) ->
+ bad_option("A module lookup filename may only be given when in incremental mode", {module_lookup_file, FileName}).
check_file_existence(#options{analysis_type = plt_remove}) -> ok;
-check_file_existence(#options{files = Files, files_rec = FilesRec}) ->
+check_file_existence(#options{files = Files, files_rec = FilesRec,
+ warning_files = WarningFiles, warning_files_rec = WarningFilesRec}) ->
assert_filenames_exist(Files),
- assert_filenames_exist(FilesRec).
+ assert_filenames_exist(FilesRec),
+ assert_filenames_exist(WarningFiles),
+ assert_filenames_exist(WarningFilesRec).
check_output_plt(Opts = #options{analysis_type = Mode, from = From,
output_plt = OutPLT}) ->
@@ -98,6 +156,89 @@ check_output_plt(Opts = #options{analysis_type = Mode, from = From,
end
end.
+check_init_plt_kind(#options{analysis_type = incremental, init_plts = InitPlts}) ->
+ RunCheck = fun(FileName) ->
+ case dialyzer_plt:plt_kind(FileName) of
+ no_file -> ok;
+ iplt -> ok;
+ cplt ->
+ bad_option("Given file is a classic PLT file, "
+ "but in incremental mode, "
+ "an incremental PLT file is expected",
+ {init_plt_file, FileName});
+ bad_file ->
+ bad_option("Given file is not a PLT file", {init_plt_file, FileName})
+ end
+ end,
+ lists:foreach(RunCheck, InitPlts);
+check_init_plt_kind(#options{analysis_type = _NotIncremental, init_plts = InitPlts}) ->
+ RunCheck = fun(FileName) ->
+ case dialyzer_plt:plt_kind(FileName) of
+ no_file -> ok;
+ cplt -> ok;
+ iplt ->
+ bad_option("Given file is an incremental PLT file, "
+ "but outside of incremental mode, "
+ "a classic PLT file is expected",
+ {init_plt_file, FileName});
+ bad_file ->
+ bad_option("Given file is not a PLT file", {init_plt_file, FileName})
+ end
+ end,
+ lists:foreach(RunCheck, InitPlts).
+
+%% If no apps are set explicitly, we fall back to config
+manage_default_incremental_apps(Opts = #options{analysis_type = incremental, files = [], files_rec = [], warning_files = [], warning_files_rec = []}) ->
+ set_default_apps(get_config(), Opts);
+manage_default_incremental_apps(Opts) ->
+ Opts.
+
+set_default_apps([ConfigElem|MoreConfig], Opts) ->
+ case ConfigElem of
+ {incremental, {default_apps, DefaultApps}=Term} when
+ is_list(DefaultApps) ->
+ AppDirs = get_app_dirs(DefaultApps),
+ assert_filenames_form(Term, AppDirs),
+ Opts#options{files_rec = AppDirs};
+ {incremental, {default_apps, DefaultApps}=TermApps,
+ {default_warning_apps, DefaultWarningApps}=TermWarns} when
+ is_list(DefaultApps), is_list(DefaultWarningApps) ->
+ AppDirs = get_app_dirs(DefaultApps ++ DefaultWarningApps),
+ assert_filenames_form(TermApps, AppDirs),
+ WarningAppDirs = get_app_dirs(DefaultWarningApps),
+ assert_filenames_form(TermWarns, WarningAppDirs),
+ Opts#options{files_rec = AppDirs, warning_files_rec = WarningAppDirs};
+ _ when element(1, ConfigElem) =:= incremental ->
+ bad_option("Given Erlang terms in 'incremental' section could not be understood as Dialyzer config", ConfigElem);
+ _ ->
+ set_default_apps(MoreConfig, Opts)
+ end;
+set_default_apps([], Opts) ->
+ Opts.
+
+get_config() ->
+ DefaultConfig = get_default_config_filename(),
+ case filelib:is_regular(DefaultConfig) of
+ true ->
+ case file:consult(DefaultConfig) of
+ {ok, Config} when is_list(Config) -> Config;
+ {error, Reason} ->
+ bad_option(file:format_error(Reason), DefaultConfig)
+ end;
+ false ->
+ []
+ end.
+
+% Intended to work like dialyzer_iplt:get_default_iplt_filename()
+-spec get_default_config_filename() -> string().
+get_default_config_filename() ->
+ case os:getenv("DIALYZER_CONFIG") of
+ false ->
+ CacheDir = filename:basedir(user_config, "erlang"),
+ filename:join(CacheDir, "dialyzer.config");
+ UserSpecConfig -> UserSpecConfig
+ end.
+
adapt_get_warnings(Opts = #options{analysis_type = Mode,
get_warnings = Warns}) ->
%% Warnings are off by default in plt mode, and on by default in
@@ -138,6 +279,18 @@ build_options([{OptionName, Value} = Term|Rest], Options) ->
OldValues = Options#options.files_rec,
assert_filenames_form(Term, Value),
build_options(Rest, Options#options{files_rec = Value ++ OldValues});
+ warning_apps ->
+ OldValues = Options#options.warning_files_rec,
+ AppDirs = get_app_dirs(Value),
+ assert_filenames_form(Term, AppDirs),
+ build_options(Rest, Options#options{warning_files_rec = AppDirs ++ OldValues});
+ warning_files ->
+ assert_filenames_form(Term, Value),
+ build_options(Rest, Options#options{warning_files = Value});
+ warning_files_rec ->
+ OldValues = Options#options.warning_files_rec,
+ assert_filenames_form(Term, Value),
+ build_options(Rest, Options#options{warning_files_rec = Value ++ OldValues});
analysis_type ->
NewOptions =
case Value of
@@ -146,6 +299,7 @@ build_options([{OptionName, Value} = Term|Rest], Options) ->
plt_build -> Options#options{analysis_type = Value};
plt_check -> Options#options{analysis_type = Value};
plt_remove -> Options#options{analysis_type = Value};
+ incremental -> Options#options{analysis_type = Value};
dataflow -> bad_option("Analysis type is no longer supported", Term);
old_style -> bad_option("Analysis type is no longer supported", Term);
Other -> bad_option("Unknown analysis type", Other)
@@ -164,20 +318,28 @@ build_options([{OptionName, Value} = Term|Rest], Options) ->
get_warnings ->
build_options(Rest, Options#options{get_warnings = Value});
plts ->
- assert_filenames(Term, Value),
+ %assert_filenames(Term, Value),
build_options(Rest, Options#options{init_plts = Value});
include_dirs ->
assert_filenames(Term, Value),
OldVal = Options#options.include_dirs,
NewVal = ordsets:union(ordsets:from_list(Value), OldVal),
build_options(Rest, Options#options{include_dirs = NewVal});
- use_spec ->
+ use_spec when is_boolean(Value) ->
build_options(Rest, Options#options{use_contracts = Value});
+ no_spec when is_boolean(Value) ->
+ build_options(Rest, Options#options{use_contracts = not Value});
old_style ->
bad_option("Analysis type is no longer supported", old_style);
output_file ->
assert_filename(Value),
build_options(Rest, Options#options{output_file = Value});
+ metrics_file ->
+ assert_filename(Value),
+ build_options(Rest, Options#options{metrics_file = Value});
+ module_lookup_file ->
+ assert_filename(Value),
+ build_options(Rest, Options#options{module_lookup_file = Value});
output_format ->
assert_output_format(Value),
build_options(Rest, Options#options{output_format = Value});
@@ -199,6 +361,9 @@ build_options([{OptionName, Value} = Term|Rest], Options) ->
callgraph_file ->
assert_filename(Value),
build_options(Rest, Options#options{callgraph_file = Value});
+ mod_deps_file ->
+ assert_filename(Value),
+ build_options(Rest, Options#options{mod_deps_file = Value});
error_location ->
assert_error_location(Value),
build_options(Rest, Options#options{error_location = Value});
@@ -220,10 +385,31 @@ build_options([], Options) ->
Options.
get_app_dirs(Apps) when is_list(Apps) ->
- dialyzer_cl_parse:get_lib_dir([atom_to_list(A) || A <- Apps]);
+ get_lib_dir([atom_to_list(A) || A <- Apps]);
get_app_dirs(Apps) ->
bad_option("Use a list of otp applications", Apps).
+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).
+
assert_filenames(Term, Files) ->
assert_filenames_form(Term, Files),
assert_filenames_exist(Files).
@@ -271,7 +457,7 @@ assert_filename_opt(fullpath) ->
assert_filename_opt(Term) ->
bad_option("Illegal value for filename_opt", Term).
-assert_plt_op(#options{analysis_type = OldVal},
+assert_plt_op(#options{analysis_type = OldVal},
#options{analysis_type = NewVal}) ->
case is_plt_mode(OldVal) andalso is_plt_mode(NewVal) of
true -> bad_option("Options cannot be combined", [OldVal, NewVal]);
@@ -282,6 +468,7 @@ is_plt_mode(plt_add) -> true;
is_plt_mode(plt_build) -> true;
is_plt_mode(plt_remove) -> true;
is_plt_mode(plt_check) -> true;
+is_plt_mode(incremental) -> true;
is_plt_mode(succ_typings) -> false.
assert_error_location(column) ->
@@ -312,6 +499,8 @@ build_warnings([Opt|Opts], Warnings) ->
ordsets:del_element(?WARN_RETURN_NO_RETURN, Warnings);
no_unused ->
ordsets:del_element(?WARN_NOT_CALLED, Warnings);
+ no_unknown ->
+ ordsets:del_element(?WARN_UNKNOWN, Warnings);
no_improper_lists ->
ordsets:del_element(?WARN_NON_PROPER_LIST, Warnings);
no_fun_app ->
@@ -362,6 +551,8 @@ build_warnings([Opt|Opts], Warnings) ->
ordsets:del_element(?WARN_CONTRACT_MISSING_RETURN, Warnings);
unknown ->
ordsets:add_element(?WARN_UNKNOWN, Warnings);
+ overlapping_contract ->
+ ordsets:add_element(?WARN_OVERLAPPING_CONTRACT, Warnings);
OtherAtom ->
bad_option("Unknown dialyzer warning option", OtherAtom)
end,