diff options
author | Björn Gustavsson <bjorn@erlang.org> | 2021-02-26 14:32:01 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-02-26 14:32:01 +0100 |
commit | f04d6b3fe1a01bc21bc36760bfb747dc7bba703b (patch) | |
tree | 58c65564da76262a6713c5356c948f7822a21de9 /lib | |
parent | 1f365da849e87ba2cf629da4fe700ca3462b161f (diff) | |
parent | 2adb932784784d00ff68f1b8aa27ccc53dfddeb9 (diff) | |
download | erlang-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.erl | 32 | ||||
-rw-r--r-- | lib/tools/test/cover_SUITE.erl | 25 |
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)), |