fix: clarify kebab-case is atomic in undefined-variable hint#230
Merged
Conversation
When `best-d` is undefined but `best` and `d` are both bound, the default ILO-T004 suggestion `did you mean 'best'?` reads like the parser split the identifier into `best - d`. The lexer regex `[a-z][a-z0-9]*(-[a-z0-9]+)*` (logos longest-match) guarantees kebab-case is atomic, so the misread is a diagnostic-layer footgun, not a parser bug. Add `kebab_subtract_hint` that fires when every dash-separated half of an undefined kebab-case ident resolves as a scoped variable, function, or builtin. For 2-segment names it also shows the explicit subtraction spelling `- a b`. Falls back to closest-match for the single-half-bound case, so unrelated typos still get the standard suggestion.
Pin the lexer guarantee that kebab-case is one Ident token in str-call argument position, list elements, multi-segment names, and digit-segment names, across tree, VM, and Cranelift engines. Also covers the explicit subtraction escape hatch `- best d` so the recommended form in the new diagnostic stays valid. Three diagnostic tests cover the new kebab-aware hint, the 3-segment variant (no subtract form recommended), and the closest-match fallback when only one half is bound.
Shows the explicit subtraction spelling (`- best d`), the kebab-case identifier lookup (`best-d` distinct from `best` and `d`), and the multi-segment form (`a-b-c`) all working in one file. The mixed function binds `best`, `d`, and `best-d` in the same scope and prints `[99, 10, 3]` to prove the parser never splits the kebab ident. Wired into the engine-asserted harness via `-- run` / `-- out` annotations so tree, VM, and Cranelift all check the same outputs.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Persona reports (lines 503, 622, 958 in
ilo_assessment_feedback.md) kept claimingbest-dinstr best-dparses asstr(best) - dwhenbestanddare both bound, eroding the kebab-case identifier win. The reports came in steadily enough that they read like a real precedence bug.It isn't. The lexer regex is
[a-z][a-z0-9]*(-[a-z0-9]+)*with logos longest-match at priority 1, sobest-dis always oneIdenttoken. The parser never seesMinusbetween the halves and cannot produceBinOp::Subtract. The cost the persona was actually paying was the error message:"Did you mean 'best'" reads exactly like "we split your identifier and only the left half resolved." So a model that hits this error mid-task concludes "kebab-case is broken in str-call position," paws around for a workaround, and logs the bug again next session.
This PR is diagnostic-layer only - no grammar surgery. When an undefined kebab-case identifier has every dash-separated half resolving as a scoped variable, function, or builtin, the hint now says:
For 3+ segments the explicit subtraction form is dropped (there's no single binary-subtract spelling for
a-b-c). When only one half resolves the standard closest-match suggestion stays. Token cost of the kill-the-confusion-at-the-source path is much lower than the recurring "log it, find the workaround, document the workaround" loop.Repro
$ ilo "f>t;best=10;d=3;str best-d" fBefore:
After:
When the kebab ident IS bound, output is unchanged (
99, not7) - the parser was always doing the right thing here, only the error path was misleading.What's in the diff
verify: clarify kebab-case in undefined-variable hint- newkebab_subtract_hinthelper next toclosest_match, wired into theILO-T004site forExpr::Refininfer_expr. Falls back to the existing closest-match suggestion when the kebab theory doesn't apply.tests: cross-engine regression for kebab-case lexer and diagnostic- newtests/regression_kebab_precedence.rs. Five positive tests pin the lexer guarantee in str-call arg, list element, multi-segment, digit-segment, and explicit-subtraction forms across tree/VM/Cranelift. Three diagnostic tests cover the new hint, the 3-segment no-subtract-form variant, and the closest-match fallback.examples: kebab-vs-subtract, three forms side by side- newexamples/kebab-vs-subtract.iloexercised byexamples_engines.rs. Same scope bindsbest,d, andbest-d;[str best-d, str best, str d]prints[99, 10, 3]to make the atomicity obvious to any future agent that lands in the file.Test plan
cargo test --release --features craneliftpasses (full suite, 0 failures)cargo fmt --all -- --checkcleancargo clippy --release --features cranelift --all-targets -- -D warningscleanstr best-dwithbest-dbound still returns the kebab value, not subtractionexamples/kebab-vs-subtract.iloruns green via the engine harness on tree, VM, and CraneliftFollow-ups
ilo_assessment_feedback.mdto the canonical✅ Addressedsection after merge, with a one-line note clarifying the diagnostic-layer root cause.