Repository navigation
Use found … but expected … for refinement errors, drop the general hint, declare every from - #525
Merged
Merged
Conversation
…y entry state Refinement errors now use the same headline frame as state errors, found X but expected Y, instead of "X is not a subtype of Y". An empty found keeps "expected Y but could not prove it", without the general hint to add a refinement or condition, which pointed away from the fix. When a call has several @StateRefinement entry states, the declared-here excerpt marks every from that makes up expected, each with its where line. Each from is located in its own annotation, not at the first annotation that writes the same text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
Clean-code reviewI audited the diff with the clean-code-principles checklist. Fixed:
Declined:
|
Collaborator
Author
Codex review, round 1
|
A concatenated value falls back to the text search, as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
Round 1 reply: fixed. |
Collaborator
Author
Codex review, round 2No findings: "I found no actionable regressions in the diff. The focused declaration-position and diagnostic tests pass." The loop ends here. |
This was referenced Nov 10, 2026
CatarinaGamboa
added a commit
that referenced
this pull request
Nov 11, 2026
…fer to (#533) Closes #532. **Stacked on #530** (→ #528 → `main`). Retarget it to `main` before #530 is merged with `--delete-branch`. Every message that shows code now marks it with backticks, inside the message string: - headlines (`found … but expected …`, `expected … but could not prove it`); - hints; - origin notes; - the declared-here gloss (`where … means …`); - alias `where` lines; - counterexamples. ## How each client sees it - **Plain text** (`getMessage()`, `getMessageNotes()`, `Hint.message()`, `getCounterexampleStr()`, `getDeclarationNotes()`, and so the Problems tab and the hover): the string with backticks, as rustc writes its messages. - **CLI:** each span in blue (the existing `Colors.BLUE`), without backticks. The backticks stay when colour is off: `NO_COLOR` set (https://no-color.org) or no console (output piped or redirected). The colour switch applies to code spans only; the CLI's other colours are unchanged. - **IDE:** the same backticked strings, plus `LJDiagnostic.getReferences()`, a `List<SourceReference>` with one entry per line reference. See below. **Escaping:** `Code.of` fences code that holds backticks with a longer run, and pads it with one space on each side when it holds a backtick or starts or ends with a space: ``` `` c == '`' `` ```. `Code.render` strips exactly that padding, so it shows any non-empty code exactly as marked. ## Source references (for vscode-liquidjava#192) `record SourceReference(String message, String text, int start, String file, int line, int column)`: - `message`: the whole message line it belongs to, exactly as `getMessageNotes()` or `Hint.message()` gives it; - `text`: the words of `message` that name the place, e.g. `line 134` or `ArrayListRefinements.java:162`; - `start`: where `text` starts in `message`, as a UTF-16 index (a Java/JS string index), so `message.substring(start, start + text.length())` is `text`. Use it instead of searching, because the code of a call can hold the same words (`d.lock("line 18")` on line 18); - `file`: the absolute path; `line`, `column`: 1-based, from Spoon. Who carries one: - every origin note: the call, parameter, constructor or return-refinement declaration it names; - the deciding-argument hint (`… on line 20 decides …`), through the new `Hint.reference()`, which is null for other hints. `getReferences()` returns the notes' references first, then the hints'. ## Before → after (CLI piped, so backticks shown) ```text A1_38 State Refinement Error: found `stream(buffered) == open && markState(buffered) == unmarked` but expected `markState(buffered) == marked && stream(buffered) == open` Origin: line 134, `new BufferedInputStream(stream)` makes `markState(buffered) == unmarked` - Call `buffered.mark(int)` before this call; afterwards `markState(buffered) == marked` where `marked(this)` means `markState(this) == marked`, `open(this)` means `stream(this) == open` A1_01 Origin: line 130, `writer.getDefaultWriteParam()` returns `compression(wParam) == compressionNotExplicit` - Call `wParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT)` before this call; afterwards `compression(wParam) == compressionExplicit` A2_04 Refinement Error: expected `timeout >= 0` but could not prove it Origin: line 94, `timeout` is a parameter of `checkCimReachable(String, int)` with no refinement --> Counterexample: `timeout == -1` A1_56 Origin: line 69, `frame.getUndoManager()` says nothing about `redoState(undoManager)` - Guard this call with `if (undoManager.canRedo())`; inside it, `redoState(undoManager) == redoable` A2_10 Refinement Error: found `failures.size() == size(this.failures) && failures.size() >= 0` but expected `failures.size() > 0` Origin: ArrayListRefinements.java:162, `failures.size()` guarantees only `failures.size() >= 0` ``` For all eight tasks (six study tasks and both practice tasks), the output with backticks removed is byte-identical to #530's. Each task still gives exactly one error, on its `reportedLine`. On a terminal the spans are blue and the backticks are gone. With `NO_COLOR=1` the backticks stay. ## Changes - `Code` (`of`, `render`), `SourceReference`, and `Colors.enabled()`. - `LJDiagnostic`: - `addMessageNote(SourceReference)` and `getReferences()`; - `toString` renders the message, notes, counterexample, hints and declaration notes. - Marked at the leaves where messages are built: - `ErrorMessage` (both headline frames); - `RefinementError` (counterexample, alias lines); - `AuxStateHandler.addMeanings` (gloss); - `StateHints`: each state name, call, receiver and constructor, with the prose words `or`, `and`, `then`, `with` left unmarked; - `OriginNotes`/`StateOrigin`, whose notes are now `SourceReference`s. - `NotFoundError` marks the missing name and the suggestion (``Alias `Positiv` could not be found``, ``Did you mean `Positive`?``). - Not marked: the other error kinds (state conflicts, syntax errors) and warnings, which the issue does not list. They are a follow-up if wanted. ## Tests - 59 existing expectations gained backticks. Each change was made by a script that accepts a new string only if removing its backticks gives exactly the old one. - `TestStateHints.assertHints` compares kind and message. - New tests, each failing without the change: - `CodeTest` (fence, colour on and off); - `TestOriginNotes.noteNamesTheLineOfTheCallForClientsToLinkTo`; - a reference check in the deciding-argument hint test. `mvn test`: 740 run, 0 failures. ## Conflicts - **#524 and #525 are merged**, and this branch sits on top of them (via #528's rebase), so it does not conflict with them. The lines of their code it changes are: - `StateHints.java`, on main: the hint templates at 116–119, 131–132, 182–184, 234, 239–240, 249, 252, 258, 260, 269, 295–297, 301, 303–305, 349, and `stateName` at 387–389; - `RefinementError.java`, on main: 71 (counterexample) and 123 (alias `where` lines); - `ErrorMessage.java` 39–41 (headline); - `AuxStateHandler.java` 865–866 (gloss). - With the #529 agent's work: both may touch `VCChecker.throwStateRefinementError`. This PR changes only the notes' parameter type there. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
CatarinaGamboa
added a commit
to liquid-java/vscode-liquidjava
that referenced
this pull request
Nov 11, 2026
The server sends the verifier's declaration positions and declaration notes (the "where ... means ..." lines) of refinement and state errors, and the Locations section shows a link to every position, with the notes under them. Needs liquidjava-verifier 0.0.38: getDeclarationNotes() comes from liquid-java/liquidjava#517 and getDeclarationPositions() from liquid-java/liquidjava#525. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CatarinaGamboa
added a commit
to liquid-java/vscode-liquidjava
that referenced
this pull request
Nov 11, 2026
Closes #181 ## What changes - **Server.** `RefinementErrorDTO` and `StateRefinementErrorDTO` now send: - `declarationPositions`, from `LJDiagnostic.getDeclarationPositions()`; - `declarationNotes`, the `where … means …` lines, from `getDeclarationNotes()`. Both are read through the verifier's generic `LJDiagnostic` API. So when the verifier adds notes or positions, they appear with no extension change. `declarationPosition`, the first position, stays as it is for the State Machine view. - **Client.** The Locations section links to **every** declaration position, then shows the notes under the links, muted and with a hanging indent. A declaration in value form (`doorState(this) == closed`), or one with unnamed sets, has no notes, so nothing changes for it. - **Fallback to one position.** The verifier's default `getDeclarationPositions()` returns the single `getDeclarationPosition()`. Refinement errors and any other error with one declaration therefore show one link, as before. ## Verifier version: needs 0.0.38 (not yet released) | Part | Verifier change | |---|---| | `declarationNotes` (the gloss) | liquid-java/liquidjava#517 (merged in main) | | `declarationPositions` (every `from`, e.g. A1_01 lines 110 and 111) | liquid-java/liquidjava#525 (**open**) | The pom is bumped to `0.0.38`, so **CI's server build fails until 0.0.38 is on Maven Central**. Please merge this PR only after a verifier release that has both #517 and #525. If #525 is not merged, drop the `declarationPositions` lines from the two DTOs (one line each) and use `declarationPosition` in `renderLocation` again. The gloss part only needs #517. I tested it locally against #525's head (`9d1bb1e`, which contains main), installed as 0.0.38 in an isolated Maven repository. ## Before → after (Locations section, at the study width) Before is main, with its server built on verifier main. After is this PR on #525's head. Headless VS Code, 1600×900, Dark Modern, default side bar. **A1_01**: both `from`s, each with its gloss ```diff E_OSS_A1_01_SlideRecoder.java:136 ImageWriteParamRefinements.java:110 + ImageWriteParamRefinements.java:111 + where compressionExplicit(this) means compression(this) == compressionExplicit + where compressionTypeSet(this) means compression(this) == compressionTypeSet ``` **A1_38** ```diff E_OSS_A1_38_FormatReaderManager.java:157 BufferedInputStreamRefinements.java:81 + where marked(this) means markState(this) == marked, open(this) means stream(this) == open ``` **A1_56**: `+ where redoable(this) means redoState(this) == redoable` **A1_09**: `+ where notDisplayable(this) means display(this) == notDisplayable` **A2_04**: unchanged. `@Refinement("_ >= 0")` has no gloss. The missing Hint in the after shot comes from #525, not from this PR. | A1_01 | A1_38 | |---|---| | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/191/A1_01.png" width="420"> | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/191/A1_38.png" width="420"> | | A1_56 | A1_09 | |---|---| | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/191/A1_56.png" width="420"> | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/191/A1_09.png" width="420"> | <details><summary>A2_04</summary> <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/191/A2_04.png" width="420"> </details> The A1_01 and A1_09 shots are scrolled to Locations. The yellow outline is the focus left on Hints by the screenshot script. ## Overlap with open PRs - **#186 (Context tab)** may conflict in `client/src/webview/views/sections.ts`. It changes `renderVariableHighlightButton` and the imports, right after `renderLocation`, which this PR edits. - **#185 (Found as facts)** may conflict in `client/src/types/diagnostics.ts`. Both PRs add fields to `RefinementError` and `StateRefinementError`, in nearby hunks. No conflict in logic. - **#189 (panel wrapping)**: the notes are in the Locations `<pre>`, which keeps `break-word`, so a long `compressionExplicit(this)` can still wrap inside the name at 240 px (see A1_01). They do not share files apart from `styles.ts`, in separate rules. ## Checks `npm run test:unit` (125 passed; the new test fails on main and also checks the copied text), `npm run test:typecheck`, `npm run lint`, server `mvn -B test` (39 passed, against #525 as 0.0.38; the converter test now also checks the notes and the positions). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.
Closes #520
Closes #521
What and why
X is not a subtype of Y, while state errors usefound X but expected Y. Refinement errors now usefound %s but expected %stoo (RefinementError). An empty Found keepsexpected Y but could not prove it.fromin declared-here (Drop the general UNKNOWN hint and show every from in declared-here #521, part 2). When Expected is the disjunction of several@StateRefinemententry states, the "Refinement declared here" excerpt marks everyfrom, and each one gets itswhere … means …line. Before,AuxStateHandlertook afindFirst().StateRefinementErrornow carries the list of declaration positions.LJDiagnostic.getDeclarationPositions()returns it, andgetDeclarationPosition()still returns the first one, so the IDE's Locations and State Machine are unchanged.fromwas found by its text in any LiquidJava annotation of the method. In A1_01, line 111'sfrom = "compressionTypeSet(this)"was placed at line 110'sto = "compressionTypeSet(this)".from/topositions now come from the annotation's own value, and the text search is only a fallback.Soundness: no message claims anything new. The excerpt and
wherelines quote the spec, and the Expected they explain is already the disjunction of these samefroms.Study tasks: before (main) → after
Each task still gives exactly one error, on its
reportedLine. A1_38, A1_56 and A1_09 are unchanged.A2_10
practice
practice2
A2_04: the headline is unchanged (
expected timeout >= 0 but could not prove it), and the hint block is gone:A1_01: declared-here, after:
Before, only line 110 was marked, and only its
whereline was shown.Tests
is not a subtype of(TestFoundBinders, TestStaticConstantNames, RefinementErrorCounterexampleTest).RefinementErrorCounterexampleTest.messageWithNothingFoundGivesNoHint: an empty Found gives no hint.TestDeclarationPositions.stateErrorPointsToEveryEntryStateWithWhatItMeanswith the new fixtureErrorStateRefinementEveryFrom.java, a named set with twofroms where the secondfromrepeats the first annotation'sto. It checks both positions and bothwherelines.mvn test: 731 tests, 0 failures.Study env: re-run after release (not edited here)
test/verify-tasks.py→test/out/verify-new.json: the practice, practice2 and A2_10 headlines, and the A2_04 hint.test/e2e.mjs):test/out/B-01…B-07-tour-step*.png(the tour runs on the practice file, whose headline changes).B-02-practice2-hover.png,B-11-practice2-hover.png,B-01/B-10-practice2-repair.png, and the block-1 practice equivalents.A-run2/A-03-A2_10-hover.png,A-run2/A-04-A2_10-liquidjava-view.png(headline).A-19-A2_04-hover.png,A-20-A2_04-liquidjava-view.png(hint removed).froms in the panel needs an extension change (it can readgetDeclarationPositions()).🤖 Generated with Claude Code