diff options
author | Björn Gustavsson <bjorn@erlang.org> | 2021-02-25 05:51:53 +0100 |
---|---|---|
committer | Björn Gustavsson <bjorn@erlang.org> | 2021-02-25 13:36:20 +0100 |
commit | 2adb932784784d00ff68f1b8aa27ccc53dfddeb9 (patch) | |
tree | 97a528abadfa32b4e1614775584750bd8d924abd /lib/tools/test | |
parent | 90d04b26ca1d7cd93c80f904ea2248889ac6ce4b (diff) | |
download | erlang-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.erl | 25 |
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)), |