1%% -*- erlang-indent-level: 2 -*- 2%% 3%% Licensed under the Apache License, Version 2.0 (the "License"); 4%% you may not use this file except in compliance with the License. 5%% You may obtain a copy of the License at 6%% 7%% http://www.apache.org/licenses/LICENSE-2.0 8%% 9%% Unless required by applicable law or agreed to in writing, software 10%% distributed under the License is distributed on an "AS IS" BASIS, 11%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12%% See the License for the specific language governing permissions and 13%% limitations under the License. 14%% 15%% @copyright 2004 Richard Carlsson 16%% @author Richard Carlsson <carlsson.richard@gmail.com> 17%% @doc Provides a better way to start Dialyzer from a script. 18 19-module(dialyzer_options). 20 21-export([build/1, build_warnings/2]). 22 23-include("dialyzer.hrl"). 24 25%%----------------------------------------------------------------------- 26 27-spec build(dial_options()) -> #options{} | {'error', string()}. 28 29build(Opts) -> 30 DefaultWarns = [?WARN_RETURN_NO_RETURN, 31 ?WARN_NOT_CALLED, 32 ?WARN_NON_PROPER_LIST, 33 ?WARN_FUN_APP, 34 ?WARN_MATCHING, 35 ?WARN_OPAQUE, 36 ?WARN_CALLGRAPH, 37 ?WARN_FAILING_CALL, 38 ?WARN_BIN_CONSTRUCTION, 39 ?WARN_MAP_CONSTRUCTION, 40 ?WARN_CONTRACT_RANGE, 41 ?WARN_CONTRACT_TYPES, 42 ?WARN_CONTRACT_SYNTAX, 43 ?WARN_BEHAVIOUR, 44 ?WARN_UNDEFINED_CALLBACK], 45 DefaultWarns1 = ordsets:from_list(DefaultWarns), 46 InitPlt = dialyzer_plt:get_default_plt(), 47 DefaultOpts = #options{}, 48 DefaultOpts1 = DefaultOpts#options{legal_warnings = DefaultWarns1, 49 init_plts = [InitPlt]}, 50 try 51 Opts1 = preprocess_opts(Opts), 52 NewOpts = build_options(Opts1, DefaultOpts1), 53 postprocess_opts(NewOpts) 54 catch 55 throw:{dialyzer_options_error, Msg} -> {error, Msg} 56 end. 57 58preprocess_opts([]) -> []; 59preprocess_opts([{init_plt, File}|Opts]) -> 60 [{plts, [File]}|preprocess_opts(Opts)]; 61preprocess_opts([Opt|Opts]) -> 62 [Opt|preprocess_opts(Opts)]. 63 64postprocess_opts(Opts = #options{}) -> 65 check_file_existence(Opts), 66 Opts1 = check_output_plt(Opts), 67 adapt_get_warnings(Opts1). 68 69check_file_existence(#options{analysis_type = plt_remove}) -> ok; 70check_file_existence(#options{files = Files, files_rec = FilesRec}) -> 71 assert_filenames_exist(Files), 72 assert_filenames_exist(FilesRec). 73 74check_output_plt(Opts = #options{analysis_type = Mode, from = From, 75 output_plt = OutPLT}) -> 76 case is_plt_mode(Mode) of 77 true -> 78 case From =:= byte_code of 79 true -> Opts; 80 false -> 81 Msg = "Byte code compiled with debug_info is needed to build the PLT", 82 throw({dialyzer_error, Msg}) 83 end; 84 false -> 85 case OutPLT =:= none of 86 true -> Opts; 87 false -> 88 Msg = io_lib:format("Output PLT cannot be specified " 89 "in analysis mode ~w", [Mode]), 90 throw({dialyzer_error, lists:flatten(Msg)}) 91 end 92 end. 93 94adapt_get_warnings(Opts = #options{analysis_type = Mode, 95 get_warnings = Warns}) -> 96 %% Warnings are off by default in plt mode, and on by default in 97 %% success typings mode. User defined warning mode overrides the 98 %% default. 99 case is_plt_mode(Mode) of 100 true -> 101 case Warns =:= maybe of 102 true -> Opts#options{get_warnings = false}; 103 false -> Opts 104 end; 105 false -> 106 case Warns =:= maybe of 107 true -> Opts#options{get_warnings = true}; 108 false -> Opts 109 end 110 end. 111 112-spec bad_option(string(), term()) -> no_return(). 113 114bad_option(String, Term) -> 115 Msg = io_lib:format("~ts: ~tP", [String, Term, 25]), 116 throw({dialyzer_options_error, lists:flatten(Msg)}). 117 118build_options([{OptName, undefined}|Rest], Options) when is_atom(OptName) -> 119 build_options(Rest, Options); 120build_options([{OptionName, Value} = Term|Rest], Options) -> 121 case OptionName of 122 apps -> 123 OldValues = Options#options.files_rec, 124 AppDirs = get_app_dirs(Value), 125 assert_filenames_form(Term, AppDirs), 126 build_options(Rest, Options#options{files_rec = AppDirs ++ OldValues}); 127 files -> 128 assert_filenames_form(Term, Value), 129 build_options(Rest, Options#options{files = Value}); 130 files_rec -> 131 OldValues = Options#options.files_rec, 132 assert_filenames_form(Term, Value), 133 build_options(Rest, Options#options{files_rec = Value ++ OldValues}); 134 analysis_type -> 135 NewOptions = 136 case Value of 137 succ_typings -> Options#options{analysis_type = Value}; 138 plt_add -> Options#options{analysis_type = Value}; 139 plt_build -> Options#options{analysis_type = Value}; 140 plt_check -> Options#options{analysis_type = Value}; 141 plt_remove -> Options#options{analysis_type = Value}; 142 dataflow -> bad_option("Analysis type is no longer supported", Term); 143 old_style -> bad_option("Analysis type is no longer supported", Term); 144 Other -> bad_option("Unknown analysis type", Other) 145 end, 146 assert_plt_op(Options, NewOptions), 147 build_options(Rest, NewOptions); 148 check_plt when is_boolean(Value) -> 149 build_options(Rest, Options#options{check_plt = Value}); 150 defines -> 151 assert_defines(Term, Value), 152 OldVal = Options#options.defines, 153 NewVal = ordsets:union(ordsets:from_list(Value), OldVal), 154 build_options(Rest, Options#options{defines = NewVal}); 155 from when Value =:= byte_code; Value =:= src_code -> 156 build_options(Rest, Options#options{from = Value}); 157 get_warnings -> 158 build_options(Rest, Options#options{get_warnings = Value}); 159 plts -> 160 assert_filenames(Term, Value), 161 build_options(Rest, Options#options{init_plts = Value}); 162 include_dirs -> 163 assert_filenames(Term, Value), 164 OldVal = Options#options.include_dirs, 165 NewVal = ordsets:union(ordsets:from_list(Value), OldVal), 166 build_options(Rest, Options#options{include_dirs = NewVal}); 167 use_spec -> 168 build_options(Rest, Options#options{use_contracts = Value}); 169 old_style -> 170 bad_option("Analysis type is no longer supported", old_style); 171 output_file -> 172 assert_filename(Value), 173 build_options(Rest, Options#options{output_file = Value}); 174 output_format -> 175 assert_output_format(Value), 176 build_options(Rest, Options#options{output_format = Value}); 177 filename_opt -> 178 assert_filename_opt(Value), 179 build_options(Rest, Options#options{filename_opt = Value}); 180 output_plt -> 181 assert_filename(Value), 182 build_options(Rest, Options#options{output_plt = Value}); 183 report_mode -> 184 build_options(Rest, Options#options{report_mode = Value}); 185 erlang_mode -> 186 build_options(Rest, Options#options{erlang_mode = true}); 187 warnings -> 188 NewWarnings = build_warnings(Value, Options#options.legal_warnings), 189 build_options(Rest, Options#options{legal_warnings = NewWarnings}); 190 callgraph_file -> 191 assert_filename(Value), 192 build_options(Rest, Options#options{callgraph_file = Value}); 193 timing -> 194 build_options(Rest, Options#options{timing = Value}); 195 solvers -> 196 assert_solvers(Value), 197 build_options(Rest, Options#options{solvers = Value}); 198 _ -> 199 bad_option("Unknown dialyzer command line option", Term) 200 end; 201build_options([], Options) -> 202 Options. 203 204get_app_dirs(Apps) when is_list(Apps) -> 205 dialyzer_cl_parse:get_lib_dir([atom_to_list(A) || A <- Apps]); 206get_app_dirs(Apps) -> 207 bad_option("Use a list of otp applications", Apps). 208 209assert_filenames(Term, Files) -> 210 assert_filenames_form(Term, Files), 211 assert_filenames_exist(Files). 212 213assert_filenames_form(Term, [FileName|Left]) when length(FileName) >= 0 -> 214 assert_filenames_form(Term, Left); 215assert_filenames_form(_Term, []) -> 216 ok; 217assert_filenames_form(Term, [_|_]) -> 218 bad_option("Malformed or non-existing filename", Term). 219 220assert_filenames_exist([FileName|Left]) -> 221 case filelib:is_file(FileName) orelse filelib:is_dir(FileName) of 222 true -> ok; 223 false -> 224 bad_option("No such file, directory or application", FileName) 225 end, 226 assert_filenames_exist(Left); 227assert_filenames_exist([]) -> 228 ok. 229 230assert_filename(FileName) when length(FileName) >= 0 -> 231 ok; 232assert_filename(FileName) -> 233 bad_option("Malformed or non-existing filename", FileName). 234 235assert_defines(Term, [{Macro, _Value}|Defs]) when is_atom(Macro) -> 236 assert_defines(Term, Defs); 237assert_defines(_Term, []) -> 238 ok; 239assert_defines(Term, [_|_]) -> 240 bad_option("Malformed define", Term). 241 242assert_output_format(raw) -> 243 ok; 244assert_output_format(formatted) -> 245 ok; 246assert_output_format(Term) -> 247 bad_option("Illegal value for output_format", Term). 248 249assert_filename_opt(basename) -> 250 ok; 251assert_filename_opt(fullpath) -> 252 ok; 253assert_filename_opt(Term) -> 254 bad_option("Illegal value for filename_opt", Term). 255 256assert_plt_op(#options{analysis_type = OldVal}, 257 #options{analysis_type = NewVal}) -> 258 case is_plt_mode(OldVal) andalso is_plt_mode(NewVal) of 259 true -> bad_option("Options cannot be combined", [OldVal, NewVal]); 260 false -> ok 261 end. 262 263is_plt_mode(plt_add) -> true; 264is_plt_mode(plt_build) -> true; 265is_plt_mode(plt_remove) -> true; 266is_plt_mode(plt_check) -> true; 267is_plt_mode(succ_typings) -> false. 268 269assert_solvers([]) -> 270 ok; 271assert_solvers([v1|Terms]) -> 272 assert_solvers(Terms); 273assert_solvers([v2|Terms]) -> 274 assert_solvers(Terms); 275assert_solvers([Term|_]) -> 276 bad_option("Illegal value for solver", Term). 277 278-spec build_warnings([atom()], dial_warn_tags()) -> dial_warn_tags(). 279 280%% The warning options are checked by the code linter. 281%% The function erl_lint:is_module_dialyzer_option/1 must 282%% be updated if options are added or removed. 283build_warnings([Opt|Opts], Warnings) -> 284 NewWarnings = 285 case Opt of 286 no_return -> 287 ordsets:del_element(?WARN_RETURN_NO_RETURN, Warnings); 288 no_unused -> 289 ordsets:del_element(?WARN_NOT_CALLED, Warnings); 290 no_improper_lists -> 291 ordsets:del_element(?WARN_NON_PROPER_LIST, Warnings); 292 no_fun_app -> 293 ordsets:del_element(?WARN_FUN_APP, Warnings); 294 no_match -> 295 ordsets:del_element(?WARN_MATCHING, Warnings); 296 no_opaque -> 297 ordsets:del_element(?WARN_OPAQUE, Warnings); 298 no_fail_call -> 299 ordsets:del_element(?WARN_FAILING_CALL, Warnings); 300 no_contracts -> 301 Warnings1 = ordsets:del_element(?WARN_CONTRACT_SYNTAX, Warnings), 302 ordsets:del_element(?WARN_CONTRACT_TYPES, Warnings1); 303 no_behaviours -> 304 ordsets:del_element(?WARN_BEHAVIOUR, Warnings); 305 no_undefined_callbacks -> 306 ordsets:del_element(?WARN_UNDEFINED_CALLBACK, Warnings); 307 unmatched_returns -> 308 ordsets:add_element(?WARN_UNMATCHED_RETURN, Warnings); 309 error_handling -> 310 ordsets:add_element(?WARN_RETURN_ONLY_EXIT, Warnings); 311 race_conditions -> 312 ordsets:add_element(?WARN_RACE_CONDITION, Warnings); 313 no_missing_calls -> 314 ordsets:del_element(?WARN_CALLGRAPH, Warnings); 315 specdiffs -> 316 S = ordsets:from_list([?WARN_CONTRACT_SUBTYPE, 317 ?WARN_CONTRACT_SUPERTYPE, 318 ?WARN_CONTRACT_NOT_EQUAL]), 319 ordsets:union(S, Warnings); 320 overspecs -> 321 ordsets:add_element(?WARN_CONTRACT_SUBTYPE, Warnings); 322 underspecs -> 323 ordsets:add_element(?WARN_CONTRACT_SUPERTYPE, Warnings); 324 unknown -> 325 ordsets:add_element(?WARN_UNKNOWN, Warnings); 326 OtherAtom -> 327 bad_option("Unknown dialyzer warning option", OtherAtom) 328 end, 329 build_warnings(Opts, NewWarnings); 330build_warnings([], Warnings) -> 331 Warnings. 332