[PATCH v2 3/3] syntax-check: Introduce sc_prohibit_local_with_subshell rule

Michal Privoznik via Devel posted 3 patches 1 week, 6 days ago
[PATCH v2 3/3] syntax-check: Introduce sc_prohibit_local_with_subshell rule
Posted by Michal Privoznik via Devel 1 week, 6 days ago
From: Michal Privoznik <mprivozn@redhat.com>

In shell, the following function doesn't echo '1' but '0':

  func() {
      local var=$(false)
      echo $?
  }

This is because '$?' does not refer to 'false' but 'local'. The
bash_builtins(1) manpage explains it well. And it also mentions
other commands behaving the same: export, declare and readonly.
Since it is really easy to miss this pattern, introduce a
syntax-check rule. Mind you, the following patter (which passes
the rule) does check for the subshell exit code:

  func() {
      local var
      var=$(false)
      echo $?
  }

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
---
 build-aux/syntax-check.mk | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/build-aux/syntax-check.mk b/build-aux/syntax-check.mk
index f605c9b0e3..022e8e6550 100644
--- a/build-aux/syntax-check.mk
+++ b/build-aux/syntax-check.mk
@@ -394,6 +394,17 @@ sc_prohibit_g_autofree_const:
 	halt='‘g_autofree’ discards ‘const’ qualifier from pointer target type' \
 	  $(_sc_search_regexp)
 
+sc_prohibit_local_with_subshell:
+	@err=0; for f in $$($(VC_LIST_EXCEPT) | $(GREP) -E '\.sh(\.in)?$$'); do \
+	  lines=$$( $(AWK) \
+	  '/^\s*(local|export|declare|readonly)\s+.*=/ { nr=NR; c=1; next } \
+	  /^\s*$$/ { if (c) next } \
+	  /\$$\?/ { if (c) { print nr; c=0; } next } \
+	  { c=0 }' $$f ) ; \
+	  for l in $$lines; do echo $$f:$$l 1>&2; err=1; done ; \
+	done; \
+	test $$err -eq 0 || { msg="Declare and assign separately to avoid masking return values" $(_sc_say_and_exit) }
+
 
 # Many of the function names below came from this filter:
 # git grep -B2 '\<_('|grep -E '\.c- *[[:alpha:]_][[:alnum:]_]* ?\(.*[,;]$' \
-- 
2.52.0

Re: [PATCH v2 3/3] syntax-check: Introduce sc_prohibit_local_with_subshell rule
Posted by Ján Tomko via Devel 1 week, 6 days ago
On a Monday in 2026, Michal Privoznik via Devel wrote:
>From: Michal Privoznik <mprivozn@redhat.com>
>
>In shell, the following function doesn't echo '1' but '0':
>
>  func() {
>      local var=$(false)
>      echo $?
>  }
>
>This is because '$?' does not refer to 'false' but 'local'. The
>bash_builtins(1) manpage explains it well. And it also mentions
>other commands behaving the same: export, declare and readonly.
>Since it is really easy to miss this pattern, introduce a
>syntax-check rule. Mind you, the following patter (which passes

*pattern

Jano

>the rule) does check for the subshell exit code:
>
>  func() {
>      local var
>      var=$(false)
>      echo $?
>  }
>
>Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
>---
> build-aux/syntax-check.mk | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>