summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBjörn Gustavsson <bjorn@erlang.org>2021-02-26 14:32:01 +0100
committerGitHub <noreply@github.com>2021-02-26 14:32:01 +0100
commitf04d6b3fe1a01bc21bc36760bfb747dc7bba703b (patch)
tree58c65564da76262a6713c5356c948f7822a21de9 /lib
parent1f365da849e87ba2cf629da4fe700ca3462b161f (diff)
parent2adb932784784d00ff68f1b8aa27ccc53dfddeb9 (diff)
downloaderlang-f04d6b3fe1a01bc21bc36760bfb747dc7bba703b.tar.gz
Merge pull request #4547 from bjorng/bjorn/tools/cover-fix-lc/OTP-17221
cover: Ensure that LC filters are compiled in guard context
Diffstat (limited to 'lib')
-rw-r--r--lib/tools/src/cover.erl32
-rw-r--r--lib/tools/test/cover_SUITE.erl25
2 files changed, 56 insertions, 1 deletions
diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl
index dd2366d7fb..9964d8ce19 100644
--- a/lib/tools/src/cover.erl
+++ b/lib/tools/src/cover.erl
@@ -1851,8 +1851,17 @@ expand(Expr) ->
Expr1.
expand({clause,Anno,Pattern,Guards,Body}, Vs, N) ->
+ %% We must not expand andalso/orelse in guards.
{ExpandedBody,N2} = expand(Body, Vs, N),
{{clause,Anno,Pattern,Guards,ExpandedBody},N2};
+expand({lc,Anno,Expr,Qs}, Vs, N) ->
+ {ExpandedExpr,N2} = expand(Expr, Vs, N),
+ {ExpandedQs,N3} = expand_qualifiers(Qs, Vs, N2),
+ {{lc,Anno,ExpandedExpr,ExpandedQs},N3};
+expand({bc,Anno,Expr,Qs}, Vs, N) ->
+ {ExpandedExpr,N2} = expand(Expr, Vs, N),
+ {ExpandedQs,N3} = expand_qualifiers(Qs, Vs, N2),
+ {{bc,Anno,ExpandedExpr,ExpandedQs},N3};
expand({op,_Anno,'andalso',ExprL,ExprR}, Vs, N) ->
{ExpandedExprL,N2} = expand(ExprL, Vs, N),
{ExpandedExprR,N3} = expand(ExprR, Vs, N2),
@@ -1881,6 +1890,29 @@ expand([E|Es], Vs, N) ->
expand(T, _Vs, N) ->
{T,N}.
+expand_qualifiers([Q|Qs], Vs, N) ->
+ {Q2,N2} = case erl_lint:is_guard_test(Q) of
+ true ->
+ %% This qualifier is a guard test and will be
+ %% compiled as such. Don't expand andalso/orelse
+ %% because that would turn it into a body
+ %% expression that may raise an exception. Here
+ %% is an example of a filter where the error
+ %% behaviour would change:
+ %%
+ %% V == a orelse element(1, V) == a
+ %%
+ {Q,N};
+ false ->
+ %% A generator or a filter that is not a guard
+ %% test.
+ expand(Q, Vs, N)
+ end,
+ {Qs2,N3} = expand_qualifiers(Qs, Vs, N2),
+ {[Q2|Qs2],N3};
+expand_qualifiers([], _Vs, N) ->
+ {[],N}.
+
vars(A, {var,_,V}) when V =/= '_' ->
[V|A];
vars(A, T) when is_tuple(T) ->
diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl
index 771f527e43..b9cd028546 100644
--- a/lib/tools/test/cover_SUITE.erl
+++ b/lib/tools/test/cover_SUITE.erl
@@ -32,7 +32,7 @@ all() ->
otp_8340,otp_8188,compile_beam_opts,eep37,
analyse_no_beam, line_0, compile_beam_no_file,
compile_beam_missing_backend,
- otp_13277, otp_13289],
+ otp_13277, otp_13289, guard_in_lc],
StartStop = [start, compile, analyse, misc, stop,
distribution, reconnect, die_and_reconnect,
dont_reconnect_after_stop, stop_node_after_disconnect,
@@ -1784,6 +1784,29 @@ otp_13289(Config) ->
ok = file:delete(File),
ok.
+guard_in_lc(Config) ->
+ Test = <<"-module(t).
+ -export([lc/1]).
+
+ lc(L) ->
+ [V || V <- L, V == a orelse element(1, V) == a].
+ ">>,
+
+ %% The filter in the list comprehension must be compiled as a
+ %% guard expression. Therefore, `cover` must NOT rewrite the list
+ %% comprehension in the test code like this:
+ %%
+ %% [V || V <- L,
+ %% case V == a of
+ %% true -> true;
+ %% false -> element(1, V) == a
+ %% end].
+
+ File = cc_mod(t, Test, Config),
+ [a,{a,good}] = t:lc([a, b, {x,y}, {a,good}, "ignore"]),
+ ok = file:delete(File),
+ ok.
+
local_only(Config) ->
ok = file:set_cwd(proplists:get_value(data_dir, Config)),