Repository navigation
Show states of named sets by their set in state hints and declared-here - #517
Conversation
The headline of a state error shows a state of a named set as a value of its set, e.g. lockState(d) == locked. The hints now name such states the same way (lockState == locked), and group alternatives in parentheses so "and" and "or" cannot be misread. The declared-here excerpt stays as written, with a "where" line under it saying what each shorthand state in it means. Display only: the VC, verdict, hints chosen and category do not change. States of unnamed sets, and names shared by sets with different names, keep the shorthand, as in the headline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clean-code reviewAudit of this PR's new code only (
No SOLID or layering issues: the hint wording stays in |
Codex review, round 1
|
When a class and its superclass each have a set with the same name, their states would read as values of one set (phase == ready and phase == busy), in the headline and in the hints. They keep the shorthand instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reply to Codex review, round 1
Full suite passes locally (728 tests). |
Codex review, round 2
|
Reply to Codex review, round 2No code changes this round.
|
Codex review, round 3No findings. Codex rebuilt the verifier, ran |
As the PM decided: a hint names a state of a named set as the message does, lockState(d) == locked, and the sentences that said "d is" or "needs it to be" a state now state the condition, e.g. "needs undoState(um) == undoable". Classes without named sets keep the shorthand wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex review, round 4On the new diff (hints in the headline's form with the receiver, as the PM decided; commit e2ce936). No findings. Codex rebuilt the verifier and ran the CLI on fresh inputs: named sets, named and unnamed sets mixed in one class, and a field receiver |
The note under "declared here" was found by matching calls in the source line. It now walks the declaration's from as parsed, before states written as values of their sets are read as the shorthand, so only a state of a named set written as the shorthand is explained. The notes are computed where the error is built and passed to it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex review, round 5On the AST-based declared-here note (commit 361d463). No correctness bug found. One test gap: the gloss assertion ( Declined: the regex is deleted, so a test that only fails against deleted code protects nothing going forward. |
Re: "why do we need CALL_ON_A_NAME if we have an AST already?"You're right, it's gone (commit 361d463). Why the regex existed. The note has to explain what the source wrote: What replaced it.
The output is unchanged for the PR's examples. A value-form |
… a call sequence works (#524) Closes #518 Closes #519 ## What and why Changes to the state hints in `StateHints` (display and choice of hints only; the VC, verdict and category are unchanged): 1. **Call sequences name only the state they lead to** (#519): `Call S before this call; afterwards <state>`. The "from" part is gone, since the Found already shows it. In A1_09 it listed every state of the set (`from display(host) == notDisplayable or display(host) == displayable`). "Afterwards" is the machine's `after` state for the changed sets, so the hint only states what the machine computes after the calls, and no `!=` is turned into `==`. The value form from #517 stays (`markState(buffered) == marked`). Classes with no named set read `afterwards f is open`. 2. **A deciding argument is written as the call** (#519): when a step's condition is `param == c` for the method's only parameter, and `c` names no variable, the step is written `wParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT)`. Any other condition keeps `r.set(int lo, int hi) with lo < hi`. 3. **No GUARD hint when a CALL_SEQUENCE hint exists** (#518). Wrapping the call in the check skips it in exactly the failing case. In A1_09 the verifier accepts that fix and the study grader marks it INCORRECT. Before, the guard was dropped only for a single known state. The unknown-state hints (A1_56) do not change: its guard stays, since it has no call sequence and the guard is the reference fix. 4. **Guard wording** (#518): `Guard this call with if (<check>); inside it, <state>`. This replaces "when it returns false", which was ambiguous next to `!host.isDisplayable()`. 5. **AVAILABLE_CALLS ("can call") is dropped when any other hint is given** (#519). FINAL_STATE is unchanged. ## Study tasks (CLI, before = main, after = this branch) Each task still gives exactly one error, on its `reportedLine`. A2_10, A2_04, practice and practice2 are unchanged. **A1_38** ``` before: - Call buffered.mark(int) before this call to move buffered from markState(buffered) == unmarked to markState(buffered) == marked - When stream(buffered) == open and markState(buffered) == unmarked, buffered can call: read(), read(byte[], int, int), skip(long), available(), mark(int), ... after: - Call buffered.mark(int) before this call; afterwards markState(buffered) == marked ``` **A1_01** ``` before: - Call wParam.setCompressionMode(int mode) with mode == ImageWriteParam.MODE_EXPLICIT before this call to move wParam from compression(wParam) == compressionNotExplicit to compression(wParam) == compressionExplicit - When tiling(wParam) == tilingNotExplicit and compression(wParam) == compressionNotExplicit, wParam can call: setTilingMode(int), setCompressionMode(int) (and possibly others) after: - Call wParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) before this call; afterwards compression(wParam) == compressionExplicit ``` **A1_09** ``` before: - Call host.dispose() before this call to move host from display(host) == notDisplayable or display(host) == displayable to display(host) == notDisplayable - Call host.removeNotify() before this call to move host from display(host) == notDisplayable or display(host) == displayable to display(host) == notDisplayable - Guard this call with if (!host.isDisplayable()): when it returns false, display(host) == notDisplayable - When display(host) == notDisplayable or display(host) == displayable, host can call: pack(), dispose(), addNotify(), removeNotify() (and possibly others) after: - Call host.dispose() before this call; afterwards display(host) == notDisplayable - Call host.removeNotify() before this call; afterwards display(host) == notDisplayable ``` `host.dispose()` is still the first hint. `analysis/check-fixes.py --selftest --task A1_09` passes 8/8 and grades the plain `dispose()` fix (P01) CORRECT. **A1_56** (the guard stays) ``` before: - The state of undoManager is unknown here, and this call needs redoState(undoManager) == redoable - Guard this call with if (undoManager.canRedo()): when it returns true, redoState(undoManager) == redoable after: - The state of undoManager is unknown here, and this call needs redoState(undoManager) == redoable - Guard this call with if (undoManager.canRedo()); inside it, redoState(undoManager) == redoable ``` ## IDE Hints reach vscode-liquidjava as plain message text, so they show there with no extension change. There are no screenshots, because nothing in the extension's layout changes. ## Tests The exact-message expectations in `TestStateHints` are updated. Each of these fails on main: the new wording, no "can call" next to another hint, and `setCompressionMode(2)` / `chain.pick(1)` for the argument form. One case is added to the existing `ErrorStateHintGuards` fixture: `reachableOnEveryPath`, the A1_09 shape (path-dependent state, a call that reaches the needed state, a query). On main it gives a sequence, a guard and "can call"; now it gives only the sequence. The full suite passes locally (728 tests). ## Open for the PM - The unknown-state hints (`anyState`, used by A1_56) can also list a call sequence after the guard. #518 scopes the rule to `forCall`, so the guard is kept there. None of the study tasks has both. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Closes #515
What and why
Since #511 the headline of a state error shows a state of a named set as a value of its set (
lockState(d) == locked), but the hints and "Refinement declared here" still used the shorthand (locked,closed(this)). One message used several notations for the same states. Now:lockState(d) == locked(PM decision). The sentences that said "d is X" or "needs it to be X" now state the condition:when it returns true, undoState(um) == undoable,this call needs undoState(um) == undoable,When doorState(d) == closed and lockState(d) == locked, d can call: .... In that form, a state of an unnamed set in the same class is shown as the headline shows it too,closed(d). Classes with no named set keep the current wording (In state created, f can call: ...).(a and b) or (c and d). This applies to unnamed sets too, since the problem is the same.whereline under it says what they mean, for those states only:where closed(this) means doorState(this) == closed, .... It is the samewherestyle the alias notes of refinement errors use.Display only: the VC, the verdict, which hints are given and the category do not change.
Same rule as the headline. Hints and the gloss both use the rule of
StateValues.toSetForm. Its set lookup is now exposed asStateValues.setNameOf(the hints use it), and the gloss runs the error's owndisplayrewrite on eachstate(arg)call in the declaration. So:stateN) sets keep the shorthand;locked(this), never!locked(this), so!locked(this)reads as!(lockState(this) == locked). A declaration in value form (lockState(this) != locked) gets no gloss, since it already uses the headline's notation.Before / after (CLI, ANSI colours stripped)
ErrorStateHintMultipleSets(setsdoorState,lockState)Before:
After:
The same spec in value form with
!=(from = "doorState(this) == closed && lockState(this) != locked")Before:
After (no gloss: the declaration is already in value form; the hints name the machine's states as before, only in the new notation):
state_hint_study_undo_manager_error, sets namedphase/undoState/redoState, withif (um.canRedo()) um.redo(); um.undo();Before:
After:
The fixture's two other errors change the same way:
this call needs redoState(undoManager) == redoable,when it returns true, redoState(undoManager) == redoable, andwhere redoable(this) means redoState(this) == redoable.Decided by the PM
lockState(d) == locked), including grouped alternatives and the guard and "needs" sentences.(this):where closed(this) means doorState(this) == closed, ....Still open for the PM
undoState). That would change which states a hint names, not only how they are written, so it is not in this PR.IDE
Hints reach vscode-liquidjava as plain message text, so the new notation shows there with no change. The extension does not show the declaration excerpt, only a location link, so the gloss needs a server and client change: liquid-java/vscode-liquidjava#181 (label
study).Tests
The existing exact-message tests in
TestStateHintsare updated, and one test is added. Each one fails on main:callSequenceNamesOnlyTheStateSetsItChanges: the hints fordoorState/lockState(named sets), in the headline form.queryWrittenWithItsStateSetIsOfferedAsAGuard: needed state and guard for setlock(named set, value-form spec).joinsAreNotTakenForAnUnknownState: grouping, with unnamed sets, which keep the shorthand.foundKeepsANamedStateSetByItsName: also checks thewhereline under the declaration.setNameOfTheClassAndOfItsSuperclassKeepsTheShorthand(fixtureErrorStateSetNameInSuperclass), from Codex round 1. When a class and its superclass each have a set namedphase, the headline (Show the states of named sets as values of their sets in state errors #511's rule) and the hints printedphase(c) == ready && phase(c) == busy, which reads as two values of one set.StateValues.setNameOf, shared by both, now keeps the shorthand in that case.The other hint tests use unnamed sets and pass unchanged, so the shorthand is kept there. The full suite passes locally (728 tests).
🤖 Generated with Claude Code