aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjörn-Egil Dahlberg <[email protected]>2013-10-09 16:37:58 +0200
committerBjörn-Egil Dahlberg <[email protected]>2014-01-28 15:56:30 +0100
commitf00675d3c682f53824e23f1599cbb09ff2d57daa (patch)
treed86b3f4610cf2712e064920cac56098f41b8a3d7
parente9a3d0b6533b72cba3d4e7ea129f835e80bd2647 (diff)
downloadotp-f00675d3c682f53824e23f1599cbb09ff2d57daa.tar.gz
otp-f00675d3c682f53824e23f1599cbb09ff2d57daa.tar.bz2
otp-f00675d3c682f53824e23f1599cbb09ff2d57daa.zip
stdlib: Deny variables as keys and disallow ':=' in map construction
In the current iteration of Maps we should deny *any* variables in Map keys.
-rw-r--r--erts/emulator/test/map_SUITE.erl2
-rw-r--r--lib/stdlib/src/erl_lint.erl57
-rw-r--r--lib/stdlib/src/erl_parse.yrl2
3 files changed, 47 insertions, 14 deletions
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index 1f4476defa..327e9b8060 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -327,7 +327,7 @@ guard_receive_loop() ->
t_list_comprehension(Config) when is_list(Config) ->
- [#{k:=1},#{k:=2},#{k:=3}] = [#{k:=I} || I <- [1,2,3]],
+ [#{k:=1},#{k:=2},#{k:=3}] = [#{k=>I} || I <- [1,2,3]],
ok.
t_guard_fun(Config) when is_list(Config) ->
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl
index 86ebe82acc..a41bcb91cc 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_variable,K}) ->
+ io_lib:format("illegal use of variable ~w in map",[K]);
format_error(illegal_bin_pattern) ->
"binary patterns cannot be matched in parallel using '='";
format_error(illegal_expr) -> "illegal expression";
@@ -232,6 +234,9 @@ format_error({illegal_guard_local_call, {F,A}}) ->
io_lib:format("call to local/imported function ~w/~w is illegal in guard",
[F,A]);
format_error(illegal_guard_expr) -> "illegal guard expression";
+%% --- maps ---
+format_error(illegal_map_construction) ->
+ "only association operators '=>' are allowed in map construction";
%% --- records ---
format_error({undefined_record,T}) ->
io_lib:format("record ~w undefined", [T]);
@@ -1368,11 +1373,17 @@ 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,_,_}=Pat, _, _, _, St) ->
- {[],[],add_error(element(2, Pat), illegal_pattern, St)};
-pattern({map_field_exact,_Line,_,P}, Vt, Old, Bvt0, St0) ->
- {Pvt,Bvt,St1} = pattern(P, Vt, Old, Bvt0, St0),
- {Pvt,Bvt,St1};
+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;
%%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) ->
@@ -1985,14 +1996,24 @@ 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) ->
- 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,Src,Es}, Vt, St) ->
expr_list([Src|Es], Vt, St);
-expr({map_field_assoc,_Line,K,V}, Vt, St) ->
- expr_list([K,V], Vt, St);
-expr({map_field_exact,_Line,K,V}, Vt, St) ->
- expr_list([K,V], 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;
expr({record_index,Line,Name,Field}, _Vt, St) ->
check_record(Line, Name, St,
fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end);
@@ -2252,6 +2273,20 @@ 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;
+ {[Var|_],_} ->
+ {false,element(1,Var)}
+ end.
+
%% record_def(Line, RecordName, [RecField], State) -> State.
%% Add a record definition if it does not already exist. Normalise
%% so that all fields have explicit initial value.
diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl
index e9a813e098..c04a1aeb89 100644
--- a/lib/stdlib/src/erl_parse.yrl
+++ b/lib/stdlib/src/erl_parse.yrl
@@ -335,8 +335,6 @@ map_expr -> expr_max '#' map_tuple :
{map, ?line('$2'),'$1','$3'}.
map_expr -> map_expr '#' map_tuple :
{map, ?line('$2'),'$1','$3'}.
-map_expr -> map_expr '.' atom :
- {map_field, ?line('$2'),'$1','$3'}.
map_tuple -> '{' '}' : [].
map_tuple -> '{' map_fields '}' : '$2'.