[PATCH] Run ZERR trap also when there was a Zsh error

Philippe Altherr <[email protected]>
Newsgroups gmane.comp.shells.zsh.devel
Message-ID <CAGdYchtG03K6UHkKT7B6-wG3gFGNuBrv7Ck6AeKniLjXYj9aww@mail.gmail.com>
In order to ease debugging, one can use the ZERR trap to catch any command
that returns a non-zero exit status (outside of a conditional expression)
and display additional information on the context where it happened. A
common source of errors in scripts are typos in variable names. These can
be turned into Zsh errors thanks to the -u option. Unfortunately, trap
execution is disabled in the presence of Zsh errors
<https://github.com/zsh-users/zsh/blob/e73499b372c398c1043b12955c3b3e7602b1a992/Src/signals.c#L1094-L1102>.
Thus, an undefined variable won't run the ZERR trap and one may still be
stuck with an utterly unhelpful message like "(anon):1: foo: parameter not
set".

There doesn't seem to be any fundamental reason to never run traps in the
presence of Zsh errors. In fact there are already exceptions. For instance, the
EXIT trap is run even if Zsh errors are present
<https://github.com/zsh-users/zsh/blob/e73499b372c398c1043b12955c3b3e7602b1a992/Src/exec.c#L1645-L1650>.
The patch below replicates this logic for the ZERR trap.

- Run ZERR trap also when there was a Zsh error
<https://github.com/zsh-users/zsh/compare/master...paltherr:zsh:run-zerr-trap-on-zsh-error>

Philippe
run-zerr-trap-on-zsh-error.txt (text/plain, 2.4 KB)
diff --git a/Src/exec.c b/Src/exec.c
index 47d70b5a9..60075f254 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -1598,7 +1598,10 @@ sublist_done:
 	if (!this_noerrexit && !donetrap && !this_donetrap) {
 	    if (sigtrapped[SIGZERR] && lastval &&
 		!(noerrexit & NOERREXIT_EXIT)) {
+		int eflag = errflag;
+		errflag = 0;
 		dotrap(SIGZERR);
+		errflag = eflag;
 		donetrap = 1;
 	    }
 	    if (lastval) {
diff --git a/Test/C03traps.ztst b/Test/C03traps.ztst
index 87b7fd1f7..9924e4f96 100644
--- a/Test/C03traps.ztst
+++ b/Test/C03traps.ztst
@@ -361,6 +361,81 @@
 >trap
 >Working 0
 
+  ok() {   true         } # All good
+  ko() {   false        } # Just a non-zero status
+  KO() { { : $KO } 2>&1 } # A Zsh error
+  fn() {
+    echo "$1"
+    set -u;
+    case $1 in
+      *-TRAP-ok) TRAPZERR() { echo "ok 1"; ok; echo "ok 2" };;
+      *-trap-ok) trap        'echo "ok 1"; ok; echo "ok 2"' ZERR;;
+      *-TRAP-KO) TRAPZERR() { echo "ok 1"; KO; echo "KO 2" };;
+      *-trap-KO) trap        'echo "ok 1"; KO; echo "KO 2"' ZERR;;
+      *        ) echo "Unrecognized id: $1";
+    esac
+    case $1 in
+      ko-*-*) ko; echo "ok 3";;
+      KO-*-*) KO; echo "KO 3";;
+      *     ) echo "Unrecognized id: $1";
+    esac
+  }
+  (fn ko-TRAP-ok; echo "ok 4"); echo "\$?=$?"
+  (fn ko-trap-ok; echo "ok 4"); echo "\$?=$?"
+  (fn ko-TRAP-KO; echo "ok 4"); echo "\$?=$?"
+  (fn ko-trap-KO; echo "ok 4"); echo "\$?=$?"
+  (fn KO-TRAP-ok; echo "KO 4"); echo "\$?=$?"
+  (fn KO-trap-ok; echo "KO 4"); echo "\$?=$?"
+  (fn KO-TRAP-KO; echo "KO 4"); echo "\$?=$?"
+  (fn KO-trap-KO; echo "KO 4"); echo "\$?=$?"
+0:DDD
+>ko-TRAP-ok
+>ok 1
+>ok 2
+>ok 3
+>ok 4
+>$?=0
+>ko-trap-ok
+>ok 1
+>ok 2
+>ok 3
+>ok 4
+>$?=0
+>ko-TRAP-KO
+>ok 1
+>KO: KO: parameter not set
+F:Shouldn't the error in the trap prevent any further execution?
+>ok 3
+>ok 4
+>$?=0
+>ko-trap-KO
+>ok 1
+>KO: KO: parameter not set
+F:Shouldn't the error in the trap prevent any further execution?
+>ok 3
+>ok 4
+>$?=0
+>KO-TRAP-ok
+>KO: KO: parameter not set
+>ok 1
+>ok 2
+>$?=1
+>KO-trap-ok
+>KO: KO: parameter not set
+>ok 1
+>ok 2
+>$?=1
+>KO-TRAP-KO
+>KO: KO: parameter not set
+>ok 1
+>KO: KO: parameter not set
+>$?=1
+>KO-trap-KO
+>KO: KO: parameter not set
+>ok 1
+>KO: KO: parameter not set
+>$?=1
+
    { trap 'echo This subshell is exiting' EXIT; } | cat
 0: EXIT trap set in current shell at left of pipeline
 >This subshell is exiting
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.