aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib/test
diff options
context:
space:
mode:
authorAnthony Ramine <[email protected]>2013-06-06 01:34:25 +0200
committerFredrik Gustafsson <[email protected]>2013-08-27 16:15:21 +0200
commitf846bf70b0c97ce66f29b0ff88a50316924bf34e (patch)
treec2a7efa35614f7eea475665c04505830f4dba839 /lib/stdlib/test
parente2c0f6dd1fc9b6bd506a481644b9c63f9d575aa1 (diff)
downloadotp-f846bf70b0c97ce66f29b0ff88a50316924bf34e.tar.gz
otp-f846bf70b0c97ce66f29b0ff88a50316924bf34e.tar.bz2
otp-f846bf70b0c97ce66f29b0ff88a50316924bf34e.zip
Fix variable usage tracking in erl_lint
When analyzing complex expressions (i.e. comprehensions, cases, tries, ifs and receives), erl_lint does not forget about old unused variables when returning the updated variable table. This causes a bug where old unused variables are not recorded as such: t(X, Y) -> #r{a=[ K || K <- Y ],b=[ K || K <- Y ]}. As erl_lint uses vtmerge_pat/2 to merge the results of the analysis of the two list comprehensions, X is marked as used and the warning is not emitted. The function vtmerge_pat/2 is used instead of the similar vtmerge/2 which does not mark multiple occurrences of a variable as usage to handle cases like the following one: t(X, Y) -> #r{a=A=X,b=A=Y}. Other simpler expressions like conses, tuples and external fun references do not correctly follow this behaviour, e.g. A is not marked as used in the following code: t(X, Y) -> {A=X,A=Y}. This commit fixes both issues and makes erl_lint not return old unused variables in updated tables and makes all compound expressions use vtmerge_pat/2. Reported-by: Anders Ramsell
Diffstat (limited to 'lib/stdlib/test')
-rw-r--r--lib/stdlib/test/erl_lint_SUITE.erl27
1 files changed, 25 insertions, 2 deletions
diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl
index 4dc7a44064..0bcf9b3e01 100644
--- a/lib/stdlib/test/erl_lint_SUITE.erl
+++ b/lib/stdlib/test/erl_lint_SUITE.erl
@@ -151,7 +151,16 @@ unused_vars_warn_basic(Config) when is_list(Config) ->
{22,erl_lint,{unused_var,'N'}},
{23,erl_lint,{shadowed_var,'N','fun'}},
{28,erl_lint,{unused_var,'B'}},
- {29,erl_lint,{unused_var,'B'}}]}}],
+ {29,erl_lint,{unused_var,'B'}}]}},
+ {basic2,
+ <<"-record(r, {x,y}).
+ f({X,Y}) -> {Z=X,Z=Y};
+ f([H|T]) -> [Z=H|Z=T];
+ f(#r{x=X,y=Y}) -> #r{x=A=X,y=A=Y}.
+ g({M, F}) -> (Z=M):(Z=F)();
+ g({M, F, Arg}) -> (Z=M):F(Z=Arg).
+ h(X, Y) -> (Z=X) + (Z=Y).">>,
+ [warn_unused_vars], []}],
?line [] = run(Config, Ts),
ok.
@@ -537,7 +546,21 @@ unused_vars_warn_rec(Config) when is_list(Config) ->
end.
">>,
[warn_unused_vars],
- {warnings,[{22,erl_lint,{unused_var,'Same'}}]}}],
+ {warnings,[{22,erl_lint,{unused_var,'Same'}}]}},
+ {rec2,
+ <<"-record(r, {a,b}).
+ f(X, Y) -> #r{a=[K || K <- Y], b=[K || K <- Y]}.
+ g(X, Y) -> #r{a=lists:map(fun (K) -> K end, Y),
+ b=lists:map(fun (K) -> K end, Y)}.
+ h(X, Y) -> #r{a=case Y of _ when is_list(Y) -> Y end,
+ b=case Y of _ when is_list(Y) -> Y end}.
+ i(X, Y) -> #r{a=if is_list(Y) -> Y end, b=if is_list(Y) -> Y end}.
+ ">>,
+ [warn_unused_vars],
+ {warnings,[{2,erl_lint,{unused_var,'X'}},
+ {3,erl_lint,{unused_var,'X'}},
+ {5,erl_lint,{unused_var,'X'}},
+ {7,erl_lint,{unused_var,'X'}}]}}],
?line [] = run(Config, Ts),
ok.