From 01b597450f81fe475704e61349c84ecb7b5f7275 Mon Sep 17 00:00:00 2001
From: Virgile Prevosto <virgile.prevosto@m4x.org>
Date: Mon, 12 Sep 2022 16:56:51 +0200
Subject: [PATCH] [kernel] AST diff can take into account enum tags depending
 on previous tags

supports pattern such as

```c
enum {
     TAG1 = 42,
     TAG2 = TAG1 << 3
};
```
---
 src/kernel_services/ast_queries/ast_diff.ml | 32 ++++++++++++++++-----
 tests/syntax/ast_diff_1.i                   |  5 +++-
 tests/syntax/ast_diff_2.i                   |  5 +++-
 3 files changed, 33 insertions(+), 9 deletions(-)

diff --git a/src/kernel_services/ast_queries/ast_diff.ml b/src/kernel_services/ast_queries/ast_diff.ml
index 7d7b9c041e1..ef9985a6b22 100644
--- a/src/kernel_services/ast_queries/ast_diff.ml
+++ b/src/kernel_services/ast_queries/ast_diff.ml
@@ -187,6 +187,12 @@ type is_same_env =
        We thus collect them in the environment.
     *)
     goto_targets: (stmt * stmt) list;
+    (* enum items are added collectively when the whole enuminfo is
+       found to be identical (or not). On the other hand, the
+       definition of an enumitem can refer to the previous ones in the
+       same enuminfo. We keep the list of previously visited tags here.
+    *)
+    enumitem: enumitem Cil_datatype.Enumitem.Map.t;
   }
 
 module type Correspondence_table = sig
@@ -271,7 +277,8 @@ let empty_env =
     logic_type_info = Cil_datatype.Logic_type_info.Map.empty;
     logic_local_vars = Cil_datatype.Logic_var.Map.empty;
     logic_type_vars = Datatype.String.Map.empty;
-    goto_targets = []
+    goto_targets = [];
+    enumitem = Cil_datatype.Enumitem.Map.empty;
   }
 
 let add_locals f f' env =
@@ -915,9 +922,12 @@ and is_same_compinfo ci ci' env =
   is_same_opt (is_same_list is_same_fieldinfo) ci.cfields ci'.cfields env
 
 and is_same_enuminfo ei ei' env =
-  Cil_datatype.Attributes.equal ei.eattr ei'.eattr &&
-  Ikind.equal ei.ekind ei'.ekind &&
-  is_same_list is_same_enumitem ei.eitems ei'.eitems env
+  let res, _ =
+    (Cil_datatype.Attributes.equal ei.eattr ei'.eattr &&
+     Ikind.equal ei.ekind ei'.ekind, env) &&&
+    is_same_list_env is_same_enumitem ei.eitems ei'.eitems
+  in
+  res
 
 and is_same_fieldinfo fi fi' env =
   (* we don't compare names: it's the order in which they appear in the
@@ -927,7 +937,12 @@ and is_same_fieldinfo fi fi' env =
   is_same_opt (fun x y _ -> x = y) fi.fbitfield fi'.fbitfield env &&
   Cil_datatype.Attributes.equal fi.fattr fi'.fattr
 
-and is_same_enumitem ei ei' env = is_same_exp ei.eival ei'.eival env
+and is_same_enumitem ei ei' env =
+  if is_same_exp ei.eival ei'.eival env then
+    true,
+    { env with enumitem = Cil_datatype.Enumitem.Map.add ei ei' env.enumitem }
+  else
+    false, env
 
 and is_same_formal (_,t,a) (_,t',a') env =
   is_same_type t t' env && Cil_datatype.Attributes.equal a a'
@@ -1365,8 +1380,11 @@ and enumitem_correspondence ?loc ei env =
     | `Not_present -> `Not_present
     | `Same _ -> Enumitem.find ei
   in
-  try Enumitem.find ei with
-  | Not_found -> add ei
+  match Cil_datatype.Enumitem.Map.find_opt ei env.enumitem with
+  | Some ei' -> `Same ei'
+  | None ->
+    (try Enumitem.find ei with
+     | Not_found -> add ei)
 
 and gvar_correspondence ?loc vi env =
   let add vi =
diff --git a/tests/syntax/ast_diff_1.i b/tests/syntax/ast_diff_1.i
index 7b64586683d..623a7b7ba87 100644
--- a/tests/syntax/ast_diff_1.i
+++ b/tests/syntax/ast_diff_1.i
@@ -95,7 +95,10 @@ void with_goto_unchanged(int c) {
   L2: X++;
 }
 
-enum e { t = 1 };
+enum e {
+  t = 1,
+  u = t + 2
+};
 
 struct s { char c[t]; };
 
diff --git a/tests/syntax/ast_diff_2.i b/tests/syntax/ast_diff_2.i
index 9ef726b37e4..b83f852c99d 100644
--- a/tests/syntax/ast_diff_2.i
+++ b/tests/syntax/ast_diff_2.i
@@ -88,7 +88,10 @@ void with_goto_unchanged(int c) {
   L2: X++;
 }
 
-enum e { t = 1 };
+enum e {
+  t = 1,
+  u = t + 2
+};
 
 struct s { char c[t]; };
 
-- 
GitLab