diff options
Diffstat (limited to 'lib/stdlib/src/erl_lint.erl')
-rw-r--r-- | lib/stdlib/src/erl_lint.erl | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index f0d50df4c7..f630db6032 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]); @@ -1366,6 +1371,19 @@ pattern({cons,_Line,H,T}, Vt, Old, Bvt, St0) -> {vtmerge_pat(Hvt, Tvt),vtmerge_pat(Bvt1,Bvt2),St2}; 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; %%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) -> @@ -1753,6 +1771,14 @@ gexpr({cons,_Line,H,T}, Vt, St) -> gexpr_list([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); +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); gexpr({record_index,Line,Name,Field}, _Vt, St) -> check_record(Line, Name, St, fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end ); @@ -1970,6 +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) -> + {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) -> + 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); @@ -2229,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. @@ -2549,6 +2607,13 @@ check_type({type, L, range, [From, To]}, SeenVars, St) -> _ -> add_error(L, {type_syntax, range}, St) end, {SeenVars, St1}; +check_type({type, _L, map, any}, SeenVars, St) -> {SeenVars, St}; +check_type({type, _L, map, Pairs}, SeenVars, St) -> + lists:foldl(fun(Pair, {AccSeenVars, AccSt}) -> + check_type(Pair, AccSeenVars, AccSt) + end, {SeenVars, St}, Pairs); +check_type({type, _L, map_field_assoc, Dom, Range}, SeenVars, St) -> + check_type({type, -1, product, [Dom, Range]}, SeenVars, St); check_type({type, _L, tuple, any}, SeenVars, St) -> {SeenVars, St}; check_type({type, _L, any}, SeenVars, St) -> {SeenVars, St}; check_type({type, L, binary, [Base, Unit]}, SeenVars, St) -> @@ -2654,6 +2719,7 @@ 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; |