Skip to content

Commit

Permalink
Merge pull request #500 from xxdavid/fix_module_info_for_greater_arities
Browse files Browse the repository at this point in the history
Fix handling of module_info for arities > 1
  • Loading branch information
erszcz authored Mar 6, 2023
2 parents fc90bb8 + ebb322f commit 1498d17
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 13 deletions.
5 changes: 5 additions & 0 deletions src/gradualizer.erl
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ type_check_file(File, Opts) ->
".beam" ->
case gradualizer_file_utils:get_forms_from_beam(File) of
{ok, Forms} ->
ok = gradualizer_db:import_beam_files([File]),
type_check_forms(File, Forms, Opts);
Error ->
throw(Error)
Expand All @@ -195,6 +196,10 @@ type_check_file(File, Opts) ->
lint_and_check_forms(Forms, File, Opts) ->
case erl_lint:module(Forms, File, [return_errors]) of
{ok, _Warnings} ->
% import the currently checked file so that it can
% reference itself even without adding its path via --pa
ok = gradualizer_db:import_erl_files([File]),

type_check_forms(File, Forms, Opts);
{error, Errors, _Warnings} ->
%% If there are lint errors (i.e. compile errors like undefined
Expand Down
6 changes: 4 additions & 2 deletions src/typechecker.erl
Original file line number Diff line number Diff line change
Expand Up @@ -1767,7 +1767,8 @@ do_type_check_expr(Env, {call, _, {atom, _, TypeOp}, [Expr, {string, _, TypeStr}
do_type_check_expr(Env, {call, _, {atom, _, record_info}, [_, _]} = Call) ->
Ty = get_record_info_type(Call, Env),
{Ty, Env, constraints:empty()};
do_type_check_expr(Env, {call, P, {remote, _, _Mod, {atom, _, module_info}} = Name, Args}) ->
do_type_check_expr(Env, {call, P, {remote, _, _Mod, {atom, _, module_info}} = Name, Args})
when length(Args) == 0 orelse length(Args) == 1 ->
Arity = arity(length(Args)),
FunTy = get_module_info_type(Arity),
type_check_call_ty(Env, expect_fun_type(Env, FunTy, Arity), Args, {Name, P, FunTy});
Expand Down Expand Up @@ -2678,7 +2679,8 @@ do_type_check_expr_in(Env, ResTy, {call, _, {atom, _, record_info}, [_, _]} = Ca
false ->
throw(type_error(Call, ResTy, Ty))
end;
do_type_check_expr_in(Env, ResTy, {call, P, {remote, _, _Mod, {atom, _, module_info}} = Name, Args} = Call) ->
do_type_check_expr_in(Env, ResTy, {call, P, {remote, _, _Mod, {atom, _, module_info}} = Name, Args} = Call)
when length(Args) == 0 orelse length(Args) == 1 ->
Arity = arity(length(Args)),
FunTy = get_module_info_type(Arity),
type_check_call(Env, ResTy, Call, expect_fun_type(Env, FunTy, Arity),
Expand Down
46 changes: 35 additions & 11 deletions test/gradualizer_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,29 @@
-define(passing, "test/should_pass/any.erl").
-define(failing, "test/should_fail/arg.erl").

type_check_erl_file_test_() ->
global_test_() ->
{setup,
fun setup_app/0,
fun cleanup_app/1,
[{generator, fun type_check_erl_file/0},
{generator, fun type_check_erl_files/0},
{generator, fun type_check_forms/0},
{generator, fun type_check_beam_file/0},
{generator, fun type_check_module/0},
{generator, fun type_check_dir/0},
{generator, fun not_found/0},
{generator, fun bad_content/0},
{generator, fun beam_without_forms/0}
]}.

type_check_erl_file() ->
[?_assertEqual(ok, gradualizer:type_check_file(?passing)),
?_assertEqual([], gradualizer:type_check_file(?passing, [return_errors])),
?_assertEqual(nok, gradualizer:type_check_file(?failing)),
?_assertMatch([_|_], gradualizer:type_check_file(?failing, [return_errors]))
].

type_check_erl_files_test_() ->
type_check_erl_files() ->
[
?_assertEqual(ok, gradualizer:type_check_files([?passing, ?passing])),
?_assertEqual(nok, gradualizer:type_check_files([?failing, ?failing])),
Expand All @@ -24,7 +39,7 @@ type_check_erl_files_test_() ->
?_assertEqual([], gradualizer:type_check_files([?passing, ?passing], [return_errors]))
].

type_check_forms_test_() ->
type_check_forms() ->
{ok, PassingForms} = epp:parse_file(?passing, []),
%% Drop the file attribute to check that type_check_forms works without it
[{attribute, _, file, _} | PassingFormsNoFile] = PassingForms,
Expand All @@ -33,19 +48,19 @@ type_check_forms_test_() ->
?_assertEqual(ok, gradualizer:type_check_forms(PassingFormsNoFile, []))
].

type_check_beam_file_test() ->
type_check_beam_file() ->
Dir = filename:dirname(?FILE), % this differs when /not/ using rebar
BeamFile = filename:join(Dir, "any.beam"),
?_assertEqual(ok, gradualizer:type_check_file(BeamFile)).

type_check_module_test() ->
type_check_module() ->
{module, Mod} = code:load_file(any),
?assertEqual(ok, gradualizer:type_check_module(Mod)).
?_assertEqual(ok, gradualizer:type_check_module(Mod)).

type_check_dir_test() ->
?assertEqual(nok, gradualizer:type_check_dir("test/dir/")).
type_check_dir() ->
?_assertEqual(nok, gradualizer:type_check_dir("test/dir/")).

not_found_test_() ->
not_found() ->
[
?_assertThrow({file_not_found, "test/not_found.erl"},
gradualizer:type_check_file("test/not_found.erl")),
Expand All @@ -61,14 +76,14 @@ not_found_test_() ->
gradualizer:type_check_module(erlang))
].

bad_content_test_() ->
bad_content() ->
{setup,
fun() -> file:write_file("test/bad_content.beam", "bad content") end,
fun(_) -> file:delete("test/bad_content.beam") end,
?_assertThrow({forms_error,{not_a_beam_file, 'test/bad_content.beam'}},
gradualizer:type_check_file("test/bad_content.beam"))}.

beam_without_forms_test_() ->
beam_without_forms() ->
{setup,
fun() ->
{ok, any} = compile:file("test/should_pass/any.erl",
Expand All @@ -77,3 +92,12 @@ beam_without_forms_test_() ->
fun(_) -> file:delete("test/should_pass/any.beam") end,
?_assertThrow({forms_not_found, "test/should_pass/any.beam"},
gradualizer:type_check_file("test/should_pass/any.beam"))}.


setup_app() ->
{ok, Apps} = application:ensure_all_started(gradualizer),
Apps.

cleanup_app(Apps) ->
[ok = application:stop(App) || App <- Apps],
ok.
9 changes: 9 additions & 0 deletions test/should_pass/module_info_higher_arity.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-module(module_info_higher_arity).

-export([two_plus_four/0, module_info/2]).

-spec module_info(number(), number()) -> number().
module_info(A, B) -> A + B.

-spec two_plus_four() -> number().
two_plus_four() -> module_info_higher_arity:module_info(2, 4).

0 comments on commit 1498d17

Please sign in to comment.