diff options
author | Hans Bolinder <[email protected]> | 2017-04-18 10:14:53 +0200 |
---|---|---|
committer | Hans Bolinder <[email protected]> | 2017-04-18 10:14:53 +0200 |
commit | 3b79995e86b0b5adb9a0083e2b8b16236c536451 (patch) | |
tree | bb2f97ca28c0ea3c4649ff8472947ba496093597 /lib/stdlib | |
parent | a5b52a94e67ec18f9f5c276f8387d27ab6c32c3b (diff) | |
parent | 3dd15a193e4a5aea1dd100d6a4e5ad3401334dfe (diff) | |
download | otp-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.erl | 63 | ||||
-rw-r--r-- | lib/stdlib/test/erl_lint_SUITE.erl | 52 |
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 |