aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib
diff options
context:
space:
mode:
authorBjörn-Egil Dahlberg <[email protected]>2014-03-18 12:20:19 +0100
committerBjörn-Egil Dahlberg <[email protected]>2014-03-18 12:20:19 +0100
commita4d314a5298d9725fdd1874763e43b33a39252a0 (patch)
tree3beaa441407a6ec335e7bae501443e67a1c9e24e /lib/stdlib
parent8d66a2823c29cbe44cf80e4de0c58f2ed5c29bd4 (diff)
parent5638ca15d05e9b05d64ade0e03492c13d020439b (diff)
downloadotp-a4d314a5298d9725fdd1874763e43b33a39252a0.tar.gz
otp-a4d314a5298d9725fdd1874763e43b33a39252a0.tar.bz2
otp-a4d314a5298d9725fdd1874763e43b33a39252a0.zip
Merge branch 'egil/maps-literals'
* egil/maps-literals: compiler: Transform M#{} to is_map(M) dialyzer: Do not native compile modules with Maps code hipe: Properly identify map() type form terms stdlib: Test Map key linting stdlib: Accept records as Map keys stdlib: Accept Maps as Map keys stdlib: Move map type to proper definition stdlib: Properly lint map key expressions compiler: Change #c_map{var} to #c_map{arg} compiler: Constant fold Maps that are safe compiler: Validate Map src compiler: Support literal maps in cerl_clauses:match/2 compiler: Guard BIF is_map/1 is pure erts: Handle literals in is_map/1 compiler: Change Maps Core Format compiler: Create literal Maps in creation if possible
Diffstat (limited to 'lib/stdlib')
-rw-r--r--lib/stdlib/src/erl_lint.erl96
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl58
2 files changed, 131 insertions, 23 deletions
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 269e4b34cf..4c0261a1ad 100644
--- a/lib/stdlib/src/erl_lint.erl
+++ b/lib/stdlib/src/erl_lint.erl
@@ -225,6 +225,8 @@ format_error({too_many_arguments,Arity}) ->
"maximum allowed is ~w", [Arity,?MAX_ARGUMENTS]);
%% --- patterns and guards ---
format_error(illegal_pattern) -> "illegal pattern";
+format_error(illegal_map_key) ->
+ "illegal map key";
format_error({illegal_map_key_variable,K}) ->
io_lib:format("illegal use of variable ~w in map",[K]);
format_error(illegal_bin_pattern) ->
@@ -1385,19 +1387,20 @@ 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) ->
- 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);
+ 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 is_valid_map_key(KP, St0) of
+ true ->
+ {Pvt,Bvt1,St1} = pattern(VP, Vt, Old, Bvt, St0),
+ {vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt0, Bvt1), St1};
+ false ->
+ {Psvt,Bvt0,add_error(L, illegal_map_key, St0)};
+ {false,variable,Var} ->
+ {Psvt,Bvt0,add_error(L, {illegal_map_key_variable,Var}, 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) ->
@@ -2237,9 +2240,10 @@ check_assoc_fields([], 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,
+ true -> St;
+ false -> add_error(Line, illegal_map_key, St);
+ {false,variable,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};
@@ -2298,11 +2302,64 @@ is_valid_call(Call) ->
_ -> true
end.
+%% is_valid_map_key(K,St) -> true | false | {false, Var::atom()}
+%% check for value expression without variables
+
is_valid_map_key(K,St) ->
case expr(K,[],St) of
- {[],_} -> true;
+ {[],_} ->
+ is_valid_map_key_value(K);
{[Var|_],_} ->
- {false,element(1,Var)}
+ {false,variable,element(1,Var)}
+ end.
+
+is_valid_map_key_value(K) ->
+ case K of
+ {char,_,_} -> true;
+ {integer,_,_} -> true;
+ {float,_,_} -> true;
+ {string,_,_} -> true;
+ {nil,_} -> true;
+ {atom,_,_} -> true;
+ {cons,_,H,T} ->
+ is_valid_map_key_value(H) andalso
+ is_valid_map_key_value(T);
+ {tuple,_,Es} ->
+ foldl(fun(E,B) ->
+ B andalso is_valid_map_key_value(E)
+ end,true,Es);
+ {map,_,Arg,Ps} ->
+ % only check for value expressions to be valid
+ % invalid map expressions are later checked in
+ % core and kernel
+ is_valid_map_key_value(Arg) andalso foldl(fun
+ ({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc;
+ Tag =:= map_field_exact ->
+ B andalso is_valid_map_key_value(Ke)
+ andalso is_valid_map_key_value(Ve)
+ end,true,Ps);
+ {map,_,Ps} ->
+ foldl(fun
+ ({Tag,_,Ke,Ve},B) when Tag =:= map_field_assoc;
+ Tag =:= map_field_exact ->
+ B andalso is_valid_map_key_value(Ke)
+ andalso is_valid_map_key_value(Ve)
+ end, true, Ps);
+ {record,_,_,Fs} ->
+ foldl(fun
+ ({record_field,_,Ke,Ve},B) ->
+ B andalso is_valid_map_key_value(Ke)
+ andalso is_valid_map_key_value(Ve)
+ end,true,Fs);
+ {bin,_,Es} ->
+ % only check for value expressions to be valid
+ % invalid binary expressions are later checked in
+ % core and kernel
+ foldl(fun
+ ({bin_element,_,E,_,_},B) ->
+ B andalso is_valid_map_key_value(E)
+ end,true,Es);
+ _ -> false
end.
%% record_def(Line, RecordName, [RecField], State) -> State.
@@ -2727,6 +2784,7 @@ check_record_types([], _Name, _DefFields, SeenVars, St, _SeenFields) ->
{SeenVars, St}.
is_var_arity_type(tuple) -> true;
+is_var_arity_type(map) -> true;
is_var_arity_type(product) -> true;
is_var_arity_type(union) -> true;
is_var_arity_type(record) -> true;
@@ -2759,7 +2817,6 @@ is_default_type({iodata, 0}) -> true;
is_default_type({iolist, 0}) -> true;
is_default_type({list, 0}) -> true;
is_default_type({list, 1}) -> true;
-is_default_type({map, 0}) -> true;
is_default_type({maybe_improper_list, 0}) -> true;
is_default_type({maybe_improper_list, 2}) -> true;
is_default_type({mfa, 0}) -> true;
@@ -2790,6 +2847,7 @@ is_default_type({timeout, 0}) -> true;
is_default_type({var, 1}) -> true;
is_default_type(_) -> false.
+%% OTP 17.0
is_newly_introduced_builtin_type({Name, _}) when is_atom(Name) -> false.
is_obsolete_builtin_type(TypePair) ->
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 5d189006a1..673a3cf159 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -63,7 +63,7 @@
too_many_arguments/1,
basic_errors/1,bin_syntax_errors/1,
predef/1,
- maps/1
+ maps/1,maps_type/1
]).
% Default timetrap timeout (set in init_per_testcase).
@@ -91,7 +91,8 @@ all() ->
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, maps].
+ too_many_arguments, basic_errors, bin_syntax_errors, predef,
+ maps,maps_type].
groups() ->
[{unused_vars_warn, [],
@@ -3388,12 +3389,61 @@ maps(Config) ->
{error_in_illegal_map_construction,
<<"t() -> #{ a := X }.">>,
[],
- {errors,[{1,erl_lint,illegal_map_construction},
+ {errors,[{1,erl_lint,illegal_map_construction},
{1,erl_lint,{unbound_var,'X'}}],
- []}}],
+ []}},
+ {errors_in_map_keys,
+ <<"t(V) -> #{ a => 1,
+ #{a=>V} => 2,
+ #{ \"hi\" => wazzup, hi => ho } => yep,
+ [try a catch _:_ -> b end] => nope,
+ ok => 1.0,
+ [3+3] => nope,
+ 1.0 => yep,
+ {3.0+3} => nope,
+ {yep} => yep,
+ [case a of a -> a end] => nope
+ }.
+ ">>,
+ [],
+ {errors,[{2,erl_lint,{illegal_map_key_variable,'V'}},
+ {4,erl_lint,illegal_map_key},
+ {6,erl_lint,illegal_map_key},
+ {8,erl_lint,illegal_map_key},
+ {10,erl_lint,illegal_map_key}],[]}}],
[] = run(Config, Ts),
ok.
+maps_type(Config) when is_list(Config) ->
+ Ts = [
+ {maps_type1,
+ <<"
+ -type m() :: #{a => integer()}.
+ -spec t1(#{k=>term()}) -> {term(), map()}.
+
+ t1(#{k:=V}=M) -> {V,M}.
+
+ -spec t2(m()) -> integer().
+
+ t2(#{a:=V}) -> V.
+ ">>,
+ [],
+ []},
+ {maps_type2,
+ <<"
+ %% Built-in var arity map type:
+ -type map() :: tuple().
+ -type a() :: map().
+
+ -spec t(a()) -> a().
+ t(M) -> M.
+ ">>,
+ [],
+ {errors,[{3,erl_lint,{redefine_type,{map,0}}}],[]}}],
+ [] = run(Config, Ts),
+ ok.
+
+
run(Config, Tests) ->
F = fun({N,P,Ws,E}, BadL) ->
case catch run_test(Config, P, Ws) of