Skip to content

Commit 1432a18

Browse files
committed
Always warn about exported variables from constructs without clauses
There is never a good reason for placing variable bindings inside the arguments of an expression.
1 parent 5ade377 commit 1432a18

File tree

1 file changed

+71
-26
lines changed

1 file changed

+71
-26
lines changed

lib/stdlib/src/erl_lint.erl

Lines changed: 71 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,13 @@ format_error_1({unbound_var,V,GuessV}) ->
451451
format_error_1({unsafe_var,V,{What,Where}}) ->
452452
{~"variable ~w unsafe in ~w ~s",
453453
[V,What,format_where(Where)]};
454+
format_error_1({exported_subexpr_var,V,{What,Where}}) ->
455+
{~"""
456+
variable ~w exported from ~w ~s.
457+
Exporting bindings from subexpressions is deprecated and
458+
may be removed in a future version of Erlang/OTP.
459+
You should move the binding of ~w before the ~w.
460+
""", [V,What,format_where(Where),V,What]};
454461
format_error_1({exported_var,V,{What,Where}}) ->
455462
{~"variable ~w exported from ~w ~s",
456463
[V,What,format_where(Where)]};
@@ -2699,21 +2706,27 @@ expr({atom,Anno,A}, _Vt, St) ->
26992706
{[],keyword_warning(Anno, A, St)};
27002707
expr({string,_Anno,_S}, _Vt, St) -> {[],St};
27012708
expr({nil,_Anno}, _Vt, St) -> {[],St};
2702-
expr({cons,_Anno,H,T}, Vt, St) ->
2703-
expr_list([H,T], Vt, St);
2709+
expr({cons,Anno,H,T}, Vt, St) ->
2710+
vtupd_export_expr_list({list, Anno}, [H, T], Vt, St);
27042711
expr({lc,_Anno,E,Qs}, Vt, St) ->
27052712
handle_comprehension(E, Qs, Vt, St);
27062713
expr({bc,_Anno,E,Qs}, Vt, St) ->
27072714
handle_comprehension(E, Qs, Vt, St);
27082715
expr({mc,_Anno,E,Qs}, Vt, St) ->
27092716
handle_comprehension(E, Qs, Vt, St);
2710-
expr({tuple,_Anno,Es}, Vt, St) ->
2711-
expr_list(Es, Vt, St);
2712-
expr({map,_Anno,Es}, Vt, St) ->
2713-
map_fields(Es, Vt, check_assoc_fields(Es, St), fun expr_list/3);
2717+
expr({tuple,Anno,Es}, Vt, St) ->
2718+
vtupd_export_expr_list({tuple, Anno}, Es, Vt, St);
2719+
expr({map,Anno,Es}, Vt, St) ->
2720+
map_fields(Es, Vt, check_assoc_fields(Es, St),
2721+
fun(Es0, Vt0, St0) ->
2722+
vtupd_export_expr_list({map, Anno}, Es0, Vt0, St0)
2723+
end);
27142724
expr({map,Anno,Src,Es}, Vt, St) ->
2715-
{Svt,St1} = expr(Src, Vt, St),
2716-
{Fvt,St2} = map_fields(Es, Vt, St1, fun expr_list/3),
2725+
{Svt,St1} = vtupd_export_expr_list({map, Anno}, [Src], Vt, St),
2726+
{Fvt,St2} = map_fields(Es, Vt, St1,
2727+
fun(Es0, Vt0, St0) ->
2728+
vtupd_export_expr_list({map, Anno}, Es0, Vt0, St0)
2729+
end),
27172730
{vtupdate(Svt, Fvt), warn_if_literal_update(Anno, Src, St2)};
27182731
expr({record_index,Anno,Name,Field}, _Vt, St) ->
27192732
check_record(Anno, Name, St,
@@ -2740,11 +2753,13 @@ expr({record,Anno,Rec,Name,Upds}, Vt, St0) ->
27402753
no -> {vtmerge(Rvt, Usvt), warn_if_literal_update(Anno, Rec, St2)};
27412754
WildAnno -> {[],add_error(WildAnno, {wildcard_in_update,Name}, St2)}
27422755
end;
2743-
expr({bin,_Anno,Fs}, Vt, St) ->
2744-
expr_bin(Fs, Vt, St, fun expr/3);
2745-
expr({block,_Anno,Es}, Vt, St) ->
2756+
expr({bin,Anno,Fs}, Vt, St) ->
2757+
{Vt1, St1} = expr_bin(Fs, Vt, St, fun expr/3),
2758+
{vtupd_export({binary, Anno}, Vt1, Vt), St1};
2759+
expr({block,Anno,Es}, Vt, St) ->
27462760
%% Unfold block into a sequence.
2747-
exprs(Es, Vt, St);
2761+
{Vt1, St1} = exprs(Es, Vt, St),
2762+
{vtupd_export({'begin', Anno}, Vt1, Vt), St1};
27482763
expr({'if',Anno,Cs}, Vt, St) ->
27492764
icrt_clauses(Cs, {'if',Anno}, Vt, St);
27502765
expr({'case',Anno,E,Cs}, Vt, St0) ->
@@ -2816,7 +2831,7 @@ expr({call,Anno,{remote,_Ar,{atom,_Am,M},{atom,Af,F}},As}, Vt, St0) ->
28162831
St2 = check_remote_function(Anno, M, F, As, St1),
28172832
St3 = check_module_name(M, Anno, St2),
28182833
St4 = check_remote_self_call(Anno, M, F, length(As), St3),
2819-
expr_list(As, Vt, St4);
2834+
vtupd_export_expr_list({call, Anno}, As, Vt, St4);
28202835
expr({call,Anno,{remote,_Ar,M,F},As}, Vt, St0) ->
28212836
St1 = keyword_warning(Anno, M, St0),
28222837
St2 = keyword_warning(Anno, F, St1),
@@ -2826,14 +2841,14 @@ expr({call,Anno,{remote,_Ar,M,F},As}, Vt, St0) ->
28262841
_ ->
28272842
St2
28282843
end,
2829-
expr_list([M,F|As], Vt, St3);
2844+
vtupd_export_expr_list({call, Anno}, [M, F | As], Vt, St3);
28302845
expr({call,Anno,{atom,Aa,F},As}, Vt, St0) ->
28312846
St1 = keyword_warning(Aa, F, St0),
2832-
{Asvt,St2} = expr_list(As, Vt, St1),
2847+
{Asvt,St2} = vtupd_export_expr_list({call, Anno}, As, Vt, St1),
28332848
{Asvt, check_call(Anno, F, As, Aa, St2)};
28342849
expr({call,Anno,F,As}, Vt, St0) ->
28352850
St = warn_invalid_call(Anno,F,St0),
2836-
expr_list([F|As], Vt, St); %They see the same variables
2851+
vtupd_export_expr_list({call, Anno}, [F | As], Vt, St); %They see the same variables
28372852
expr({'try',Anno,Es,Scs,Ccs,As}, Vt, St0) ->
28382853
%% The only exports we allow are from the try expressions to the
28392854
%% success clauses.
@@ -2880,19 +2895,19 @@ expr({'maybe',MaybeAnno,Es,{'else',ElseAnno,Cs}}, Vt, St) ->
28802895
Cvt2 = vtmerge(Cvt0, Cvt1),
28812896
{vtmerge(Evt2, Cvt2),St2};
28822897
%% No comparison or boolean operators yet.
2883-
expr({op,_Anno,_Op,A}, Vt, St) ->
2884-
expr(A, Vt, St);
2898+
expr({op,Anno,Op,A}, Vt, St) ->
2899+
vtupd_export_expr_list({Op, Anno}, [A], Vt, St);
28852900
expr({op,Anno,Op,L,R}, Vt, St0) when Op =:= 'orelse'; Op =:= 'andalso' ->
2886-
{Evt1,St1} = expr(L, Vt, St0),
2901+
{Evt1, St1} = vtupd_export_expr_list({Op, Anno}, [L], Vt, St0),
28872902
Vt1 = vtupdate(Evt1, Vt),
28882903
{Evt2,St2} = expr(R, Vt1, St1),
28892904
Evt3 = vtupd_unsafe({Op, Anno}, Evt2, Vt1),
28902905
{vtmerge(Evt1, Evt3),St2};
2891-
expr({op,_Anno,EqOp,L,R}, Vt, St0) when EqOp =:= '=:='; EqOp =:= '=/=' ->
2906+
expr({op,Anno,EqOp,L,R}, Vt, St0) when EqOp =:= '=:='; EqOp =:= '=/=' ->
28922907
St = expr_check_match_zero(R, expr_check_match_zero(L, St0)),
2893-
expr_list([L,R], Vt, St); %They see the same variables
2894-
expr({op,_Anno,_Op,L,R}, Vt, St) ->
2895-
expr_list([L,R], Vt, St); %They see the same variables
2908+
vtupd_export_expr_list({EqOp, Anno}, [L, R], Vt, St); %They see the same variables
2909+
expr({op,Anno,Op,L,R}, Vt, St) ->
2910+
vtupd_export_expr_list({Op, Anno}, [L, R], Vt, St); %They see the same variables
28962911
%% The following are not allowed to occur anywhere!
28972912
expr({remote,_Anno,M,_F}, _Vt, St) ->
28982913
{[],add_error(erl_parse:first_anno(M), illegal_expr, St)};
@@ -2973,6 +2988,12 @@ expr_list(Es, Vt, St0) ->
29732988
vtmerge_pat(Evt, Esvt, St2)
29742989
end, {[], St0}, Es).
29752990

2991+
%% as expr_list but mark new vars as exported
2992+
2993+
vtupd_export_expr_list(Where, Es, Vt, St) ->
2994+
{Evt, St1} = expr_list(Es, Vt, St),
2995+
{vtupd_export(Where, Evt, Vt), St1}.
2996+
29762997
record_expr(Anno, Rec, Vt, St0) ->
29772998
St1 = warn_invalid_record(Anno, Rec, St0),
29782999
expr(Rec, Vt, St1).
@@ -4380,12 +4401,18 @@ do_expr_var(V, Anno, Vt, St) ->
43804401
{[{V,{bound,used,As}}],
43814402
add_error(Anno, {unsafe_var,V,In}, St)};
43824403
{ok,{{export,From},_Usage,As}} ->
4383-
case is_warn_enabled(export_vars, St) of
4404+
case exported_subexpr_var(From) of
43844405
true ->
43854406
{[{V,{bound,used,As}}],
4386-
add_warning(Anno, {exported_var,V,From}, St)};
4407+
add_warning(Anno, {exported_subexpr_var,V,From}, St)};
43874408
false ->
4388-
{[{V,{{export,From},used,As}}],St}
4409+
case is_warn_enabled(export_vars, St) of
4410+
true ->
4411+
{[{V,{bound,used,As}}],
4412+
add_warning(Anno, {exported_var,V,From}, St)};
4413+
false ->
4414+
{[{V,{{export,From},used,As}}],St}
4415+
end
43894416
end;
43904417
{ok,{stacktrace,_Usage,As}} ->
43914418
{[{V,{bound,used,As}}],
@@ -4408,6 +4435,14 @@ exported_var(Anno, V, From, St) ->
44084435
false -> St
44094436
end.
44104437

4438+
%% warn about exporting from non-block subexpressions
4439+
exported_subexpr_var({'if',_}) -> false;
4440+
exported_subexpr_var({'case',_}) -> false;
4441+
exported_subexpr_var({'receive',_}) -> false;
4442+
exported_subexpr_var({'try',_}) -> false;
4443+
exported_subexpr_var({'begin',_}) -> false;
4444+
exported_subexpr_var(_) -> true.
4445+
44114446
shadow_vars(Vt, Vt0, In, St0) ->
44124447
case is_warn_enabled(shadow_vars, St0) of
44134448
true ->
@@ -4473,6 +4508,16 @@ vtunsafe({Tag,Anno}, Uvt, Vt) ->
44734508
vtupd_unsafe(Where, NewVt, OldVt) ->
44744509
vtupdate(vtunsafe(Where, NewVt, OldVt), NewVt).
44754510

4511+
%% vtexport(From, UpdVarTable, VarTable) -> ExpVarTable.
4512+
%% Return all new variables in UpdVarTable as exported.
4513+
4514+
vtexport({Tag, FileLine}, Uvt, Vt) ->
4515+
Line = erl_anno:location(FileLine),
4516+
[{V, {{export, {Tag, Line}}, U, Ls}} || {V, {_, U, Ls}} <- vtnew(Uvt, Vt)].
4517+
4518+
vtupd_export(Where, NewVt, OldVt) ->
4519+
vtupdate(vtexport(Where, NewVt, OldVt), NewVt).
4520+
44764521
%% vtmerge(VarTable, VarTable) -> VarTable.
44774522
%% Merge two variables tables generating a new vartable. Give priority to
44784523
%% errors then warnings.

0 commit comments

Comments
 (0)