From 9f2250bc2e1907f95fd6437fe13eecf95fbd5879 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Tue, 21 Apr 2026 00:29:23 +0300 Subject: [PATCH 1/4] plugin: remove subshell from EXECUTABLE_NODE_TYPES A subshell open parenthesis is never reported as "executed" by bash so we shouldn't mark at as "executable". It should be treated the same as a "compound_statement". --- coverage_sh/plugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/coverage_sh/plugin.py b/coverage_sh/plugin.py index 77d328b..61f3ba7 100644 --- a/coverage_sh/plugin.py +++ b/coverage_sh/plugin.py @@ -37,7 +37,6 @@ TMP_PATH = Path(os.environ.get("XDG_RUNTIME_DIR", "/tmp")) # noqa: S108 TRACEFILE_PREFIX = "shelltrace" EXECUTABLE_NODE_TYPES = { - "subshell", "redirected_statement", "variable_assignment", "variable_assignments", From cb24ac3b9809668d174555d2fd9024b1dd71bb3c Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Tue, 21 Apr 2026 00:32:30 +0300 Subject: [PATCH 2/4] test_executable_lines: add func_brace and func_paren, check same behavior --- tests/test_plugin.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index a6afb2d..b3133f1 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -248,6 +248,30 @@ def test_lines_should_return_executable_lines(self, tmp_path: Path) -> None: pytest.param( "case x in\n x) echo match ;;\nesac\n", {2, 3}, id="esac_excluded" ), + pytest.param( + """\ + echo aaa + func() + { + echo bbb + } + func + """, + {2, 5, 7}, + id="func_brace", + ), + pytest.param( + """\ + echo aaa + func() + ( + echo aaa + ) + func + """, + {2, 5, 7}, + id="func_paren", + ), ], ) def test_executable_lines( From d8382df19c114ca4f8494b0fcff05fee802b7d4b Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Tue, 21 Apr 2026 15:32:31 +0300 Subject: [PATCH 3/4] plugin: remove pipeline from EXECUTABLE_NODE_TYPES The pipeline not is a container for multiple "command" children, it does it need to be marked executable by itself. This fixes empty braces at the start of a pipeline being reported as uncovered. --- coverage_sh/plugin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/coverage_sh/plugin.py b/coverage_sh/plugin.py index 61f3ba7..da1e74a 100644 --- a/coverage_sh/plugin.py +++ b/coverage_sh/plugin.py @@ -50,7 +50,6 @@ "while_statement", "if_statement", "case_statement", - "pipeline", "list", } SUPPORTED_MIME_TYPES = {"text/x-shellscript"} From 32607fb26b204e4b376e7baed823229ba404da5f Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Tue, 21 Apr 2026 15:34:20 +0300 Subject: [PATCH 4/4] test_executable_lines: add long_pipeline test An empty brace part of a pipeline is not reported as "executed" by bash and should not be marked as "executable", otherwise it will incorrectly end up as "uncovered". --- tests/test_plugin.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index b3133f1..7ec48cb 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -272,6 +272,17 @@ def test_lines_should_return_executable_lines(self, tmp_path: Path) -> None: {2, 5, 7}, id="func_paren", ), + pytest.param( + """\ + { + echo aaa + # comment + echo bbb + } | grep a + """, + {3, 5, 6}, + id="long_pipeline", + ), ], ) def test_executable_lines(