From 975af37806ff0aa422c54444095d57399b5e0474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 23 Nov 2016 10:43:35 +0100 Subject: compiler: Warn for repeated identical map keys A map expression such as, #{'a' => 1, 'b' => 2, 'a' => 3} will produce a warning for the repeated key 'a'. --- lib/compiler/src/v3_core.erl | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index f40cf97f57..14cd41ae27 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -883,19 +883,33 @@ badmap_term(Map, #core{in_guard=false}) -> c_tuple([#c_literal{val=badmap},Map]). map_build_pairs(Map, Es0, Ann, St0) -> - {Es,Pre,St1} = map_build_pairs_1(Es0, St0), + {Es,Pre,_,St1} = map_build_pairs_1(Es0, cerl_sets:new(), St0), {ann_c_map(Ann, Map, Es),Pre,St1}. -map_build_pairs_1([{Op0,L,K0,V0}|Es], St0) -> +map_build_pairs_1([{Op0,L,K0,V0}|Es], Used0, St0) -> {K,Pre0,St1} = safe(K0, St0), {V,Pre1,St2} = safe(V0, St1), - {Pairs,Pre2,St3} = map_build_pairs_1(Es, St2), + {Pairs,Pre2,Used1,St3} = map_build_pairs_1(Es, Used0, St2), As = lineno_anno(L, St3), Op = map_op(Op0), + {Used2,St4} = maybe_warn_repeated_keys(K, L, Used1, St3), Pair = cerl:ann_c_map_pair(As, Op, K, V), - {[Pair|Pairs],Pre0++Pre1++Pre2,St3}; -map_build_pairs_1([], St) -> - {[],[],St}. + {[Pair|Pairs],Pre0++Pre1++Pre2,Used2,St4}; +map_build_pairs_1([], Used, St) -> + {[],[],Used,St}. + +maybe_warn_repeated_keys(Ck,Line,Used,St) -> + case cerl:is_literal(Ck) of + false -> {Used,St}; + true -> + K = cerl:concrete(Ck), + case cerl_sets:is_element(K,Used) of + true -> + {Used, add_warning(Line, {map_key_repeated,K}, St)}; + false -> + {cerl_sets:add_element(K,Used), St} + end + end. map_op(map_field_assoc) -> #c_literal{val=assoc}; map_op(map_field_exact) -> #c_literal{val=exact}. @@ -2603,7 +2617,11 @@ format_error(nomatch) -> format_error(bad_binary) -> "binary construction will fail because of a type mismatch"; format_error(badmap) -> - "map construction will fail because of a type mismatch". + "map construction will fail because of a type mismatch"; +format_error({map_key_repeated,Key}) when is_atom(Key) -> + io_lib:format("key '~w' will be overridden in expression", [Key]); +format_error({map_key_repeated,Key}) -> + io_lib:format("key ~p will be overridden in expression", [Key]). add_warning(Anno, Term, #core{ws=Ws,file=[{file,File}]}=St) -> case erl_anno:generated(Anno) of -- cgit v1.2.3 From fd6ba06462488da02a59ad8aa55819f0fa9ce83d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Wed, 23 Nov 2016 15:22:04 +0100 Subject: compiler: Test repeated map key warnings --- lib/compiler/test/warnings_SUITE.erl | 107 ++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/compiler/test/warnings_SUITE.erl b/lib/compiler/test/warnings_SUITE.erl index 35f7db82bd..c8e42af1ad 100644 --- a/lib/compiler/test/warnings_SUITE.erl +++ b/lib/compiler/test/warnings_SUITE.erl @@ -628,7 +628,112 @@ maps(Config) when is_list(Config) -> id(I) -> I. ">>, [], - []}], + []}, + {repeated_keys1, + <<" + foo1() -> + #{a=>1, + b=> 2, + a=>3}. + + bar1(M) -> + M#{a=>1, b=> 2, a:=3}. + + baz1(M) -> + M#{a=>1, b=> 2, a:=3}. + + foo2() -> + #{\"a\"=>1, \"b\"=> 2, \"a\"=>3}. + + bar2(M) -> + M#{\"a\"=>1, \"b\"=> 2, \"a\":=3}. + + baz2(M) -> + M#{\"a\"=>1, \"b\"=> 2, \"a\":=3}. + + foo3() -> + #{\"a\"=>1, + \"b\"=> 2, + \"a\"=>3}. + + bar3(M) -> + M#{\"a\"=>1, \"b\"=> 2, \"a\":=3}. + + baz3(M) -> + M#{<<\"a\">>=>1, <<\"b\">>=> 2, <<\"a\">>:=3}. + ">>, + [], + {warnings,[{3,v3_core,{map_key_repeated,a}}, + {8,v3_core,{map_key_repeated,a}}, + {11,v3_core,{map_key_repeated,a}}, + {14,v3_core,{map_key_repeated,"a"}}, + {17,v3_core,{map_key_repeated,"a"}}, + {20,v3_core,{map_key_repeated,"a"}}, + {23,v3_core,{map_key_repeated,"a"}}, + {28,v3_core,{map_key_repeated,"a"}}, + {31,v3_core,{map_key_repeated,<<"a">>}}]}}, + {repeated_keys2, + <<" + foo4(K) -> + #{\"a\"=>1, K => 1, \"b\"=> 2, \"a\"=>3, K=>2}. + + bar4(M,K) -> + M#{a=>1, K =>1, b=> 2, a:=3, K=>2}. + + baz4(M,K) -> + M#{<<\"a\">>=>1, + K => 1, <<\"b\">>=> 2, + <<\"a\">>:=3, K=>2}. + + foo5(K) -> + #{{\"a\",1}=>1, K => 1, \"b\"=> 2, {\"a\",1}=>3, K=>2}. + + bar5(M,K) -> + M#{{\"a\",<<\"b\">>}=>1, K =>1, + \"b\"=> 2, {\"a\",<<\"b\">>}:=3, K=>2}. + + baz5(M,K) -> + M#{{<<\"a\">>,1}=>1, K => 1, + <<\"b\">>=> 2, {<<\"a\">>,1}:=3,K=>2}. + + foo6(K) -> + #{#{\"a\"=>1}=>1, K => 1, \"b\"=> 2, #{\"a\"=>1}=>3, K=>2}. + + bar6(M,K) -> + M#{#{\"a\"=><<\"b\">>}=>1, K =>1, + \"b\"=> 2, #{\"a\"=><<\"b\">>}:=3, K=>2}. + + baz6(M,K) -> + M#{#{<<\"a\">>=>1}=>1, + K => 1, + <<\"b\">>=> 2, + #{<<\"a\">>=>1}:=3,K=>2}. + + foo7(K) -> + M1 = #{#{\"a\"=>1}=>1, K => 1, \"b\"=> 2}, + M1#{#{\"a\"=>1}=>3, K=>2}. + + bar7(M,K) -> + M1 = M#{#{\"a\"=><<\"b\">>}=>1, K =>1, \"b\"=> 2}, + M1#{#{\"a\"=><<\"b\">>}:=3, K=>2}. + + baz7(M,K) -> + M1 = M#{#{<<\"a\">>=>1}=>1, + K => 1, + <<\"b\">>=> 2}, + M1#{#{<<\"a\">>=>1}:=3,K=>2}. + ">>, + [], + {warnings,[{3,v3_core,{map_key_repeated,"a"}}, + {6,v3_core,{map_key_repeated,a}}, + {9,v3_core,{map_key_repeated,<<"a">>}}, + {14,v3_core,{map_key_repeated,{"a",1}}}, + {17,v3_core,{map_key_repeated,{"a",<<"b">>}}}, + {21,v3_core,{map_key_repeated,{<<"a">>,1}}}, + {25,v3_core,{map_key_repeated,#{"a" => 1}}}, + {28,v3_core,{map_key_repeated,#{"a" => <<"b">>}}}, + {32,v3_core,{map_key_repeated,#{<<"a">> => 1}}}]}} + ], run(Config, Ts), ok. -- cgit v1.2.3