Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:23
erlang
4272-stdlib-Let-the-linter-check-unused-types-p...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 4272-stdlib-Let-the-linter-check-unused-types-properly.patch of Package erlang
From 59cc16df4cb0b171e09e38e7924b7d719af1f834 Mon Sep 17 00:00:00 2001 From: Hans Bolinder <hasse@erlang.org> Date: Fri, 4 Jun 2021 09:22:59 +0200 Subject: [PATCH 2/2] stdlib: Let the linter check unused types properly See also https://github.com/erlang/otp/issues/4784. A type like `t() :: [t()]' can now be reported as unused. Types occurring in specs of unused functions or in unused records are considered used. This is consistent with how the warning for unused records is implemented. --- lib/stdlib/src/erl_lint.erl | 67 +++++++++++++++++++++--------- lib/stdlib/test/erl_lint_SUITE.erl | 62 ++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 21 deletions(-) diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 7803273c5e..599f8443ff 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -93,6 +93,14 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> -record(typeinfo, {attr, line}). +-type type_id() :: {'export', []} + | {'record', atom()} + | {'spec', mfa()} + | {'type', ta()}. + +-record(used_type, {anno :: erl_anno:anno(), + at = {export, []} :: type_id()}). + %% Usage of records, functions, and imports. The variable table, which %% is passed on as an argument, holds the usage of variables. -record(usage, { @@ -130,6 +140,7 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> not_removed=gb_sets:empty() %Not considered removed :: gb_sets:set(module_or_mfa()), func=[], %Current function + type_id=[], %Current type id warn_format=0, %Warn format calls enabled_warnings=[], %All enabled warnings (ordset). nowarn_bif_clash=[], %All no warn bif clashes (ordset). @@ -1288,10 +1299,10 @@ check_undefined_types(#lint{usage=Usage,types=Def}=St0) -> TA <- UTAs, not is_map_key(TA, Def), not is_default_type(TA)], - foldl(fun ({TA,L}, St) -> - foldl( fun(Anno, St1) -> + foldl(fun ({TA,UsedTypeList}, St) -> + foldl( fun(#used_type{anno = Anno}, St1) -> add_error(Anno, {undefined_type,TA}, St1) - end, St, L) + end, St, UsedTypeList) end, St0, Undef). %% check_bif_clashes(Forms, State0) -> State @@ -1432,7 +1443,8 @@ export_type(Line, ETs, #lint{exp_types = ETs0} = St0) -> Warn = {duplicated_export_type,TA}, add_warning(Line, Warn, St2); false -> - used_type(TA, Line, St2) + St3 = St2#lint{type_id = {export, []}}, + used_type(TA, Line, St3) end, {gb_sets:add_element(TA, E), St} end, @@ -2674,7 +2686,8 @@ record_def(Line, Name, Fs0, St0) -> St2 = St1#lint{records=maps:put(Name, {Line,Fs1}, St1#lint.records)}, Types = [T || {typed_record_field, _, T} <- Fs0], - check_type({type, nowarn(), product, Types}, St2) + St3 = St2#lint{type_id = {record, Name}}, + check_type({type, nowarn(), product, Types}, St3) end. %% def_fields([RecDef], RecordName, State) -> {[DefField],State}. @@ -2894,7 +2907,8 @@ type_def(Attr, Anno, TypeName, ProtoType, Args, St0) -> fun(St) -> NewDefs = maps:put(TypePair, Info, TypeDefs), CheckType = {type, nowarn(), product, [ProtoType|Args]}, - check_type(CheckType, St#lint{types=NewDefs}) + St1 = St#lint{types=NewDefs, type_id={type, TypePair}}, + check_type(CheckType, St1) end, case is_default_type(TypePair) of true -> @@ -3089,7 +3103,9 @@ check_record_types([], _Name, _DefFields, SeenVars, St, _SeenFields) -> used_type(TypePair, L, #lint{usage = Usage, file = File} = St) -> Used = Usage#usage.used_types, - NewUsed = maps_prepend(TypePair, erl_anno:set_file(File, L), Used), + UsedType = #used_type{anno = erl_anno:set_file(File, L), + at = St#lint.type_id}, + NewUsed = maps_prepend(TypePair, UsedType, Used), St#lint{usage=Usage#usage{used_types=NewUsed}}. is_default_type({Name, NumberOfTypeVariables}) -> @@ -3120,12 +3136,12 @@ spec_decl(Line, MFA0, TypeSpecs, St00 = #lint{specs = Specs, module = Mod}) -> case is_map_key(MFA, Specs) of true -> add_error(Line, {redefine_spec, MFA0}, St1); false -> - case MFA of - {Mod, _, _} -> - check_specs(TypeSpecs, spec_wrong_arity, Arity, St1); - _ -> - add_error(Line, {bad_module, MFA}, St1) - end + St2 = case MFA of + {Mod, _, _} -> St1; + _ -> add_error(Line, {bad_module, MFA}, St1) + end, + St3 = St2#lint{type_id = {spec, MFA}}, + check_specs(TypeSpecs, spec_wrong_arity, Arity, St3) end. %% callback_decl(Line, Fun, Types, State) -> State. @@ -3141,8 +3157,9 @@ callback_decl(Line, MFA0, TypeSpecs, St1 = St0#lint{callbacks = maps:put(MFA, Line, Callbacks)}, case is_map_key(MFA, Callbacks) of true -> add_error(Line, {redefine_callback, MFA0}, St1); - false -> check_specs(TypeSpecs, callback_wrong_arity, - Arity, St1) + false -> + St2 = St1#lint{type_id = {spec, MFA}}, + check_specs(TypeSpecs, callback_wrong_arity, Arity, St2) end end. @@ -3263,11 +3280,10 @@ check_unused_types(Forms, St) -> false -> St end. -check_unused_types_1(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) -> +check_unused_types_1(Forms, #lint{types=Ts}=St) -> case [File || {attribute,_L,file,{File,_Line}} <- Forms] of [FirstFile|_] -> - D = Usage#usage.used_types, - L = gb_sets:to_list(ExpTs) ++ maps:keys(D), + L = reached_types(St), UsedTypes = gb_sets:from_list(L), FoldFun = fun({{record, _}=_Type, 0}, _, AccSt) -> @@ -3291,6 +3307,19 @@ check_unused_types_1(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) -> St end. +reached_types(#lint{usage = Usage}) -> + Es = [{From, {type, To}} || + {To, UsedTs} <- maps:to_list(Usage#usage.used_types), + #used_type{at = From} <- UsedTs], + Initial = initially_reached_types(Es), + G = sofs:family_to_digraph(sofs:rel2fam(sofs:relation(Es))), + R = digraph_utils:reachable(Initial, G), + true = digraph:delete(G), + [T || {type, T} <- R]. + +initially_reached_types(Es) -> + [FromTypeId || {{T, _}=FromTypeId, _} <- Es, T =/= type]. + check_local_opaque_types(St) -> #lint{types=Ts, exp_types=ExpTs} = St, FoldFun = diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index f6888721db..77337a7e05 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -74,7 +74,8 @@ unused_type/1,binary_types/1,removed/1, otp_16516/1, inline_nifs/1, warn_missing_spec/1, - unused_record/1]). + unused_record/1, + unused_type2/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -98,7 +99,7 @@ all() -> stacktrace_syntax, otp_14285, otp_14378, external_funs, otp_15456, otp_15563, unused_type, binary_types, removed, otp_16516, inline_nifs, warn_missing_spec, - unused_record]. + unused_record, unused_type2]. groups() -> [{unused_vars_warn, [], @@ -4634,6 +4635,63 @@ unused_record(Config) when is_list(Config) -> ok. +unused_type2(Config) when is_list(Config) -> + Ts = [{unused_type2_1, + <<"-type t() :: [t()]. + t() -> + a. + ">>, + {[]}, + {warnings,[{{1,22},erl_lint,{unused_type,{t,0}}}, + {{2,15},erl_lint,{unused_function,{t,0}}}]}}, + {unused_type2_2, + <<"-type t1() :: t2(). + -type t2() :: t1(). + t() -> + a. + ">>, + {[]}, + {warnings,[{{1,22},erl_lint,{unused_type,{t1,0}}}, + {{2,16},erl_lint,{unused_type,{t2,0}}}, + {{3,16},erl_lint,{unused_function,{t,0}}}]}}, + {unused_type2_3, + <<"-callback cb() -> t(). + -type t() :: atom(). + t() -> + a. + ">>, + {[]}, + {warnings,[{{3,16},erl_lint,{unused_function,{t,0}}}]}}, + {unused_type2_4, + <<"-spec t() -> t(). + -type t() :: atom(). + t() -> + a. + ">>, + {[]}, + {warnings,[{{3,16},erl_lint,{unused_function,{t,0}}}]}}, + {unused_type2_5, + <<"-export_type([t/0]). + -type t() :: atom(). + t() -> + a. + ">>, + {[]}, + {warnings,[{{3,16},erl_lint,{unused_function,{t,0}}}]}}, + {unused_type2_6, + <<"-record(r, {f :: t()}). + -type t() :: atom(). + t() -> + a. + ">>, + {[]}, + {warnings,[{{1,22},erl_lint,{unused_record,r}}, + {{3,16},erl_lint,{unused_function,{t,0}}}]}} + ], + [] = run(Config, Ts), + + ok. + format_error(E) -> lists:flatten(erl_lint:format_error(E)). -- 2.26.2
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor