Skip to content

Commit 99f0a82

Browse files
committed
feat: assertion failure display in trace display
1 parent b967597 commit 99f0a82

File tree

2 files changed

+76
-4
lines changed

2 files changed

+76
-4
lines changed

ProofWidgets/Demos/TraceDisplay.lean

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,3 +565,24 @@ def exampleBMCSatFound : Json := json% {"trace":
565565
"result": "no_violation_found"}
566566

567567
#displayTrace exampleBMCSatFound
568+
569+
570+
def exampleAssertionFailure : Json := json% {"violation":
571+
{"kind": "assertion_failure",
572+
"exception_id": 1,
573+
"assertion_info":
574+
{"procedureName": "send", "moduleName": "Ring", "line": 45, "column": 2}},
575+
"trace":
576+
{"theory": {},
577+
"states":
578+
[{"transition": "after_init",
579+
"index": 0,
580+
"fields": {"pending": [], "leader": []}},
581+
{"transition": {"send": {"next": 1, "n": 0}},
582+
"index": 1,
583+
"fields": {"pending": [], "leader": []},
584+
"failing": true}]},
585+
"state_fingerprint": "10967581844109946240",
586+
"result": "found_violation"}
587+
588+
#displayTrace exampleAssertionFailure

widget/src/traceDisplay.tsx

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,24 @@ interface ParsedState {
2323
index: number;
2424
tag?: string; // Optional tag associated with this state (e.g. transition/action label)
2525
fields: Record<string, unknown>;
26+
failing?: boolean; // True if an assertion failed while executing the transition to this state
2627
}
2728
type Trace = ParsedState[];
2829

29-
type ViolationKind = "safety_failure" | "deadlock";
30+
type ViolationKind = "safety_failure" | "deadlock" | "assertion_failure";
31+
32+
interface AssertionInfo {
33+
procedureName: string;
34+
moduleName: string;
35+
line: number;
36+
column: number;
37+
}
3038

3139
interface Violation {
3240
kind: ViolationKind;
3341
violates?: string[]; // Array of violated property names (only present for safety_failure)
42+
exception_id?: number; // Only present for assertion_failure
43+
assertion_info?: AssertionInfo; // Only present for assertion_failure
3444
}
3545

3646
interface EarlyTerminationCondition {
@@ -49,6 +59,7 @@ interface TraceData {
4959
index: number;
5060
fields: Record<string, unknown>;
5161
transition: unknown;
62+
failing?: boolean;
5263
}>;
5364
instantiation?: Record<string, unknown>;
5465
extraVals?: Record<string, unknown>;
@@ -191,6 +202,7 @@ const StateCard: React.FC<{
191202
onHideField?: (fieldName: string) => void;
192203
onResetForceOpen?: () => void; // Called when user clicks header while forceOpen is active
193204
}> = ({ st, highlighted = false, changes, showRemovals = false, forceOpen = null, hiddenFields, onHideField, onResetForceOpen }) => {
205+
const isFailing = st.failing === true;
194206
const [localOpen, setLocalOpen] = React.useState(true);
195207

196208
// Sync local state when forceOpen changes - this ensures that when we
@@ -218,11 +230,12 @@ const StateCard: React.FC<{
218230
const entries = Object.entries(st.fields).filter(([k]) => !hiddenFields?.has(k));
219231

220232
return (
221-
<div className={`state-card ${highlighted ? "is-highlighted" : ""}`}>
233+
<div className={`state-card ${highlighted ? "is-highlighted" : ""} ${isFailing ? "is-failing" : ""}`}>
222234
<div className="state-header" onClick={handleHeaderClick}>
223235
<span className="action-chip" title={st.tag ?? ''}>
224236
{st.tag || '(no action)'}
225237
</span>
238+
{isFailing && <span className="failing-badge">FAILED</span>}
226239
<span className="state-id">(index: {st.index})</span>
227240
<div className="state-toggle">{open ? "▼" : "▶"}</div>
228241
</div>
@@ -281,8 +294,21 @@ const ResultHeader: React.FC<{
281294
terminationReason?: TerminationReason;
282295
}> = ({ resultType, violation, exploredStates, terminationReason }) => {
283296
if (resultType === "found_violation" && violation) {
284-
const icon = violation.kind === "deadlock" ? "🔒" : "⚠️";
285-
const label = violation.kind === "deadlock" ? "Deadlock Detected" : "Safety Violation Found";
297+
let icon: string;
298+
let label: string;
299+
switch (violation.kind) {
300+
case "deadlock":
301+
icon = "🔒";
302+
label = "Deadlock Detected";
303+
break;
304+
case "assertion_failure":
305+
icon = "💥";
306+
label = "Assertion Failed";
307+
break;
308+
default:
309+
icon = "⚠️";
310+
label = "Safety Violation Found";
311+
}
286312
return (
287313
<div className="result-header result-violation">
288314
<span className="result-icon">{icon}</span>
@@ -292,6 +318,11 @@ const ResultHeader: React.FC<{
292318
<strong>Violated properties:</strong> {violation.violates.join(", ")}
293319
</div>
294320
)}
321+
{violation.kind === "assertion_failure" && violation.assertion_info && (
322+
<div className="result-details">
323+
<strong>Location:</strong> {violation.assertion_info.moduleName}.{violation.assertion_info.procedureName} (line {violation.assertion_info.line}, column {violation.assertion_info.column})
324+
</div>
325+
)}
295326
</div>
296327
);
297328
}
@@ -341,6 +372,7 @@ function traceDataToStates(traceData: TraceData): ParsedState[] {
341372
index: st.index,
342373
tag: formatActionLabel(st.transition),
343374
fields: st.fields,
375+
failing: st.failing,
344376
}));
345377
}
346378

@@ -458,6 +490,25 @@ const ModelCheckerView: React.FC<ModelCheckerViewProps> = ({
458490
outline: 2px solid var(--vscode-editor-selectionHighlightBorder);
459491
box-shadow: 0 0 0 3px var(--vscode-editor-selectionBackground);
460492
}
493+
.state-card.is-failing {
494+
outline: 2px solid var(--vscode-inputValidation-errorBorder);
495+
box-shadow: 0 0 0 3px var(--vscode-inputValidation-errorBackground);
496+
}
497+
.state-card.is-failing .state-header {
498+
background: var(--vscode-inputValidation-errorBackground);
499+
}
500+
.failing-badge {
501+
display: inline-block;
502+
font-family: var(--mono);
503+
font-size: 10px;
504+
font-weight: 600;
505+
background: var(--vscode-inputValidation-errorBorder);
506+
color: var(--vscode-editor-background);
507+
border-radius: 3px;
508+
padding: 2px 6px;
509+
text-transform: uppercase;
510+
letter-spacing: 0.5px;
511+
}
461512
.state-header {
462513
background: var(--header);
463514
border-bottom: 1px solid var(--border);

0 commit comments

Comments
 (0)