From c2661efb0bdbf54ee288f50fc49be85ff1714fa8 Mon Sep 17 00:00:00 2001 From: Tom Davies Date: Mon, 23 Jan 2023 09:00:01 -0800 Subject: [PATCH] dialyzer: Fix crash when adding multiple PLTs to a PLT The code previously assumed that multiple PLTs would only be used in combination with an analysis, but adding multiple PLTs to another without analysis was not considered. The existing logic for merging PLTs was used in the context of an analysis, with different data to hand, so here we just validate the merge, then sequentially add each PLT. --- lib/dialyzer/src/dialyzer_cl.erl | 19 ++++++++++++----- lib/dialyzer/test/dialyzer_cl_SUITE.erl | 28 ++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl index dd235d90259a..a46336617c5b 100644 --- a/lib/dialyzer/src/dialyzer_cl.erl +++ b/lib/dialyzer/src/dialyzer_cl.erl @@ -105,7 +105,16 @@ init_opts_for_build(Opts) -> add_to_plt(Opts) -> Opts1 = init_opts_for_add(Opts), AddFiles = get_files_from_opts(Opts1), - plt_common(Opts1, [], AddFiles). + case Opts1#options.init_plts of + [] -> plt_common(Opts1, [], AddFiles); + [_] -> plt_common(Opts1, [], AddFiles); + PltFiles -> + Plts = [dialyzer_cplt:from_file(F) || F <- PltFiles], + % Check merge safety + _ = dialyzer_cplt:merge_plts_or_report_conflicts(PltFiles, Plts), + _ = [plt_common(Opts#options{init_plts=[Plt]}, [], AddFiles) || Plt <- PltFiles], + {{?RET_NOTHING_SUSPICIOUS, []}, [], []} + end. init_opts_for_add(Opts) -> case Opts#options.output_plt =:= none of @@ -229,19 +238,19 @@ plt_common(#options{init_plts = [InitPlt]} = Opts, RemoveFiles, AddFiles) -> enrich_with_modules_changed(do_analysis(AnalFiles, Opts, Plt, #plt_info{files=Md5, mod_deps=ModDeps1}), ChangedOrRemovedMods) end; {error, no_such_file} -> - Msg = io_lib:format("Could not find the PLT: ~ts\n~s", + Msg = io_lib:format("Could not find the PLT: ~ts~n~s", [InitPlt, default_plt_error_msg()]), cl_error(Msg); {error, not_valid} -> - Msg = io_lib:format("The file: ~ts is not a valid PLT file\n~s", + Msg = io_lib:format("The file: ~ts is not a valid PLT file~n~s", [InitPlt, default_plt_error_msg()]), cl_error(Msg); {error, read_error} -> - Msg = io_lib:format("Could not read the PLT: ~ts\n~s", + Msg = io_lib:format("Could not read the PLT: ~ts~n~s", [InitPlt, default_plt_error_msg()]), cl_error(Msg); {error, {no_file_to_remove, F}} -> - Msg = io_lib:format("Could not remove the file ~ts from the PLT: ~ts\n", + Msg = io_lib:format("Could not remove the file ~ts from the PLT: ~ts~n", [F, InitPlt]), cl_error(Msg) end. diff --git a/lib/dialyzer/test/dialyzer_cl_SUITE.erl b/lib/dialyzer/test/dialyzer_cl_SUITE.erl index ed84fc6068e6..a639b1cae479 100644 --- a/lib/dialyzer/test/dialyzer_cl_SUITE.erl +++ b/lib/dialyzer/test/dialyzer_cl_SUITE.erl @@ -9,14 +9,16 @@ %% Test cases must be exported. -export([ + can_add_multiple_plts_to_another_plt/1, unknown_function_warning_includes_callsite/1, call_to_missing_warning_includes_callsite/1 ]). -suite() -> [{timetrap, {minutes, 1}}]. +suite() -> [{timetrap, {minutes, 3}}]. all() -> [ + can_add_multiple_plts_to_another_plt, unknown_function_warning_includes_callsite, call_to_missing_warning_includes_callsite ]. @@ -119,6 +121,30 @@ unknown_function_warning_includes_callsite(Config) when is_list(Config) -> ok. +% See GitHub issue erlang/OTP #6850 +can_add_multiple_plts_to_another_plt(Config) when is_list(Config) -> + + PrivDir = proplists:get_value(priv_dir,Config), + + StdlibPlt = filename:join(PrivDir, "stdlib.plt"), + ErtsPlt = filename:join(PrivDir, "erts.plt"), + OutputPlt = filename:join(PrivDir, "merged.plt"), + + _ = dialyzer:run([{analysis_type, plt_build}, + {apps, [stdlib]}, + {output_plt, StdlibPlt}]), + _ = dialyzer:run([{analysis_type, plt_build}, + {apps, [erts]}, + {output_plt, ErtsPlt}]), + ?assertEqual( + [], + dialyzer:run([{analysis_type, plt_add}, + {apps, [erts, stdlib]}, + {plts, [ErtsPlt, StdlibPlt]}, + {output_plt, OutputPlt}])), + + ok. + compile(Config, Module, CompileOpts) -> Source = lists:concat([Module, ".erl"]), PrivDir = proplists:get_value(priv_dir,Config),