Wrong return type spec for release_handler:eval_appup_script/4
Describe the bug
While running dialyzer on my old setup application, I got this warning:
src/setup.erl
Line 865 Column 18: The pattern [U = {_, Mode} | _] can never match the type [atom()]
This comes from code calling release_handler:eval_appup_script/4, which assumes that
the Unpurged list is a list of {Module, PurgeMethod} pairs. But the type spec claims it's a
list of module names.
Eyeballing the code in release_handler.erl, it calls release_handler_1.erl, which in fact uses
the unpurged list as a list of tuples. Not sure why Dialyzer doesn't catch that …
To Reproduce
Running rebar3 dialyzer on the setup application will trigger the warning.
Unfortunately, while setup does have a few test applications, which can be used to
test the setup:reload_app/1, the test code only results in an empty Unpurged list.
FWIW, getting setup to run the reload_app/1 function:
make test
make run_test
[in the resulting erlang shell, with some call trace enabled]
4> code:add_patha("/.../setup/xtest/testapp-2/_build/default/lib/testapp/ebin").
true
5> setup:reload_app(testapp).
=INFO REPORT==== 28-Nov-2025::15:16:22.979301 ===
[testapp vsn "2"] soft upgrade from "1"
(<0.104.0>) call release_handler:eval_appup_script(testapp,"2","/Users/uwiger/uw/setup/xtest/testapp-2/_build/default/lib/testapp",[{load_object_code,{testapp,"2",[testapp_app,testapp_sup]}},
point_of_no_return,
{suspend,[testapp_app,testapp_sup]},
{load,{testapp_app,brutal_purge,brutal_purge}},
{load,{testapp_sup,brutal_purge,brutal_purge}},
{code_change,up,[{testapp_app,setup},{testapp_sup,setup}]},
{resume,[testapp_app,testapp_sup]}])
(<0.104.0>) returned from release_handler:eval_appup_script/4 -> {ok,[]}
{ok,[]}
(But again, since the list is empty, this isn't enough as a runnable example).
So for now, I read the code, and also asked Grok to read it. Grok agreed with me (as it is wont to do).
Affected versions The specs seem to have been added last year, so I'm guessing OTP 27 and later.
Making a tweak in the test application:
5> setup:reload_app(testapp).
=INFO REPORT==== 28-Nov-2025::19:04:22.250206 ===
[testapp vsn "2"] soft upgrade from "1"
(<0.105.0>) call release_handler:eval_appup_script(testapp,"2","/Users/uwiger/uw/setup/xtest/testapp-2/_build/default/lib/testapp",[{load_object_code,{testapp,"2",[testapp_app,testapp_sup]}},
point_of_no_return,
{suspend,[testapp_app,testapp_sup]},
{load,{testapp_app,brutal_purge,brutal_purge}},
{load,{testapp_sup,brutal_purge,brutal_purge}},
{code_change,up,[{testapp_app,setup},{testapp_sup,setup}]},
{resume,[testapp_app,testapp_sup]},
{remove,{testapp_p1,brutal_purge,brutal_purge}},
{purge,[testapp_p1]}])
=SUPERVISOR REPORT==== 28-Nov-2025::19:04:22.269705 ===
supervisor: {local,testapp_sup}
errorContext: child_terminated
reason: killed
offender: [{pid,<0.97.0>},
{id,testapp_p1},
{mfargs,{testapp_p1,start_link,[]}},
{restart_type,permanent},
{significant,false},
{shutdown,5000},
{child_type,worker}]
(<0.105.0>) returned from release_handler:eval_appup_script/4 -> {ok,
[{testapp_p1,
brutal_purge}]}
Which confirms that release_handler:eval_appup_script/4 in fact returns a list of tuples, and not a list of atoms, as the spec and docs suggest.