summaryrefslogtreecommitdiff
path: root/lib/tools/test
diff options
context:
space:
mode:
authorBjörn Gustavsson <bjorn@erlang.org>2021-02-25 05:51:53 +0100
committerBjörn Gustavsson <bjorn@erlang.org>2021-02-25 13:36:20 +0100
commit2adb932784784d00ff68f1b8aa27ccc53dfddeb9 (patch)
tree97a528abadfa32b4e1614775584750bd8d924abd /lib/tools/test
parent90d04b26ca1d7cd93c80f904ea2248889ac6ce4b (diff)
downloaderlang-2adb932784784d00ff68f1b8aa27ccc53dfddeb9.tar.gz
cover: Ensure that LC filters are compiled in guard context
A subtle detail in the semantics of list comprehensions is that filters will be compiled as guard expressions if possible. That means that the following list comprehension will never raise an exception as long as it is passed a proper list: lc(L) -> [V || V <- L, V =:= a orelse element(1, V) =:= a]. Thus, the following call: lc([a, {a,b}, {}, "ignore"]) will return `[a, {a,b}]`. When the calls `element(1, {}` and `element(1, "ignore")` fail, no exception is raised and the corresponding list elements are discarded. The `cover` tools rewrites rewrites `andalso` and `orelse` to `case` constructions in order to produce better coverage data. For this example, that rewriting would change the error behavior of the list comprehension: lc(L) -> [V || V <- L, case V =:= a of true -> true; false -> element(1, V) =:= a end]. Because of the `case`, the filter is no longer a guard, and the `element(1, {}` call will now raise a `badarg` exception. To avoid that problem, stop rewriting `andalso`/`orelse` in filters that are guard tests.
Diffstat (limited to 'lib/tools/test')
-rw-r--r--lib/tools/test/cover_SUITE.erl25
1 files changed, 24 insertions, 1 deletions
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)),