diff options
Diffstat (limited to 'lib/stdlib')
-rw-r--r-- | lib/stdlib/doc/src/Makefile | 1 | ||||
-rw-r--r-- | lib/stdlib/doc/src/maps.xml | 318 | ||||
-rw-r--r-- | lib/stdlib/doc/src/ref_man.xml | 1 | ||||
-rw-r--r-- | lib/stdlib/doc/src/specs.xml | 1 | ||||
-rw-r--r-- | lib/stdlib/doc/src/supervisor.xml | 6 | ||||
-rw-r--r-- | lib/stdlib/src/dets.erl | 12 | ||||
-rw-r--r-- | lib/stdlib/src/erl_lint.erl | 218 | ||||
-rw-r--r-- | lib/stdlib/src/gen.erl | 6 | ||||
-rw-r--r-- | lib/stdlib/src/maps.erl | 15 | ||||
-rw-r--r-- | lib/stdlib/test/dets_SUITE.erl | 75 | ||||
-rw-r--r-- | lib/stdlib/test/erl_lint_SUITE.erl | 163 | ||||
-rw-r--r-- | lib/stdlib/test/erl_lint_SUITE_data/predef.erl | 4 |
12 files changed, 660 insertions, 160 deletions
diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile index 6f1e61e70c..ff77c3eea0 100644 --- a/lib/stdlib/doc/src/Makefile +++ b/lib/stdlib/doc/src/Makefile @@ -71,6 +71,7 @@ XML_REF3_FILES = \ lib.xml \ lists.xml \ log_mf_h.xml \ + maps.xml \ math.xml \ ms_transform.xml \ orddict.xml \ diff --git a/lib/stdlib/doc/src/maps.xml b/lib/stdlib/doc/src/maps.xml new file mode 100644 index 0000000000..76137e3dee --- /dev/null +++ b/lib/stdlib/doc/src/maps.xml @@ -0,0 +1,318 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>2013</year><year>2014</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + </legalnotice> + <title>maps</title> + <prepared>Björn-Egil Dahlberg</prepared> + <docno>1</docno> + <date>2014-02-28</date> + <rev>A</rev> + </header> + <module>maps</module> + <modulesummary>Maps Processing Functions</modulesummary> + <description> + <p>This module contains functions for maps processing.</p> + </description> + <funcs> + + <func> + <name name="find" arity="2"/> + <fsummary></fsummary> + <desc> + <p> + Returns a tuple <c>{ok, Value}</c> where <c><anno>Value</anno></c> is the value associated with <c><anno>Key</anno></c>, + or <c>error</c> if no value is associated with <c><anno>Key</anno></c> in <c><anno>Map</anno></c>. + </p> + <p>Example:</p> + <code type="none"> +> Map = #{"hi" => 42}, + Key = "hi", + maps:find(Key,Map). +{ok,42} </code> + </desc> + </func> + + <func> + <name name="fold" arity="3"/> + <fsummary></fsummary> + <desc> + <p> + Calls <c>F(K, V, AccIn)</c> for every <c><anno>K</anno></c> to value <c><anno>V</anno></c> + association in <c><anno>Map</anno></c> in + arbitrary order. The function <c>fun F/3</c> must return a new accumulator + which is passed to the next successive call. <c>maps:fold/3</c> returns the final + value of the accumulator. The initial accumulator value <c><anno>Init</anno></c> is returned if + the map is empty. + </p> + <p>Example:</p> + <code type="none"> +> Fun = fun(K,V,AccIn) when is_list(K) -> AccIn + V end, + Map = #{"k1" => 1, "k2" => 2, "k3" => 3}, + maps:fold(Fun,0,Map). +6</code> + </desc> + </func> + + <func> + <name name="from_list" arity="1"/> + <fsummary></fsummary> + <desc> + <p> + The function takes a list of key-value tuples elements and builds a + map. The associations may be in any order and both keys and values in the + association may be of any term. If the same key appears more than once, + the latter (rightmost) value is used and the previous values are ignored. + </p> + <p>Example:</p> + <code type="none"> +> List = [{"a",ignored},{1337,"value two"},{42,value_three},{"a",1}], + maps:from_list(List). +#{42 => value_three,1337 => "value two","a" => 1}</code> + </desc> + </func> + + <func> + <name name="get" arity="2"/> + <fsummary></fsummary> + <desc> + <p> + Returns the value <c><anno>Value</anno></c> associated with <c><anno>Key</anno></c> if + <c><anno>Map</anno></c> contains <c><anno>Key</anno></c>. + If no value is associated with <c><anno>Key</anno></c> then the call will + fail with an exception. + </p> + <p>Example:</p> + <code type="none"> +> Key = 1337, + Map = #{42 => value_two,1337 => "value one","a" => 1}, + maps:get(Key,Map). +"value one"</code> + </desc> + </func> + + <func> + <name name="is_key" arity="2"/> + <fsummary></fsummary> + <desc> + <p> + Returns <c>true</c> if map <c><anno>Map</anno></c> contains <c><anno>Key</anno></c> and returns + <c>false</c> if it does not contain the <c><anno>Key</anno></c>. + The function will fail with an exception if <c><anno>Map</anno></c> is not a Map. + </p> + <p>Example:</p> + <code type="none"> +> Map = #{"42" => value}. +#{"42"> => value} +> maps:is_key("42",Map). +true +> maps:is_key(value,Map). +false</code> + </desc> + </func> + + <func> + <name name="keys" arity="1"/> + <fsummary></fsummary> + <desc> + <p> + Returns a complete list of keys, in arbitrary order, which resides within <c><anno>Map</anno></c>. + </p> + <p>Example:</p> + <code type="none"> +> Map = #{42 => value_three,1337 => "value two","a" => 1}, + maps:keys(Map). +[42,1337,"a"]</code> + </desc> + </func> + + <func> + <name name="map" arity="2"/> + <fsummary></fsummary> + <desc> + <p> + The function produces a new map <c><anno>Map2</anno></c> by calling the function <c>fun F(K, V1)</c> for + every <c><anno>K</anno></c> to value <c><anno>V1</anno></c> association in <c><anno>Map1</anno></c> in arbitrary order. + The function <c>fun F/2</c> must return the value <c><anno>V2</anno></c> to be associated with key <c><anno>K</anno></c> for + the new map <c><anno>Map2</anno></c>. + </p> + <p>Example:</p> + <code type="none"> +> Fun = fun(K,V1) when is_list(K) -> V1*2 end, + Map = #{"k1" => 1, "k2" => 2, "k3" => 3}, + maps:map(Fun,Map). +#{"k1" => 2,"k2" => 4,"k3" => 6}</code> + </desc> + </func> + + <func> + <name name="merge" arity="2"/> + <fsummary></fsummary> + <desc> + <p> + Merges two maps into a single map <c><anno>Map3</anno></c>. If two keys exists in both maps the + value in <c><anno>Map1</anno></c> will be superseded by the value in <c><anno>Map2</anno></c>. + </p> + <p>Example:</p> + <code type="none"> +> Map1 = #{a => "value_one", b => "value_two"}, + Map2 = #{a => 1, c => 2}, + maps:merge(Map1,Map2). +#{a => 1,b => "value_two",c => 2}</code> + </desc> + </func> + + <func> + <name name="new" arity="0"/> + <fsummary></fsummary> + <desc> + <p> + Returns a new empty map. + </p> + <p>Example:</p> + <code type="none"> +> maps:new(). +#{}</code> + </desc> + </func> + + <func> + <name name="put" arity="3"/> + <fsummary></fsummary> + <desc> + <p> + Associates <c><anno>Key</anno></c> with value <c><anno>Value</anno></c> and inserts the association into map <c>Map2</c>. + If key <c><anno>Key</anno></c> already exists in map <c><anno>Map1</anno></c>, the old associated value is + replaced by value <c><anno>Value</anno></c>. The function returns a new map <c><anno>Map2</anno></c> containing the new association and + the old associations in <c><anno>Map1</anno></c>. + </p> + <p>Example:</p> + <code type="none"> +> Map = #{"a" => 1}. +#{"a" => 1} +> maps:put("a", 42, Map). +#{"a" => 42} +> maps:put("b", 1337, Map). +#{"a" => 1,"b" => 1337}</code> + </desc> + </func> + + <func> + <name name="remove" arity="2"/> + <fsummary></fsummary> + <desc> + <p> + The function removes the <c><anno>Key</anno></c>, if it exists, and its associated value from + <c><anno>Map1</anno></c> and returns a new map <c><anno>Map2</anno></c> without key <c><anno>Key</anno></c>. + </p> + <p>Example:</p> + <code type="none"> +> Map = #{"a" => 1}. +#{"a" => 1} +> maps:remove("a",Map). +#{} +> maps:remove("b",Map). +#{"a" => 1}</code> + </desc> + </func> + + <func> + <name name="size" arity="1"/> + <fsummary></fsummary> + <desc> + <p> + The function returns the number of key-value associations in the <c><anno>Map</anno></c>. + This operation happens in constant time. + </p> + <p>Example:</p> + <code type="none"> +> Map = #{42 => value_two,1337 => "value one","a" => 1}, + maps:size(Map). +3</code> + </desc> + </func> + + <func> + <name name="to_list" arity="1"/> + <fsummary></fsummary> + <desc> + <p> + The fuction returns a list of pairs representing the key-value associations of <c><anno>Map</anno></c>, + where the pairs, <c>[{K1,V1}, ..., {Kn,Vn}]</c>, are returned in arbitrary order. + </p> + <p>Example:</p> + <code type="none"> +> Map = #{42 => value_three,1337 => "value two","a" => 1}, + maps:to_list(Map). +[{42,value_three},{1337,"value two"},{"a",1}]</code> + </desc> + </func> + + <func> + <name name="update" arity="3"/> + <fsummary></fsummary> + <desc> + <p> + If <c><anno>Key</anno></c> exists in <c><anno>Map1</anno></c> the old associated value is + replaced by value <c><anno>Value</anno></c>. The function returns a new map <c><anno>Map2</anno></c> containing + the new associated value. If <c><anno>Key</anno></c> does not exist in <c><anno>Map1</anno></c> an exception is + generated. + </p> + <p>Example:</p> + <code type="none"> +> Map = #{"a" => 1}. +#{"a" => 1} +> maps:update("a", 42, Map). +#{"a" => 42}</code> + </desc> + </func> + + <func> + <name name="values" arity="1"/> + <fsummary></fsummary> + <desc> + <p> + Returns a complete list of values, in arbitrary order, contained in map <c>M</c>. + </p> + <p>Example:</p> + <code type="none"> +> Map = #{42 => value_three,1337 => "value two","a" => 1}, + maps:values(Map). +[value_three,"value two",1]</code> + </desc> + </func> + + <func> + <name name="without" arity="2"/> + <fsummary></fsummary> + <desc> + <p> + Returns a new map <c><anno>Map2</anno></c> without the keys <c>K1</c> through <c>Kn</c> and their associated values from map <c><anno>Map1</anno></c>. + Any key in <c><anno>Ks</anno></c> that does not exist in <c><anno>Map1</anno></c> are ignored. + </p> + <p>Example:</p> + <code type="none"> +> Map = #{42 => value_three,1337 => "value two","a" => 1}, + Ks = ["a",42,"other key"], + maps:without(Ks,Map). +#{1337 => "value two"}</code> + </desc> + </func> + </funcs> +</erlref> diff --git a/lib/stdlib/doc/src/ref_man.xml b/lib/stdlib/doc/src/ref_man.xml index 4ecd02a4bf..6c35578bdf 100644 --- a/lib/stdlib/doc/src/ref_man.xml +++ b/lib/stdlib/doc/src/ref_man.xml @@ -68,6 +68,7 @@ <xi:include href="lib.xml"/> <xi:include href="lists.xml"/> <xi:include href="log_mf_h.xml"/> + <xi:include href="maps.xml"/> <xi:include href="math.xml"/> <xi:include href="ms_transform.xml"/> <xi:include href="orddict.xml"/> diff --git a/lib/stdlib/doc/src/specs.xml b/lib/stdlib/doc/src/specs.xml index 213ce7563f..60a04ed5e7 100644 --- a/lib/stdlib/doc/src/specs.xml +++ b/lib/stdlib/doc/src/specs.xml @@ -34,6 +34,7 @@ <xi:include href="../specs/specs_lib.xml"/> <xi:include href="../specs/specs_lists.xml"/> <xi:include href="../specs/specs_log_mf_h.xml"/> + <xi:include href="../specs/specs_maps.xml"/> <xi:include href="../specs/specs_math.xml"/> <xi:include href="../specs/specs_ms_transform.xml"/> <xi:include href="../specs/specs_orddict.xml"/> diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml index 8197684d2d..3a5027d595 100644 --- a/lib/stdlib/doc/src/supervisor.xml +++ b/lib/stdlib/doc/src/supervisor.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2013</year> + <year>1996</year><year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -262,12 +262,12 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} locally as <c>Name</c> using <c>register/2</c>. If <c><anno>SupName</anno>={global,Name}</c> the supervisor is registered globally as <c>Name</c> using <c>global:register_name/2</c>. If - <c><anno>SupName</anno>={via,Module,Name}</c> the supervisor + <c><anno>SupName</anno>={via,<anno>Module</anno>,<anno>Name</anno>}</c> the supervisor is registered as <c>Name</c> using the registry represented by <c>Module</c>. The <c>Module</c> callback should export the functions <c>register_name/2</c>, <c>unregister_name/1</c> and <c>send/2</c>, which should behave like the corresponding functions in <c>global</c>. - Thus, <c>{via,global,Name}</c> is a valid reference.</p> + Thus, <c>{via,global,<anno>Name</anno>}</c> is a valid reference.</p> <p>If no name is provided, the supervisor is not registered.</p> <p><c><anno>Module</anno></c> is the name of the callback module.</p> <p><c><anno>Args</anno></c> is an arbitrary term which is passed as diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl index 44dad04f43..c32da1624f 100644 --- a/lib/stdlib/src/dets.erl +++ b/lib/stdlib/src/dets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -1785,6 +1785,7 @@ read_file_header(FileName, Access, RamFile) -> Version =:= 9 -> dets_v9:read_file_header(Fd, FileName); true -> + _ = file:close(Fd), throw({error, {not_a_dets_file, FileName}}) end. @@ -2113,6 +2114,8 @@ test_bchunk_format(Head, Term) -> do_open_file([Fname, Verbose], Parent, Server, Ref) -> case catch fopen2(Fname, Ref) of + {error, {tooshort, _}} -> + err({error, {not_a_dets_file, Fname}}); {error, _Reason} = Error -> err(Error); {ok, Head} -> @@ -2126,11 +2129,10 @@ do_open_file([Fname, Verbose], Parent, Server, Ref) -> [Bad]), {error, {dets_bug, Fname, Bad}} end; -do_open_file([Tab, OpenArgs, Verb], Parent, Server, Ref) -> +do_open_file([Tab, OpenArgs, Verb], Parent, Server, _Ref) -> case catch fopen3(Tab, OpenArgs) of {error, {tooshort, _}} -> - _ = file:delete(OpenArgs#open_args.file), - do_open_file([Tab, OpenArgs, Verb], Parent, Server, Ref); + err({error, {not_a_dets_file, OpenArgs#open_args.file}}); {error, _Reason} = Error -> err(Error); {ok, Head} -> @@ -2486,7 +2488,6 @@ fopen2(Fname, Tab) -> {ok, _} -> Acc = read_write, Ram = false, - %% Fd is not always closed upon error, but exit is soon called. {ok, Fd, FH} = read_file_header(Fname, Acc, Ram), Mod = FH#fileheader.mod, Do = case Mod:check_file_header(FH, Fd) of @@ -2542,7 +2543,6 @@ fopen_existing_file(Tab, OpenArgs) -> ram_file = Ram, delayed_write = CacheSz, auto_save = Auto, access = Acc, version = Version, debug = Debug} = OpenArgs, - %% Fd is not always closed upon error, but exit is soon called. {ok, Fd, FH} = read_file_header(Fname, Acc, Ram), V9 = (Version =:= 9) or (Version =:= default), MinF = (MinSlots =:= default) or (MinSlots =:= FH#fileheader.min_no_slots), diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 9f5be2da37..269e4b34cf 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -344,10 +344,19 @@ format_error(spec_wrong_arity) -> "spec has the wrong arity"; format_error(callback_wrong_arity) -> "callback has the wrong arity"; -format_error({deprecated_type, {Name, Arity}, {Mod, NewName}, Rel}) -> +format_error({deprecated_builtin_type, {Name, Arity}, + Replacement, Rel}) -> + UseS = case Replacement of + {Mod, NewName} -> + io_lib:format("use ~w:~w/~w", [Mod, NewName, Arity]); + {Mod, NewName, NewArity} -> + io_lib:format("use ~w:~w/~w or preferably ~w:~w/~w", + [Mod, NewName, Arity, + Mod, NewName, NewArity]) + end, io_lib:format("type ~w/~w is deprecated and will be " - "removed in ~s; use ~w:~w/~w", - [Name, Arity, Rel, Mod, NewName, Arity]); + "removed in ~s; use ~s", + [Name, Arity, Rel, UseS]); format_error({not_exported_opaque, {TypeName, Arity}}) -> io_lib:format("opaque type ~w~s is not exported", [TypeName, gen_type_paren(Arity)]); @@ -499,6 +508,9 @@ start(File, Opts) -> {deprecated_function, bool_option(warn_deprecated_function, nowarn_deprecated_function, true, Opts)}, + {deprecated_type, + bool_option(warn_deprecated_type, nowarn_deprecated_type, + true, Opts)}, {obsolete_guard, bool_option(warn_obsolete_guard, nowarn_obsolete_guard, true, Opts)}, @@ -1373,18 +1385,19 @@ pattern({cons,_Line,H,T}, Vt, Old, Bvt, St0) -> pattern({tuple,_Line,Ps}, Vt, Old, Bvt, St) -> pattern_list(Ps, Vt, Old, Bvt, St); pattern({map,_Line,Ps}, Vt, Old, Bvt, St) -> - pattern_list(Ps, Vt, Old, Bvt, St); -pattern({map_field_assoc,Line,_,_}, _, _, _, St) -> - {[],[],add_error(Line, illegal_pattern, St)}; -pattern({map_field_exact,Line,KP,VP}, Vt, Old, Bvt0, St0) -> - %% if the key pattern has variables we should fail - case expr(KP,[],St0) of - {[],_} -> - pattern(VP, Vt, Old, Bvt0, St0); - {[Var|_],_} -> - %% found variables in key expression - {Vt,Old,add_error(Line,{illegal_map_key_variable,element(1,Var)},St0)} - end; + foldl(fun ({map_field_assoc,L,_,_}, {Psvt,Bvt0,St0}) -> + {Psvt,Bvt0,add_error(L, illegal_pattern, St0)}; + ({map_field_exact,L,KP,VP}, {Psvt,Bvt0,St0}) -> + case expr(KP, [], St0) of + {[],_} -> + {Pvt,Bvt1,St1} = pattern(VP, Vt, Old, Bvt, St0), + {vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt0, Bvt1), + St1}; + {[Var|_],_} -> + Error = {illegal_map_key_variable,element(1, Var)}, + {Psvt,Bvt0,add_error(L, Error, St0)} + end + end, {[],[],St}, Ps); %%pattern({struct,_Line,_Tag,Ps}, Vt, Old, Bvt, St) -> %% pattern_list(Ps, Vt, Old, Bvt, St); pattern({record_index,Line,Name,Field}, _Vt, _Old, _Bvt, St) -> @@ -1773,13 +1786,11 @@ gexpr({cons,_Line,H,T}, Vt, St) -> gexpr({tuple,_Line,Es}, Vt, St) -> gexpr_list(Es, Vt, St); gexpr({map,_Line,Es}, Vt, St) -> - gexpr_list(Es, Vt, St); + map_fields(Es, Vt, check_assoc_fields(Es, St), fun gexpr_list/3); gexpr({map,_Line,Src,Es}, Vt, St) -> - gexpr_list([Src|Es], Vt, St); -gexpr({map_field_assoc,_Line,K,V}, Vt, St) -> - gexpr_list([K,V], Vt, St); -gexpr({map_field_exact,_Line,K,V}, Vt, St) -> - gexpr_list([K,V], Vt, St); + {Svt,St1} = gexpr(Src, Vt, St), + {Fvt,St2} = map_fields(Es, Vt, St1, fun gexpr_list/3), + {vtmerge(Svt, Fvt),St2}; gexpr({record_index,Line,Name,Field}, _Vt, St) -> check_record(Line, Name, St, fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end ); @@ -1852,6 +1863,10 @@ gexpr({op,Line,Op,A}, Vt, St0) -> true -> {Avt,St1}; false -> {Avt,add_error(Line, illegal_guard_expr, St1)} end; +gexpr({op,_,'andalso',L,R}, Vt, St) -> + gexpr_list([L,R], Vt, St); +gexpr({op,_,'orelse',L,R}, Vt, St) -> + gexpr_list([L,R], Vt, St); gexpr({op,Line,Op,L,R}, Vt, St0) -> {Avt,St1} = gexpr_list([L,R], Vt, St0), case is_gexpr_op(Op, 2) of @@ -1938,12 +1953,14 @@ is_gexpr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,F}]},As}, RDs) -> is_gexpr({call,L,{remote,Lt,{atom,Lm,erlang},{atom,Lf,F}},As}, RDs); is_gexpr({op,_L,Op,A}, RDs) -> is_gexpr_op(Op, 1) andalso is_gexpr(A, RDs); +is_gexpr({op,_L,'andalso',A1,A2}, RDs) -> + is_gexpr_list([A1,A2], RDs); +is_gexpr({op,_L,'orelse',A1,A2}, RDs) -> + is_gexpr_list([A1,A2], RDs); is_gexpr({op,_L,Op,A1,A2}, RDs) -> is_gexpr_op(Op, 2) andalso is_gexpr_list([A1,A2], RDs); is_gexpr(_Other, _RDs) -> false. -is_gexpr_op('andalso', 2) -> true; -is_gexpr_op('orelse', 2) -> true; is_gexpr_op(Op, A) -> try erl_internal:op_type(Op, A) of arith -> true; @@ -1997,24 +2014,12 @@ expr({bc,_Line,E,Qs}, Vt, St) -> handle_comprehension(E, Qs, Vt, St); expr({tuple,_Line,Es}, Vt, St) -> expr_list(Es, Vt, St); -expr({map,Line,Es}, Vt, St) -> - {Rvt,St1} = expr_list(Es,Vt,St), - case is_valid_map_construction(Es) of - true -> {Rvt,St1}; - false -> {[],add_error(Line,illegal_map_construction,St1)} - end; +expr({map,_Line,Es}, Vt, St) -> + map_fields(Es, Vt, check_assoc_fields(Es, St), fun expr_list/3); expr({map,_Line,Src,Es}, Vt, St) -> - expr_list([Src|Es], Vt, St); -expr({map_field_assoc,Line,K,V}, Vt, St) -> - case is_valid_map_key(K,St) of - true -> expr_list([K,V], Vt, St); - {false,Var} -> {[],add_error(Line,{illegal_map_key_variable,Var},St)} - end; -expr({map_field_exact,Line,K,V}, Vt, St) -> - case is_valid_map_key(K,St) of - true -> expr_list([K,V], Vt, St); - {false,Var} -> {[],add_error(Line,{illegal_map_key_variable,Var},St)} - end; + {Svt,St1} = expr(Src, Vt, St), + {Fvt,St2} = map_fields(Es, Vt, St1, fun expr_list/3), + {vtupdate(Svt, Fvt),St2}; expr({record_index,Line,Name,Field}, _Vt, St) -> check_record(Line, Name, St, fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end); @@ -2222,6 +2227,25 @@ record_expr(Line, Rec, Vt, St0) -> St1 = warn_invalid_record(Line, Rec, St0), expr(Rec, Vt, St1). +check_assoc_fields([{map_field_exact,Line,_,_}|Fs], St) -> + check_assoc_fields(Fs, add_error(Line, illegal_map_construction, St)); +check_assoc_fields([{map_field_assoc,_,_,_}|Fs], St) -> + check_assoc_fields(Fs, St); +check_assoc_fields([], St) -> + St. + +map_fields([{Tag,Line,K,V}|Fs], Vt, St, F) when Tag =:= map_field_assoc; + Tag =:= map_field_exact -> + St1 = case is_valid_map_key(K, St) of + true -> St; + {false,Var} -> add_error(Line, {illegal_map_key_variable,Var}, St) + end, + {Pvt,St2} = F([K,V], Vt, St1), + {Vts,St3} = map_fields(Fs, Vt, St2, F), + {vtupdate(Pvt, Vts),St3}; +map_fields([], Vt, St, _) -> + {Vt,St}. + %% warn_invalid_record(Line, Record, State0) -> State %% Adds warning if the record is invalid. @@ -2274,13 +2298,6 @@ is_valid_call(Call) -> _ -> true end. -%% check_map_construction -%% Only #{ K => V }, i.e. assoc is a valid construction -is_valid_map_construction([{map_field_assoc,_,_,_}|Es]) -> - is_valid_map_construction(Es); -is_valid_map_construction([]) -> true; -is_valid_map_construction(_) -> false. - is_valid_map_key(K,St) -> case expr(K,[],St) of {[],_} -> true; @@ -2518,32 +2535,39 @@ type_def(Attr, Line, TypeName, ProtoType, Args, St0) -> CheckType = {type, -1, product, [ProtoType|Args]}, check_type(CheckType, St#lint{types=NewDefs}) end, - case (dict:is_key(TypePair, TypeDefs) orelse is_var_arity_type(TypeName)) of - true -> - case is_default_type(TypePair) of - true -> - case is_newly_introduced_builtin_type(TypePair) of - %% allow some types just for bootstrapping - true -> - Warn = {new_builtin_type, TypePair}, - St1 = add_warning(Line, Warn, St0), + case is_default_type(TypePair) of + true -> + case is_obsolete_builtin_type(TypePair) of + 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(Line, Warn, St0), StoreType(St1); - false -> - add_error(Line, {builtin_type, TypePair}, St0) - end; - false -> add_error(Line, {redefine_type, TypePair}, St0) - end; - false -> - St1 = case - Attr =:= opaque andalso - is_underspecified(ProtoType, Arity) - of - true -> - Warn = {underspecified_opaque, TypePair}, - add_warning(Line, Warn, St0); - false -> St0 - end, - StoreType(St1) + false -> + add_error(Line, {builtin_type, TypePair}, St0) + end + end; + false -> + case + dict:is_key(TypePair, TypeDefs) + orelse is_var_arity_type(TypeName) + of + true -> add_error(Line, {redefine_type, TypePair}, St0); + false -> + St1 = case + Attr =:= opaque andalso + is_underspecified(ProtoType, Arity) + of + true -> + Warn = {underspecified_opaque, TypePair}, + add_warning(Line, Warn, St0); + false -> St0 + end, + StoreType(St1) + end end. is_underspecified({type,_,term,[]}, 0) -> true; @@ -2637,10 +2661,11 @@ check_type({type, La, TypeName, Args}, SeenVars, St) -> St1 = case is_var_arity_type(TypeName) of true -> St; false -> - Obsolete = obsolete_type(TypePair), + Obsolete = (is_warn_enabled(deprecated_type, St) + andalso obsolete_builtin_type(TypePair)), IsObsolete = case Obsolete of - {deprecated, {M, _}, _} when M =/= Module -> + {deprecated, Repl, _} when element(1, Repl) =/= Module -> case dict:find(TypePair, Types) of {ok, _} -> false; error -> true @@ -2650,7 +2675,8 @@ check_type({type, La, TypeName, Args}, SeenVars, St) -> case IsObsolete of true -> {deprecated, Replacement, Rel} = Obsolete, - W = {deprecated_type, TypePair, Replacement, Rel}, + Tag = deprecated_builtin_type, + W = {Tag, TypePair, Replacement, Rel}, add_warning(La, W, St); false -> OldUsed = Usage#usage.used_types, @@ -2764,31 +2790,29 @@ is_default_type({timeout, 0}) -> true; is_default_type({var, 1}) -> true; is_default_type(_) -> false. -%% R13 -is_newly_introduced_builtin_type({arity, 0}) -> true; -is_newly_introduced_builtin_type({array, 0}) -> true; % opaque -is_newly_introduced_builtin_type({bitstring, 0}) -> true; -is_newly_introduced_builtin_type({dict, 0}) -> true; % opaque -is_newly_introduced_builtin_type({digraph, 0}) -> true; % opaque -is_newly_introduced_builtin_type({gb_set, 0}) -> true; % opaque -is_newly_introduced_builtin_type({gb_tree, 0}) -> true; % opaque -is_newly_introduced_builtin_type({iodata, 0}) -> true; -is_newly_introduced_builtin_type({queue, 0}) -> true; % opaque -is_newly_introduced_builtin_type({set, 0}) -> true; % opaque -%% R13B01 -is_newly_introduced_builtin_type({boolean, 0}) -> true; is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false. +is_obsolete_builtin_type(TypePair) -> + obsolete_builtin_type(TypePair) =/= no. + %% Obsolete in OTP 17.0. -obsolete_type({array, 0}) -> {deprecated, {array, array}, "OTP 18.0"}; -obsolete_type({dict, 0}) -> {deprecated, {dict, dict}, "OTP 18.0"}; -obsolete_type({digraph, 0}) -> {deprecated, {digraph, graph}, "OTP 18.0"}; -obsolete_type({gb_set, 0}) -> {deprecated, {gb_sets, set}, "OTP 18.0"}; -obsolete_type({gb_tree, 0}) -> {deprecated, {gb_trees, tree}, "OTP 18.0"}; -obsolete_type({queue, 0}) -> {deprecated, {queue, queue}, "OTP 18.0"}; -obsolete_type({set, 0}) -> {deprecated, {sets, set}, "OTP 18.0"}; -obsolete_type({tid, 0}) -> {deprecated, {ets, tid}, "OTP 18.0"}; -obsolete_type({Name, _}) when is_atom(Name) -> no. +obsolete_builtin_type({array, 0}) -> + {deprecated, {array, array, 1}, "OTP 18.0"}; +obsolete_builtin_type({dict, 0}) -> + {deprecated, {dict, dict, 2}, "OTP 18.0"}; +obsolete_builtin_type({digraph, 0}) -> + {deprecated, {digraph, graph}, "OTP 18.0"}; +obsolete_builtin_type({gb_set, 0}) -> + {deprecated, {gb_sets, set, 1}, "OTP 18.0"}; +obsolete_builtin_type({gb_tree, 0}) -> + {deprecated, {gb_trees, tree, 2}, "OTP 18.0"}; +obsolete_builtin_type({queue, 0}) -> + {deprecated, {queue, queue, 1}, "OTP 18.0"}; +obsolete_builtin_type({set, 0}) -> + {deprecated, {sets, set, 1}, "OTP 18.0"}; +obsolete_builtin_type({tid, 0}) -> + {deprecated, {ets, tid}, "OTP 18.0"}; +obsolete_builtin_type({Name, A}) when is_atom(Name), is_integer(A) -> no. %% spec_decl(Line, Fun, Types, State) -> State. diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl index 7281549ea7..63116fa16e 100644 --- a/lib/stdlib/src/gen.erl +++ b/lib/stdlib/src/gen.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -37,7 +37,9 @@ %%----------------------------------------------------------------- -type linkage() :: 'link' | 'nolink'. --type emgr_name() :: {'local', atom()} | {'global', term()} | {via, atom(), term()}. +-type emgr_name() :: {'local', atom()} + | {'global', term()} + | {'via', Module :: module(), Name :: term()}. -type start_ret() :: {'ok', pid()} | 'ignore' | {'error', term()}. diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl index 57b5072639..1f94d9e69d 100644 --- a/lib/stdlib/src/maps.erl +++ b/lib/stdlib/src/maps.erl @@ -45,7 +45,6 @@ -compile(no_native). -%% Shadowed by erl_bif_types: maps:get/3 -spec get(Key,Map) -> Value when Key :: term(), Map :: map(), @@ -54,7 +53,6 @@ get(_,_) -> erlang:nif_error(undef). -%% Shadowed by erl_bif_types: maps:find/3 -spec find(Key,Map) -> {ok, Value} | error when Key :: term(), Map :: map(), @@ -63,8 +61,8 @@ get(_,_) -> erlang:nif_error(undef). find(_,_) -> erlang:nif_error(undef). -%% Shadowed by erl_bif_types: maps:from_list/1 --spec from_list([{Key,Value}]) -> Map when +-spec from_list(List) -> Map when + List :: [{Key,Value}], Key :: term(), Value :: term(), Map :: map(). @@ -72,7 +70,6 @@ find(_,_) -> erlang:nif_error(undef). from_list(_) -> erlang:nif_error(undef). -%% Shadowed by erl_bif_types: maps:is_key/2 -spec is_key(Key,Map) -> boolean() when Key :: term(), Map :: map(). @@ -80,7 +77,6 @@ from_list(_) -> erlang:nif_error(undef). is_key(_,_) -> erlang:nif_error(undef). -%% Shadowed by erl_bif_types: maps:keys/1 -spec keys(Map) -> Keys when Map :: map(), Keys :: [Key], @@ -89,7 +85,6 @@ is_key(_,_) -> erlang:nif_error(undef). keys(_) -> erlang:nif_error(undef). -%% Shadowed by erl_bif_types: maps:merge/2 -spec merge(Map1,Map2) -> Map3 when Map1 :: map(), Map2 :: map(), @@ -99,14 +94,12 @@ merge(_,_) -> erlang:nif_error(undef). -%% Shadowed by erl_bif_types: maps:new/0 -spec new() -> Map when Map :: map(). new() -> erlang:nif_error(undef). -%% Shadowed by erl_bif_types: maps:put/3 -spec put(Key,Value,Map1) -> Map2 when Key :: term(), Value :: term(), @@ -116,7 +109,6 @@ new() -> erlang:nif_error(undef). put(_,_,_) -> erlang:nif_error(undef). -%% Shadowed by erl_bif_types: maps:put/3 -spec remove(Key,Map1) -> Map2 when Key :: term(), Map1 :: map(), @@ -125,7 +117,6 @@ put(_,_,_) -> erlang:nif_error(undef). remove(_,_) -> erlang:nif_error(undef). -%% Shadowed by erl_bif_types: maps:to_list/1 -spec to_list(Map) -> [{Key,Value}] when Map :: map(), Key :: term(), @@ -134,7 +125,6 @@ remove(_,_) -> erlang:nif_error(undef). to_list(_) -> erlang:nif_error(undef). -%% Shadowed by erl_bif_types: maps:update/3 -spec update(Key,Value,Map1) -> Map2 when Key :: term(), Value :: term(), @@ -144,7 +134,6 @@ to_list(_) -> erlang:nif_error(undef). update(_,_,_) -> erlang:nif_error(undef). -%% Shadowed by erl_bif_types: maps:values/1 -spec values(Map) -> Keys when Map :: map(), Keys :: [Key], diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl index 059d553b00..00a5da42ad 100644 --- a/lib/stdlib/test/dets_SUITE.erl +++ b/lib/stdlib/test/dets_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2013. All Rights Reserved. +%% Copyright Ericsson AB 1996-2014. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -52,7 +52,7 @@ simultaneous_open/1, insert_new/1, repair_continuation/1, otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1, otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1, - otp_8923/1, otp_9282/1, otp_11245/1]). + otp_8923/1, otp_9282/1, otp_11245/1, otp_11709/1]). -export([dets_dirty_loop/0]). @@ -109,7 +109,7 @@ all() -> many_clients, otp_4906, otp_5402, simultaneous_open, insert_new, repair_continuation, otp_5487, otp_6206, otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898, - otp_8899, otp_8903, otp_8923, otp_9282, otp_11245 + otp_8899, otp_8903, otp_8923, otp_9282, otp_11245, otp_11709 ]. groups() -> @@ -772,9 +772,9 @@ open_1(Config, V) -> crash(Fname, TypePos), {error, {invalid_type_code,Fname}} = dets:open_file(Fname), truncate(Fname, HeadSize - 10), - {error, {tooshort,Fname}} = dets:open_file(Fname), - {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]), - ok = dets:close(TabRef), + {error,{not_a_dets_file,Fname}} = dets:open_file(Fname), + {error,{not_a_dets_file,Fname}} = + dets:open_file(TabRef, [{file,Fname},{version,V}]), file:delete(Fname), {error,{file_error,{foo,bar},_}} = dets:is_dets_file({foo,bar}), @@ -967,10 +967,12 @@ fast_init_table(Config) -> {'EXIT', _} = (catch dets:init_table(TabRef, fun(foo) -> bar end, {format,bchunk})), dets:close(TabRef), + file:delete(Fname), {ok, _} = dets:open_file(TabRef, Args), {'EXIT', _} = (catch dets:init_table(TabRef, fun() -> foo end, {format,bchunk})), dets:close(TabRef), + file:delete(Fname), {ok, _} = dets:open_file(TabRef, Args), {'EXIT', {badarg, _}} = (catch dets:init_table(TabRef, nofun, {format,bchunk})), @@ -979,10 +981,12 @@ fast_init_table(Config) -> away = (catch dets:init_table(TabRef, fun(_) -> throw(away) end, {format,bchunk})), dets:close(TabRef), + file:delete(Fname), {ok, _} = dets:open_file(TabRef, Args), {error, {init_fun, fopp}} = dets:init_table(TabRef, fun(read) -> fopp end, {format,bchunk}), dets:close(TabRef), + file:delete(Fname), {ok, _} = dets:open_file(TabRef, Args), dets:safe_fixtable(TabRef, true), {error, {fixed_table, TabRef}} = @@ -1389,23 +1393,6 @@ repair(Config, V) -> {ok, TabRef} = dets:open_file(TabRef, [{file,Fname},{version,V}]), ok = ins(TabRef, 100), ok = dets:close(TabRef), - truncate(Fname, HeadSize - 10), - %% a new file is created ('tooshort') - {ok, TabRef} = dets:open_file(TabRef, - [{file,Fname},{version,V}, - {min_no_slots,1000}, - {max_no_slots,1000000}]), - case dets:info(TabRef, no_slots) of - undefined -> ok; - {Min1,Slot1,Max1} -> - true = Min1 =< Slot1, true = Slot1 =< Max1, - true = 1000 < Min1, true = 1000+256 > Min1, - true = 1000000 < Max1, true = (1 bsl 20)+256 > Max1 - end, - 0 = dets:info(TabRef, size), - no_keys_test(TabRef), - _ = histogram(TabRef, silent), - ok = dets:close(TabRef), file:delete(Fname), %% version bump (v8) @@ -3920,6 +3907,48 @@ otp_11245(Config) when is_list(Config) -> file:delete(File), ok. +otp_11709(doc) -> + ["OTP-11709. Bugfixes."]; +otp_11709(suite) -> + []; +otp_11709(Config) when is_list(Config) -> + Short = <<"foo">>, + Long = <<"a sufficiently long text">>, + + %% Bug: leaking file descriptor + P0 = pps(), + File = filename(otp_11709, Config), + ok = file:write_file(File, Long), + false = dets:is_dets_file(File), + check_pps(P0), + + %% Bug: deleting file + Args = [[{access, A}, {repair, R}] || + A <- [read, read_write], + R <- [true, false, force]], + Fun1 = fun(S, As) -> + P1 = pps(), + ok = file:write_file(File, S), + {error,{not_a_dets_file,File}} = dets:open_file(File, As), + {ok, S} = file:read_file(File), + check_pps(P1) + end, + Fun2 = fun(S) -> + _ = [Fun1(S, As) || As <- Args], + ok + end, + ok = Fun2(Long), % no change here + ok = Fun2(Short), % mimic the behaviour for longer files + + %% open_file/1 + ok = file:write_file(File, Long), + {error,{not_a_dets_file,File}} = dets:open_file(File), % no change + ok = file:write_file(File, Short), + {error,{not_a_dets_file,File}} = dets:open_file(File), % mimic + + _ = file:delete(File), + ok. + %% %% Parts common to several test cases %% diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 1614a2722f..5d189006a1 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -52,6 +52,7 @@ guard/1, otp_4886/1, otp_4988/1, otp_5091/1, otp_5276/1, otp_5338/1, otp_5362/1, otp_5371/1, otp_7227/1, otp_5494/1, otp_5644/1, otp_5878/1, otp_5917/1, otp_6585/1, otp_6885/1, otp_10436/1, otp_11254/1, + otp_11772/1, otp_11771/1, export_all/1, bif_clash/1, behaviour_basic/1, behaviour_multiple/1, @@ -61,7 +62,8 @@ on_load_successful/1, on_load_failing/1, too_many_arguments/1, basic_errors/1,bin_syntax_errors/1, - predef/1 + predef/1, + maps/1 ]). % Default timetrap timeout (set in init_per_testcase). @@ -85,10 +87,11 @@ all() -> unsized_binary_in_bin_gen_pattern, otp_4886, otp_4988, otp_5091, otp_5276, otp_5338, otp_5362, otp_5371, otp_7227, otp_5494, otp_5644, - otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, otp_11254,export_all, + otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, otp_11254, + otp_11772, otp_11771, export_all, bif_clash, behaviour_basic, behaviour_multiple, otp_7550, otp_8051, format_warn, {group, on_load}, - too_many_arguments, basic_errors, bin_syntax_errors, predef]. + too_many_arguments, basic_errors, bin_syntax_errors, predef, maps]. groups() -> [{unused_vars_warn, [], @@ -1494,7 +1497,15 @@ guard(Config) when is_list(Config) -> [], {errors,[{1,erl_lint,illegal_guard_expr}, {2,erl_lint,illegal_guard_expr}, - {3,erl_lint,illegal_guard_expr}],[]}} + {3,erl_lint,illegal_guard_expr}],[]}}, + {guard9, + <<"t(X, Y) when erlang:'andalso'(X, Y) -> ok; + t(X, Y) when erlang:'orelse'(X, Y) -> ok. + ">>, + [], + {errors,[{1,erl_lint,illegal_guard_expr}, + {2,erl_lint,illegal_guard_expr}], + []}} ], ?line [] = run(Config, Ts1), ok. @@ -2553,7 +2564,7 @@ otp_10436(Config) when is_list(Config) -> ok. otp_11254(doc) -> - "OTP-11254. Warnings for opaque types."; + "OTP-11254. M:F/A could crash the linter."; otp_11254(suite) -> []; otp_11254(Config) when is_list(Config) -> Ts = <<"-module(p2). @@ -2566,6 +2577,62 @@ otp_11254(Config) when is_list(Config) -> run_test2(Config, Ts, []), ok. +otp_11772(doc) -> + "OTP-11772. Reintroduce errors for redefined builtin types."; +otp_11772(suite) -> []; +otp_11772(Config) when is_list(Config) -> + Ts = <<" + -module(newly). + + -compile(export_all). + + %% Built-in: + -type node() :: node(). + -type mfa() :: tuple(). + -type gb_tree() :: mfa(). % Allowed since Erlang/OTP 17.0 + -type digraph() :: [_]. % Allowed since Erlang/OTP 17.0 + + -type t() :: mfa() | digraph() | gb_tree() | node(). + + -spec t() -> t(). + + t() -> + 1. + ">>, + {errors,[{7,erl_lint,{builtin_type,{node,0}}}, + {8,erl_lint,{builtin_type,{mfa,0}}}], + []} = run_test2(Config, Ts, []), + ok. + +otp_11771(doc) -> + "OTP-11771. Do not allow redefinition of the types arity(_) &c.."; +otp_11771(suite) -> []; +otp_11771(Config) when is_list(Config) -> + Ts = <<" + -module(newly). + + -compile(export_all). + + %% No longer allowed in 17.0: + -type arity() :: atom(). + -type bitstring() :: list(). + -type iodata() :: integer(). + -type boolean() :: iodata(). + + -type t() :: arity() | bitstring() | iodata() | boolean(). + + -spec t() -> t(). + + t() -> + 1. + ">>, + {errors,[{7,erl_lint,{builtin_type,{arity,0}}}, + {8,erl_lint,{builtin_type,{bitstring,0}}}, + {9,erl_lint,{builtin_type,{iodata,0}}}, + {10,erl_lint,{builtin_type,{boolean,0}}}], + []} = run_test2(Config, Ts, []), + ok. + export_all(doc) -> "OTP-7392. Warning for export_all."; export_all(Config) when is_list(Config) -> @@ -3243,20 +3310,88 @@ bin_syntax_errors(Config) -> ok. predef(doc) -> - "Predefined types: array(), digraph(), and so on"; + "OTP-10342: Predefined types: array(), digraph(), and so on"; predef(suite) -> []; predef(Config) when is_list(Config) -> W = get_compilation_warnings(Config, "predef", []), [] = W, W2 = get_compilation_warnings(Config, "predef2", []), - [{7,erl_lint,{deprecated_type,{array,0},{array,array},"OTP 18.0"}}, - {12,erl_lint,{deprecated_type,{dict,0},{dict,dict},"OTP 18.0"}}, - {17,erl_lint,{deprecated_type,{digraph,0},{digraph,graph},"OTP 18.0"}}, - {27,erl_lint,{deprecated_type,{gb_set,0},{gb_sets,set},"OTP 18.0"}}, - {32,erl_lint,{deprecated_type,{gb_tree,0},{gb_trees,tree},"OTP 18.0"}}, - {37,erl_lint,{deprecated_type,{queue,0},{queue,queue},"OTP 18.0"}}, - {42,erl_lint,{deprecated_type,{set,0},{sets,set},"OTP 18.0"}}, - {47,erl_lint,{deprecated_type,{tid,0},{ets,tid},"OTP 18.0"}}] = W2, + Tag = deprecated_builtin_type, + [{7,erl_lint,{Tag,{array,0},{array,array,1},"OTP 18.0"}}, + {12,erl_lint,{Tag,{dict,0},{dict,dict,2},"OTP 18.0"}}, + {17,erl_lint,{Tag,{digraph,0},{digraph,graph},"OTP 18.0"}}, + {27,erl_lint,{Tag,{gb_set,0},{gb_sets,set,1},"OTP 18.0"}}, + {32,erl_lint,{Tag,{gb_tree,0},{gb_trees,tree,2},"OTP 18.0"}}, + {37,erl_lint,{Tag,{queue,0},{queue,queue,1},"OTP 18.0"}}, + {42,erl_lint,{Tag,{set,0},{sets,set,1},"OTP 18.0"}}, + {47,erl_lint,{Tag,{tid,0},{ets,tid},"OTP 18.0"}}] = W2, + Ts = [{otp_10342_1, + <<"-compile(nowarn_deprecated_type). + + -spec t(dict()) -> non_neg_integer(). + + t(D) -> + erlang:phash2(D, 3000). + ">>, + {[nowarn_unused_function]}, + []}, + {otp_10342_2, + <<"-spec t(dict()) -> non_neg_integer(). + + t(D) -> + erlang:phash2(D, 3000). + ">>, + {[nowarn_unused_function]}, + {warnings,[{1,erl_lint, + {deprecated_builtin_type,{dict,0},{dict,dict,2}, + "OTP 18.0"}}]}}], + [] = run(Config, Ts), + ok. + +maps(Config) -> + %% TODO: test key patterns, not done because map patterns are going to be + %% changed a lot. + Ts = [{illegal_map_construction, + <<"t() -> + #{ a := b, + c => d, + e := f + }#{ a := b, + c => d, + e := f }; + t() when is_map(#{ a := b, + c => d + }#{ a := b, + c => d, + e := f }) -> + ok. + ">>, + [], + {errors,[{2,erl_lint,illegal_map_construction}, + {4,erl_lint,illegal_map_construction}, + {8,erl_lint,illegal_map_construction}], + []}}, + {illegal_pattern, + <<"t(#{ a := A, + c => d, + e := F, + g := 1 + 1, + h := _, + i := (_X = _Y), + j := (x ! y) }) -> + {A,F}. + ">>, + [], + {errors,[{2,erl_lint,illegal_pattern}, + {7,erl_lint,illegal_pattern}], + []}}, + {error_in_illegal_map_construction, + <<"t() -> #{ a := X }.">>, + [], + {errors,[{1,erl_lint,illegal_map_construction}, + {1,erl_lint,{unbound_var,'X'}}], + []}}], + [] = run(Config, Ts), ok. run(Config, Tests) -> diff --git a/lib/stdlib/test/erl_lint_SUITE_data/predef.erl b/lib/stdlib/test/erl_lint_SUITE_data/predef.erl index c2364fd1c2..ee9073aa67 100644 --- a/lib/stdlib/test/erl_lint_SUITE_data/predef.erl +++ b/lib/stdlib/test/erl_lint_SUITE_data/predef.erl @@ -5,8 +5,8 @@ -export_type([array/0, digraph/0, gb_set/0]). -%% Before R17B local re-definitions of pre-defined opaque types were -%% ignored but did not generate any warning. +%% Before Erlang/OTP 17.0 local re-definitions of pre-defined opaque +%% types were ignored but did not generate any warning. -opaque array() :: atom(). -opaque digraph() :: atom(). -opaque gb_set() :: atom(). |