From 5acc63c0653b363d3d6dc9217e0a29d7e44cbcaa Mon Sep 17 00:00:00 2001
From: Chimrod <>
Date: Mon, 6 Nov 2023 15:07:28 +0100
Subject: In the string expression simplification, only report the strings
 converted into strings

---
 lib/syntax/nested_strings.ml | 51 +++++++++++++++++++++++++++++++-------------
 lib/syntax/type_of.ml        |  3 +++
 lib/syntax/type_of.mli       | 10 +++++++++
 readme.md                    |  4 +++-
 test/nested_string.ml        | 33 +++++++++++++++++-----------
 5 files changed, 72 insertions(+), 29 deletions(-)

diff --git a/lib/syntax/nested_strings.ml b/lib/syntax/nested_strings.ml
index 16ec4ac..c7b0b83 100644
--- a/lib/syntax/nested_strings.ml
+++ b/lib/syntax/nested_strings.ml
@@ -5,10 +5,11 @@ let description = "Check for unnecessary use of expression encoded in string"
 let active = ref true
 
 module Expression : S.Expression with type t' = Report.t list = struct
-  type t = Report.t list
-  type t' = t
+  type t = Report.t list * Type_of.Expression.t
+  type t' = Report.t list
 
-  let v : t -> t' = Fun.id
+  let get_type t = Type_of.Expression.v t |> Type_of.get_type
+  let v : t -> t' = Stdlib.fst
 
   (** Identify the expressions reprented as string. That’s here that the report
       are added. 
@@ -16,28 +17,48 @@ module Expression : S.Expression with type t' = Report.t list = struct
       All the rest of the module only push thoses warning to the top level. *)
   let literal : S.pos -> t T.literal list -> t =
    fun pos content ->
+    let type_of =
+      List.map content ~f:(function
+        | T.Text _ as text -> text
+        | T.Expression expr -> T.Expression (List.map ~f:snd expr))
+      |> Type_of.Expression.literal pos
+    in
+
     match content with
-    | [ T.Expression expr; T.Text "" ] ->
-        ignore expr;
-        let msg = Report.debug pos "This expression can be simplified" in
-        [ msg ]
-    | _ -> []
+    | [ T.Expression [ (_, t') ]; T.Text "" ] -> (
+        match get_type t' with
+        | Type_of.Helper.Integer -> ([], type_of)
+        | _ ->
+            let msg = Report.debug pos "This expression can be simplified" in
+            ([ msg ], type_of))
+    | _ -> ([], type_of)
 
   let ident : (S.pos, t) S.variable -> t =
    fun { pos; name : string; index : t option } ->
-    ignore pos;
-    ignore name;
-    match index with None -> [] | Some v -> v
+    match index with
+    | None ->
+        let type_ = Type_of.Expression.ident { pos; name; index = None } in
+        ([], type_)
+    | Some (v, t') ->
+        let type_ = Type_of.Expression.ident { pos; name; index = Some t' } in
+        (v, type_)
 
-  let integer : S.pos -> string -> t = fun _ _ -> []
+  let integer : S.pos -> string -> t =
+   fun pos t -> ([], Type_of.Expression.integer pos t)
 
   let function_ : S.pos -> T.function_ -> t list -> t =
-   fun _ _ expressions -> List.concat expressions
+   fun pos f expressions ->
+    let r, t = List.split expressions in
+
+    let type_of = Type_of.Expression.function_ pos f t in
+    (List.concat r, type_of)
 
-  let uoperator : S.pos -> T.uoperator -> t -> t = fun _ _ expr1 -> expr1
+  let uoperator : S.pos -> T.uoperator -> t -> t =
+   fun pos op (r, expr1) -> (r, Type_of.Expression.uoperator pos op expr1)
 
   let boperator : S.pos -> T.boperator -> t -> t -> t =
-   fun _ _ expr1 expr2 -> expr1 @ expr2
+   fun pos op (r1, expr1) (r2, expr2) ->
+    (r1 @ r2, Type_of.Expression.boperator pos op expr1 expr2)
 end
 
 module Instruction :
diff --git a/lib/syntax/type_of.ml b/lib/syntax/type_of.ml
index 223633c..15d6fe8 100644
--- a/lib/syntax/type_of.ml
+++ b/lib/syntax/type_of.ml
@@ -483,3 +483,6 @@ module Location = struct
     in
     report
 end
+
+let get_type : Expression.t' -> Helper.type_of =
+ fun t' -> match (fst t').result with Raw r -> r | Variable r -> r
diff --git a/lib/syntax/type_of.mli b/lib/syntax/type_of.mli
index 551f9ac..a009c48 100644
--- a/lib/syntax/type_of.mli
+++ b/lib/syntax/type_of.mli
@@ -1,3 +1,11 @@
+module Helper : sig
+  type type_of =
+    | Integer  (** A numeric value *)
+    | Bool  (** A boolean, not a real type  *)
+    | String  (** String value *)
+    | NumericString
+end
+
 include S.Analyzer
 (** The module [type_of] populate the report with differents inconsistency
    errors in the types.
@@ -5,3 +13,5 @@ include S.Analyzer
    - Assigning a [string] value in an [integer] variable
    - Comparing a [string] with an [integer]
    - Giving the wrong type in the argument for a function and so one. *)
+
+val get_type : Expression.t' -> Helper.type_of
diff --git a/readme.md b/readme.md
index d18b371..e6efa88 100644
--- a/readme.md
+++ b/readme.md
@@ -77,4 +77,6 @@ instructions are given, which can lead to dead end in the code.
 The application will report text strings containing only one expression with a
 `debug` message.
 
-For example `"<<$variable>>"` can be written directly: `$variable>`.
+For example `"<<$variable>>"` can be written directly: `$variable>`. This test
+will not report this usage when an integer converted into a string this way.
+
diff --git a/test/nested_string.ml b/test/nested_string.ml
index b121667..3338fa3 100644
--- a/test/nested_string.ml
+++ b/test/nested_string.ml
@@ -5,23 +5,30 @@ let _position = (Lexing.dummy_pos, Lexing.dummy_pos)
 let _test_instruction : string -> Qsp_syntax.Report.t list -> unit =
   Check._test_instruction
 
-let nothing () = _test_instruction {|
-"value = <<$variable>>"
-  |} []
+let report =
+  [
+    {
+      Qsp_syntax.Report.level = Debug;
+      Qsp_syntax.Report.loc = _position;
+      Qsp_syntax.Report.message = "This expression can be simplified";
+    };
+  ]
 
-let simple_expression () =
-  _test_instruction {|"<<$variable>>"|}
-    [
-      {
-        level = Debug;
-        loc = _position;
-        message = "This expression can be simplified";
-      };
-    ]
+let nothing () = _test_instruction {|"value = <<$variable>>"|} []
+let string () = _test_instruction {|"<<'ABC'>>"|} report
+let string_variable () = _test_instruction {|"<<$variable>>"|} report
+
+(** It’s OK to use this system to convert a numeric value into a string *)
+let integer () = _test_instruction {|"<<123>>"|} []
+
+let integer_variable () = _test_instruction {|"<<variable>>"|} []
 
 let test =
   ( "Nested_strings checker",
     [
       Alcotest.test_case "Ok" `Quick nothing;
-      Alcotest.test_case "Simple expression" `Quick simple_expression;
+      Alcotest.test_case "String variable" `Quick string_variable;
+      Alcotest.test_case "Integer" `Quick integer;
+      Alcotest.test_case "String" `Quick string;
+      Alcotest.test_case "Integer variable" `Quick integer_variable;
     ] )
-- 
cgit v1.2.3