File 3572-features-Clean-up-naming-and-more.patch of Package erlang

From 14c58f82e7c229e3d34eb99af3188521f2c25add Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cons=20T=20=C3=85hs?= <cons@erlang.org>
Date: Tue, 26 Apr 2022 15:57:22 +0200
Subject: [PATCH 2/6] [features] Clean up naming and more

* Improve naming in erl_features
  * feature_info/1 -> info/1
  * features/0 -> all/0
  * features_used/1 -> used/1
  * features_enabled/0 -> enabled/0
  * Rename and unexport internal functions
* Add specs and improve typing
* Testing
  * Improve possibilities to debug tests on failure
  * Add simple tests for info/1 and features/0
  * Add tests for long/short feature descriptions
* Fix typos in test features
---
 erts/test/lux/bad_feature_erlc.lux |   4 +-
 erts/test/lux/feature_erl.lux      |  38 ++++++--
 erts/test/lux/feature_erlc.lux     |  26 ++++-
 erts/test/lux/feature_runtime.lux  |  14 +--
 erts/test/lux/features.luxinc      |  10 +-
 lib/stdlib/src/epp.erl             |   5 +-
 lib/stdlib/src/erl_compile.erl     |  11 ++-
 lib/stdlib/src/erl_features.erl    | 152 +++++++++++++++--------------
 lib/stdlib/src/erl_lint.erl        |   2 +-
 9 files changed, 159 insertions(+), 103 deletions(-)

diff --git a/erts/test/lux/bad_feature_erlc.lux b/erts/test/lux/bad_feature_erlc.lux
index 903696d2ce..bd8b8b76e8 100644
--- a/erts/test/lux/bad_feature_erlc.lux
+++ b/erts/test/lux/bad_feature_erlc.lux
@@ -186,14 +186,14 @@
 
 [shell erl]
     # Verify that only ifn_expr is used
-    !Fs = erl_features:features_used(f_directives_2).
+    !Fs = erl_features:used(f_directives_2).
     ?$ERLPROMPT
     [invoke same-members Fs "[ifn_expr,cond_expr,maps]"]
     !f(Fs).
     ?$ERLPROMPT
 
     # Verify that only ifn_expr is used
-    !Fs = erl_features:features_used(f_directives_3).
+    !Fs = erl_features:used(f_directives_3).
     ?$ERLPROMPT
     [invoke same-members Fs "[ifn_expr,cond_expr,maps]"]
     !f(Fs).
diff --git a/erts/test/lux/feature_erl.lux b/erts/test/lux/feature_erl.lux
index 88d71a06a6..1205f9a601 100644
--- a/erts/test/lux/feature_erl.lux
+++ b/erts/test/lux/feature_erl.lux
@@ -10,39 +10,59 @@
     -undefined function
     [invoke start-erl ""]
 
-    [invoke same-members "erl_features:enabled_features()" "[maps,cond_expr]"]
+    # Verify that the known test features are returned
+    [invoke same-members "erl_features:all()" "[maps,cond_expr,while_expr,ifn_expr,ifnot_expr,unless_expr]"]
+
+    # Verify that we get an error if trying to get information about
+    # an unknown feature.
+    !erl_features:info(loop_expr).
+    ?error.*invalid_feature
+    ?unknown feature
+    ?$ERLPROMPT
+
+    # Verify that we get a map with info for a known feature
+    !IMap = erl_features:info(maps).
+    ?$ERLPROMPT
+    !is_map(IMap).
+    ?true
+    ?$ERLPROMPT
+    !lists:all(fun(K) -> is_map_key(K, IMap) end, [status,type,description,short,experimental]).
+    ?true
+    ?$ERLPROMPT
+
+    [invoke same-members "erl_features:enabled()" "[maps,cond_expr]"]
 
     [invoke quit-erl]
 
     [invoke start-erl "-enable-feature ifn_expr"]
 
-    [invoke same-members "erl_features:enabled_features()" "[maps,cond_expr,ifn_expr]"]
+    [invoke same-members "erl_features:enabled()" "[maps,cond_expr,ifn_expr]"]
 
     [invoke quit-erl]
 
     [invoke start-erl "-enable-feature ifn_expr -enable-feature while_expr"]
 
-    [invoke same-members "erl_features:enabled_features()" "[maps,cond_expr,while_expr,ifn_expr]"]
+    [invoke same-members "erl_features:enabled()" "[maps,cond_expr,while_expr,ifn_expr]"]
 
     [invoke quit-erl]
 
 
     [invoke start-erl "-enable-feature ifn_expr while_expr"]
 
-    [invoke same-members "erl_features:enabled_features()" "[cond_expr,maps,while_expr,ifn_expr]"]
+    [invoke same-members "erl_features:enabled()" "[cond_expr,maps,while_expr,ifn_expr]"]
 
     [invoke quit-erl]
 
     [invoke start-erl "-enable-feature ifn_expr while_expr -enable-feature ifnot_expr"]
 
-    [invoke same-members "erl_features:enabled_features()" "[cond_expr,maps,while_expr,ifn_expr,ifnot_expr]"]
+    [invoke same-members "erl_features:enabled()" "[cond_expr,maps,while_expr,ifn_expr,ifnot_expr]"]
 
     [invoke quit-erl]
 
     # Enabling the same feature twice does not hurt
     [invoke start-erl "-enable-feature ifn_expr while_expr -enable-feature ifnot_expr ifn_expr"]
 
-    [invoke same-members "erl_features:enabled_features()" "[cond_expr,maps,while_expr,ifn_expr,ifnot_expr]"]
+    [invoke same-members "erl_features:enabled()" "[cond_expr,maps,while_expr,ifn_expr,ifnot_expr]"]
 
     [invoke quit-erl]
 
@@ -52,18 +72,18 @@
     # problem since we have a lazy init of the feature set.
     [invoke start-erl "-enable-feature no_ftr"]
 
-    [invoke same-members "erl_features:enabled_features()" "[cond_expr,maps]"]
+    [invoke same-members "erl_features:enabled()" "[cond_expr,maps]"]
 
     [invoke quit-erl]
 
     [invoke start-erl "-disable-feature cond_expr"]
 
-    [invoke same-members "erl_features:enabled_features()" "[maps]"]
+    [invoke same-members "erl_features:enabled()" "[maps]"]
     # FIXME Need to check that keywords have changed as well!
     [invoke quit-erl]
 
     [invoke start-erl "-disable-feature all"]
-    !erl_features:enabled_features().
+    !erl_features:enabled().
     ??[]
     ?$ERLPROMPT
 
diff --git a/erts/test/lux/feature_erlc.lux b/erts/test/lux/feature_erlc.lux
index 3a033d0aad..5082f56ad1 100644
--- a/erts/test/lux/feature_erlc.lux
+++ b/erts/test/lux/feature_erlc.lux
@@ -196,7 +196,7 @@
     ??{error,not_allowed}
     ?$ERLPROMPT
 
-    !Fs = erl_features:features_used(f_maybe_ifn).
+    !Fs = erl_features:used(f_maybe_ifn).
     [invoke same-members Fs "[cond_expr,maps,while_expr,ifn_expr]"]
     !f(Fs).
     ?$ERLPROMPT
@@ -249,7 +249,7 @@
     ?$ERLPROMPT
 
     # Check which features were used during compilation
-    !Fs = erl_features:features_used(f_macros).
+    !Fs = erl_features:used(f_macros).
     ?$ERLPROMPT
     [invoke same-members Fs "[maps,cond_expr,ifn_expr]"]
     !f(Fs).
@@ -405,7 +405,7 @@
     [invoke ok]
 
 [shell erl]
-    !Fs = erl_features:features_used(foo).
+    !Fs = erl_features:used(foo).
     ?$ERLPROMPT
     [invoke same-members Fs "[unless_expr,ifn_expr,while_expr,cond_expr,maps,ifnot_expr]"]
     !f(Fs).
@@ -419,7 +419,7 @@
     [invoke ok]
 
 [shell erl]
-    !Fs = erl_features:features_used(foo).
+    !Fs = erl_features:used(foo).
     ?$ERLPROMPT
     !maps:from_keys([ifn_expr,while_expr,cond_expr,maps,ifnot_expr], foo) == maps:from_keys(Fs, foo).
     ?true
@@ -427,6 +427,24 @@
     !f(Fs).
     ?$ERLPROMPT
 
+[shell cmds]
+    !erlc -list-features
+    ?cond_expr
+    ?ifn_expr
+    ?ifnot_expr
+    ?maps
+    ?unless_expr
+    ?while_expr
+    ?SH-PROMPT:
+
+    !erlc -describe-feature maps
+    ?Add new data type for maps
+    ?SH-PROMPT:
+
+    !erlc -describe-feature loop_macro
+    ?Unknown feature: loop_macro
+    [invoke notok]
+
 [cleanup]
     [invoke no-dump]
     !rm -fr $outdir
diff --git a/erts/test/lux/feature_runtime.lux b/erts/test/lux/feature_runtime.lux
index 723d650d5a..1048164a52 100644
--- a/erts/test/lux/feature_runtime.lux
+++ b/erts/test/lux/feature_runtime.lux
@@ -11,7 +11,7 @@
     !erl
     ?$ERLPROMPT
 
-    [invoke same-members "erl_features:enabled_features()" "[cond_expr,maps]"]
+    [invoke same-members "erl_features:enabled()" "[cond_expr,maps]"]
 
     !erl_features:keywords().
     ??[]
@@ -33,14 +33,14 @@
 
     [invoke same-members "erl_features:enable_feature(while_expr)" "[cond_expr,maps,ifn_expr,while_expr]"]
 
-    [invoke same-members "erl_features:enabled_features()" "[cond_expr,maps,ifn_expr,while_expr]"]
+    [invoke same-members "erl_features:enabled()" "[cond_expr,maps,ifn_expr,while_expr]"]
 
     !erl_features:keywords().
     ??['while','until','ifn']
 
     [invoke same-members "erl_features:disable_feature(ifn_expr)" "[cond_expr,maps,while_expr]"]
 
-    [invoke same-members "erl_features:enabled_features()" "[cond_expr,maps,while_expr]"]
+    [invoke same-members "erl_features:enabled()" "[cond_expr,maps,while_expr]"]
 
     !erl_features:keywords().
     ??['while','until']
@@ -68,21 +68,21 @@
     # If there is no Meta chunk, we should just get [] as answer,
     # not a failure.  We use a file compiled with an older release to be
     # sure no meta chunk is present
-    !erl_features:features_used(older).
+    !erl_features:used(older).
     ???[]
     ?$ERLPROMPT
 
-    !erl_features:features_used("older.beam").
+    !erl_features:used("older.beam").
     ???[]
     ?$ERLPROMPT
 
 
     # Test getting used features for unknown modules and files
-    !erl_features:features_used(no_module).
+    !erl_features:used(no_module).
     ??not_found
     ?$ERLPROMPT
 
-    !erl_features:features_used("none.beam").
+    !erl_features:used("none.beam").
     ??not_found
     ?$ERLPROMPT
 
diff --git a/erts/test/lux/features.luxinc b/erts/test/lux/features.luxinc
index 2998f67dd9..376aca4b97 100644
--- a/erts/test/lux/features.luxinc
+++ b/erts/test/lux/features.luxinc
@@ -68,12 +68,18 @@
 [endmacro]
 
 # Nifty way to compare that two lists have the same members
-# One drawback is that it os not easy to debug, i.e., see the actual
+# One drawback is that it is not easy to debug, i.e., see the actual
 # members.
 [macro same-members l1 l2]
-    !maps:from_keys($l1,foo) == maps:from_keys($l2,foo).
+    !{L1, L2} = {$l1, $l2}.
+    ?$ERLPROMPT
+    !maps:from_keys(L1,foo) == maps:from_keys(L2,foo).
     ?true
     ?$ERLPROMPT
+    !f(L1).
+    ?$ERLPROMPT
+    !f(L2).
+    ?$ERLPROMPT
 [endmacro]
 
 [macro no-dump]
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index 066b02f483..95a933c9fa 100644
--- a/lib/stdlib/src/epp.erl
+++ b/lib/stdlib/src/epp.erl
@@ -647,7 +647,7 @@ predef_macros(File, EnabledFeatures) ->
     Machine = list_to_atom(erlang:system_info(machine)),
     Anno = line1(),
     OtpVersion = list_to_integer(erlang:system_info(otp_release)),
-    AvailableFeatures = erl_features:features(),
+    AvailableFeatures = erl_features:all(),
     Defs = [{'FILE', 	           {none,[{string,Anno,File}]}},
 	    {'FUNCTION_NAME',      undefined},
 	    {'FUNCTION_ARITY',     undefined},
@@ -1038,10 +1038,9 @@ update_features(St0, Ind, Ftr, Loc) ->
     case erl_features:keyword_fun(Ind, Ftr, Ftrs0, KeywordFun) of
         {error, Reason} ->
             {error, {Reason, Loc}};
-        {ok, ResWordFun1, Ftrs1} ->
+        {ok, {Ftrs1, ResWordFun1}} ->
             Macs0 = St0#epp.macs,
             Macs1 = Macs0#{'FEATURE_ENABLED' => [ftr_macro(Ftrs1)]},
-            %% ?liof("ok!\n", []),
             %% FIXME WE need to keep any other scan_opts
             %% present.  Right now, there are no other, but
             %% that might change.
diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl
index 6cb8590f13..84923f21fb 100644
--- a/lib/stdlib/src/erl_compile.erl
+++ b/lib/stdlib/src/erl_compile.erl
@@ -329,13 +329,20 @@ show_info(#options{specific = Spec}) ->
 
     case G([list_features, describe_feature]) of
         {list_features, true} ->
-            Features = erl_features:features(),
+            Features = erl_features:all(),
             Msg = ["Available features:\n",
                    [io_lib:format(" ~-13s ~s\n", [Ftr, erl_features:short(Ftr)])
                     || Ftr <- Features]],
             {ok, Msg};
         {describe_feature, Ftr} ->
-            {ok, erl_features:long(Ftr)};
+            Description =
+                try
+                    erl_features:long(Ftr)
+                catch
+                    error:invalid_feature ->
+                        io_lib:format("Unknown feature: ~p\n", [Ftr])
+                end,
+            {ok, Description};
         _ ->
             false
     end.
diff --git a/lib/stdlib/src/erl_features.erl b/lib/stdlib/src/erl_features.erl
index 77ada029cb..edb1e71d52 100644
--- a/lib/stdlib/src/erl_features.erl
+++ b/lib/stdlib/src/erl_features.erl
@@ -19,15 +19,11 @@
 %%
 -module(erl_features).
 
-%% FIXME divide the exported functions in public and internal for the
-%% sake of documentation.
--export([features/0,
-         feature_info/1,
-         collect_features/1,
+-export([all/0,
+         info/1,
          short/1,
          long/1,
-         enabled_features/0,
-         is_valid_feature/1,
+         enabled/0,
          load_allowed/1,
          keywords/0,
          keywords/1,
@@ -35,21 +31,21 @@
          keyword_fun/4,
          enable_feature/1,
          disable_feature/1,
+         used/1,
          format_error/1,
          format_error/2]).
 
--export([features_used/1]).
-
 -type type() :: 'extension' | 'backwards_incompatible_change'.
 -type status() :: 'experimental'
                   | 'approved'
                   | 'permanent'
                   | 'rejected'.
 -type release() :: non_neg_integer().
+-type feature() :: atom().
 -type error() :: {?MODULE, {'invalid_features', [atom()]}}.
 
 -define(VALID_FEATURE(Feature),
-        (case is_valid_feature(Feature) of
+        (case is_valid(Feature) of
              false ->
                  error(invalid_feature, [Feature],
                        [{error_info,
@@ -70,31 +66,32 @@ feature_specs() ->
             keywords => ['maybe', 'else'],
             type => extension}}.
 
-%% Currently known features
--spec features() -> [atom()].
-features() ->
+%% Return all currently known features.
+-spec all() -> [feature()].
+all() ->
     Map = case persistent_term:get({?MODULE, feature_specs}, none) of
               none -> init_specs();
               M -> M
           end,
     maps:keys(Map).
 
-is_valid_feature(Ftr) ->
-    lists:member(Ftr, features()).
+is_valid(Ftr) ->
+    lists:member(Ftr, all()).
 
--spec short(atom()) -> iolist().
+-spec short(feature()) -> iolist() | no_return().
 short(Feature) ->
     #{short := Short,
-      status := Status} = Info = feature_info(Feature),
+      status := Status} = Info = info(Feature),
     #{Status := Release} = Info,
     io_lib:format("~-40s ~-12s (~p)", [Short, Status, Release]).
 
+-spec long(feature()) -> iolist() | no_return().
 long(Feature) ->
     #{short := Short,
       description := Description,
       status := Status,
       keywords := Keywords,
-      type := Type} = Info = feature_info(Feature),
+      type := Type} = Info = info(Feature),
     StatusFmt = "  ~-10s ~-12s (~p)\n",
     History = [io_lib:format(StatusFmt, [T, S, R])
                || {T, S, R} <- history(Status, Info)],
@@ -148,7 +145,7 @@ adjust(Col, [{W, L}| WLs], Ws) ->
     end.
 
 
--spec feature_info(atom()) -> FeatureInfoMap | no_return()
+-spec info(feature()) -> FeatureInfoMap | no_return()
               when
       Description :: string(),
       FeatureInfoMap ::
@@ -162,19 +159,18 @@ adjust(Col, [{W, L}| WLs], Ws) ->
           permanent => release(),
           rejected => release()
          }.
-feature_info(Feature) ->
+info(Feature) ->
     ?VALID_FEATURE(Feature),
 
     Map = persistent_term:get({?MODULE, feature_specs}),
     maps:get(Feature, Map).
 
-%% New keywords for a feature.  The current set is just for
-%% tests and development.
--spec keywords(atom()) -> [atom()].
+%% New keywords introduced by a feature.
+-spec keywords(feature()) -> [atom()] | no_return().
 keywords(Ftr) ->
     ?VALID_FEATURE(Ftr),
 
-    #{keywords := Keywords} = feature_info(Ftr),
+    #{keywords := Keywords} = info(Ftr),
     Keywords.
 
 %% Internal - Ftr is valid
@@ -183,9 +179,9 @@ keywords(Ftr, Map) ->
 
 %% Utilities
 %% Returns list of enabled features and a new keywords function
-%% -spec keyword_fun_add_feature(atom(), fun((atom()) -> boolean())) ->
-%%           {'ok', fun((atom()) -> boolean())}
-%%               | {'error', error()}.
+-spec keyword_fun([term()], fun((atom()) -> boolean())) ->
+          {'ok', {[feature()], fun((atom()) -> boolean())}}
+              | {'error', error()}.
 keyword_fun(Opts, KeywordFun) ->
     %% Get items enabling or disabling features, preserving order.
     IsFtr = fun({feature, _, enable}) -> true;
@@ -197,9 +193,9 @@ keyword_fun(Opts, KeywordFun) ->
     %% FIXME check that all features are known at this stage so we
     %% don't miss out on reporting any unknown features.
 
-    case keyword_fun_add_features(AddFeatures, KeywordFun) of
+    case add_features_fun(AddFeatures, KeywordFun) of
         {ok, Fun} ->
-            case keyword_fun_remove_features(DelFeatures, Fun) of
+            case remove_features_fun(DelFeatures, Fun) of
                 {ok, FunX} ->
                     {ok, {AddFeatures -- DelFeatures, FunX}};
                 {error, _} = Error ->
@@ -211,36 +207,33 @@ keyword_fun(Opts, KeywordFun) ->
             Error
     end.
 
-%% -spec keyword_fun_add_feature(atom(), fun((atom()) -> boolean())) ->
-%%           {'ok', fun((atom()) -> boolean())}
-%%               | {'error', error()}.
+-spec keyword_fun('enable' | 'disable', feature(), [feature()],
+                  fun((atom()) -> boolean())) ->
+          {'ok', {[feature()], fun((atom()) -> boolean())}}
+              | {'error', error()}.
 keyword_fun(Ind, Feature, Ftrs, KeywordFun) ->
-    case is_valid_feature(Feature) of
+    case is_valid(Feature) of
         true ->
             case Ind of
                 enable ->
-                    {ok,
-                     add_feature(Feature, KeywordFun),
-                     [Feature | Ftrs]};
+                    {ok, {[Feature | Ftrs],
+                          add_feature_fun(Feature, KeywordFun)}};
                 disable ->
-                    {ok,
-                     remove_feature(Feature, KeywordFun),
-                     Ftrs -- [Feature]}
+                    {ok, {Ftrs -- [Feature],
+                          remove_feature_fun(Feature, KeywordFun)}}
             end;
         false ->
             {error, {?MODULE, {invalid_features, [Feature]}}}
     end.
 
-%% FIXME Rename this to reflect that it returns a function!
-add_feature(Feature, F) ->
+add_feature_fun(Feature, F) ->
     Words = keywords(Feature),
     fun(Word) ->
             lists:member(Word, Words)
                 orelse F(Word)
     end.
 
-%% FIXME Rename this to reflect that it returns a function!
-remove_feature(Feature, F) ->
+remove_feature_fun(Feature, F) ->
     Words = keywords(Feature),
     fun(Word) ->
             case lists:member(Word, Words) of
@@ -249,37 +242,46 @@ remove_feature(Feature, F) ->
             end
     end.
 
--spec keyword_fun_add_features([atom()], fun((atom()) -> boolean())) ->
+-spec add_features_fun([feature()], fun((atom()) -> boolean())) ->
           {'ok', fun((atom()) -> boolean())}
               | {'error', error()}.
-keyword_fun_add_features(Features, F) ->
-    case lists:all(fun is_valid_feature/1, Features) of
+add_features_fun(Features, F) ->
+    case lists:all(fun is_valid/1, Features) of
         true ->
-            {ok, lists:foldl(fun add_feature/2, F, Features)};
+            {ok, lists:foldl(fun add_feature_fun/2, F, Features)};
         false ->
-            IsInvalid = fun(Ftr) -> not is_valid_feature(Ftr) end,
+            IsInvalid = fun(Ftr) -> not is_valid(Ftr) end,
             Invalid = lists:filter(IsInvalid, Features),
             {error, {?MODULE, {invalid_features, Invalid}}}
     end.
 
--spec keyword_fun_remove_features([atom()], fun((atom()) -> boolean())) ->
+-spec remove_features_fun([feature()], fun((atom()) -> boolean())) ->
           {'ok', fun((atom()) -> boolean())}
               | {'error', error()}.
-keyword_fun_remove_features(Features, F) ->
-    case lists:all(fun is_valid_feature/1, Features) of
+remove_features_fun(Features, F) ->
+    case lists:all(fun is_valid/1, Features) of
         true ->
-            {ok, lists:foldl(fun remove_feature/2, F, Features)};
+            {ok, lists:foldl(fun remove_feature_fun/2, F, Features)};
         false ->
-            IsInvalid = fun(Ftr) -> not is_valid_feature(Ftr) end,
+            IsInvalid = fun(Ftr) -> not is_valid(Ftr) end,
             Invalid = lists:filter(IsInvalid, Features),
             {error, {?MODULE, {invalid_features, Invalid}}}
     end.
 
+-spec format_error(Reason, StackTrace) -> ErrorDescription
+              when Reason :: term(),
+                   StackTrace :: erlang:stacktrace(),
+                   ArgumentPosition :: pos_integer(),
+                   ErrorDescription :: #{ArgumentPosition => unicode:chardata(),
+                                         general => unicode:chardata(),
+                                         reason => unicode:chardata()}.
 format_error(Reason, [{_M, _F, _Args, Info}| _St]) ->
     ErrorInfo = proplists:get_value(error_info, Info, #{}),
     ErrorMap = maps:get(cause, ErrorInfo),
     ErrorMap#{reason => io_lib:format("~p: ~p", [?MODULE, Reason])}.
 
+-spec format_error(Reason) -> iolist()
+              when Reason :: term().
 format_error({invalid_features, Features}) ->
     Fmt = fun F([Ftr]) -> io_lib:fwrite("'~p'", [Ftr]);
               F([Ftr1, Ftr2]) ->
@@ -326,7 +328,7 @@ init_features() ->
     F = fun({Tag, String}) ->
                 try
                     Atom = list_to_atom(String),
-                    case is_valid_feature(Atom) of
+                    case is_valid(Atom) of
                         true -> {true, {feature, Atom, Cnv(Tag)}};
                         false when Atom == all ->
                             {true, {feature, Atom, Cnv(Tag)}};
@@ -377,7 +379,7 @@ ensure_init() ->
 enable_feature(Feature) ->
     ?VALID_FEATURE(Feature),
 
-    Features = enabled_features(),
+    Features = enabled(),
     case lists:member(Feature, Features) of
         true ->
             %% already there, maybe raise an error
@@ -394,7 +396,7 @@ enable_feature(Feature) ->
 disable_feature(Feature) ->
     ?VALID_FEATURE(Feature),
 
-    Features = enabled_features(),
+    Features = enabled(),
     case lists:member(Feature, Features) of
         true ->
             NewFeatures = Features -- [Feature],
@@ -408,13 +410,17 @@ disable_feature(Feature) ->
             Features
     end.
 
-enabled_features() ->
+%% Return list of currently enabled features
+-spec enabled() -> [feature()].
+enabled() ->
     ensure_init(),
     persistent_term:get({?MODULE, enabled_features}).
 
 enabled_features(Ftrs) ->
     persistent_term:put({?MODULE, enabled_features}, Ftrs).
 
+%% Return list of keywords activated by enabled features
+-spec keywords() -> [atom()].
 keywords() ->
     ensure_init(),
     persistent_term:get({?MODULE, keywords}).
@@ -434,7 +440,7 @@ load_allowed(Binary) ->
                 undefined ->
                     true;
                 Used ->
-                    Enabled = enabled_features(),
+                    Enabled = enabled(),
                     lists:all(fun(UFtr) ->
                                       lists:member(UFtr, Enabled)
                               end,
@@ -444,14 +450,15 @@ load_allowed(Binary) ->
 
 
 %% Return features used by module or beam file
-features_used(Module) when is_atom(Module) ->
+-spec used(module() | file:filename()) -> [feature()].
+used(Module) when is_atom(Module) ->
     case code:get_object_code(Module) of
         error ->
             not_found;
         {_Mod, Bin, _Fname} ->
             features_in(Bin)
     end;
-features_used(FName) when is_list(FName) ->
+used(FName) when is_list(FName) ->
     features_in(FName).
 
 features_in(NameOrBin) ->
@@ -465,33 +472,33 @@ features_in(NameOrBin) ->
             not_found
     end.
 
-approved_features() ->
-    [Ftr || Ftr <- features(),
-            maps:get(status, feature_info(Ftr)) == approved].
+approved() ->
+    [Ftr || Ftr <- all(),
+            maps:get(status, info(Ftr)) == approved].
 
-permanent_features() ->
-    [Ftr || Ftr <- features(),
-            maps:get(status, feature_info(Ftr)) == permanent].
+permanent() ->
+    [Ftr || Ftr <- all(),
+            maps:get(status, info(Ftr)) == permanent].
 
 %% Interpret feature ops (enable or disable) to build the full set of
 %% features.  The meta feature 'all' is expanded to all known
 %% features.
 collect_features(FOps) ->
     %% Features enabled by default
-    Enabled = approved_features() ++ permanent_features(),
+    Enabled = approved() ++ permanent(),
     collect_features(FOps, Enabled, []).
 
 collect_features([], Add, Del) ->
     {Add, Del};
 collect_features([{feature, all, enable}| FOps], Add, _Del) ->
-    All = features(),
+    All = all(),
     Add1 = lists:foldl(fun add_ftr/2, Add, All),
     collect_features(FOps, Add1, []);
 collect_features([{feature, Feature, enable}| FOps], Add, Del) ->
     collect_features(FOps, add_ftr(Feature, Add), Del -- [Feature]);
 collect_features([{feature, all, disable}| FOps], _Add, Del) ->
     %% Start over
-    All = features(),
+    All = all(),
     collect_features(FOps, [], Del -- All);
 collect_features([{feature, Feature, disable}| FOps], Add, Del) ->
     collect_features(FOps, Add -- [Feature],
@@ -532,7 +539,7 @@ test_features() ->
             keywords => ['ifnot'],
             type => extension},
       unless_expr =>
-          #{short => "`unless <cond> -> <bodby> end",
+          #{short => "`unless <cond> -> <body> end",
             description =>
                 "Introduction of new expression `unless <cond> -> <body> end."
             " Truly experimental.",
@@ -542,9 +549,8 @@ test_features() ->
             type => extension},
       maps =>
           #{short => "Add maps as new data type",
-            description => "Add new low data type maps with syntactic "
-            "support in Erlang as well native support in the beam. "
-            "Insert, lookup and delete are asymptotically constant.",
+            description => "Add new data type for maps with syntactic "
+            "support in Erlang as well native support in the beam.",
             status => permanent,
             experimental => 17,
             approved => 18,
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 3ae2bc41c4..cc76090c59 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -4174,7 +4174,7 @@ test_overriden_by_local(Anno, OldTest, Arity, St) ->
 keyword_warning(Anno, Atom, St) ->
     case is_warn_enabled(keyword_warning, St) of
         true ->
-            Ftrs = erl_features:features(),
+            Ftrs = erl_features:all(),
             Reserved =
                 fun(Ftr) ->
                         lists:member(Atom, erl_features:keywords(Ftr))
-- 
2.35.3

openSUSE Build Service is sponsored by