GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — Origin note: say where the wrong state of a receiver comes from by CatarinaGamboa · Pull Request #528 · liquid-java/liquidjava Conversation
|
| Case | Text |
|---|---|
| F1, the call that last set the sets | Origin: line 134, new BufferedInputStream(stream) makes markState(buffered) == unmarked |
| F2, a returned value | Origin: line 130, writer.getDefaultWriteParam() returns compression(wParam) == compressionNotExplicit |
| F2, nothing known | Origin: line 69, frame.getUndoManager() says nothing about redoState(undoManager) |
| F1 fallback, creation | Origin: line L, new T(…) creates x with <state> |
| F3 (#526) | Origin: line 94, timeout is a parameter of checkCimReachable(String, int) with no refinement |
"Makes" is used only for the call the walk proves last set the shown sets. The fallback says "creates … with", because a later call may have set the state again. That is a fact about the constructor, not about the current state.
No origin note is given when the receiver may change state out of the verifier's sight before the call. That covers three cases:
this is used, before the call, other than as the target of a call or field access or in ==/!=. That means copied, passed as an argument (including to the failing call itself), stored in an array, returned, bound in a method reference (x::start), or used in a lambda or a local class. "Before" means earlier in the source, in the failing call's arguments, or in a loop around the call, unless the use is inside a return/throw.Declared state contracts are trusted, as the verifier trusts them. A body that breaks its own declared contract is a verifier issue, outside the notes (see the round 3 reply).
The note covers the sets that the failing call's from checks. When the state machine models the call, it narrows them to the sets where some state the receiver may be in is the state of no allowed state. This only selects what is shown (S1).
Walk back from the receiver through StateChange/previous() to the first change whose to names a shown set. The note is printed only if:
StateChange, so the walk crosses no join, loop head or unknown origin;to keeps the sets it does not name, through set(this) == set(old(this));StateSolver.implies(<instance after that change>, <Found's facts on the shown sets>) holds.Text: Origin: line L, <call> makes <Found on the sets that change names>. With two wrong sets, a change to one is not given the other.
Fallback, when the walk breaks. The note gives the creation fact only if all hold:
Text: Origin: line L, new T(…) creates x with <state>. It is a fact about the constructor. The last condition stops it from speaking when the state may have changed since (A1_09).
Printed only if the walk reaches, possibly after crossing changes to other sets, an instance with no StateChange that holds the call result it was assigned. Instances now record a CallResult when a local is declared or assigned from a call to a refined method. A state-preserving call keeps it, as it keeps the StateChange. Joins, loop heads and transitions make new instances without one.
Origin: line L, <call> returns <facts>: the conjuncts of the instantiated return refinement on the shown sets. Printed only if they imply Found's facts on those sets.Origin: line L, <call> says nothing about <set>(x): printed only if all hold:
to (as to = "running(_)");_, and uses no alias. This rules out _ == this-style returns;state1), which the message would otherwise never show.With -d, the VC at buffered.reset() (line 157) has a single buffered instance, #buffered_259 : (stream == open) ∧ (markState == unmarked). That is the constructor's state, with no ITE and no loop-head instance. The note also took the F1 path, which needs a StateChange on that instance. So neither loop (lines 136 and 150) creates a join for buffered.
A broken walk now logs [WARN] Origin note: no known call gives #x_n its state, e.g. a join of branches. A1_09 logs it for #host_95.
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 State Refinement Error: found compression(wParam) == compressionNotExplicit but expected compression(wParam) == compressionExplicit || compression(wParam) == compressionTypeSet
+ Origin: line 130, writer.getDefaultWriteParam() returns compression(wParam) == compressionNotExplicit State Refinement Error: expected redoState(undoManager) == redoable but could not prove it
+ Origin: line 69, frame.getUndoManager() says nothing about redoState(undoManager)Refinement Error: expected timeout >= 0 but could not prove it - From: timeout is a parameter of checkCimReachable(String, int) (line 94) with no refinement + Origin: line 94, timeout is a parameter of checkCimReachable(String, int) with no refinement
new JFrame(…) gives.Each task still gives exactly one error, on its reportedLine. All three notes are the review's mock-ups, word for word.
I checked every state-error fixture of the suite. Each note names the creating or the last call. After the Codex rounds, the fallback fires in none, and fixtures where the receiver is captured, a field, this in a loop, or given an unspecified call get no note.
TestOriginNotes, three tests that fail without the change:
ErrorStateHintMultipleSets (existing): Origin: line 27, d.lock() makes lockState(d) == locked. Only the wrong set is named, and the walk stops at the change to it;
ErrorStateFromReturnedValue (new, small; no existing fixture assigns a state from a return refinement): Origin: line 27, FromReturnedTask.create() returns phase(task) == idle.
ErrorDeferredCallbackState (existing): the receiver captured by a lambda gets no note.
mvn test: 736 run, 0 failures.
AuxStateHandler declared-here): the one throwStateRefinementError call in changeState, which gets a messageNotes argument. The rest is additive.StateHints): none.throwStateRefinementError, a one-line rebase.🤖 Generated with Claude Code
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Clean-code reviewI audited the diff with the clean-code-principles checklist. Fixed, while writing it:
Declined:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Codex review, round 1
Soundness prompt. It found three programs with misleading notes:
The verifier itself does not follow state changes through aliases (#393 is open), so Found is already wrong in cases 1 and 3. A From note must not present that as a cause. Fixed, all four, in "Leave out the From note for an aliased receiver, and follow a returned value across other sets":
All four programs now print no note, or a sound one. The study tasks are unchanged: A1_38, A1_01 and A1_56 keep their mock-up notes, and A1_09 has none. In the suite's fixtures:
New test: Tests: 733 run, 0 failures. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Codex review, round 2
Soundness prompt. It found five programs, all
In each one, the note named the constructor or the factory as the origin. Fixed, all, in "Count a receiver passed to a method as aliased, and say only what a change set":
Checks:
Tests: 733 run, 0 failures. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Codex review, round 3
Soundness prompt. It found three misleading notes:
Fixed (1) in "Leave out the From note after an unspecified method body on the receiver". "Says nothing" also needs the called method to have no state refinement with a Fixed, a related gap, while looking at (2). No note when a method of the analyzed code with no state refinement is called on the receiver before the failing call. The verifier assumes such a call keeps the state, but that is an assumption, not a contract. Declined (2) and (3). In both, a method or constructor body breaks its declared state contract, and the verifier does not check bodies against state contracts. The error trusts the contract (its Found is Checks:
Tests: 733 run, 0 failures. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Codex review, round 4
Soundness prompt. It found three programs, all with a note where the alias rule asks for none:
Fixed (1) and all three programs in "Apply the alias checks to this and to the failing call's own arguments":
All three programs now print no note. Declined (2): it does not happen. Tests: 733 run, 0 failures. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Codex review, round 5 (last)Soundness prompt. A bound method reference keeps the receiver: Fixed in "Count a method reference to the receiver as aliasing it". Only the target of a call or a field access is a safe use, and a method reference counts as an escape. The two programs (three errors) now print no note. The study tasks and fixture notes are unchanged.
This is round 4's point again. Declined, as there: Tests: 733 run, 0 failures. This is the fifth round, the agreed limit. Each round found narrower cases, from aliases to arguments, |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
"From" read like the from of @StateRefinement, which declared-here shows. The note is now "Origin: line L, <p> is a parameter of <m> with no refinement", the shape every origin note takes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
A state refinement error now has a From message note when what the verifier knows proves it: - the call that last set the wrong state set, found by walking the receiver's StateChange chain through changes to other sets only, and printed only if that change's instance proves the Found on the set; - else, for a receiver assigned the value of a call and not changed since, what the call's return refinement says about the set, or that it says nothing about it when Found says nothing either; - else, when the walk breaks (a join, a loop head) and the receiver is a local only ever written by its constructor initializer, what the constructor makes it, if Found still says that state. The note names only the sets where no possible state is allowed, when the state machine tells. Instances record the call result they were assigned, and a state-preserving call keeps it, as it keeps the StateChange. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…d value across other sets - No note when the receiver may be held by another name before the call: a local or parameter copied, returned or captured by a lambda or local class earlier or in a loop around the call, or a field. - The creation note also needs no other call on the local to a method that changes state, nor a failing call a loop repeats. - A walk that crosses changes to other sets and reaches the call result the receiver was assigned gives the returned-value note, reading what Found says about that instance. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…hange set - A local or parameter used before the call other than as a call's receiver or in a comparison (passed to a method, stored in an array, copied) may be held by another name, so it gets no From note. A use in a return or a throw cannot run again in a loop around the call. - The last-set note shows only the facts on the shown sets the change names, so a change to one of two wrong sets is not given the other. Co-Authored-By: Claude Opus 5.5 <[email protected]>
- No note when a method of the analyzed code with no state refinement is called on the receiver before the failing call: the verifier takes it to keep the state, which its body may still change. - "says nothing" also needs the called method to give no state through a state refinement, as to = "running(_)" does. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- the call that last set the sets "makes" their state; - a call the receiver was assigned "returns" it, or "says nothing about" the sets; - the creation fact says the constructor "creates" the receiver with its state, not that it makes the current one. Co-Authored-By: Claude Opus 5.5 <[email protected]>
4757b3b to
51666ee
Compare
Origin rename (PM review), and Codex review of itWhat changed:
Codex review (one round, as asked):
Tests: 736 run, 0 failures. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
…argument it gives (#530) Closes #522, part 3: **origin note F4**, a value returned by a call. Design: #522 (comment) **Stacked on #528** (`CallResult` and the Origin rename). Retarget this PR to `main` before #528 is merged with `--delete-branch`, or GitHub closes it. A refinement error whose Expected is about a **call argument that is itself a call to a refined method** now says what that method's return refinement guarantees. This is the UX sign-off's amendment 1 for A2_10: instantiated as the error writes it, with the ghost-defining conjunct dropped. ## When it is printed For each variable of Expected that is an argument instance with no variable of its own (shown as the call), holding the `CallResult` of a refined method whose refinement has a source position. Locals assigned from a call are left to #528's state notes. A refinement-error form for them (`Origin: line 44, read() guarantees only n >= -1`) is a question for the PM. It is also left out when the refinement still names an argument the verifier calls after the callee's parameter (`twice(0)` for `_ == b * 2`), which would read as that parameter. **Text:** `Origin: <File>:<line>, <call> guarantees only <R'>`, or `Origin: <File>:<line>, <call> guarantees nothing about <call>` when nothing is left. This is the shape #528 gives every origin note. **Why "only" is sound.** `R'` is the instantiated return refinement minus each conjunct `_ == g(…)` where all hold: - `g` is a declared ghost (`@Ghost`), and never a built-in name like `length`, even when some class declares a ghost of that name; - `g`, on any arguments, appears in no other conjunct and not in Expected. Any arguments, because an alias of the receiver may constrain the ghost under another name; - every fact of the error's Found that applies `g` is the argument's own equality with it; - no alias application in the refinement, Expected or Found may hide a use of `g`. Then nothing constrains `g(…)` but that equality, and ∃g. (_ == g ∧ Q(_)) ≡ Q(_), since `g := _` is a witness. So `R'` is exactly what the refinement says about the value. Nothing else is dropped. Otherwise the equality is kept, e.g. `Origin: …, count(xs) guarantees only count(xs) == length(xs)`. ## A2_10, before and after ```diff 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 75 | public Failure get() { ``` This is the amended mock-up, in the Origin shape. The other notes are unchanged from #528, and every task still gives one error on its `reportedLine`. Across all refinement-error fixtures, the only new notes are the two call arguments in `classes/found_binders_error`: `items.size()` and `count()`. ## Test `TestOriginNotes.callArgumentIsTracedToWhatItsReturnRefinementGuaranteesAboutIt`, on the existing `classes/found_binders_error` (the A2_10 reproducer, line 25): `Origin: ArrayListRefinements.java:13, items.size() guarantees only items.size() >= 0`. It fails without the change. `mvn test`: 737 run, 0 failures. ## Conflicts - With #520/#521 (another agent, `RefinementError` headline): none in the files changed here. `MethodsFunctionsChecker.createVariableRepresentingArgument` gets one line, and `Utils` gets `isBuiltin(name)` over its existing `DEFAULT_NAMES`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <[email protected]>
…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 <[email protected]>
…name The server sends each diagnostic's message notes and the source references of its notes and hints. The panel shows the notes under Found, and renders every message line (headline, notes, hints, declared-here gloss) with the code the verifier marks in backticks in the editor font and each reference, such as `line 134`, as a link that opens that line. Needs a liquidjava-verifier release with liquid-java/liquidjava#528, #530 and #533. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…name The server sends each diagnostic's message notes and the source references of its notes and hints. The panel shows the notes under Found, and renders every message line (headline, notes, hints, declared-here gloss) with the code the verifier marks in backticks in the editor font and each reference, such as `line 134`, as a link that opens that line. Needs a liquidjava-verifier release with liquid-java/liquidjava#528, #530 and #533. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…name The server sends each diagnostic's message notes and the source references of its notes and hints. The panel shows the notes under Found, and renders every message line (headline, notes, hints, declared-here gloss) with the code the verifier marks in backticks in the editor font and each reference, such as `line 134`, as a link that opens that line. Needs a liquidjava-verifier release with liquid-java/liquidjava#528, #530 and #533. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…name (#193) Closes #192 **Stacked on #191** (base `feat/181-declaration-notes`): it renders #191's declared-here gloss, and it needs the same 0.0.38 bump. Retarget this PR to `main` before #191 is merged with `--delete-branch`. ## What changes **Server** - `LJDiagnosticDTO` now sends two more fields for every diagnostic: - `messageNotes`, from `getMessageNotes()`; - `references`, from `getReferences()`, as `SourceReferenceDTO(message, text, start, position)`. The position counts lines and columns from 0, like the other DTO positions. - Both come from the verifier's generic `LJDiagnostic` API. New notes and references therefore show up with no extension change. **Panel (Verification tab)** - The **message notes**, e.g. `Origin: line 134, …`, are shown under Found and the Counterexample. Before this PR the panel did not show notes at all. - Every message line goes through one renderer, `webview/message-text.ts`: the header message, notes, hints and the declared-here gloss. - **Code** the verifier marks with backticks is shown in the editor font, without the backticks, with VS Code's inline-code colours (`textPreformat`). - Spans are parsed as the verifier's `Code.render` parses them: the same run-of-backticks pattern, and one space of padding stripped. - **Each reference** (`line 134`, `ArrayListRefinements.java:162`) is a link that opens that file at that line. It is placed at the reference's `start`; the text is not searched for, as the verifier asks. It reuses the panel's existing `location-link` handler. - **Hints** are now in the UI font, so their code stands out. Before, the whole hint was monospace. - **Header message:** it is now escaped. Before, it was put into the HTML unescaped. **Hover and Problems:** unchanged by this PR. See the two options below. ## Verifier version Needs a release (0.0.38 or later) containing **liquid-java/liquidjava#528, #530 and #533**, on top of #517 and #525 (both on main). - `getReferences()`, `SourceReference` and the backticks come from #533. - The Origin notes come from #528/#530. I tested against #533's head (`1e94930`), installed as 0.0.38 into an isolated Maven repository. Like #191, CI's server build fails until 0.0.38 is on Maven Central. ## Panel at the study width: before (#191) → after (this PR) Both use verifier #533, with Hints open. The notes are new, the code is set apart, and `line 134` is a link. | A1_38 | A2_10 | |---|---| | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A1_38.png" width="420"> | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A2_10.png" width="420"> | | A2_04 | A1_56 | |---|---| | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A2_04.png" width="420"> | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A1_56.png" width="420"> | | A1_01 | A1_09 (hints) | |---|---| | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A1_01.png" width="420"> | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A1_09.png" width="420"> | **Clicking `line 134`** in A1_38 opens line 134, `new BufferedInputStream(stream)`: <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A1_38-link.png" width="860"> Panel text of the notes (verifier #533): ``` A1_38 Origin: line 134, new BufferedInputStream(stream) makes markState(buffered) == unmarked A1_01 Origin: line 130, writer.getDefaultWriteParam() returns compression(wParam) == compressionNotExplicit A2_04 Origin: line 94, timeout is a parameter of checkCimReachable(String, int) with no refinement A2_10 Origin: ArrayListRefinements.java:162, failures.size() guarantees only failures.size() >= 0 A1_56 Origin: line 69, frame.getUndoManager() says nothing about redoState(undoManager) ``` A1_09 has no Origin note. ## Hover: the two options VS Code shows a diagnostic's message as plain text. So the diagnostic hover and the Problems view keep the backticks. - **A, plain text (#190):** the diagnostic message is the headline, then the notes, then the counterexample, all plain with backticks, as rustc writes them. One text, the same as the CLI with colour off, in the hover and in Problems. - **B, markdown provider (prototype on branch `proto/192-markdown-hover`, not in this PR):** - The diagnostic message stays the headline only, as on main. - The extension's `HoverProvider` adds a markdown block with the notes and the counterexample: code spans, and `line 134` as a link. - VS Code draws it under the problem part, so nothing is shown twice. That holds only as long as #190's notes are not also in the diagnostic message. | | A: plain (#190 + #533) | B: markdown provider | |---|---|---| | A1_38 | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A1_38-hover-a.png" width="430"> | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A1_38-hover-b.png" width="430"> | | A2_04 | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A2_04-hover-a.png" width="430"> | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A2_04-hover-b.png" width="430"> | | A2_10 | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A2_10-hover-a.png" width="430"> | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A2_10-hover-b.png" width="430"> | | A1_01 | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A1_01-hover-a.png" width="430"> | <img src="https://raw.githubusercontent.com/liquid-java/vscode-liquidjava/pr-screenshots/193/A1_01-hover-b.png" width="430"> | **Recommendation: A for the study.** - **Same text everywhere.** The hover is the one surface the baseline shares, and the joint review's decision 5 says compared elements must be in the hover/CLI text. A gives one string: hover = Problems = CLI with colour off. - **B splits the message.** It shows the message in two hover parts with two styles: the headline plain with backticks, the notes styled. The Problems view also loses the notes. - **B reads better on its own:** the code is set apart, and the cause is one click away. It is a good candidate after the study. Moving to it means taking the notes back out of #190's diagnostic message, so the two must not both be merged. ## Overlap with open PRs - **#191:** the base of this PR. - **#186** may conflict in `sections.ts`: both change the imports at the top. - **#189** may conflict in `errors.ts` (imports, and next to the Expected lines) and in `styles.ts` (separate rules, so unlikely). - **#185:** probably no textual conflict. It edits `LJErrorDTO` and the error types in `types/diagnostics.ts`; this PR edits `LJDiagnosticDTO` and `BaseDiagnostic`. - **#190:** no conflict in files. It is option A of the hover (above). ## Checks `npm run test:unit` (126 passed; the new panel test fails on the base), `npm run test:typecheck`, `npm run lint`, server `mvn -B test` (40 passed, against #533 as 0.0.38; the new converter test fails on the base). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <[email protected]>
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
Say where the wrong state or value comes from (From note), and counterexamples for state errors
There was an error while loading. Please reload this page.