testmark-bash

document.bash: point at docs/hacking/bash-pitfalls.md where code relies on its rules.

commit 764825d0a731929c05d42f89cc9d19a63c33e72c · parent 8d0fe9cfbc · browse this tree
Ximianth, 2026-10-23 23:35:12 UTC

Each pointer names a section heading, replacing a restatement where there was one:
the storage comment's rule for name-keyed elements, the assoc declares before the commit's namerefs,
the NUL check, and the body loop (a slice there would be quadratic over a whole dump).
The read already points at its section, from the commit that changed it.
diff --git a/lib/testmark/document.bash b/lib/testmark/document.bash
index 0a66374..d40ad71 100644
--- a/lib/testmark/document.bash
+++ b/lib/testmark/document.bash
@@ -10,8 +10,7 @@
 #   pid          $BASHPID at parse, for refusing state changes from a subshell
 # So forgetting a handle is one `unset` per kind, and nothing else needs cleaning up.
 # Variable names are built only from handles that pass _testmark_handle, and kinds have no "_", so two handles' variables never meet.
-# Hunk names come from documents and may contain shell syntax: on some bash versions, arithmetic, `[[ -v ]]`, `unset`, and `printf -v` on an element run `$(...)` from inside its key.
-# So touch name-keyed elements only as `a[$k]=v`, `"${a[$k]}"`, `"${a[$k]-}"`, `${a[$k]+set}`, and `"${!a[@]}"`.
+# Hunk names come from documents and may contain shell syntax, so name-keyed elements are touched only as "Using assoc arrays safely" in docs/hacking/bash-pitfalls.md allows.
 _testmark_kinds=(lines start end info names pid)
 
 _testmark_err() {
@@ -46,7 +45,7 @@ _testmark_parse() {
 	local i line name names='' open='' k='' at=''
 	for ((i = 0; i < ${#lines[@]}; i++)); do
 		line=${lines[i]}
-		# mapfile ends a line at a NUL, dropping its "\n".  So a line without one is the last line, or has a NUL; read -d '' tells which.
+		# A line without its "\n" is the last line, or was cut at a NUL; read -d '' tells which.  See "NUL bytes vanish unless you check for them" in docs/hacking/bash-pitfalls.md.
 		if [[ $line != *$'\n' ]] && IFS= read -r -d '' _ <"$file"; then
 			_testmark_err "$file:$((i + 1)): contains a NUL byte"
 			return 1
@@ -105,7 +104,7 @@ _testmark_parse() {
 	}
 
 	# Parsed without error; only now are globals written.
-	# Assoc kinds are declared first, since a nameref to an undeclared name makes an indexed array.
+	# Assoc kinds are declared first; see "assoc arrays must be declared as such" in docs/hacking/bash-pitfalls.md.
 	declare -gA "_testmark_start_$h" "_testmark_end_$h" "_testmark_info_$h"
 	local -n g_lines=_testmark_lines_$h g_start=_testmark_start_$h g_end=_testmark_end_$h g_info=_testmark_info_$h
 	local -n g_names=_testmark_names_$h g_pid=_testmark_pid_$h
@@ -151,7 +150,7 @@ _testmark_body() {
 	_testmark_hunk "$1" "$2" || return 1
 	local -n lines=_testmark_lines_$1 start=_testmark_start_$1 end=_testmark_end_$1
 	local i=${start[$2]} last=${end[$2]} body=
-	for (( ; i < last; i++)); do body+=${lines[i]}; done
+	for (( ; i < last; i++)); do body+=${lines[i]}; done # not a slice; see "Quadratic operations" in docs/hacking/bash-pitfalls.md
 	printf '%s' "$body"
 }