From c0bc0c6ae10dc7c0441711c39378c93a5bb82708 Mon Sep 17 00:00:00 2001 From: afadesigns Date: Thu, 25 Jun 2026 22:34:58 +0200 Subject: [PATCH] fix: ZC1075 skips flag-led expansions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A flag-led parameter expansion such as `${(%):-default}` or `${(P)…}` carries a flag plus a default that the parser cannot resolve to a subject, so it leaves the expansion's Left nil and `${ }` as the rendered text. ZC1075 then flagged it as an elision hazard, but a `(%)` prompt expansion with a `:-` default never produces an empty word, so the "quote it" advice is wrong. When the parser could not resolve the expansion subject (Left is nil), ZC1075 now skips it rather than guessing. Ordinary scalars and array elements still fire. Removes 1 finding from the violation baseline (zinit); regression test added. --- .github/violation-baseline.txt | 2 +- pkg/katas/katatests/fp_round7_test.go | 31 +++++++++++++++++++++++++++ pkg/katas/zc1000s.go | 8 +++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 pkg/katas/katatests/fp_round7_test.go diff --git a/.github/violation-baseline.txt b/.github/violation-baseline.txt index ea2c95be..64288802 100644 --- a/.github/violation-baseline.txt +++ b/.github/violation-baseline.txt @@ -1248,7 +1248,7 @@ zinit/zinit-autoload.zsh ZC1049 1 zinit/zinit-autoload.zsh ZC1053 1 zinit/zinit-autoload.zsh ZC1064 1 zinit/zinit-autoload.zsh ZC1073 3 -zinit/zinit-autoload.zsh ZC1075 41 +zinit/zinit-autoload.zsh ZC1075 40 zinit/zinit-autoload.zsh ZC1083 2 zinit/zinit-autoload.zsh ZC1091 32 zinit/zinit-autoload.zsh ZC1098 3 diff --git a/pkg/katas/katatests/fp_round7_test.go b/pkg/katas/katatests/fp_round7_test.go new file mode 100644 index 00000000..1d7ed83a --- /dev/null +++ b/pkg/katas/katatests/fp_round7_test.go @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +// Copyright the ZShellCheck contributors. +package katas + +import ( + "testing" + + "github.com/afadesigns/zshellcheck/pkg/testutil" +) + +// TestZC1075FlagLedExpansionNotFlagged pins the flag-led-expansion false +// positive. A `${(%):-default}` / `${(P)…}` carries a parameter flag and a +// default the parser cannot fully model; it never produces an empty word, +// so the elision warning does not apply. +func TestZC1075FlagLedExpansionNotFlagged(t *testing.T) { + for _, src := range []string{ + "echo ${(%):-default}", + "echo ${(P):-x}", + `read -q ${(%):-"?prompt "}`, + } { + if n := len(testutil.Check(src, "ZC1075")); n != 0 { + t.Errorf("ZC1075 should not flag a flag-led expansion: %q (got %d)", src, n) + } + } + // Ordinary unquoted scalars and array elements still elide and fire. + for _, src := range []string{"echo $plain", "echo ${arr[1]}"} { + if n := len(testutil.Check(src, "ZC1075")); n == 0 { + t.Errorf("ZC1075 should still fire on %q", src) + } + } +} diff --git a/pkg/katas/zc1000s.go b/pkg/katas/zc1000s.go index ab3c2217..f26ec626 100644 --- a/pkg/katas/zc1000s.go +++ b/pkg/katas/zc1000s.go @@ -5486,6 +5486,14 @@ func checkZC1075(node ast.Node) []Violation { }) } } else if aa, ok := arg.(*ast.ArrayAccess); ok { + // A flag-led expansion the parser cannot resolve to a subject + // (`${(%):-default}`, `${(P)…}`, `${(l:n:)…}`) leaves Left nil. + // Without the subject the elision behaviour is unknowable, and + // these forms commonly carry a `:-` default or a width modifier + // that never produces an empty word, so do not flag them. + if aa.Left == nil { + continue + } // A `:-word` / `:=word` / `:+word` default-value expansion // always yields a value, so it never elides — flagging it // warns against the canonical `: ${VAR:=default}` idiom.