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-module(dialyzer_cl_parse). 16 17-export([start/0, get_lib_dir/1]). 18-export([collect_args/1]). % used also by typer 19 20-include("dialyzer.hrl"). 21 22%%----------------------------------------------------------------------- 23 24-type dial_cl_parse_ret() :: {'check_init', #options{}} 25 | {'plt_info', #options{}} 26 | {'cl', #options{}} 27 | {'gui', #options{}} 28 | {'error', string()}. 29 30-type deep_string() :: string() | [deep_string()]. 31 32%%----------------------------------------------------------------------- 33 34-spec start() -> dial_cl_parse_ret(). 35 36start() -> 37 init(), 38 Args = init:get_plain_arguments(), 39 try 40 Ret = cl(Args), 41 Ret 42 catch 43 throw:{dialyzer_cl_parse_error, Msg} -> {error, Msg}; 44 _:R:S -> 45 Msg = io_lib:format("~tp\n~tp\n", [R, S]), 46 {error, lists:flatten(Msg)} 47 end. 48 49cl(["--add_to_plt"|T]) -> 50 put(dialyzer_options_analysis_type, plt_add), 51 cl(T); 52cl(["--apps"|T]) -> 53 T1 = get_lib_dir(T), 54 {Args, T2} = collect_args(T1), 55 append_var(dialyzer_options_files_rec, Args), 56 cl(T2); 57cl(["--build_plt"|T]) -> 58 put(dialyzer_options_analysis_type, plt_build), 59 cl(T); 60cl(["--check_plt"|T]) -> 61 put(dialyzer_options_analysis_type, plt_check), 62 cl(T); 63cl(["-n"|T]) -> 64 cl(["--no_check_plt"|T]); 65cl(["--no_check_plt"|T]) -> 66 put(dialyzer_options_check_plt, false), 67 cl(T); 68cl(["-nn"|T]) -> 69 cl(["--no_native"|T]); 70cl(["--no_native"|T]) -> 71 put(dialyzer_options_native, false), 72 cl(T); 73cl(["--no_native_cache"|T]) -> 74 put(dialyzer_options_native_cache, false), 75 cl(T); 76cl(["--plt_info"|T]) -> 77 put(dialyzer_options_analysis_type, plt_info), 78 cl(T); 79cl(["--get_warnings"|T]) -> 80 put(dialyzer_options_get_warnings, true), 81 cl(T); 82cl(["-D"|_]) -> 83 cl_error("No defines specified after -D"); 84cl(["-D"++Define|T]) -> 85 Def = re:split(Define, "=", [{return, list}, unicode]), 86 append_defines(Def), 87 cl(T); 88cl(["-h"|_]) -> 89 help_message(); 90cl(["--help"|_]) -> 91 help_message(); 92cl(["-I"]) -> 93 cl_error("no include directory specified after -I"); 94cl(["-I", Dir|T]) -> 95 append_include(Dir), 96 cl(T); 97cl(["-I"++Dir|T]) -> 98 append_include(Dir), 99 cl(T); 100cl(["-c"++_|T]) -> 101 NewTail = command_line(T), 102 cl(NewTail); 103cl(["-r"++_|T0]) -> 104 {Args, T} = collect_args(T0), 105 append_var(dialyzer_options_files_rec, Args), 106 cl(T); 107cl(["--remove_from_plt"|T]) -> 108 put(dialyzer_options_analysis_type, plt_remove), 109 cl(T); 110cl(["--com"++_|T]) -> 111 NewTail = command_line(T), 112 cl(NewTail); 113cl(["--output"]) -> 114 cl_error("No outfile specified"); 115cl(["-o"]) -> 116 cl_error("No outfile specified"); 117cl(["--output",Output|T]) -> 118 put(dialyzer_output, Output), 119 cl(T); 120cl(["--output_plt"]) -> 121 cl_error("No outfile specified for --output_plt"); 122cl(["--output_plt",Output|T]) -> 123 put(dialyzer_output_plt, Output), 124 cl(T); 125cl(["-o", Output|T]) -> 126 put(dialyzer_output, Output), 127 cl(T); 128cl(["-o"++Output|T]) -> 129 put(dialyzer_output, Output), 130 cl(T); 131cl(["--raw"|T]) -> 132 put(dialyzer_output_format, raw), 133 cl(T); 134cl(["--fullpath"|T]) -> 135 put(dialyzer_filename_opt, fullpath), 136 cl(T); 137cl(["-pa", Path|T]) -> 138 case code:add_patha(Path) of 139 true -> cl(T); 140 {error, _} -> cl_error("Bad directory for -pa: " ++ Path) 141 end; 142cl(["--plt"]) -> 143 error("No plt specified for --plt"); 144cl(["--plt", PLT|T]) -> 145 put(dialyzer_init_plts, [PLT]), 146 cl(T); 147cl(["--plts"]) -> 148 error("No plts specified for --plts"); 149cl(["--plts"|T]) -> 150 {PLTs, NewT} = get_plts(T, []), 151 put(dialyzer_init_plts, PLTs), 152 cl(NewT); 153cl(["-q"|T]) -> 154 put(dialyzer_options_report_mode, quiet), 155 cl(T); 156cl(["--quiet"|T]) -> 157 put(dialyzer_options_report_mode, quiet), 158 cl(T); 159cl(["--src"|T]) -> 160 put(dialyzer_options_from, src_code), 161 cl(T); 162cl(["--no_spec"|T]) -> 163 put(dialyzer_options_use_contracts, false), 164 cl(T); 165cl(["--statistics"|T]) -> 166 put(dialyzer_timing, true), 167 cl(T); 168cl(["--resources"|T]) -> 169 put(dialyzer_options_report_mode, quiet), 170 put(dialyzer_timing, debug), 171 cl(T); 172cl(["-v"|_]) -> 173 io:format("Dialyzer version "++?VSN++"\n"), 174 erlang:halt(?RET_NOTHING_SUSPICIOUS); 175cl(["--version"|_]) -> 176 io:format("Dialyzer version "++?VSN++"\n"), 177 erlang:halt(?RET_NOTHING_SUSPICIOUS); 178cl(["--verbose"|T]) -> 179 put(dialyzer_options_report_mode, verbose), 180 cl(T); 181cl(["-W"|_]) -> 182 cl_error("-W given without warning"); 183cl(["-Whelp"|_]) -> 184 help_warnings(); 185cl(["-W"++Warn|T]) -> 186 append_var(dialyzer_warnings, [list_to_atom(Warn)]), 187 cl(T); 188cl(["--dump_callgraph"]) -> 189 cl_error("No outfile specified for --dump_callgraph"); 190cl(["--dump_callgraph", File|T]) -> 191 put(dialyzer_callgraph_file, File), 192 cl(T); 193cl(["--gui"|T]) -> 194 put(dialyzer_options_mode, gui), 195 cl(T); 196cl(["--solver", Solver|T]) -> % not documented 197 append_var(dialyzer_solvers, [list_to_atom(Solver)]), 198 cl(T); 199cl([H|_] = L) -> 200 case filelib:is_file(H) orelse filelib:is_dir(H) of 201 true -> 202 NewTail = command_line(L), 203 cl(NewTail); 204 false -> 205 cl_error("Unknown option: " ++ H) 206 end; 207cl([]) -> 208 {RetTag, Opts} = 209 case get(dialyzer_options_analysis_type) =:= plt_info of 210 true -> 211 put(dialyzer_options_analysis_type, plt_check), 212 {plt_info, cl_options()}; 213 false -> 214 case get(dialyzer_options_mode) of 215 gui -> {gui, common_options()}; 216 cl -> 217 case get(dialyzer_options_analysis_type) =:= plt_check of 218 true -> {check_init, cl_options()}; 219 false -> {cl, cl_options()} 220 end 221 end 222 end, 223 case dialyzer_options:build(Opts) of 224 {error, Msg} -> cl_error(Msg); 225 OptsRecord -> {RetTag, OptsRecord} 226 end. 227 228%%----------------------------------------------------------------------- 229 230command_line(T0) -> 231 {Args, T} = collect_args(T0), 232 append_var(dialyzer_options_files, Args), 233 %% if all files specified are ".erl" files, set the 'src' flag automatically 234 case lists:all(fun(F) -> filename:extension(F) =:= ".erl" end, Args) of 235 true -> put(dialyzer_options_from, src_code); 236 false -> ok 237 end, 238 T. 239 240-spec cl_error(deep_string()) -> no_return(). 241 242cl_error(Str) -> 243 Msg = lists:flatten(Str), 244 throw({dialyzer_cl_parse_error, Msg}). 245 246init() -> 247 put(dialyzer_options_mode, cl), 248 put(dialyzer_options_files_rec, []), 249 put(dialyzer_options_report_mode, normal), 250 put(dialyzer_warnings, []), 251 DefaultOpts = #options{}, 252 put(dialyzer_include, DefaultOpts#options.include_dirs), 253 put(dialyzer_options_defines, DefaultOpts#options.defines), 254 put(dialyzer_options_files, DefaultOpts#options.files), 255 put(dialyzer_output_format, formatted), 256 put(dialyzer_filename_opt, basename), 257 put(dialyzer_options_check_plt, DefaultOpts#options.check_plt), 258 put(dialyzer_timing, DefaultOpts#options.timing), 259 put(dialyzer_solvers, DefaultOpts#options.solvers), 260 ok. 261 262append_defines([Def, Val]) -> 263 {ok, Tokens, _} = erl_scan:string(Val++"."), 264 {ok, ErlVal} = erl_parse:parse_term(Tokens), 265 append_var(dialyzer_options_defines, [{list_to_atom(Def), ErlVal}]); 266append_defines([Def]) -> 267 append_var(dialyzer_options_defines, [{list_to_atom(Def), true}]). 268 269append_include(Dir) -> 270 append_var(dialyzer_include, [Dir]). 271 272append_var(Var, List) when is_list(List) -> 273 put(Var, get(Var) ++ List), 274 ok. 275 276%%----------------------------------------------------------------------- 277 278-spec collect_args([string()]) -> {[string()], [string()]}. 279 280collect_args(List) -> 281 collect_args_1(List, []). 282 283collect_args_1(["-"++_|_] = L, Acc) -> 284 {lists:reverse(Acc), L}; 285collect_args_1([Arg|T], Acc) -> 286 collect_args_1(T, [Arg|Acc]); 287collect_args_1([], Acc) -> 288 {lists:reverse(Acc), []}. 289 290%%----------------------------------------------------------------------- 291 292cl_options() -> 293 [{files, get(dialyzer_options_files)}, 294 {files_rec, get(dialyzer_options_files_rec)}, 295 {output_file, get(dialyzer_output)}, 296 {output_format, get(dialyzer_output_format)}, 297 {filename_opt, get(dialyzer_filename_opt)}, 298 {analysis_type, get(dialyzer_options_analysis_type)}, 299 {get_warnings, get(dialyzer_options_get_warnings)}, 300 {timing, get(dialyzer_timing)}, 301 {callgraph_file, get(dialyzer_callgraph_file)} 302 |common_options()]. 303 304common_options() -> 305 [{defines, get(dialyzer_options_defines)}, 306 {from, get(dialyzer_options_from)}, 307 {include_dirs, get(dialyzer_include)}, 308 {plts, get(dialyzer_init_plts)}, 309 {output_plt, get(dialyzer_output_plt)}, 310 {report_mode, get(dialyzer_options_report_mode)}, 311 {use_spec, get(dialyzer_options_use_contracts)}, 312 {warnings, get(dialyzer_warnings)}, 313 {check_plt, get(dialyzer_options_check_plt)}, 314 {solvers, get(dialyzer_solvers)}]. 315 316%%----------------------------------------------------------------------- 317 318-spec get_lib_dir([string()]) -> [string()]. 319 320get_lib_dir(Apps) -> 321 get_lib_dir(Apps, []). 322 323get_lib_dir([H|T], Acc) -> 324 NewElem = 325 case code:lib_dir(list_to_atom(H)) of 326 {error, bad_name} -> 327 case H =:= "erts" of % hack for including erts in an un-installed system 328 true -> filename:join(code:root_dir(), "erts/preloaded/ebin"); 329 false -> H 330 end; 331 LibDir -> LibDir ++ "/ebin" 332 end, 333 get_lib_dir(T, [NewElem|Acc]); 334get_lib_dir([], Acc) -> 335 lists:reverse(Acc). 336 337%%----------------------------------------------------------------------- 338 339get_plts(["--"|T], Acc) -> {lists:reverse(Acc), T}; 340get_plts(["-"++_Opt = H|T], Acc) -> {lists:reverse(Acc), [H|T]}; 341get_plts([H|T], Acc) -> get_plts(T, [H|Acc]); 342get_plts([], Acc) -> {lists:reverse(Acc), []}. 343 344%%----------------------------------------------------------------------- 345 346-spec help_warnings() -> no_return(). 347 348help_warnings() -> 349 S = warning_options_msg(), 350 io:put_chars(S), 351 erlang:halt(?RET_NOTHING_SUSPICIOUS). 352 353-spec help_message() -> no_return(). 354 355help_message() -> 356 S = "Usage: dialyzer [--help] [--version] [--shell] [--quiet] [--verbose] 357 [-pa dir]* [--plt plt] [--plts plt*] [-Ddefine]* 358 [-I include_dir]* [--output_plt file] [-Wwarn]* [--raw] 359 [--src] [--gui] [files_or_dirs] [-r dirs] 360 [--apps applications] [-o outfile] 361 [--build_plt] [--add_to_plt] [--remove_from_plt] 362 [--check_plt] [--no_check_plt] [--plt_info] [--get_warnings] 363 [--dump_callgraph file] [--no_native] [--fullpath] 364 [--statistics] [--no_native_cache] 365Options: 366 files_or_dirs (for backwards compatibility also as: -c files_or_dirs) 367 Use Dialyzer from the command line to detect defects in the 368 specified files or directories containing .erl or .beam files, 369 depending on the type of the analysis. 370 -r dirs 371 Same as the previous but the specified directories are searched 372 recursively for subdirectories containing .erl or .beam files in 373 them, depending on the type of analysis. 374 --apps applications 375 Option typically used when building or modifying a plt as in: 376 dialyzer --build_plt --apps erts kernel stdlib mnesia ... 377 to conveniently refer to library applications corresponding to the 378 Erlang/OTP installation. However, the option is general and can also 379 be used during analysis in order to refer to Erlang/OTP applications. 380 In addition, file or directory names can also be included, as in: 381 dialyzer --apps inets ssl ./ebin ../other_lib/ebin/my_module.beam 382 -o outfile (or --output outfile) 383 When using Dialyzer from the command line, send the analysis 384 results to the specified outfile rather than to stdout. 385 --raw 386 When using Dialyzer from the command line, output the raw analysis 387 results (Erlang terms) instead of the formatted result. 388 The raw format is easier to post-process (for instance, to filter 389 warnings or to output HTML pages). 390 --src 391 Override the default, which is to analyze BEAM files, and 392 analyze starting from Erlang source code instead. 393 -Dname (or -Dname=value) 394 When analyzing from source, pass the define to Dialyzer. (**) 395 -I include_dir 396 When analyzing from source, pass the include_dir to Dialyzer. (**) 397 -pa dir 398 Include dir in the path for Erlang (useful when analyzing files 399 that have '-include_lib()' directives). 400 --output_plt file 401 Store the plt at the specified file after building it. 402 --plt plt 403 Use the specified plt as the initial plt (if the plt was built 404 during setup the files will be checked for consistency). 405 --plts plt* 406 Merge the specified plts to create the initial plt -- requires 407 that the plts are disjoint (i.e., do not have any module 408 appearing in more than one plt). 409 The plts are created in the usual way: 410 dialyzer --build_plt --output_plt plt_1 files_to_include 411 ... 412 dialyzer --build_plt --output_plt plt_n files_to_include 413 and then can be used in either of the following ways: 414 dialyzer files_to_analyze --plts plt_1 ... plt_n 415 or: 416 dialyzer --plts plt_1 ... plt_n -- files_to_analyze 417 (Note the -- delimiter in the second case) 418 -Wwarn 419 A family of options which selectively turn on/off warnings 420 (for help on the names of warnings use dialyzer -Whelp). 421 --shell 422 Do not disable the Erlang shell while running the GUI. 423 --version (or -v) 424 Print the Dialyzer version and some more information and exit. 425 --help (or -h) 426 Print this message and exit. 427 --quiet (or -q) 428 Make Dialyzer a bit more quiet. 429 --verbose 430 Make Dialyzer a bit more verbose. 431 --statistics 432 Prints information about the progress of execution (analysis phases, 433 time spent in each and size of the relative input). 434 --build_plt 435 The analysis starts from an empty plt and creates a new one from the 436 files specified with -c and -r. Only works for beam files. 437 Use --plt(s) or --output_plt to override the default plt location. 438 --add_to_plt 439 The plt is extended to also include the files specified with -c and -r. 440 Use --plt(s) to specify which plt to start from, and --output_plt to 441 specify where to put the plt. Note that the analysis might include 442 files from the plt if they depend on the new files. 443 This option only works with beam files. 444 --remove_from_plt 445 The information from the files specified with -c and -r is removed 446 from the plt. Note that this may cause a re-analysis of the remaining 447 dependent files. 448 --check_plt 449 Check the plt for consistency and rebuild it if it is not up-to-date. 450 Actually, this option is of rare use as it is on by default. 451 --no_check_plt (or -n) 452 Skip the plt check when running Dialyzer. Useful when working with 453 installed plts that never change. 454 --plt_info 455 Make Dialyzer print information about the plt and then quit. The plt 456 can be specified with --plt(s). 457 --get_warnings 458 Make Dialyzer emit warnings even when manipulating the plt. Warnings 459 are only emitted for files that are actually analyzed. 460 --dump_callgraph file 461 Dump the call graph into the specified file whose format is determined 462 by the file name extension. Supported extensions are: raw, dot, and ps. 463 If something else is used as file name extension, default format '.raw' 464 will be used. 465 --no_native (or -nn) 466 Bypass the native code compilation of some key files that Dialyzer 467 heuristically performs when dialyzing many files; this avoids the 468 compilation time but it may result in (much) longer analysis time. 469 --no_native_cache 470 By default, Dialyzer caches the results of native compilation in the 471 $XDG_CACHE_HOME/erlang/dialyzer_hipe_cache directory. 472 XDG_CACHE_HOME defaults to $HOME/.cache. Use this option to disable 473 caching. 474 --fullpath 475 Display the full path names of files for which warnings are emitted. 476 --gui 477 Use the GUI. 478 479Note: 480 * denotes that multiple occurrences of these options are possible. 481 ** options -D and -I work both from command-line and in the Dialyzer GUI; 482 the syntax of defines and includes is the same as that used by \"erlc\". 483 484" ++ warning_options_msg() ++ " 485The exit status of the command line version is: 486 0 - No problems were encountered during the analysis and no 487 warnings were emitted. 488 1 - Problems were encountered during the analysis. 489 2 - No problems were encountered, but warnings were emitted. 490", 491 io:put_chars(S), 492 erlang:halt(?RET_NOTHING_SUSPICIOUS). 493 494warning_options_msg() -> 495 "Warning options: 496 -Wno_return 497 Suppress warnings for functions that will never return a value. 498 -Wno_unused 499 Suppress warnings for unused functions. 500 -Wno_improper_lists 501 Suppress warnings for construction of improper lists. 502 -Wno_fun_app 503 Suppress warnings for fun applications that will fail. 504 -Wno_match 505 Suppress warnings for patterns that are unused or cannot match. 506 -Wno_opaque 507 Suppress warnings for violations of opacity of data types. 508 -Wno_fail_call 509 Suppress warnings for failing calls. 510 -Wno_contracts 511 Suppress warnings about invalid contracts. 512 -Wno_behaviours 513 Suppress warnings about behaviour callbacks which drift from the published 514 recommended interfaces. 515 -Wno_missing_calls 516 Suppress warnings about calls to missing functions. 517 -Wno_undefined_callbacks 518 Suppress warnings about behaviours that have no -callback attributes for 519 their callbacks. 520 -Wunmatched_returns *** 521 Include warnings for function calls which ignore a structured return 522 value or do not match against one of many possible return value(s). 523 -Werror_handling *** 524 Include warnings for functions that only return by means of an exception. 525 -Wrace_conditions *** 526 Include warnings for possible race conditions. 527 -Wunderspecs *** 528 Warn about underspecified functions 529 (those whose -spec is strictly more allowing than the success typing). 530 -Wunknown *** 531 Let warnings about unknown functions and types affect the 532 exit status of the command line version. The default is to ignore 533 warnings about unknown functions and types when setting the exit 534 status. When using the Dialyzer from Erlang, warnings about unknown 535 functions and types are returned; the default is not to return 536 such warnings. 537 538The following options are also available but their use is not recommended: 539(they are mostly for Dialyzer developers and internal debugging) 540 -Woverspecs *** 541 Warn about overspecified functions 542 (those whose -spec is strictly less allowing than the success typing). 543 -Wspecdiffs *** 544 Warn when the -spec is different than the success typing. 545 546*** Identifies options that turn on warnings rather than turning them off. 547". 548