Skip to content

fix(hy-check): reject known non-string fmt format operands (HYB-E1002) — Fixes #4195 - #4196

Merged
limitlessprojects merged 1 commit into
limitlessprojects:mainfrom
9xj89gzrtw-hue:fix/fmt-known-kind-format-arg
Nov 9, 2026
Merged

limitlessprojects merged 1 commit into
limitlessprojects:mainfrom
9xj89gzrtw-hue:fix/fmt-known-kind-format-arg

Conversation

@9xj89gzrtw-hue

Copy link
Copy Markdown
Contributor

Fixes #4195.

Change

Extends the printf-family operand validation from #4087 (which only rejected non-string literals) to statically known non-string expressions:

  • hy-check/buildable.rs: in check_call_operands, both the printf/sprintf/eprintf/errorf branch (format = args[0]) and the fprintf branch (format = args[1]) now also check kind_of_expr of the format operand. A definitely-non-string kind (int, float64, bool, …) fires the existing HYB-E1002. Str, Unknown, and any (Interface) stay fail-open, mirroring every other accept-side guard in the file.
  • hy-check/tests/fmt_special_member_calls_buildable.rs: 8 regression tests (int local, int/float binop, sprintf/eprintf/errorf int local, fprintf int-local format; plus string-local / string-concat / any accepts).

Evidence

Before (current main @ ce6219cd, fresh hyb check):

  • x := 42; fmt.printf(x) → OK (0 diagnostics)
  • fmt.printf(42 * 1), fmt.printf(1 + 2), fmt.sprintf(x), fmt.fprintf(os.stdout, x) → all OK (0 diagnostics)

After this change (same binary rebuilt from this branch):

  • each of the above → error[HYB-E1002]: \fmt.printf` requires a string format argument, found non-string (int) (HYB-E1002)`
  • s := "hello"; fmt.printf(s) → still OK (0 diagnostics)

Tests

  • cargo test -p hy-check --test fmt_special_member_calls_buildable: 21/21 ok (13 existing + 8 new).
  • cargo test --workspace --no-fail-fast: 0 regressions — the only failures are 10 tests in 6 hyb-crate binaries that shell out to hyb build and require the pinned go1.26.9 (HYB-E9001 on this machine's go1.27.2); verified byte-identical failures on pristine origin/main without this change.
  • python3 tools/check_test_registration.py → OK (40 test file(s), 36 discovery pattern(s)).

Payout wallet (Base USDC): 0xbbe6966949d7265a7f5f0b793f08293b9b7fe926

The fmt printf-family validator from limitlessprojects#4087 only rejected non-string
*literals* as the format argument, so a statically known non-string
expression - fmt.printf(x) with x := 42, fmt.printf(42 * 1),
fmt.fprintf(w, x) - passed hyb check with 0 diagnostics and failed
go build with cannot use ... as string in argument to fmt.Printf.

Extends the printf/sprintf/eprintf/errorf and fprintf branches of
check_call_operands with a kind check: when kind_of_expr of the
format operand is definitely non-string (Int/Float/Bool/...) the
existing HYB-E1002 fires. Str, Unknown, and any (Interface) stay
fail-open exactly like every other accept-side guard in buildable.rs.

Tests: 8 regression tests in
fmt_special_member_calls_buildable.rs (int local, int/float binop,
sprintf/eprintf/errorf int local, fprintf int local; accepts for
string local, string concat, any).
@github-actions github-actions Bot added the bot:land-candidate Shepherd: fresh, gate-clean, verifiable (set from classify()) label Nov 8, 2026
@limitlessprojects
limitlessprojects merged commit a0b15b7 into limitlessprojects:main Nov 9, 2026
2 checks passed
@limitlessprojects

Copy link
Copy Markdown
Owner

Landed — merge a0b15b78, remote-asserted (full battery ALL-GREEN)

Thank you @9xj89gzrtw-hue — this closes the #4087 literal-only blind spot exactly as #4195 describes: fmt.printf(x) with x := 42 passed hyb check and died in go build. The nonstring_format_kind helper (Str/Unknown/Interface permissive) is the right conservative shape, and the new accept control correctly reads every binding.

Review note for transparency: the two-point diff against the PR-reported base made it look like this PR deleted #4093's fmt_scan_family_buildable.rs (−176) and 13 WORKLOG lines. That was a base-staleness artifact — the true merge-base is ce6219cd (pre-#4093), and the real patch is +34/+69 purely additive. The auto-merge kept all landed fmt arms and suites intact; verified before landing.

Verification evidence (run on the merge commit):

Gate Result
Merge gate auto-merge clean, no conflict; landed fmt arms + scan suite verified present in merged tree
cargo test --workspace 3198 passed / 0 failed; 486 suites = 486 Running lines (1:1)
corpus harness 139/139, tripwire not fired (verdict-level 2/139, as-written 3/139, threshold ~25%)
differential loop FULL AGREEMENT — accept 47/47 + reject 92/92

Protocol followed: merge --no-ff with hand-written credit commit (evidence in the merge message), pushed and git ls-remote == local HEAD asserted before this reply. No silent closes, no fixture fixups needed.

Bounty rail: recorded as a Row-13 BUILDABLE soundness improvement for the current cycle per BOUNTY.md — main pool ladder ($500/cycle, 1st $75 → 10th $45) plus Wildcard Track, judged in discussion #295. This one qualifies on the main track: it turns a real go-build-failure class into a checker-level reject with a positive control.

Next in queue: #4198/#4199/#4221. Pending author fixes: #4202/#4201 (suite controls), #4206, #4097, #4217, #4219, #4215/#4213/#4211 (allowlist re-target).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:land-candidate Shepherd: fresh, gate-clean, verifiable (set from classify())

Projects

None yet

Development

Successfully merging this pull request may close these issues.

hyb check passes fmt printf with statically-known non-string format operand (x := 42)

2 participants