aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib
diff options
context:
space:
mode:
authorHans Bolinder <[email protected]>2017-04-18 10:14:53 +0200
committerHans Bolinder <[email protected]>2017-04-18 10:14:53 +0200
commit3b79995e86b0b5adb9a0083e2b8b16236c536451 (patch)
treebb2f97ca28c0ea3c4649ff8472947ba496093597 /lib/stdlib
parenta5b52a94e67ec18f9f5c276f8387d27ab6c32c3b (diff)
parent3dd15a193e4a5aea1dd100d6a4e5ad3401334dfe (diff)
downloadotp-3b79995e86b0b5adb9a0083e2b8b16236c536451.tar.gz
otp-3b79995e86b0b5adb9a0083e2b8b16236c536451.tar.bz2
otp-3b79995e86b0b5adb9a0083e2b8b16236c536451.zip
Merge branch 'hasse/stdlib/linter_check_dialyzer/OTP-14323'
* hasse/stdlib/linter_check_dialyzer/OTP-14323: stdlib: Add checks of the dialyzer attribute to the linter
Diffstat (limited to 'lib/stdlib')
-rw-r--r--lib/stdlib/src/erl_lint.erl63
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl52
2 files changed, 108 insertions, 7 deletions
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 0789f5dfb7..78b7a0e751 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -404,6 +404,10 @@ format_error({not_exported_opaque, {TypeName, Arity}}) ->
format_error({underspecified_opaque, {TypeName, Arity}}) ->
io_lib:format("opaque type ~w~s is underspecified and therefore meaningless",
[TypeName, gen_type_paren(Arity)]);
+format_error({bad_dialyzer_attribute,Term}) ->
+ io_lib:format("badly formed dialyzer attribute: ~w", [Term]);
+format_error({bad_dialyzer_option,Term}) ->
+ io_lib:format("unknown dialyzer warning option: ~w", [Term]);
%% --- obsolete? unused? ---
format_error({format_error, {Fmt, Args}}) ->
io_lib:format(Fmt, Args).
@@ -796,8 +800,7 @@ attribute_state(Form, St) ->
%% State'
%% Allow for record, type and opaque type definitions and spec
%% declarations to be intersperced within function definitions.
-%% Dialyzer attributes are also allowed everywhere, but are not
-%% checked at all.
+%% Dialyzer attributes are also allowed everywhere.
function_state({attribute,L,record,{Name,Fields}}, St) ->
record_def(L, Name, Fields, St);
@@ -883,7 +886,8 @@ post_traversal_check(Forms, St0) ->
StD = check_on_load(StC),
StE = check_unused_records(Forms, StD),
StF = check_local_opaque_types(StE),
- check_callback_information(StF).
+ StG = check_dialyzer_attribute(Forms, StF),
+ check_callback_information(StG).
%% check_behaviour(State0) -> State
%% Check that the behaviour attribute is valid.
@@ -3116,6 +3120,59 @@ check_local_opaque_types(St) ->
end,
dict:fold(FoldFun, St, Ts).
+check_dialyzer_attribute(Forms, St0) ->
+ Vals = [{L,V} ||
+ {attribute,L,dialyzer,Val} <- Forms,
+ V0 <- lists:flatten([Val]),
+ V <- case V0 of
+ {O,F} ->
+ [{A,B} ||
+ A <- lists:flatten([O]),
+ B <- lists:flatten([F])];
+ T -> [T]
+ end],
+ {Wellformed, Bad} =
+ lists:partition(fun ({_,{Option,FA}}) when is_atom(Option) ->
+ is_fa(FA);
+ ({_,Option}) when is_atom(Option) -> true;
+ (_) -> false
+ end, Vals),
+ St1 = foldl(fun ({L,Term}, St) ->
+ add_error(L, {bad_dialyzer_attribute,Term}, St)
+ end, St0, Bad),
+ DefFunctions = (gb_sets:to_list(St0#lint.defined) -- pseudolocals()),
+ Fun = fun ({L,{Option,FA}}, St) ->
+ case is_function_dialyzer_option(Option) of
+ true ->
+ case lists:member(FA, DefFunctions) of
+ true -> St;
+ false ->
+ add_error(L, {undefined_function,FA}, St)
+ end;
+ false ->
+ add_error(L, {bad_dialyzer_option,Option}, St)
+ end;
+ ({L,Option}, St) ->
+ case is_module_dialyzer_option(Option) of
+ true -> St;
+ false ->
+ add_error(L, {bad_dialyzer_option,Option}, St)
+ end
+ end,
+ foldl(Fun, St1, Wellformed).
+
+is_function_dialyzer_option(nowarn_function) -> true;
+is_function_dialyzer_option(Option) ->
+ is_module_dialyzer_option(Option).
+
+is_module_dialyzer_option(Option) ->
+ lists:member(Option,
+ [no_return,no_unused,no_improper_lists,no_fun_app,
+ no_match,no_opaque,no_fail_call,no_contracts,
+ no_behaviours,no_undefined_callbacks,unmatched_returns,
+ error_handling,race_conditions,no_missing_calls,
+ specdiffs,overspecs,underspecs,unknown]).
+
%% icrt_clauses(Clauses, In, ImportVarTable, State) ->
%% {UpdVt,State}.
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index fd7de65302..c469624fb4 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -64,8 +64,8 @@
predef/1,
maps/1,maps_type/1,maps_parallel_match/1,
otp_11851/1,otp_11879/1,otp_13230/1,
- record_errors/1, otp_xxxxx/1,
- non_latin1_module/1]).
+ record_errors/1, otp_11879_cont/1,
+ non_latin1_module/1, otp_14323/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -85,7 +85,7 @@ all() ->
too_many_arguments, basic_errors, bin_syntax_errors, predef,
maps, maps_type, maps_parallel_match,
otp_11851, otp_11879, otp_13230,
- record_errors, otp_xxxxx, non_latin1_module].
+ record_errors, otp_11879_cont, non_latin1_module, otp_14323].
groups() ->
[{unused_vars_warn, [],
@@ -3875,7 +3875,7 @@ record_errors(Config) when is_list(Config) ->
{3,erl_lint,{redefine_field,r,a}}],[]}}],
run(Config, Ts).
-otp_xxxxx(Config) ->
+otp_11879_cont(Config) ->
Ts = [{constraint1,
<<"-export([t/1]).
-spec t(X) -> X when is_subtype(integer()).
@@ -3942,6 +3942,50 @@ do_non_latin1_module(Mod) ->
ok.
+%% OTP-14323: Check the dialyzer attribute.
+otp_14323(Config) ->
+ Ts = [
+ {otp_14323_1,
+ <<"-import(mod, [m/1]).
+
+ -export([f/0, g/0, h/0]).
+
+ -dialyzer({nowarn_function,module_info/0}). % undefined function
+ -dialyzer({nowarn_function,record_info/2}). % undefined function
+ -dialyzer({nowarn_function,m/1}). % undefined function
+
+ -dialyzer(nowarn_function). % unknown option
+ -dialyzer(1). % badly formed
+ -dialyzer(malformed). % unkonwn option
+ -dialyzer({malformed,f/0}). % unkonwn option
+ -dialyzer({nowarn_function,a/1}). % undefined function
+ -dialyzer({nowarn_function,{a,-1}}). % badly formed
+
+ -dialyzer([no_return, no_match]).
+ -dialyzer({nowarn_function, f/0}).
+ -dialyzer(no_improper_lists).
+ -dialyzer([{nowarn_function, [f/0]}, no_improper_lists]).
+ -dialyzer({no_improper_lists, g/0}).
+ -dialyzer({[no_return, no_match], [g/0, h/0]}).
+
+ f() -> a.
+ g() -> b.
+ h() -> c.">>,
+ [],
+ {errors,[{5,erl_lint,{undefined_function,{module_info,0}}},
+ {6,erl_lint,{undefined_function,{record_info,2}}},
+ {7,erl_lint,{undefined_function,{m,1}}},
+ {9,erl_lint,{bad_dialyzer_option,nowarn_function}},
+ {10,erl_lint,{bad_dialyzer_attribute,1}},
+ {11,erl_lint,{bad_dialyzer_option,malformed}},
+ {12,erl_lint,{bad_dialyzer_option,malformed}},
+ {13,erl_lint,{undefined_function,{a,1}}},
+ {14,erl_lint,{bad_dialyzer_attribute,
+ {nowarn_function,{a,-1}}}}],
+ []}}],
+ [] = run(Config, Ts),
+ ok.
+
run(Config, Tests) ->
F = fun({N,P,Ws,E}, BadL) ->
case catch run_test(Config, P, Ws) of