From 98ecd1ee421deebfb436ffb925be2200bf0e5c71 Mon Sep 17 00:00:00 2001 From: Sal Date: Thu, 3 Sep 2026 02:47:07 +0100 Subject: [PATCH] refactor(core): remove the duplicate helper and correct drifted headers .zi-at-eval was defined in both lib/zsh/autoload.zsh and lib/zsh/install.zsh. The two agreed on behaviour and differed in wording, so nothing was broken, but both libraries are sourced conditionally and in different orders depending on the command, so which definition won varied by code path. That is a heisenbug waiting for the first divergence. The only call sites are install.zsh:2410 and install.zsh:2430, inside the file that defines it. It is absent from contracts/public-contract-v1.json and has no consumer anywhere in the organization, so removing the autoload.zsh copy leaves one deterministic definition next to its callers. Two `# FUNCTION:' headers named a different function than they preceded: lib/zsh/autoload.zsh .zi-update-in-parallel -> .zi-update-all-parallel zi.zsh +zi-parse-opts -> .zi-parse-opts The second was not in the issue; a systematic sweep of every header against the following definition found it. Neither defect can be caught behaviourally. Duplicate definitions that agree behave correctly until they drift, and a wrong header misleads only the next reader. tests/source-hygiene.zsh asserts both structurally over zi.zsh and lib/zsh/*.zsh, currently 175 functions, and was observed failing with each defect reintroduced separately. Issue item 2 is not reproducible: the header above @zsh-plugin-run-on-unload names that function correctly. Item 4, the .zi-load-ices snippet-directory TODO, is untouched; the issue itself says it needs an ID-collision test before it can be classified as a defect. Closes #449 --- .github/workflows/zsh-n.yml | 13 ++++++ lib/zsh/autoload.zsh | 10 +---- tests/source-hygiene.zsh | 80 +++++++++++++++++++++++++++++++++++++ zi.zsh | 2 +- 4 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 tests/source-hygiene.zsh diff --git a/.github/workflows/zsh-n.yml b/.github/workflows/zsh-n.yml index 34b59e7..934a28b 100644 --- a/.github/workflows/zsh-n.yml +++ b/.github/workflows/zsh-n.yml @@ -25,6 +25,7 @@ on: - "tests/fixtures/plugin-standard-callbacks/**" - "tests/self-update-reload.zsh" - "tests/snippet-directory-mirror.zsh" + - "tests/source-hygiene.zsh" - "tests/subst-nesting.zsh" - "tests/version-reporting.zsh" pull_request: @@ -47,6 +48,7 @@ on: - "tests/fixtures/plugin-standard-callbacks/**" - "tests/self-update-reload.zsh" - "tests/snippet-directory-mirror.zsh" + - "tests/source-hygiene.zsh" - "tests/subst-nesting.zsh" - "tests/version-reporting.zsh" workflow_dispatch: {} @@ -259,6 +261,17 @@ jobs: - name: Test message formatting run: zsh -f tests/message-formatting.zsh + source-hygiene: + name: Source Hygiene + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - name: Install Zsh + run: sudo apt update && sudo apt-get install -yq zsh + - name: Test source hygiene + run: zsh -f tests/source-hygiene.zsh + subst-nesting: name: Subst Nesting runs-on: ubuntu-latest diff --git a/lib/zsh/autoload.zsh b/lib/zsh/autoload.zsh index d136afd..b7f4d58 100755 --- a/lib/zsh/autoload.zsh +++ b/lib/zsh/autoload.zsh @@ -299,14 +299,6 @@ ZI[EXTENDED_GLOB]="" fi return 0 } # ]]] -# FUNCTION: .zi-at-eval [[[ -.zi-at-eval() { - local atclone="$2" atpull="$1" - integer retval - @zi-substitute atclone atpull - [[ $atpull = "%atclone" ]] && { eval "$atclone"; retval=$?; } || { eval "$atpull"; retval=$?; } - return $retval -} # ]]] # # Format functions @@ -1917,7 +1909,7 @@ ZI[EXTENDED_GLOB]="" +zi-message "{mmdsh}{happy} Zi{rst} » {info3}update took {num}$SECONDS{info3} seconds{rst}{…}" return "$retval" } # ]]] -# FUNCTION: .zi-update-in-parallel [[[ +# FUNCTION: .zi-update-all-parallel [[[ .zi-update-all-parallel() { builtin emulate -LR zsh ${=${options[xtrace]:#off}:+-o xtrace} builtin setopt extended_glob warn_create_global typeset_silent no_short_loops no_monitor no_notify diff --git a/tests/source-hygiene.zsh b/tests/source-hygiene.zsh new file mode 100644 index 0000000..7324399 --- /dev/null +++ b/tests/source-hygiene.zsh @@ -0,0 +1,80 @@ +#!/usr/bin/env zsh +# -*- mode: zsh; sh-indentation: 2; indent-tabs-mode: nil; sh-basic-offset: 2; -*- +# vim: ft=zsh sw=2 ts=2 et +# +# Structural checks over the shipped sources. Neither can be caught by a +# behavioural test: a duplicate definition whose copies agree behaves correctly +# until they drift, and a wrong header misleads only the next reader. Both are +# cheap to assert and expensive to rediscover. + +builtin emulate -R zsh +setopt extended_glob pipe_fail + +fail() { + builtin print -u2 -r -- "not ok - $1" + exit 1 +} + +typeset project_root="${ZI_TEST_CHECKOUT:-${0:A:h:h}}" +builtin cd -q "$project_root" || fail "enter the project root" + +typeset -a sources +sources=( zi.zsh lib/zsh/*.zsh(N) ) +(( ${#sources} )) || fail "locate the shipped sources" + +# A top-level definition: a name in column one followed by `()'. Indented +# definitions are anonymous or nested helpers and are deliberately not counted. +typeset -r name_class='[A-Za-z_@.:+][A-Za-z0-9_@.:+-]#' + +typeset -A seen_in +typeset -a duplicates mismatches +typeset src name header defined +typeset -a lines +typeset -i i j + +for src in "${sources[@]}"; do + lines=( "${(@f)$(<$src)}" ) + + for (( i = 1; i <= ${#lines}; i++ )); do + # Every function is defined exactly once across the shipped sources. Both + # libraries are sourced conditionally and in different orders depending on + # the command, so a second definition does not reliably override the first: + # which one wins varies by code path. `.zi-at-eval' was defined in both + # lib/zsh/autoload.zsh and lib/zsh/install.zsh with matching behaviour and + # different wording, exactly the shape that becomes a heisenbug once the + # two drift. + if [[ ${lines[i]} == (#b)(${~name_class})[[:space:]]#'()'* ]]; then + name="${match[1]}" + if [[ -n ${seen_in[$name]} ]]; then + duplicates+=( "${name}: ${seen_in[$name]} and ${src}:${i}" ) + else + seen_in[$name]="${src}:${i}" + fi + continue + fi + + # Every `# FUNCTION: ' header names the function it precedes. + if [[ ${lines[i]} == (#b)'#'[[:space:]]#'FUNCTION:'[[:space:]]#(${~name_class})* ]]; then + header="${match[1]%.}" + for (( j = i + 1; j <= i + 6 && j <= ${#lines}; j++ )); do + [[ ${lines[j]} == (#b)(${~name_class})[[:space:]]#'()'* ]] || continue + defined="${match[1]}" + [[ $header == $defined ]] || + mismatches+=( "${src}:${i} header '${header}' precedes '${defined}'" ) + break + done + fi + done +done + +if (( ${#duplicates} )); then + builtin print -u2 -rl -- "functions defined more than once:" "${duplicates[@]}" + fail "a function is defined in more than one shipped source" +fi + +if (( ${#mismatches} )); then + builtin print -u2 -rl -- "FUNCTION headers naming a different function:" "${mismatches[@]}" + fail "a FUNCTION header does not match the function it precedes" +fi + +builtin print -r -- "ok - every function is defined once and every FUNCTION header matches (${#seen_in} functions)" diff --git a/zi.zsh b/zi.zsh index ea35881..f81e5b5 100644 --- a/zi.zsh +++ b/zi.zsh @@ -2379,7 +2379,7 @@ builtin setopt no_aliases "the list of the {cmd}subcommands$bcol.{rst}" } } # ]]] -# FUNCTION: +zi-parse-opts. [[[ +# FUNCTION: .zi-parse-opts. [[[ .zi-parse-opts() { builtin emulate -LR zsh ${=${options[xtrace]:#off}:+-o xtrace} builtin setopt extended_glob typeset_silent no_short_loops rc_quotes no_auto_pushd