Sprint 30: Diagnostics & Polish
Prerequisites
Sprints 19 (-map, -why_live), 29 (fortsh audit informs diagnostic quality).
Goals
Raise every diagnostic surface to afs-as's caret-under-line standard. Polish --help, --version, -t, error recovery in binary parsers. Ship-quality UX for linker errors.
Deliverables
1. Binary-input diagnostics
Every parser error cites the input file, the byte offset, and a caret pointing at the offending region in a hex dump:
afs-ld: error: in input.o at byte 0x1a4: LC_SEGMENT_64 claims nsects=3 but cmdsize fits only 2 section headers
0x1a0: 00 01 00 00 48 02 00 00 03 00 00 00 00 00 00 00
^^
cmdsize=0x248 accommodates 2 × 80-byte section_64 entries; nsects=3 needs 240+72=312 bytes.
Implemented in src/diag.rs with helpers for "byte offset → nearest load command, section, atom, symbol" so every error can contextualize itself.
2. Source-level backmapping
When diagnosing a reloc error, point at the originating .s line if the object's symbol table includes debug info (afs-as emits no debug info today; this is a forward-compatible hook). Otherwise, map to the offending atom's symbol name and input file.
3. Did-you-mean everywhere
- Undefined symbol (Sprint 8).
- Unknown flag (Sprint 19).
- Missing library (
-lFoo→ did you mean-lfoo?). - Mistyped architecture (
-arch arm86→ did you meanarm64?).
Levenshtein-3, capped at the 10 closest matches.
4. Colorized output
ANSI color codes on TTY stderr. Flagged off under NO_COLOR env var and under --color=never. Matches afs-as's approach in afs-as/src/diag*.rs.
5. Verbose and trace modes
-v: version + target + active flag summary.-t/--trace: every input file logged as it's loaded.-verbose_deprecation: warnings for deprecated flags (we accept them forldcompatibility but note they're deprecated).
6. --help format
Mirrors ld's. Sections: inputs, outputs, symbols, diagnostics, platform. Each flag has a one-line description and (where applicable) a default value. Fits on 80 columns, readable.
7. --version format
afs-ld <version>
Bespoke ARM64 Mach-O linker for armfortas
Target: arm64-apple-macos
Commit: <git hash>
8. Deterministic stderr
Error output is the same for the same input across runs. No wall clock, no pid, no thread-id. Supports scripted diffing in CI.
9. Error-code conventions
Exit codes:
- 0: success.
- 1: link failure (undefined symbol, ambiguous resolution, etc.).
- 2: CLI misuse (bad flag, missing required arg).
- 64–78: BSD
<sysexits.h>codes where they fit (EX_USAGE=64, EX_DATAERR=65, EX_NOINPUT=66, EX_UNAVAILABLE=69, EX_SOFTWARE=70).
10. Regression: no diagnostic regresses below afs-as quality
Every diagnostic in afs-ld should be at least as useful as the closest afs-as analog. Cross-checked in an audit.
Testing Strategy
- Snapshot tests for every major error category: undefined symbol, duplicate symbol, missing library, bad flag, malformed input. Compare against a stored expected-output; diff the text (modulo terminal width and colors).
--helpand--versionsnapshot tests.- TTY detection test via a pty harness (or a manual verification step).
Definition of Done
- Every error category has a snapshot test that matches a stored golden.
- Did-you-mean fires on the five categories listed.
--helpfits 80 cols and is scannable.- Color on TTY, off under
NO_COLOR/--color=never. - Exit codes follow the convention.
View source
| 1 | # Sprint 30: Diagnostics & Polish |
| 2 | |
| 3 | ## Prerequisites |
| 4 | Sprints 19 (`-map`, `-why_live`), 29 (fortsh audit informs diagnostic quality). |
| 5 | |
| 6 | ## Goals |
| 7 | Raise every diagnostic surface to afs-as's caret-under-line standard. Polish `--help`, `--version`, `-t`, error recovery in binary parsers. Ship-quality UX for linker errors. |
| 8 | |
| 9 | ## Deliverables |
| 10 | |
| 11 | ### 1. Binary-input diagnostics |
| 12 | |
| 13 | Every parser error cites the input file, the byte offset, and a caret pointing at the offending region in a hex dump: |
| 14 | |
| 15 | ``` |
| 16 | afs-ld: error: in input.o at byte 0x1a4: LC_SEGMENT_64 claims nsects=3 but cmdsize fits only 2 section headers |
| 17 | |
| 18 | 0x1a0: 00 01 00 00 48 02 00 00 03 00 00 00 00 00 00 00 |
| 19 | ^^ |
| 20 | cmdsize=0x248 accommodates 2 × 80-byte section_64 entries; nsects=3 needs 240+72=312 bytes. |
| 21 | ``` |
| 22 | |
| 23 | Implemented in `src/diag.rs` with helpers for "byte offset → nearest load command, section, atom, symbol" so every error can contextualize itself. |
| 24 | |
| 25 | ### 2. Source-level backmapping |
| 26 | |
| 27 | When diagnosing a reloc error, point at the originating `.s` line if the object's symbol table includes debug info (afs-as emits no debug info today; this is a forward-compatible hook). Otherwise, map to the offending atom's symbol name and input file. |
| 28 | |
| 29 | ### 3. Did-you-mean everywhere |
| 30 | |
| 31 | - Undefined symbol (Sprint 8). |
| 32 | - Unknown flag (Sprint 19). |
| 33 | - Missing library (`-lFoo` → did you mean `-lfoo`?). |
| 34 | - Mistyped architecture (`-arch arm86` → did you mean `arm64`?). |
| 35 | |
| 36 | Levenshtein-3, capped at the 10 closest matches. |
| 37 | |
| 38 | ### 4. Colorized output |
| 39 | |
| 40 | ANSI color codes on TTY stderr. Flagged off under `NO_COLOR` env var and under `--color=never`. Matches afs-as's approach in `afs-as/src/diag*.rs`. |
| 41 | |
| 42 | ### 5. Verbose and trace modes |
| 43 | |
| 44 | - `-v`: version + target + active flag summary. |
| 45 | - `-t` / `--trace`: every input file logged as it's loaded. |
| 46 | - `-verbose_deprecation`: warnings for deprecated flags (we accept them for `ld` compatibility but note they're deprecated). |
| 47 | |
| 48 | ### 6. `--help` format |
| 49 | |
| 50 | Mirrors `ld`'s. Sections: inputs, outputs, symbols, diagnostics, platform. Each flag has a one-line description and (where applicable) a default value. Fits on 80 columns, readable. |
| 51 | |
| 52 | ### 7. `--version` format |
| 53 | |
| 54 | ``` |
| 55 | afs-ld <version> |
| 56 | Bespoke ARM64 Mach-O linker for armfortas |
| 57 | Target: arm64-apple-macos |
| 58 | Commit: <git hash> |
| 59 | ``` |
| 60 | |
| 61 | ### 8. Deterministic stderr |
| 62 | |
| 63 | Error output is the same for the same input across runs. No wall clock, no pid, no thread-id. Supports scripted diffing in CI. |
| 64 | |
| 65 | ### 9. Error-code conventions |
| 66 | |
| 67 | Exit codes: |
| 68 | - 0: success. |
| 69 | - 1: link failure (undefined symbol, ambiguous resolution, etc.). |
| 70 | - 2: CLI misuse (bad flag, missing required arg). |
| 71 | - 64–78: BSD `<sysexits.h>` codes where they fit (EX_USAGE=64, EX_DATAERR=65, EX_NOINPUT=66, EX_UNAVAILABLE=69, EX_SOFTWARE=70). |
| 72 | |
| 73 | ### 10. Regression: no diagnostic regresses below afs-as quality |
| 74 | |
| 75 | Every diagnostic in afs-ld should be at least as useful as the closest afs-as analog. Cross-checked in an audit. |
| 76 | |
| 77 | ## Testing Strategy |
| 78 | |
| 79 | - Snapshot tests for every major error category: undefined symbol, duplicate symbol, missing library, bad flag, malformed input. Compare against a stored expected-output; diff the text (modulo terminal width and colors). |
| 80 | - `--help` and `--version` snapshot tests. |
| 81 | - TTY detection test via a pty harness (or a manual verification step). |
| 82 | |
| 83 | ## Definition of Done |
| 84 | |
| 85 | - Every error category has a snapshot test that matches a stored golden. |
| 86 | - Did-you-mean fires on the five categories listed. |
| 87 | - `--help` fits 80 cols and is scannable. |
| 88 | - Color on TTY, off under `NO_COLOR` / `--color=never`. |
| 89 | - Exit codes follow the convention. |