Record the command's id in CFSetExitCode for built-in handlers - #3516
Record the command's id in CFSetExitCode for built-in handlers#3516Eljees wants to merge 1 commit into
Conversation
handleCommand receives the whole T_SimpleCommand as `cmd`, and the ordinary path registers the exit code against it through `handleOthers (getId cmd) ...`. regularExpansionWithStatus, used by the built-in table, shadowed that name with the command's first word, so printf, unset, wait, mapfile, readarray, read and the four DEFINE_* commands recorded their exit code under the id of a T_NormalWord instead. Consumers that resolve the id through idMap then get a word where they expect a command. checkOverwrittenExitCode is one of them: getCommandBasename is Nothing for a T_NormalWord, so isPrinting never matched and SC2320 stayed silent for printf while firing for echo, which is not in the table and goes through handleOthers. Drop the shadowing pattern so cmd again refers to the command. Fixes koalaman#3490
|
Ping — this one and #3517 have been open since 9 August with no review. Both are green (16 checks each) and independent of each other, so they can be taken in either order. |
e-kwsm
left a comment
There was a problem hiding this comment.
POSIX specifies that printf shall return nonzero when an error occurs.
[ $# -eq 1 ] || exit
printf '%d' "$1" > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo "$1 is an integer"
else
echo "$1 is NOT an integer"
fiThe snippet above checks whether $1 is an integer or not, and does not take $?, but shellcheck 566b295 issues SC2181.
ksh 2020.0.0 returns zero, whereas the followings return one:
- dash 0.5.13.4
- bash 5.3.15
- busybox sh 1.36.1
- GNU coreutils 9.11
- bsdutils 13.2
|
SC2181 does not come from this PR. It is emitted by As a control I ran your snippet through the 0.11.0 release binary, which predates this branch: it reports the same On the POSIX point you are right — If SC2320 is the check you actually meant, tell me and I will re-check on your snippet. And if you think SC2181 should not fire on that idiom, that is a separate question from this PR — I am happy to look at it in its own issue. |
|
I meant SC2181 is true positive but SC2320 is false positive, as it checks the return code of |
|
You are right that SC2320 fires there and that it is wrong for that idiom - POSIX requires What this PR changes is only that
Every printf row has an echo twin that master already reports the same way. So the false positive lives in SC2320's premise - If the conclusion is that neither should warn when the status is used directly, that is a change to Worth noting separately: rows 4 and 5 show the warning survives the fix it recommends. Tell me which way you would like this to go and I will follow it. |
Fixes #3490.
Symptom
SC2320 fires for
echobut not forprintf:A four-way probe puts the discriminator on the command name rather than on quoting:
printf '...%d' $?printf '...%d' "$?"echo "...$?"echo $?Cause
handleCommand cmd vars args literalCmdreceives the wholeT_SimpleCommandascmd, and the ordinary path registers the exit code against it:regular = handleOthers (getId cmd) vars args literalCmdregularExpansionWithStatus, which the built-in table uses, shadows that name with the command's first word:So
printf,unset,wait,mapfile,readarray,readand the fourDEFINE_*commands all register their exit code under the id of aT_NormalWord. A consumer that resolves that id throughidMapthen gets a word where it expects a command, andcheckOverwrittenExitCodeis exactly such a consumer:getCommandBasenameisNothingfor aT_NormalWord, soisPrintingnever matches.echois not in the table, goes throughhandleOthers, and therefore works.Fix
Drop the shadowing pattern so
cmdrefers to the command again, matchinghandleOthers. One token; the body of the helper is unchanged.Tests
Two
prop_cases next to the existing ones:The second pins the intended narrowness:
readalso goes through the helper and also gets a corrected id, but it is neither a condition nor a printing command, so nothing new is reported for it.Verified on GHC 9.8.4:
cabal testfails (*** Failed! Falsified (after 1 test),Test suite test-shellcheck: FAIL);cabal testpasses in full;CLAUDE.md: the reporter's script now reports SC2320,echois unchanged, andmycommand; unset foo; [ $? -eq 0 ]stays silent apart from the unrelated SC2181.AI usage
I used Claude to help trace the CFG path and to draft the patch and the two tests, following the workflow in this repository's
.claude/CLAUDE.md. I read every line of the change, ran the four-way probe to establish the cause rather than assume it, ran the new tests against unpatchedmasterfirst to confirm they fail without the fix, and ran the fullcabal testand the end-to-end checks myself.