From 2adb932784784d00ff68f1b8aa27ccc53dfddeb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 25 Feb 2021 05:51:53 +0100 Subject: 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. --- lib/tools/test/cover_SUITE.erl | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'lib/tools/test') 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)), -- cgit v1.2.1