diff options
author | Hans Bolinder <[email protected]> | 2019-04-29 16:13:37 +0200 |
---|---|---|
committer | Hans Bolinder <[email protected]> | 2019-05-03 12:17:02 +0200 |
commit | 60e47cba35ff565a33b25ecb01e96881ab7fe0da (patch) | |
tree | 511707cede18a65f93dd4d3d852de4da7b97c1e3 | |
parent | e4c8e5a7997205ccc371415ae5c1caf544b025c6 (diff) | |
download | otp-60e47cba35ff565a33b25ecb01e96881ab7fe0da.tar.gz otp-60e47cba35ff565a33b25ecb01e96881ab7fe0da.tar.bz2 otp-60e47cba35ff565a33b25ecb01e96881ab7fe0da.zip |
stdlib: Do not allow specs for functions in other modules
See also https://bugs.erlang.org/browse/ERL-845.
[Kostis:]
My suggestion is that the compiler refuses to compile modules that
contain specs for functions that are not from this module. I do not
remember when / why this `feature' was introduced, but thinking about
it I see a lot of (ugly) semantics issues with it. For example, should
one be allowed to declare in the foo module that lists:flatten/1 takes
an integer() as an argument and returns a binary()? Should one be
allowed to declare a spec in some module m1 for a function of m2 that
is not defined in m2?
There are all kinds of checks that will need to be added to dialyzer
to protect itself from these semantics issues. The compiler already
refuses to compile modules that contain specs for non-existing
functions of the module. Similarly, it should refuse to compile
modules that contain specs for functions of other modules - unless it
can somehow check that these functions are indeed defined, but it is
not how the compiler currently works.
-rw-r--r-- | lib/dialyzer/test/small_SUITE_data/results/spec_other_module | 2 | ||||
-rw-r--r-- | lib/dialyzer/test/small_SUITE_data/src/spec_other_module.erl | 7 | ||||
-rw-r--r-- | lib/stdlib/src/erl_lint.erl | 12 | ||||
-rw-r--r-- | lib/stdlib/test/erl_lint_SUITE.erl | 24 | ||||
-rw-r--r-- | lib/stdlib/test/erl_pp_SUITE.erl | 2 |
5 files changed, 32 insertions, 15 deletions
diff --git a/lib/dialyzer/test/small_SUITE_data/results/spec_other_module b/lib/dialyzer/test/small_SUITE_data/results/spec_other_module deleted file mode 100644 index ab2e35cf55..0000000000 --- a/lib/dialyzer/test/small_SUITE_data/results/spec_other_module +++ /dev/null @@ -1,2 +0,0 @@ - -spec_other_module.erl:7: Contract for function that does not exist: lists:flatten/1 diff --git a/lib/dialyzer/test/small_SUITE_data/src/spec_other_module.erl b/lib/dialyzer/test/small_SUITE_data/src/spec_other_module.erl deleted file mode 100644 index b36742b1bd..0000000000 --- a/lib/dialyzer/test/small_SUITE_data/src/spec_other_module.erl +++ /dev/null @@ -1,7 +0,0 @@ --module(spec_other_module). - -%% OTP-15562 and ERL-845. Example provided by Kostis. - --type deep_list(A) :: [A | deep_list(A)]. - --spec lists:flatten(deep_list(A)) -> [A]. diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index e0c37ca030..0cd0aef124 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2018. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -386,6 +386,8 @@ format_error({redefine_callback, {F, A}}) -> format_error({bad_callback, {M, F, A}}) -> io_lib:format("explicit module not allowed for callback ~tw:~tw/~w", [M, F, A]); +format_error({bad_module, {M, F, A}}) -> + io_lib:format("spec for function ~w:~tw/~w from other module", [M, F, A]); format_error({spec_fun_undefined, {F, A}}) -> io_lib:format("spec for undefined function ~tw/~w", [F, A]); format_error({missing_spec, {F,A}}) -> @@ -3010,7 +3012,13 @@ spec_decl(Line, MFA0, TypeSpecs, St00 = #lint{specs = Specs, module = Mod}) -> St1 = St0#lint{specs = dict:store(MFA, Line, Specs)}, case dict:is_key(MFA, Specs) of true -> add_error(Line, {redefine_spec, MFA0}, St1); - false -> check_specs(TypeSpecs, spec_wrong_arity, Arity, St1) + false -> + case MFA of + {Mod, _, _} -> + check_specs(TypeSpecs, spec_wrong_arity, Arity, St1); + _ -> + add_error(Line, {bad_module, MFA}, St1) + end end. %% callback_decl(Line, Fun, Types, State) -> State. diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index fe98a3796d..e7882e0daf 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2018. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -68,7 +68,7 @@ non_latin1_module/1, otp_14323/1, stacktrace_syntax/1, otp_14285/1, otp_14378/1, - external_funs/1,otp_15456/1]). + external_funs/1,otp_15456/1,otp_15563/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -90,7 +90,7 @@ all() -> otp_11851, otp_11879, otp_13230, record_errors, otp_11879_cont, non_latin1_module, otp_14323, stacktrace_syntax, otp_14285, otp_14378, external_funs, - otp_15456]. + otp_15456, otp_15563]. groups() -> [{unused_vars_warn, [], @@ -4010,6 +4010,8 @@ non_latin1_module(Config) -> format_error(non_latin1_module_unsupported), BadCallback = {bad_callback,{'кирилли́ческий атом','кирилли́ческий атом',0}}, + BadModule = + {bad_module,{'кирилли́ческий атом','кирилли́ческий атом',0}}, "explicit module not allowed for callback " "'кирилли́ческий атом':'кирилли́ческий атом'/0" = format_error(BadCallback), @@ -4052,6 +4054,7 @@ non_latin1_module(Config) -> {11,erl_lint,illegal_guard_expr}, {15,erl_lint,non_latin1_module_unsupported}, {17,erl_lint,non_latin1_module_unsupported}, + {17,erl_lint,BadModule}, {20,erl_lint,non_latin1_module_unsupported}, {23,erl_lint,non_latin1_module_unsupported}, {25,erl_lint,non_latin1_module_unsupported}], @@ -4229,6 +4232,21 @@ external_funs(Config) when is_list(Config) -> run(Config, Ts), ok. +otp_15563(Config) when is_list(Config) -> + Ts = [{otp_15563, + <<"-type deep_list(A) :: [A | deep_list(A)]. + -spec lists:flatten(deep_list(A)) -> [A]. + -callback lists:concat([_]) -> string(). + -spec ?MODULE:foo() -> any(). + foo() -> a. + ">>, + [warn_unused_vars], + {errors,[{2,erl_lint,{bad_module,{lists,flatten,1}}}, + {3,erl_lint,{bad_callback,{lists,concat,1}}}], + []}}], + [] = run(Config, Ts), + ok. + format_error(E) -> lists:flatten(erl_lint:format_error(E)). diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index 3eb1670806..c0cfd26925 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -829,7 +829,7 @@ type_examples() -> "(t24()) -> D when is_subtype(D, atom())," " is_subtype(D, t14())," " is_subtype(D, '\\'t::4'()).">>}, - {ex32,<<"-spec mod:t2() -> any(). ">>}, + {ex32,<<"-spec erl_pp_test:t2() -> any(). ">>}, {ex33,<<"-opaque attributes_data() :: " "[{'column', column()} | {'line', info_line()} |" " {'text', string()}] | {line(),column()}. ">>}, |