Skip to content

Commit 91e2958

Browse files
committed
Add Diagnostic Sections
1 parent 0ca443f commit 91e2958

File tree

7 files changed

+156
-0
lines changed

7 files changed

+156
-0
lines changed

_sass/custom/custom.scss

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,18 @@
4545
box-shadow: 0 18px 36px rgba(15, 23, 42, 0.18);
4646
}
4747

48+
.main-content .diagnostic-image {
49+
display: block;
50+
width: 100%;
51+
max-width: 100%;
52+
margin: 1rem 0 1.5rem;
53+
overflow: hidden;
54+
border: 1px solid rgba(38, 48, 68, 0.45);
55+
border-radius: 1rem;
56+
background: linear-gradient(180deg, #172033 0%, #111827 100%);
57+
box-shadow: 0 18px 36px rgba(15, 23, 42, 0.18);
58+
}
59+
4860
.main-content pre,
4961
.main-content pre.highlight code,
5062
.main-content div.highlighter-rouge pre,

assets/images/error-message.png

128 KB
Loading

diagnostics/custom-messages.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
title: Custom Messages
3+
parent: Diagnostics
4+
nav_order: 3
5+
---
6+
7+
# Custom Messages
8+
9+
A custom message can be provided to any `@Refinement` or `@StateRefinement` annotation using the `msg` parameter. This message will be included in the diagnostic when a violation of that annotation is reported, to provide a clearer explanation of the API rule that was violated.
10+
11+
```java
12+
import liquidjava.specification.*;
13+
14+
@StateSet({"open", "closed"})
15+
public class MyFile {
16+
@StateRefinement(to="open()")
17+
public MyFile() {}
18+
19+
@StateRefinement(from="open()", msg="file must be open to read")
20+
public void read(@Refinement(value="_ > 0", msg="bytes must be greater than zero") int bytes) {}
21+
22+
@StateRefinement(from="open()", to="closed()", msg="file must be open to close")
23+
public void close() {}
24+
}
25+
```
26+
27+
This is useful to explain the API rule in domain terms for users to quickly understand what went wrong.

diagnostics/errors.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
title: Errors
3+
parent: Diagnostics
4+
nav_order: 1
5+
---
6+
7+
# Errors
8+
9+
An error can be caused by a refinement violation, an invalid refinement, or another particular issue.
10+
11+
| Error | Meaning |
12+
| --- | --- |
13+
| `RefinementError` | A refinement was violated |
14+
| `StateRefinementError` | A state refinement was violated |
15+
| `NotFoundError` | An element used in a refinement could not be found |
16+
| `SyntaxError` | The syntax used in a refinement is invalid |
17+
| `ArgumentMismatchError` | A ghost or state invocation has the wrong number or type of arguments |
18+
| `StateConflictError` | A state refinement becomes unsatisfiable because it combines disjoint states |
19+
| `IllegalConstructorTransitionError` | A constructor state refinement declares an illegal `from` transition |
20+
| `InvalidRefinementError` | A refinement is semantically invalid, such as a non-boolean expression |
21+
| `CustomError` | Any other error, such as providing a non-existent path to verify |
22+
23+
LiquidJava is only able to report **at most one error per method** to avoid cascading failures. If there are multiple errors in a method, the verifier will report the first one it encounters, and stop verifying the rest of the method, meaning that fixing one error can sometimes reveal another that was previously hidden.

diagnostics/index.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
title: Diagnostics
3+
nav_order: 3
4+
has_children: true
5+
has_toc: false
6+
permalink: /diagnostics/
7+
description: Understand LiquidJava errors, warnings, diagnostic structure, and custom diagnostic messages.
8+
cards:
9+
- title: Errors
10+
url: /diagnostics/errors/
11+
description: Learn about the different verification errors LiquidJava can report and what each one means.
12+
- title: Warnings
13+
url: /diagnostics/warnings/
14+
description: Learn about the different verification warnings LiquidJava can report and what each one means.
15+
- title: Structure
16+
url: /diagnostics/structure/
17+
description: Learn how to understand a diagnostic message.
18+
- title: Custom Messages
19+
url: /diagnostics/custom-messages/
20+
description: Learn how to provide clearer diagnostic messages using the `msg` parameter.
21+
---
22+
23+
# Diagnostics
24+
25+
Learn how to understand LiquidJava diagnostics, from verification errors and warnings to the structure of a full diagnostic message.
26+
27+
{% include card_grid.html cards=page.cards %}

diagnostics/structure.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
title: Diagnostic Structure
3+
parent: Diagnostics
4+
nav_order: 4
5+
---
6+
7+
# Diagnostic Structure
8+
9+
Consider the following refinement error:
10+
11+
![Example LiquidJava refinement error]({{ '/assets/images/error-message.png' | relative_url }})
12+
{: .diagnostic-image }
13+
14+
Each part gives a different piece of information:
15+
16+
| Part | Meaning |
17+
| --- | --- |
18+
| `Refinement Error` | The kind of diagnostic being reported |
19+
| `#ret² == x / 2 && x > 0` | What LiquidJava knows at the return site. Here, the returned value is `x / 2`, and the parameter refinement says `x > 0` |
20+
| `is not a subtype of` | LiquidJava is checking whether the inferred refinement is strong enough to satisfy the declared one |
21+
| `#ret² > 0` | The declared return refinement, obtained from `@Refinement(value="_ > 0", ...)`. |
22+
| Source snippet | The surrounding lines help locate the failing code in context |
23+
| Caret line | The `^` marks the exact expression that caused the violation |
24+
| `result must be positive` | The custom error message from the annotation's `msg` parameter |
25+
| `Counterexample: x == 1 && #ret² == 0` | A concrete model showing why the check fails. Here we can see the failure arises because if `x == 1`, then `x / 2 == 0` |
26+
27+
## Understanding the Counterexample
28+
29+
The counterexample is a concrete assignment of values that makes the verification fail.
30+
31+
In the example above:
32+
- `x == 1` satisfies the parameter refinement `x > 0`
33+
- `#ret² == 0` follows from the return expression `x / 2`, since integer division makes `1 / 2 == 0`
34+
- `0 > 0` is false, so the declared return refinement is violated
35+
36+
So the counterexample explains exactly why LiquidJava cannot prove that `x / 2` is always positive even when `x > 0`. To fix this error, we would need to either change the return expression to allow to return zero with the refinement `_ >= 0`, or strengthen the parameter refinement to require `x > 1`.
37+
38+
## Internal Instance Variables
39+
40+
Names such as `#ret²` are internal variables introduced by LiquidJava during verification. The small superscript number is a counter used to keep those generated variables unique.
41+
42+
LiquidJava converts expressions into an internal A-normal form (ANF) before verification. Every time it creates a fresh internal variable during that process, the counter is incremented. This is why you may see names such as `#ret²` in the diagnostics.
43+
44+
In practice, you can read `#ret²` as "the internal variable that represents this return value occurrence".
45+
46+
```java
47+
int example(int x) { // x⁰
48+
x = x + 1; // x¹ == x⁰ + 1
49+
int y = x / 2; // y² == x¹ / 2
50+
return y; // #ret³ == y²
51+
}
52+
```

diagnostics/warnings.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
title: Warnings
3+
parent: Diagnostics
4+
nav_order: 2
5+
---
6+
7+
# Warnings
8+
9+
Warnings do not affect the verification like errors, but they indicate that there is an issue that should probably be addressed.
10+
11+
| Warning | Meaning |
12+
| --- | --- |
13+
| `ExternalClassNotFoundWarning` | A class referenced by an external refinement cannot be found |
14+
| `ExternalMethodNotFoundWarning` | A method referenced by an external refinement cannot be found in the target class |
15+
| `CustomWarning` | Any other warning, such as reporting that Java compilation errors are present in the program |

0 commit comments

Comments
 (0)