From f7e1335980fbd03fb7bf7262a3293014be26af90 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20B=C3=BChler?= <david.buhler@cea.fr>
Date: Mon, 8 Jun 2020 10:35:15 +0200
Subject: [PATCH] [ivette] Adds filters to the property table.

---
 ivette/src/renderer/Properties.tsx | 293 +++++++++++++++++++++++++----
 1 file changed, 260 insertions(+), 33 deletions(-)

diff --git a/ivette/src/renderer/Properties.tsx b/ivette/src/renderer/Properties.tsx
index 80d42e5d81d..9a06620a0d1 100644
--- a/ivette/src/renderer/Properties.tsx
+++ b/ivette/src/renderer/Properties.tsx
@@ -10,6 +10,146 @@ import { Label, Code } from 'dome/controls/labels';
 import { ArrayModel } from 'dome/table/arrays';
 import { Table, Column, ColumnProps, Renderer } from 'dome/table/views';
 import { Component } from 'frama-c/LabViews';
+import { Vfill } from 'dome/layout/boxes';
+import { Splitter } from 'dome/layout/splitters';
+import { Form, Section, FieldCheckbox } from 'dome/layout/forms';
+
+// --------------------------------------------------------------------------
+// --- Filters
+// --------------------------------------------------------------------------
+
+const defaultStatusFilter =
+{
+  valid: true,
+  valid_hyp: true,
+  unknown: true,
+  invalid: true,
+  invalid_hyp: true,
+  considered_valid: false,
+  untried: false,
+  dead: false,
+  inconsistent: true,
+};
+
+const defaultKindFilter =
+{
+  assert: true,
+  invariant: true,
+  variant: true,
+  requires: true,
+  ensures: true,
+  instance: true,
+  assumes: true,
+  assigns: true,
+  from: true,
+  allocates: true,
+  behavior: false,
+  reachable: false,
+  axiomatic: true,
+  pragma: true,
+  others: true,
+};
+
+const defaultAlarmsFilter =
+{
+  alarms: true, // show properties that are alarms
+  others: true, // show properties that are not alarms
+  overflow: true,
+  division_by_zero: true,
+  mem_access: true,
+  index_bound: true,
+  pointer_value: true,
+  shift: true,
+  ptr_comparison: true,
+  differing_blocks: true,
+  separation: true,
+  overlap: true,
+  initialization: true,
+  dangling_pointer: true,
+  special_float: true,
+  float_to_int: true,
+  function_pointer: true,
+  union_initialization: true,
+  bool_value: true,
+};
+
+const defaultFilter =
+{
+  currentFunction: false,
+  status: defaultStatusFilter,
+  kind: defaultKindFilter,
+  alarms: defaultAlarmsFilter,
+};
+
+
+function filterStatus(f: typeof defaultStatusFilter, status: string) {
+  switch (status) {
+    case 'valid':
+    case 'valid_but_dead': return f.valid;
+    case 'valid_under_hyp': return f.valid_hyp;
+    case 'invalid':
+    case 'invalid_but_dead': return f.invalid;
+    case 'invalid_under_hyp': return f.invalid_hyp;
+    case 'unknown':
+    case 'unknown_but_dead': return f.unknown;
+    case 'considered_valid': return f.considered_valid;
+    case 'never_tried': return f.untried;
+    case 'dead': return f.dead;
+    case 'inconsistent': return f.inconsistent;
+    default: return true;
+  }
+}
+
+function filterKind(f: typeof defaultKindFilter, kind: string) {
+  switch (kind) {
+    case 'assert': return f.assert;
+    case 'invariant': return f.invariant;
+    case 'variant': return f.variant;
+    case 'requires': return f.requires;
+    case 'ensures': return f.ensures;
+    case 'instance': return f.instance;
+    case 'assigns': return f.assigns;
+    case 'from': return f.from;
+    case 'allocates': return f.allocates;
+    case 'behavior': return f.behavior;
+    case 'reachable': return f.reachable;
+    case 'axiomatic': return f.axiomatic;
+    case 'pragma': return f.pragma;
+    default: return f.others;
+  }
+}
+
+function filterAlarm(f: typeof defaultAlarmsFilter, alarm: string) {
+  switch (alarm) {
+    case 'overflow': return f.overflow;
+    case 'division_by_zero': return f.division_by_zero;
+    case 'mem_access': return f.mem_access;
+    case 'index_bound': return f.index_bound;
+    case 'pointer_value': return f.pointer_value;
+    case 'shift': return f.shift;
+    case 'ptr_comparison': return f.ptr_comparison;
+    case 'differing_blocks': return f.differing_blocks;
+    case 'separation': return f.separation;
+    case 'overlap': return f.overlap;
+    case 'initialization': return f.initialization;
+    case 'dangling_pointer': return f.dangling_pointer;
+    case 'is_nan_or_infinite':
+    case 'is_nan': return f.special_float;
+    case 'float_to_int': return f.float_to_int;
+    case 'function_pointer': return f.function_pointer;
+    case 'initialization_of_union': return f.union_initialization;
+    case 'bool_value': return f.bool_value;
+    default: return true;
+  }
+}
+
+function filterProperty(f: typeof defaultFilter, item: Property) {
+  return filterStatus(f.status, item.status)
+    && filterKind(f.kind, item.kind)
+    && ((item.alarm && f.alarms.alarms)
+      || (!item.alarm && f.alarms.others))
+    && (!item.alarm || filterAlarm(f.alarms, item.alarm));
+}
 
 // --------------------------------------------------------------------------
 // --- Property Columns
@@ -35,11 +175,12 @@ const renderNames: Renderer<string[]> =
   (names?: string[]) => {
     const label = names?.join(': ');
     return (label ? <Label label={label} /> : null);
-  }
+  };
 
 const renderFile: Renderer<SourceLoc> =
-  (loc?: SourceLoc) =>
-    (loc ? <Label label={loc.base} title={loc.file} /> : null);
+  (loc?: SourceLoc) => (
+    loc ? <Label label={loc.base} title={loc.file} /> : null
+  );
 
 // --------------------------------------------------------------------------
 // --- Properties Table
@@ -112,6 +253,8 @@ const RenderTable = () => {
     States.useDictionary('kernel.dictionary.propstatus');
   const [select, setSelect] =
     States.useSelection();
+  const selectedFunction = select?.function;
+  const filter = React.useRef(defaultFilter);
 
   React.useEffect(() => {
     const data = _.toArray(items);
@@ -132,38 +275,122 @@ const RenderTable = () => {
 
   const selection = select?.marker;
 
+  /* --- Filters selection -------------------------------------------------- */
+
+  React.useEffect(() => {
+
+    function filtering(item: Property) {
+      const current =
+        !filter.current.currentFunction
+        || (selectedFunction === item?.function);
+      return current && filterProperty(filter.current, item);
+    }
+
+    model.setFilter(filtering);
+  }, [model, filter, selectedFunction]);
+
+  const filtersList = (
+    <Form
+      value={filter.current}
+      onChange={() => model.reload()}
+    >
+      <FieldCheckbox label="Current function" path="currentFunction" />
+      <Section label="Status" unfold path="status">
+        <FieldCheckbox label="Valid" path="valid" />
+        <FieldCheckbox label="Valid under hyp." path="valid_hyp" />
+        <FieldCheckbox label="Unknown" path="unknown" />
+        <FieldCheckbox label="Invalid" path="invalid" />
+        <FieldCheckbox label="Invalid under hyp." path="invalid_hyp" />
+        <FieldCheckbox label="Considered valid" path="considered_valid" />
+        <FieldCheckbox label="Untried" path="untried" />
+        <FieldCheckbox label="Dead" path="dead" />
+        <FieldCheckbox label="Inconsistent" path="inconsistent" />
+      </Section>
+      <Section label="Property kind" path="kind">
+        <FieldCheckbox label="Assertions" path="assert" />
+        <FieldCheckbox label="Invariants" path="invariant" />
+        <FieldCheckbox label="Variants" path="variant" />
+        <FieldCheckbox label="Preconditions" path="requires" />
+        <FieldCheckbox label="Postconditions" path="ensures" />
+        <FieldCheckbox label="Instance" path="instance" />
+        <FieldCheckbox label="Assigns clauses" path="assigns" />
+        <FieldCheckbox label="From clauses" path="from" />
+        <FieldCheckbox label="Allocates" path="allocates" />
+        <FieldCheckbox label="Behaviors" path="behavior" />
+        <FieldCheckbox label="Reachables" path="reachable" />
+        <FieldCheckbox label="Axiomatics" path="axiomatic" />
+        <FieldCheckbox label="Pragma" path="pragma" />
+        <FieldCheckbox label="Others" path="others" />
+      </Section>
+      <Section label="Alarms" path="alarms">
+        <FieldCheckbox label="Alarms" path="alarms" />
+        <FieldCheckbox label="Others" path="others" />
+      </Section>
+      <Section label="Alarms kind" path="alarms">
+        <FieldCheckbox label="Overflows" path="overflow" />
+        <FieldCheckbox label="Divisions by zero" path="division_by_zero" />
+        <FieldCheckbox label="Shifts" path="shift" />
+        <FieldCheckbox label="Special floats" path="special_float" />
+        <FieldCheckbox label="Float to int" path="float_to_int" />
+        <FieldCheckbox label="_Bool values" path="bool_value" />
+        <FieldCheckbox label="Memory accesses" path="mem_access" />
+        <FieldCheckbox label="Index bounds" path="index_bound" />
+        <FieldCheckbox label="Initializations" path="initialization" />
+        <FieldCheckbox label="Dangling pointers" path="dangling_pointer" />
+        <FieldCheckbox label="Pointer values" path="pointer_value" />
+        <FieldCheckbox label="Function pointers" path="function_pointer" />
+        <FieldCheckbox label="Pointer comparisons" path="ptr_comparison" />
+        <FieldCheckbox label="Differing blocks" path="differing_blocks" />
+        <FieldCheckbox label="Separations" path="separation" />
+        <FieldCheckbox label="Overlaps" path="overlap" />
+        <FieldCheckbox
+          label="Initialization of unions"
+          path="union_initialization"
+        />
+      </Section>
+    </Form>
+  );
+
   // Rendering
   return (
-    <Table<string, Property>
-      model={model}
-      sorting={model}
-      selection={selection}
-      onSelection={onSelection}
-      settings="ivette.properties.table"
-    >
-      <Column
-        id="path"
-        label="Directory"
-        width={240}
-        visible={false}
-        getter={(prop: Property) => prop.source.dir}
-      />
-      <Column id="source" label="File" width={120} render={renderFile} />
-      <ColumnCode id="function" label="Function" width={120} />
-      <ColumnCode id="kind" label="Property kind" width={120} />
-      <ColumnCode id="alarm" label="Alarms" width={160} />
-      <Column id="names" label="Names" width={240} visible={false}
-        render={renderNames} />
-      <ColumnCode id="predicate" label="Predicate" fill />
-      <ColumnCode id="descr" label="Property" fill visible={false} />
-      <ColumnTag
-        id="status"
-        label="Status"
-        width={100}
-        align="center"
-        getter={getStatus}
-      />
-    </Table>
+    <Splitter dir="LEFT">
+      <Vfill> {filtersList} </Vfill>
+      <Table<string, Property>
+        model={model}
+        sorting={model}
+        selection={selection}
+        onSelection={onSelection}
+        settings="ivette.properties.table"
+      >
+        <Column
+          id="path"
+          label="Directory"
+          width={240}
+          visible={false}
+          getter={(prop: Property) => prop.source.dir}
+        />
+        <Column id="source" label="File" width={120} render={renderFile} />
+        <ColumnCode id="function" label="Function" width={120} />
+        <ColumnCode id="kind" label="Property kind" width={120} />
+        <ColumnCode id="alarm" label="Alarms" width={160} />
+        <Column
+          id="names"
+          label="Names"
+          width={240}
+          visible={false}
+          render={renderNames}
+        />
+        <ColumnCode id="predicate" label="Predicate" fill />
+        <ColumnCode id="descr" label="Property" fill visible={false} />
+        <ColumnTag
+          id="status"
+          label="Status"
+          width={100}
+          align="center"
+          getter={getStatus}
+        />
+      </Table>
+    </Splitter>
   );
 };
 
-- 
GitLab