From c9dcc47db22fb0fb6bf4be7ccdae450a631e8d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr> Date: Mon, 11 Sep 2023 22:01:15 +0200 Subject: [PATCH] [wp/ivette] goals filter --- ivette/src/frama-c/plugins/wp/api/index.ts | 10 +++-- ivette/src/frama-c/plugins/wp/goals.tsx | 48 ++++++++++++++-------- ivette/src/frama-c/plugins/wp/index.tsx | 45 ++++++++++++++++++-- src/plugins/wp/wpApi.ml | 4 ++ 4 files changed, 83 insertions(+), 24 deletions(-) diff --git a/ivette/src/frama-c/plugins/wp/api/index.ts b/ivette/src/frama-c/plugins/wp/api/index.ts index e8bd3107906..583cfa27486 100644 --- a/ivette/src/frama-c/plugins/wp/api/index.ts +++ b/ivette/src/frama-c/plugins/wp/api/index.ts @@ -193,6 +193,8 @@ export interface goalsData { name: string; /** Smoking (or not) goal */ smoke: boolean; + /** Valid or Passed goal */ + passed: boolean; /** Verdict, Status */ status: status; /** Prover Stats Summary */ @@ -213,6 +215,7 @@ export const jGoalsData: Json.Decoder<goalsData> = thy: Json.jOption(Json.jString), name: Json.jString, smoke: Json.jBoolean, + passed: Json.jBoolean, status: jStatus, stats: jStats, script: Json.jOption(Json.jString), @@ -223,8 +226,8 @@ export const jGoalsData: Json.Decoder<goalsData> = export const byGoalsData: Compare.Order<goalsData> = Compare.byFields <{ wpo: goal, property: marker, fct?: fct, bhv?: string, thy?: string, - name: string, smoke: boolean, status: status, stats: stats, - script?: string, saved: boolean }>({ + name: string, smoke: boolean, passed: boolean, status: status, + stats: stats, script?: string, saved: boolean }>({ wpo: byGoal, property: byMarker, fct: Compare.defined(byFct), @@ -232,6 +235,7 @@ export const byGoalsData: Compare.Order<goalsData> = thy: Compare.defined(Compare.string), name: Compare.string, smoke: Compare.boolean, + passed: Compare.boolean, status: byStatus, stats: byStats, script: Compare.defined(Compare.string), @@ -288,7 +292,7 @@ export const goals: State.Array<goal,goalsData> = goals_internal; /** Default value for `goalsData` */ export const goalsDataDefault: goalsData = { wpo: goalDefault, property: markerDefault, fct: undefined, - bhv: undefined, thy: undefined, name: '', smoke: false, + bhv: undefined, thy: undefined, name: '', smoke: false, passed: false, status: statusDefault, stats: statsDefault, script: undefined, saved: false }; diff --git a/ivette/src/frama-c/plugins/wp/goals.tsx b/ivette/src/frama-c/plugins/wp/goals.tsx index a02b4300db2..45d135b2773 100644 --- a/ivette/src/frama-c/plugins/wp/goals.tsx +++ b/ivette/src/frama-c/plugins/wp/goals.tsx @@ -22,8 +22,8 @@ import React from 'react'; import { IconKind, Cell } from 'dome/controls/labels'; +import { Filter } from 'dome/table/models'; import { Table, Column } from 'dome/table/views'; -import * as Ivette from 'ivette'; import * as States from 'frama-c/states'; import * as WP from 'frama-c/plugins/wp/api'; @@ -62,14 +62,33 @@ function renderStatus(s : Status): JSX.Element { return <Cell {...s} />; } +/* -------------------------------------------------------------------------- */ +/* --- Goals Filter --- */ +/* -------------------------------------------------------------------------- */ + +function filterGoal( + failed: boolean, + scope: string | undefined, +): Filter<WP.goal, WP.goalsData> { + return (goal: WP.goalsData): boolean => { + if (failed && goal.passed) return false; + if (scope && scope && goal.fct !== scope) return false; + return true; + }; +} + /* -------------------------------------------------------------------------- */ /* --- Goals Table --- */ /* -------------------------------------------------------------------------- */ -function WPGoals(): JSX.Element { - const model = States.useSyncArrayModel(WP.goals); +export interface GoalTableProps { + scope: string | undefined; + failed: boolean; +} - // TODO: from AST selection, find WPO +export function GoalTable(props: GoalTableProps): JSX.Element { + const { scope, failed } = props; + const model = States.useSyncArrayModel(WP.goals); const [_, updateAstSelection] = States.useSelection(); const [wpoSelection, setWpoSelection] = React.useState(WP.goalDefault); @@ -81,6 +100,14 @@ function WPGoals(): JSX.Element { }, [updateAstSelection], ); + React.useEffect(() => { + if (failed || !!scope) { + model.setFilter(filterGoal(failed, scope)); + } else { + model.setFilter(); + } + }, [model, scope, failed]); + return ( <Table model={model} @@ -96,16 +123,3 @@ function WPGoals(): JSX.Element { } /* -------------------------------------------------------------------------- */ -/* --- Goals Component --- */ -/* -------------------------------------------------------------------------- */ - -Ivette.registerComponent({ - id: 'frama-c.plugins.wp.goals', - group: 'frama-c.plugins', - rank: 10, - label: 'WP Goals', - title: 'WP Generated Verification Conditions', - children: <WPGoals />, -}); - -/* -------------------------------------------------------------------------- */ diff --git a/ivette/src/frama-c/plugins/wp/index.tsx b/ivette/src/frama-c/plugins/wp/index.tsx index aa17e7478a6..9b6fd0efcde 100644 --- a/ivette/src/frama-c/plugins/wp/index.tsx +++ b/ivette/src/frama-c/plugins/wp/index.tsx @@ -24,13 +24,50 @@ // --- Eva Values // -------------------------------------------------------------------------- +import React from 'react'; +import * as Dome from 'dome'; +import { IconButton } from 'dome/controls/buttons'; import * as Ivette from 'ivette'; -import './goals'; +import * as States from 'frama-c/states'; +import { GoalTable } from './goals'; import './style.css'; -// -------------------------------------------------------------------------- -// --- Export Component -// -------------------------------------------------------------------------- +/* -------------------------------------------------------------------------- */ +/* --- Goal Component --- */ +/* -------------------------------------------------------------------------- */ + +function WPGoals(): JSX.Element { + const [scoped, flipScoped] = Dome.useFlipSettings('frama-c.wp.goals.scoped'); + const [failed, flipFailed] = Dome.useFlipSettings('frama-c.wp.goals.failed'); + const [selection] = States.useSelection(); + const fct = selection?.current?.fct; + const scope = scoped ? fct : undefined; + return ( + <> + <Ivette.TitleBar> + <IconButton icon='COMPONENT' title='Current Scope Only' + enabled={!!fct} + selected={scoped} onClick={flipScoped} /> + <IconButton icon='CIRC.QUESTION' title='Unresolved Goals Only' + selected={failed} onClick={flipFailed} /> + </Ivette.TitleBar> + <GoalTable scope={scope} failed={failed} /> + </> + ); +} + +Ivette.registerComponent({ + id: 'frama-c.plugins.wp.goals', + group: 'frama-c.plugins', + rank: 10, + label: 'WP Goals', + title: 'WP Generated Verification Conditions', + children: <WPGoals />, +}); + +/* -------------------------------------------------------------------------- */ +/* --- WP View --- */ +/* -------------------------------------------------------------------------- */ Ivette.registerView({ id: 'wp.main', diff --git a/src/plugins/wp/wpApi.ml b/src/plugins/wp/wpApi.ml index 95f1c92875a..7f63a336980 100644 --- a/src/plugins/wp/wpApi.ml +++ b/src/plugins/wp/wpApi.ml @@ -213,6 +213,10 @@ let () = S.column gmodel ~name:"smoke" ~descr:(Md.plain "Smoking (or not) goal") ~data:(module D.Jbool) ~get:Wpo.is_smoke_test +let () = S.column gmodel ~name:"passed" + ~descr:(Md.plain "Valid or Passed goal") + ~data:(module D.Jbool) ~get:Wpo.is_passed + let () = S.column gmodel ~name:"status" ~descr:(Md.plain "Verdict, Status") ~data:(module STATUS) ~get:get_status -- GitLab