summaryrefslogtreecommitdiff
path: root/lib/stdlib/src/erl_lint.erl
diff options
context:
space:
mode:
authorBjörn Gustavsson <bjorn@erlang.org>2022-10-12 11:50:58 +0200
committerGitHub <noreply@github.com>2022-10-12 11:50:58 +0200
commit5b279e38171637826501a6539ddc7fa01dc9b923 (patch)
tree7873b963c72fc88710bf870ce52ddc4d434cd000 /lib/stdlib/src/erl_lint.erl
parente8f26bba73949dc0eb8a49516a7191ffebfd6487 (diff)
parent4d08fc95830b650d03fc86c7f29f5a5f043a972b (diff)
downloaderlang-5b279e38171637826501a6539ddc7fa01dc9b923.tar.gz
Merge pull request #6335 from bjorng/bjorn/dialyzer/local-type-redefinition/GH-6132
Allow local redefinition of built-in types OTP-18282
Diffstat (limited to 'lib/stdlib/src/erl_lint.erl')
-rw-r--r--lib/stdlib/src/erl_lint.erl134
1 files changed, 80 insertions, 54 deletions
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index a2c943c45b..da0b6c67b8 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -437,12 +437,8 @@ format_error({undefined_type, {TypeName, Arity}}) ->
io_lib:format("type ~tw~s undefined", [TypeName, gen_type_paren(Arity)]);
format_error({unused_type, {TypeName, Arity}}) ->
io_lib:format("type ~tw~s is unused", [TypeName, gen_type_paren(Arity)]);
-format_error({new_builtin_type, {TypeName, Arity}}) ->
- io_lib:format("type ~w~s is a new builtin type; "
- "its (re)definition is allowed only until the next release",
- [TypeName, gen_type_paren(Arity)]);
-format_error({builtin_type, {TypeName, Arity}}) ->
- io_lib:format("type ~w~s is a builtin type; it cannot be redefined",
+format_error({redefine_builtin_type, {TypeName, Arity}}) ->
+ io_lib:format("local redefinition of built-in type: ~w~s",
[TypeName, gen_type_paren(Arity)]);
format_error({renamed_type, OldName, NewName}) ->
io_lib:format("type ~w() is now called ~w(); "
@@ -675,7 +671,10 @@ start(File, Opts) ->
true, Opts)},
{keyword_warning,
bool_option(warn_keywords, nowarn_keywords,
- false, Opts)}
+ false, Opts)},
+ {redefined_builtin_type,
+ bool_option(warn_redefined_builtin_type, nowarn_redefined_builtin_type,
+ true, Opts)}
],
Enabled1 = [Category || {Category,true} <- Enabled0],
Enabled = ordsets:from_list(Enabled1),
@@ -2976,17 +2975,13 @@ type_def(Attr, Anno, TypeName, ProtoType, Args, St0) ->
not member(no_auto_import_types, St0#lint.compile) of
true ->
case is_obsolete_builtin_type(TypePair) of
- true -> StoreType(St0);
+ true ->
+ StoreType(St0);
false ->
- case is_newly_introduced_builtin_type(TypePair) of
- %% allow some types just for bootstrapping
- true ->
- Warn = {new_builtin_type, TypePair},
- St1 = add_warning(Anno, Warn, St0),
- StoreType(St1);
- false ->
- add_error(Anno, {builtin_type, TypePair}, St0)
- end
+ %% Starting from OTP 26, redefining built-in types
+ %% is allowed.
+ St1 = StoreType(St0),
+ warn_redefined_builtin_type(Anno, TypePair, St1)
end;
false ->
case is_map_key(TypePair, TypeDefs) of
@@ -3006,12 +3001,29 @@ type_def(Attr, Anno, TypeName, ProtoType, Args, St0) ->
end
end.
+warn_redefined_builtin_type(Anno, TypePair, #lint{compile=Opts}=St) ->
+ case is_warn_enabled(redefined_builtin_type, St) of
+ true ->
+ NoWarn = [Type ||
+ {nowarn_redefined_builtin_type, Type0} <- Opts,
+ Type <- lists:flatten([Type0])],
+ case lists:member(TypePair, NoWarn) of
+ true ->
+ St;
+ false ->
+ Warn = {redefine_builtin_type, TypePair},
+ add_warning(Anno, Warn, St)
+ end;
+ false ->
+ St
+ end.
+
is_underspecified({type,_,term,[]}, 0) -> true;
is_underspecified({type,_,any,[]}, 0) -> true;
is_underspecified(_ProtType, _Arity) -> false.
check_type(Types, St) ->
- {SeenVars, St1} = check_type(Types, maps:new(), St),
+ {SeenVars, St1} = check_type_1(Types, maps:new(), St),
maps:fold(fun(Var, {seen_once, Anno}, AccSt) ->
case atom_to_list(Var) of
"_"++_ -> AccSt;
@@ -3021,24 +3033,39 @@ check_type(Types, St) ->
AccSt
end, St1, SeenVars).
-check_type({ann_type, _A, [_Var, Type]}, SeenVars, St) ->
- check_type(Type, SeenVars, St);
-check_type({remote_type, A, [{atom, _, Mod}, {atom, _, Name}, Args]},
+check_type_1({type, Anno, TypeName, Args}=Type, SeenVars, #lint{types=Types}=St) ->
+ TypePair = {TypeName,
+ if
+ is_list(Args) -> length(Args);
+ true -> 0
+ end},
+ case is_map_key(TypePair, Types) of
+ true ->
+ check_type_2(Type, SeenVars, used_type(TypePair, Anno, St));
+ false ->
+ check_type_2(Type, SeenVars, St)
+ end;
+check_type_1(Types, SeenVars, St) ->
+ check_type_2(Types, SeenVars, St).
+
+check_type_2({ann_type, _A, [_Var, Type]}, SeenVars, St) ->
+ check_type_1(Type, SeenVars, St);
+check_type_2({remote_type, A, [{atom, _, Mod}, {atom, _, Name}, Args]},
SeenVars, St00) ->
St0 = check_module_name(Mod, A, St00),
St = deprecated_type(A, Mod, Name, Args, St0),
CurrentMod = St#lint.module,
case Mod =:= CurrentMod of
- true -> check_type({user_type, A, Name, Args}, SeenVars, St);
+ true -> check_type_2({user_type, A, Name, Args}, SeenVars, St);
false ->
lists:foldl(fun(T, {AccSeenVars, AccSt}) ->
- check_type(T, AccSeenVars, AccSt)
+ check_type_1(T, AccSeenVars, AccSt)
end, {SeenVars, St}, Args)
end;
-check_type({integer, _A, _}, SeenVars, St) -> {SeenVars, St};
-check_type({atom, _A, _}, SeenVars, St) -> {SeenVars, St};
-check_type({var, _A, '_'}, SeenVars, St) -> {SeenVars, St};
-check_type({var, A, Name}, SeenVars, St) ->
+check_type_2({integer, _A, _}, SeenVars, St) -> {SeenVars, St};
+check_type_2({atom, _A, _}, SeenVars, St) -> {SeenVars, St};
+check_type_2({var, _A, '_'}, SeenVars, St) -> {SeenVars, St};
+check_type_2({var, A, Name}, SeenVars, St) ->
NewSeenVars =
case maps:find(Name, SeenVars) of
{ok, {seen_once, _}} -> maps:put(Name, seen_multiple, SeenVars);
@@ -3046,34 +3073,34 @@ check_type({var, A, Name}, SeenVars, St) ->
error -> maps:put(Name, {seen_once, A}, SeenVars)
end,
{NewSeenVars, St};
-check_type({type, A, bool, []}, SeenVars, St) ->
+check_type_2({type, A, bool, []}, SeenVars, St) ->
{SeenVars, add_warning(A, {renamed_type, bool, boolean}, St)};
-check_type({type, A, 'fun', [Dom, Range]}, SeenVars, St) ->
+check_type_2({type, A, 'fun', [Dom, Range]}, SeenVars, St) ->
St1 =
case Dom of
{type, _, product, _} -> St;
{type, _, any} -> St;
_ -> add_error(A, {type_syntax, 'fun'}, St)
end,
- check_type({type, nowarn(), product, [Dom, Range]}, SeenVars, St1);
-check_type({type, A, range, [From, To]}, SeenVars, St) ->
+ check_type_2({type, nowarn(), product, [Dom, Range]}, SeenVars, St1);
+check_type_2({type, A, range, [From, To]}, SeenVars, St) ->
St1 =
case {erl_eval:partial_eval(From), erl_eval:partial_eval(To)} of
{{integer, _, X}, {integer, _, Y}} when X < Y -> St;
_ -> add_error(A, {type_syntax, range}, St)
end,
{SeenVars, St1};
-check_type({type, _A, map, any}, SeenVars, St) ->
+check_type_2({type, _A, map, any}, SeenVars, St) ->
{SeenVars, St};
-check_type({type, _A, map, Pairs}, SeenVars, St) ->
+check_type_2({type, _A, map, Pairs}, SeenVars, St) ->
lists:foldl(fun(Pair, {AccSeenVars, AccSt}) ->
- check_type(Pair, AccSeenVars, AccSt)
+ check_type_2(Pair, AccSeenVars, AccSt)
end, {SeenVars, St}, Pairs);
-check_type({type, _A, map_field_assoc, [Dom, Range]}, SeenVars, St) ->
- check_type({type, nowarn(), product, [Dom, Range]}, SeenVars, St);
-check_type({type, _A, tuple, any}, SeenVars, St) -> {SeenVars, St};
-check_type({type, _A, any}, SeenVars, St) -> {SeenVars, St};
-check_type({type, A, binary, [Base, Unit]}, SeenVars, St) ->
+check_type_2({type, _A, map_field_assoc, [Dom, Range]}, SeenVars, St) ->
+ check_type_2({type, nowarn(), product, [Dom, Range]}, SeenVars, St);
+check_type_2({type, _A, tuple, any}, SeenVars, St) -> {SeenVars, St};
+check_type_2({type, _A, any}, SeenVars, St) -> {SeenVars, St};
+check_type_2({type, A, binary, [Base, Unit]}, SeenVars, St) ->
St1 =
case {erl_eval:partial_eval(Base), erl_eval:partial_eval(Unit)} of
{{integer, _, BaseVal},
@@ -3081,20 +3108,20 @@ check_type({type, A, binary, [Base, Unit]}, SeenVars, St) ->
_ -> add_error(A, {type_syntax, binary}, St)
end,
{SeenVars, St1};
-check_type({type, A, record, [Name|Fields]}, SeenVars, St) ->
+check_type_2({type, A, record, [Name|Fields]}, SeenVars, St) ->
case Name of
{atom, _, Atom} ->
St1 = used_record(Atom, St),
check_record_types(A, Atom, Fields, SeenVars, St1);
_ -> {SeenVars, add_error(A, {type_syntax, record}, St)}
end;
-check_type({type, _A, Tag, Args}, SeenVars, St) when Tag =:= product;
+check_type_2({type, _A, Tag, Args}, SeenVars, St) when Tag =:= product;
Tag =:= union;
Tag =:= tuple ->
lists:foldl(fun(T, {AccSeenVars, AccSt}) ->
- check_type(T, AccSeenVars, AccSt)
+ check_type_1(T, AccSeenVars, AccSt)
end, {SeenVars, St}, Args);
-check_type({type, Anno, TypeName, Args}, SeenVars, St) ->
+check_type_2({type, Anno, TypeName, Args}, SeenVars, St) ->
#lint{module = Module, types=Types} = St,
Arity = length(Args),
TypePair = {TypeName, Arity},
@@ -3110,20 +3137,21 @@ check_type({type, Anno, TypeName, Args}, SeenVars, St) ->
Tag = deprecated_builtin_type,
W = {Tag, TypePair, Replacement, Rel},
add_warning(Anno, W, St)
- end;
- _ -> St
- end,
- check_type({type, nowarn(), product, Args}, SeenVars, St1);
-check_type({user_type, A, TypeName, Args}, SeenVars, St) ->
+ end;
+ _ ->
+ St
+ end,
+ check_type_2({type, nowarn(), product, Args}, SeenVars, St1);
+check_type_2({user_type, A, TypeName, Args}, SeenVars, St) ->
Arity = length(Args),
TypePair = {TypeName, Arity},
St1 = used_type(TypePair, A, St),
lists:foldl(fun(T, {AccSeenVars, AccSt}) ->
- check_type(T, AccSeenVars, AccSt)
+ check_type_1(T, AccSeenVars, AccSt)
end, {SeenVars, St1}, Args);
-check_type([{typed_record_field,Field,_T}|_], SeenVars, St) ->
+check_type_2([{typed_record_field,Field,_T}|_], SeenVars, St) ->
{SeenVars, add_error(element(2, Field), old_abstract_code, St)};
-check_type(I, SeenVars, St) ->
+check_type_2(I, SeenVars, St) ->
case erl_eval:partial_eval(I) of
{integer,_A,_Integer} -> {SeenVars, St};
_Other ->
@@ -3158,7 +3186,7 @@ check_record_types([{type, _, field_type, [{atom, Anno, FName}, Type]}|Left],
false -> St1
end,
%% Check Type
- {NewSeenVars, St3} = check_type(Type, SeenVars, St2),
+ {NewSeenVars, St3} = check_type_2(Type, SeenVars, St2),
NewSeenFields = ordsets:add_element(FName, SeenFields),
check_record_types(Left, Name, DefFields, NewSeenVars, St3, NewSeenFields);
check_record_types([], _Name, _DefFields, SeenVars, St, _SeenFields) ->
@@ -3174,8 +3202,6 @@ used_type(TypePair, Anno, #lint{usage = Usage, file = File} = St) ->
is_default_type({Name, NumberOfTypeVariables}) ->
erl_internal:is_type(Name, NumberOfTypeVariables).
-is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false.
-
is_obsolete_builtin_type(TypePair) ->
obsolete_builtin_type(TypePair) =/= no.