diff options
Diffstat (limited to 'lib/checks')
-rw-r--r-- | lib/checks/check.ml | 356 | ||||
-rw-r--r-- | lib/checks/check.mli | 8 | ||||
-rw-r--r-- | lib/checks/compose.ml | 130 | ||||
-rw-r--r-- | lib/checks/dead_end.ml | 11 | ||||
-rw-r--r-- | lib/checks/dead_end.mli | 7 | ||||
-rw-r--r-- | lib/checks/default.ml | 47 | ||||
-rw-r--r-- | lib/checks/dune | 1 | ||||
-rw-r--r-- | lib/checks/dup_test.ml | 2 | ||||
-rw-r--r-- | lib/checks/dup_test.mli | 2 | ||||
-rw-r--r-- | lib/checks/dynamics.ml | 19 | ||||
-rw-r--r-- | lib/checks/dynamics.mli | 2 | ||||
-rw-r--r-- | lib/checks/get_type.ml | 291 | ||||
-rw-r--r-- | lib/checks/get_type.mli | 25 | ||||
-rw-r--r-- | lib/checks/locations.ml | 10 | ||||
-rw-r--r-- | lib/checks/nested_strings.ml | 73 | ||||
-rw-r--r-- | lib/checks/nested_strings.mli | 2 | ||||
-rw-r--r-- | lib/checks/type_of.ml | 115 | ||||
-rw-r--r-- | lib/checks/type_of.mli | 10 | ||||
-rw-r--r-- | lib/checks/write_only.ml | 33 |
19 files changed, 660 insertions, 484 deletions
diff --git a/lib/checks/check.ml b/lib/checks/check.ml index 6169bb1..597bc0a 100644 --- a/lib/checks/check.ml +++ b/lib/checks/check.ml @@ -1,5 +1,4 @@ module S = Qsp_syntax.S -module C = Qsp_syntax.Catalog (** The the Id module, wrap a value in an existencial type with a witness associate with. *) @@ -11,10 +10,13 @@ let get : type a. a Type.Id.t -> result -> a option = | Some Type.Equal -> Some value | None -> None -type t = Qsp_syntax.Catalog.ex +let set : type a. a Type.Id.t -> result -> a -> result option = + fun typeid (R { witness; _ }) value -> + match Type.Id.provably_equal typeid witness with + | Some Type.Equal -> Some (R { witness; value }) + | None -> None -let get_module : t -> (module S.Analyzer) = - fun (E { module_; _ }) -> (module_ :> (module S.Analyzer)) +type t = Qsp_syntax.Identifier.t module type App = sig val t : t array @@ -42,23 +44,85 @@ module Make (A : App) = struct let description = "Internal module" let is_global = false let active = ref false + let depends = [] (* This modules depends of nothing *) + + type ex = Qsp_syntax.Identifier.t type context = result Array.t (** We associate each context from the differents test in an array. The context for this module is a sort of context of contexts *) + (* Initialize the modules to check here, at the module level *) + let checks = + (* Collect all the dependencies and build the execution order *) + let graph : ex list = + let rec build_deps l acc = + List.fold_left + (l : ex list) + ~init:acc + ~f:(fun + acc (Qsp_syntax.Identifier.E { module_ = (module S); _ } as ex) -> + let acc' = ex :: acc in + + build_deps S.depends acc') + in + build_deps (Array.to_list A.t) [] + in + + (* Convert the dependenciees using the module identifier only, the + Tsort.sort function use structural equality comparaison function which + does not wok with the module embeded in first class module. *) + let graph_name = + List.map graph + ~f:(fun (Qsp_syntax.Identifier.E { module_ = (module S); _ }) -> + let deps' = + List.map + ~f:(fun (Qsp_syntax.Identifier.E { module_ = (module S); _ }) -> + S.identifier) + S.depends + in + (S.identifier, deps')) + in + match Tsort.sort graph_name with + | Tsort.Sorted sorted_graph -> + (* From the identifier, extract the associated check *) + let _ = + List.map sorted_graph ~f:(fun name -> + List.find_map graph + ~f:(fun + (Qsp_syntax.Identifier.E { module_ = (module S); _ } as + check) + -> + match String.equal name S.identifier with + | false -> None + | true -> Some check) + |> Option.get) + (* It’s ok to use unchecked option.get here, because the list was + created from the same source just before *) + in + + Array.of_list graph + | Tsort.ErrorCycle _ -> + (* This is very unlikely to happen, as it would reflect an error in + the compilation units order *) + raise Not_found + (** Initialize each test, and keep the result in the context. *) let initialize : unit -> context = fun () -> - Array.map A.t ~f:(fun (C.E { module_ = (module S); context; _ }) -> + Array.map checks + ~f:(fun (Qsp_syntax.Identifier.E { module_ = (module S); context; _ }) -> let value = S.initialize () in R { value; witness = context }) let finalize : result Array.t -> (string * Qsp_syntax.Report.t) list = fun context_array -> let _, report = - Array.fold_left A.t ~init:(0, []) - ~f:(fun (i, acc) (C.E { module_ = (module S); context; _ }) -> + Array.fold_left checks ~init:(0, []) + ~f:(fun + (i, acc) + (Qsp_syntax.Identifier.E { module_ = (module S); context; _ }) + -> let result = Array.get context_array i in let local_context = Option.get (get context result) in let reports = S.finalize local_context in @@ -67,111 +131,195 @@ module Make (A : App) = struct report (* Global variable for the whole module *) - let len = Array.length A.t + let len = Array.length checks module Expression : S.Expression with type t' = result array = struct type t = result array type t' = result array - let literal : S.pos -> t Qsp_syntax.T.literal list -> t = - fun pos values -> - Array.mapi A.t - ~f:(fun i (C.E { module_ = (module S); expr_witness; _ }) -> - (* Map every values to the Checker *) - let values' = - List.map values - ~f: - (Qsp_syntax.T.map_litteral ~f:(fun expr -> - Option.get (get expr_witness (Array.get expr i)))) - in - let value = S.Expression.literal pos values' in - R { value; witness = expr_witness }) - - let integer : S.pos -> string -> t = - fun pos value -> - Array.map A.t ~f:(fun (C.E { module_ = (module S); expr_witness; _ }) -> - let value = S.Expression.integer pos value in - R { value; witness = expr_witness }) + let build_ctx : result option array -> Qsp_syntax.S.extract_context = + fun results -> + { + f = + (fun id -> + Array.find_map results ~f:(function + | Some result -> get id result + | None -> None)); + } + + let literal : + ctx:Qsp_syntax.S.extract_context -> + S.pos -> + t Qsp_syntax.T.literal list -> + t = + fun ~ctx pos values -> + ignore ctx; + let results = Array.make len None in + (* Create the new array, filled with None at the begining. + + Then populate the array in place in order to read the previous values + if requested *) + (* Extract the result with the given ID from the array *) + let ctx = build_ctx results in + let () = + Array.mapi_inplace results ~f:(fun i _ -> + let (Qsp_syntax.Identifier.E + { module_ = (module S); expr_witness; _ }) = + Array.get checks i + in + (* Map every values to the Checker *) + let values' = + List.map values + ~f: + (Qsp_syntax.T.map_litteral ~f:(fun expr -> + Option.get (get expr_witness (Array.get expr i)))) + in + let value = S.Expression.literal ~ctx pos values' in + Some (R { value; witness = expr_witness })) + in + Array.map results ~f:Option.get - (** Unary operator like [-123] or [+'Text']*) - let uoperator : S.pos -> Qsp_syntax.T.uoperator -> t -> t = - fun pos op values -> - (* Evaluate the nested expression *) - let results = values in + let integer : ctx:Qsp_syntax.S.extract_context -> S.pos -> string -> t = + fun ~ctx pos value -> + ignore ctx; + let results = Array.make len None in + let ctx = build_ctx results in - (* Now evaluate the remaining expression. + let () = + Array.mapi_inplace results ~f:(fun i _ -> + let (Qsp_syntax.Identifier.E + { module_ = (module S); expr_witness; _ }) = + Array.get checks i + in + let value = S.Expression.integer ~ctx pos value in + Some (R { value; witness = expr_witness })) + in + Array.map results ~f:Option.get - Traverse both the module the apply, and the matching expression already - evaluated. + (** Unary operator like [-123] or [+'Text']*) + let uoperator : + ctx:Qsp_syntax.S.extract_context -> + S.pos -> + Qsp_syntax.T.uoperator -> + t -> + t = + fun ~ctx pos op values -> + ignore ctx; + let results = Array.make len None in + let ctx = build_ctx results in - It’s easer to use [map] and declare [report] as reference instead of - [fold_left2] and accumulate the report inside the closure, because I - don’t manage the order of the results. - *) - let results = - Array.map2 A.t results - ~f:(fun (C.E { module_ = (module S); expr_witness; _ }) value -> + (* Evaluate the nested expression *) + let () = + Array.mapi_inplace results ~f:(fun i _ -> + let (Qsp_syntax.Identifier.E + { module_ = (module S); expr_witness; _ }) = + Array.get checks i + in + let value = Array.get values i in match get expr_witness value with | None -> failwith "Does not match" | Some value -> (* Evaluate the single expression *) - let value = S.Expression.uoperator pos op value in - R { witness = expr_witness; value }) + let value = S.Expression.uoperator ~ctx pos op value in + Some (R { witness = expr_witness; value })) in - results + Array.map results ~f:Option.get (** Basically the same as uoperator, but operate over two operands instead of a single one. *) - let boperator : S.pos -> Qsp_syntax.T.boperator -> t -> t -> t = - fun pos op expr1 expr2 -> - Array.init len ~f:(fun i -> - let (E { module_ = (module S); expr_witness; _ }) = Array.get A.t i in - match - ( get expr_witness (Array.get expr1 i), - get expr_witness (Array.get expr2 i) ) - with - | Some value_1, Some value_2 -> - let value = S.Expression.boperator pos op value_1 value_2 in - R { witness = expr_witness; value } - | _ -> failwith "Does not match") + let boperator : + ctx:Qsp_syntax.S.extract_context -> + S.pos -> + Qsp_syntax.T.boperator -> + t -> + t -> + t = + fun ~ctx pos op expr1 expr2 -> + ignore ctx; + let results = Array.make len None in + let ctx = build_ctx results in + let () = + Array.mapi_inplace results ~f:(fun i _ -> + let (E { module_ = (module S); expr_witness; _ }) = + Array.get checks i + in + match + ( get expr_witness (Array.get expr1 i), + get expr_witness (Array.get expr2 i) ) + with + | Some value_1, Some value_2 -> + let value = + S.Expression.boperator ~ctx pos op value_1 value_2 + in + Some (R { witness = expr_witness; value }) + | _ -> failwith "Does not match") + in + Array.map results ~f:Option.get (** Call a function. The functions list is hardcoded in lib/lexer.mll *) - let function_ : S.pos -> Qsp_syntax.T.function_ -> t list -> t = - fun pos func args -> - Array.init len ~f:(fun i -> - let (E { module_ = (module S); expr_witness; _ }) = Array.get A.t i in - (* Extract the arguments for each module *) - let args_i = List.rev (Helper.expr_i args expr_witness i).values in - let value = S.Expression.function_ pos func args_i in - R { witness = expr_witness; value }) - - let ident : (S.pos, t) S.variable -> t = - fun { pos : S.pos; name : string; index : t option } -> - Array.init len ~f:(fun i -> - let (E { module_ = (module S); expr_witness; _ }) = Array.get A.t i in - - match index with - | None -> - (* Easest case, just return the plain ident *) - let value = S.Expression.ident { pos; name; index = None } in - R { witness = expr_witness; value } - | Some t -> ( - match get expr_witness (Array.get t i) with - | None -> failwith "Does not match" - | Some value_1 -> - let value = - S.Expression.ident { pos; name; index = Some value_1 } - in - R { witness = expr_witness; value })) + let function_ : + ctx:Qsp_syntax.S.extract_context -> + S.pos -> + Qsp_syntax.T.function_ -> + t list -> + t = + fun ~ctx pos func args -> + ignore ctx; + let results = Array.make len None in + let ctx = build_ctx results in + let () = + Array.mapi_inplace results ~f:(fun i _ -> + let (E { module_ = (module S); expr_witness; _ }) = + Array.get checks i + in + (* Extract the arguments for each module *) + let args_i = List.rev (Helper.expr_i args expr_witness i).values in + let value = S.Expression.function_ ~ctx pos func args_i in + Some (R { witness = expr_witness; value })) + in + Array.map results ~f:Option.get + + let ident : ctx:Qsp_syntax.S.extract_context -> (S.pos, t) S.variable -> t = + fun ~ctx { pos : S.pos; name : string; index : t option } -> + ignore ctx; + let results = Array.make len None in + let ctx = build_ctx results in + let () = + Array.mapi_inplace results ~f:(fun i _ -> + let (E { module_ = (module S); expr_witness; _ }) = + Array.get checks i + in + + match index with + | None -> + (* Easest case, just return the plain ident *) + let value = + S.Expression.ident ~ctx { pos; name; index = None } + in + Some (R { witness = expr_witness; value }) + | Some t -> ( + match get expr_witness (Array.get t i) with + | None -> failwith "Does not match" + | Some value_1 -> + let value = + S.Expression.ident ~ctx + { pos; name; index = Some value_1 } + in + Some (R { witness = expr_witness; value }))) + in + Array.map results ~f:Option.get (** Convert each internal represention for the expression into its external representation *) let v : t -> t' = fun t -> let result = - Array.map2 A.t t + Array.map2 checks t ~f:(fun - (C.E { module_ = (module S); expr_witness; expr'; _ }) result -> + (Qsp_syntax.Identifier.E + { module_ = (module S); expr_witness; expr'; _ }) + result + -> match get expr_witness result with | None -> failwith "Does not match" | Some value -> @@ -191,21 +339,30 @@ module Make (A : App) = struct let location : S.pos -> string -> t = fun pos label -> - Array.map A.t ~f:(fun (C.E { module_ = (module S); instr_witness; _ }) -> + Array.map checks + ~f:(fun + (Qsp_syntax.Identifier.E { module_ = (module S); instr_witness; _ }) + -> let value = S.Instruction.location pos label in R { value; witness = instr_witness }) let comment : S.pos -> t = fun pos -> - Array.map A.t ~f:(fun (C.E { module_ = (module S); instr_witness; _ }) -> + Array.map checks + ~f:(fun + (Qsp_syntax.Identifier.E { module_ = (module S); instr_witness; _ }) + -> let value = S.Instruction.comment pos in R { value; witness = instr_witness }) let expression : expression -> t = fun expr -> - Array.map2 A.t expr + Array.map2 checks expr ~f:(fun - (C.E { module_ = (module S); instr_witness; expr'; _ }) result -> + (Qsp_syntax.Identifier.E + { module_ = (module S); instr_witness; expr'; _ }) + result + -> match get expr' result with | None -> failwith "Does not match" | Some value -> @@ -219,7 +376,7 @@ module Make (A : App) = struct actually the list of each expression in the differents modules. *) Array.init len ~f:(fun i -> let (E { module_ = (module S); expr'; instr_witness; _ }) = - Array.get A.t i + Array.get checks i in let values = List.rev (Helper.expr_i args expr' i).values in @@ -231,7 +388,7 @@ module Make (A : App) = struct fun pos ~label instructions -> Array.init len ~f:(fun i -> let (E { module_ = (module S); instr_witness; expr'; _ }) = - Array.get A.t i + Array.get checks i in let values = List.rev (Helper.expr_i instructions instr_witness i).values @@ -254,7 +411,7 @@ module Make (A : App) = struct fun pos { pos = var_pos; name; index } op expression -> Array.init len ~f:(fun i -> let (E { module_ = (module A); instr_witness; expr'; _ }) = - Array.get A.t i + Array.get checks i in let index_i = @@ -303,7 +460,7 @@ module Make (A : App) = struct in Array.init len ~f:(fun i -> let (E { module_ = (module A); instr_witness; expr'; _ }) = - Array.get A.t i + Array.get checks i in let clause = rebuild_clause i instr_witness expr' clause @@ -324,9 +481,12 @@ module Make (A : App) = struct let v : t -> t' = fun t -> let result = - Array.map2 A.t t + Array.map2 checks t ~f:(fun - (C.E { module_ = (module S); instr_witness; instr'; _ }) result -> + (Qsp_syntax.Identifier.E + { module_ = (module S); instr_witness; instr'; _ }) + result + -> match get instr_witness result with | None -> failwith "Does not match" | Some value -> @@ -358,7 +518,7 @@ module Make (A : App) = struct context; _; }) = - Array.get A.t i + Array.get checks i in let local_context = @@ -377,7 +537,7 @@ module Make (A : App) = struct let () = Array.iteri args ~f:(fun i result -> let (E { module_ = (module A); location_witness; _ }) = - Array.get A.t i + Array.get checks i in match get location_witness result with | None -> failwith "Does not match" diff --git a/lib/checks/check.mli b/lib/checks/check.mli index ebed0df..34d953f 100644 --- a/lib/checks/check.mli +++ b/lib/checks/check.mli @@ -13,19 +13,19 @@ end) ]} *) -val get_module : Qsp_syntax.Catalog.ex -> (module Qsp_syntax.S.Analyzer) - type result val get : 'a Type.Id.t -> result -> 'a option (** The method [get] can be used to get the internal value for one of the checker used. *) +val set : 'a Type.Id.t -> result -> 'a -> result option + module Make (A : sig - val t : Qsp_syntax.Catalog.ex array + val t : Qsp_syntax.Identifier.t array end) : sig include - Qsp_syntax.S.Analyzer + Qsp_syntax.Analyzer.T with type Location.t = result array and type context = result array end diff --git a/lib/checks/compose.ml b/lib/checks/compose.ml deleted file mode 100644 index b29c22e..0000000 --- a/lib/checks/compose.ml +++ /dev/null @@ -1,130 +0,0 @@ -(** Build a module with the result from another one module *) - -open StdLabels -module S = Qsp_syntax.S -module T = Qsp_syntax.T - -(** Make a module lazy *) -module Lazier (E : S.Expression) : - S.Expression with type t' = E.t' Lazy.t and type t = E.t Lazy.t = struct - type t = E.t Lazy.t - type t' = E.t' Lazy.t - - let v : E.t Lazy.t -> E.t' Lazy.t = Lazy.map E.v - let integer : S.pos -> string -> t = fun pos i -> lazy (E.integer pos i) - - let ident : (S.pos, t) S.variable -> t = - fun { pos; name : string; index : t option } -> - lazy (E.ident { pos; name; index = Option.map Lazy.force index }) - - let literal : S.pos -> t T.literal list -> t = - fun pos litts -> - lazy - (let e_litts = List.map litts ~f:(T.map_litteral ~f:Lazy.force) in - E.literal pos e_litts) - - let function_ : S.pos -> T.function_ -> t list -> t = - fun pos f e -> - lazy - (let e' = List.map ~f:Lazy.force e in - E.function_ pos f e') - - let uoperator : S.pos -> T.uoperator -> t -> t = - fun pos op t -> - let t' = lazy (E.uoperator pos op (Lazy.force t)) in - t' - - let boperator : S.pos -> T.boperator -> t -> t -> t = - fun pos op t1 t2 -> - let t' = lazy (E.boperator pos op (Lazy.force t1) (Lazy.force t2)) in - t' -end - -(** Build an expression module with the result from another expression. The - signature of the fuctions is a bit different, as they all receive the result - from the previous evaluated element in argument. *) -module Expression (E : S.Expression) = struct - module type SIG = sig - type t - type t' - - (* Override the type [t] in the definition of all the functions. The - signatures differs a bit from the standard signature as they get the - result from E.t in last argument *) - - val ident : (S.pos, E.t' Lazy.t * t) S.variable -> E.t' Lazy.t -> t - val integer : S.pos -> string -> E.t' Lazy.t -> t - val literal : S.pos -> (E.t' Lazy.t * t) T.literal list -> E.t' Lazy.t -> t - - val function_ : - S.pos -> T.function_ -> (E.t' Lazy.t * t) list -> E.t' Lazy.t -> t - - val uoperator : S.pos -> T.uoperator -> E.t' Lazy.t * t -> E.t' Lazy.t -> t - - val boperator : - S.pos -> - T.boperator -> - E.t' Lazy.t * t -> - E.t' Lazy.t * t -> - E.t' Lazy.t -> - t - - val v : E.t' Lazy.t * t -> t' - (** Convert from the internal representation to the external one. *) - end - - (* Create a lazy version of the module *) - module E = Lazier (E) - - module Make (M : SIG) : S.Expression with type t' = M.t' = struct - type t = E.t * M.t - type t' = M.t' - - let v' : E.t -> E.t' = E.v - let v : t -> t' = fun (type_of, v) -> M.v (v' type_of, v) - - let ident : (S.pos, t) S.variable -> t = - fun { pos; name : string; index : t option } -> - let t' = E.ident { pos; name; index = Option.map fst index } in - let index' = Option.map (fun (e, m) -> (v' e, m)) index in - (t', M.ident { pos; name; index = index' } (v' t')) - - let integer : S.pos -> string -> t = - fun pos i -> - let t' = E.integer pos i in - (t', M.integer pos i (v' t')) - - let literal : S.pos -> t T.literal list -> t = - fun pos litts -> - let litts' = - List.map litts ~f:(T.map_litteral ~f:(fun (e, m) -> (v' e, m))) - in - - let t' = - let e_litts = List.map litts ~f:(T.map_litteral ~f:fst) in - E.literal pos e_litts - in - (t', M.literal pos litts' (v' t')) - - let function_ : S.pos -> T.function_ -> t list -> t = - fun pos f expressions -> - let e = List.map ~f:fst expressions - and expressions' = List.map ~f:(fun (e, m) -> (v' e, m)) expressions in - - let t' = E.function_ pos f e in - (t', M.function_ pos f expressions' (v' t')) - - let uoperator : S.pos -> T.uoperator -> t -> t = - fun pos op (t, expr) -> - let t' = E.uoperator pos op t in - (t', M.uoperator pos op (v' t, expr) (v' t')) - - let boperator : S.pos -> T.boperator -> t -> t -> t = - fun pos op (t1, expr1) (t2, expr2) -> - let t' = E.boperator pos op t1 t2 in - (t', M.boperator pos op (v' t1, expr1) (v' t2, expr2) (v' t')) - end -end - -module TypeBuilder = Expression (Get_type) -(** Builder adding the type for the expression *) diff --git a/lib/checks/dead_end.ml b/lib/checks/dead_end.ml index 629a966..dd3e945 100644 --- a/lib/checks/dead_end.ml +++ b/lib/checks/dead_end.ml @@ -7,7 +7,9 @@ let identifier = "dead_end" let description = "Check for dead end in the code" let is_global = false let active = ref false +let depends = [] +type ex = Qsp_syntax.Identifier.t type context = unit let initialize = Fun.id @@ -40,10 +42,8 @@ module Instruction = struct (** For each instruction, return thoses two informations : - - the intruction contains at [gt] - - the last instruction is a [gt] - - *) + - the intruction contains at [gt] + - the last instruction is a [gt] *) let v : t -> t' = fun t -> t let default = @@ -73,7 +73,8 @@ module Instruction = struct (** Raw expression *) let expression : Expression.t' -> t = fun _ -> default - (** The content of a block is very linear, I only need to check the last element *) + (** The content of a block is very linear, I only need to check the last + element *) let check_block : S.pos -> t list -> t = fun pos instructions -> let last_element = diff --git a/lib/checks/dead_end.mli b/lib/checks/dead_end.mli index d8fe7d6..73ec86a 100644 --- a/lib/checks/dead_end.mli +++ b/lib/checks/dead_end.mli @@ -1,6 +1,5 @@ -(** Checker looking for the dead ends in the source. +(** Checker looking for the dead ends in the source. - A dead end is a state where the user does not have any action. - *) + A dead end is a state where the user does not have any action. *) -include Qsp_syntax.S.Analyzer +include Qsp_syntax.Analyzer.T with type ex = Qsp_syntax.Identifier.t diff --git a/lib/checks/default.ml b/lib/checks/default.ml index 0c4d761..0ec1084 100644 --- a/lib/checks/default.ml +++ b/lib/checks/default.ml @@ -21,25 +21,56 @@ struct type t' = T'.t - let ident : (S.pos, T'.t) S.variable -> T'.t = fun _ -> T'.default + let ident : + ctx:Qsp_syntax.S.extract_context -> (S.pos, T'.t) S.variable -> T'.t = + fun ~ctx _ -> + ignore ctx; + T'.default (* Basic values, text, number… *) - let integer : S.pos -> string -> T'.t = fun _ _ -> T'.default - let literal : S.pos -> T'.t T.literal list -> T'.t = fun _ _ -> T'.default + let integer : ctx:Qsp_syntax.S.extract_context -> S.pos -> string -> T'.t = + fun ~ctx _ _ -> + ignore ctx; + T'.default + + let literal : + ctx:Qsp_syntax.S.extract_context -> S.pos -> T'.t T.literal list -> T'.t = + fun ~ctx _ _ -> + ignore ctx; + T'.default (** Call a function. The functions list is hardcoded in lib/lexer.mll *) - let function_ : S.pos -> T.function_ -> T'.t list -> T'.t = - fun _ _ _ -> T'.default + let function_ : + ctx:Qsp_syntax.S.extract_context -> + S.pos -> + T.function_ -> + T'.t list -> + T'.t = + fun ~ctx _ _ _ -> + ignore ctx; + T'.default (** Unary operator like [-123] or [+'Text']*) - let uoperator : S.pos -> T.uoperator -> T'.t -> T'.t = fun _ _ _ -> T'.default + let uoperator : + ctx:Qsp_syntax.S.extract_context -> S.pos -> T.uoperator -> T'.t -> T'.t = + fun ~ctx _ _ _ -> + ignore ctx; + T'.default (** Binary operator, for a comparaison, or an operation *) - let boperator : S.pos -> T.boperator -> T'.t -> T'.t -> T'.t = - fun _ _ _ _ -> T'.default + let boperator : + ctx:Qsp_syntax.S.extract_context -> + S.pos -> + T.boperator -> + T'.t -> + T'.t -> + T'.t = + fun ~ctx _ _ _ _ -> + ignore ctx; + T'.default end module Instruction (Expression : sig diff --git a/lib/checks/dune b/lib/checks/dune index 3bd22e0..75b311b 100644 --- a/lib/checks/dune +++ b/lib/checks/dune @@ -1,6 +1,7 @@ (library (name qsp_checks) (libraries + tsort qsp_syntax ) diff --git a/lib/checks/dup_test.ml b/lib/checks/dup_test.ml index c29eca9..4de9a4d 100644 --- a/lib/checks/dup_test.ml +++ b/lib/checks/dup_test.ml @@ -13,7 +13,9 @@ let identifier = "duplicate_test" let description = "Check for duplicate tests" let is_global = false let active = ref true +let depends = [] +type ex = Qsp_syntax.Identifier.t type context = unit let initialize = Fun.id diff --git a/lib/checks/dup_test.mli b/lib/checks/dup_test.mli index 6446c67..a771a46 100644 --- a/lib/checks/dup_test.mli +++ b/lib/checks/dup_test.mli @@ -1 +1 @@ -include Qsp_syntax.S.Analyzer +include Qsp_syntax.Analyzer.T with type ex = Qsp_syntax.Identifier.t diff --git a/lib/checks/dynamics.ml b/lib/checks/dynamics.ml index 0c16ff8..f88550b 100644 --- a/lib/checks/dynamics.ml +++ b/lib/checks/dynamics.ml @@ -7,7 +7,9 @@ let identifier = "dynamics" let description = "Report all dynamics string in the module" let is_global = true let active = ref false +let depends = [] +type ex = Qsp_syntax.Identifier.t type text = { content : string; position : S.pos } [@@deriving eq, ord] module StringSet = Set.Make (struct @@ -54,8 +56,10 @@ module Expression = struct let v : t -> t' = Fun.id (** Only keep the raw strings *) - let literal : S.pos -> t T.literal list -> t = - fun position content -> + let literal : + ctx:Qsp_syntax.S.extract_context -> S.pos -> t T.literal list -> t = + fun ~ctx position content -> + ignore ctx; ignore position; match content with | [ T.Text content ] -> Text { content; position } @@ -91,13 +95,16 @@ module Expression = struct (** Consider the integer as text. This is easier for evaluating the indices in the arrays (it use the same code as text indices), and will report bad use of dynamics. *) - let integer : S.pos -> string -> t = - fun position content -> Text { content; position } + let integer : ctx:Qsp_syntax.S.extract_context -> S.pos -> string -> t = + fun ~ctx position content -> + ignore ctx; + Text { content; position } (** If the identifier uses any unmanaged expression in the indices, ignore it. *) - let ident : (S.pos, t) S.variable -> t = - fun ({ index; _ } as ident) -> + let ident : ctx:Qsp_syntax.S.extract_context -> (S.pos, t) S.variable -> t = + fun ~ctx ({ index; _ } as ident) -> + ignore ctx; let is_valid = Option.fold ~none:true index ~some:(fun opt -> match opt with None -> false | _ -> true) diff --git a/lib/checks/dynamics.mli b/lib/checks/dynamics.mli index b4cdc96..588a05e 100644 --- a/lib/checks/dynamics.mli +++ b/lib/checks/dynamics.mli @@ -1,4 +1,4 @@ -include Qsp_syntax.S.Analyzer +include Qsp_syntax.Analyzer.T with type ex = Qsp_syntax.Identifier.t type text = { content : string; position : Qsp_syntax.S.pos } diff --git a/lib/checks/get_type.ml b/lib/checks/get_type.ml index 2486afa..00270c2 100644 --- a/lib/checks/get_type.ml +++ b/lib/checks/get_type.ml @@ -17,116 +17,183 @@ type type_of = (** String containing a numeric value *) [@@deriving show { with_path = false }, eq] -type t = Variable of type_of | Raw of type_of [@@deriving show, eq] -type t' = t - -let v = Fun.id -let get_type : t -> type_of = function Raw r -> r | Variable r -> r - -let map : t -> type_of -> t = - fun t type_of -> - match t with Raw _ -> Raw type_of | Variable _ -> Variable type_of - -let get_nature : t -> t -> type_of -> t = - fun t1 t2 type_of -> - match (t1, t2) with - | Variable _, _ -> Variable type_of - | _, Variable _ -> Variable type_of - | Raw _, Raw _ -> Raw type_of - -let integer : S.pos -> string -> t = fun _ _ -> Raw Integer - -let ident : (S.pos, 'any) S.variable -> t = - fun var -> - match var.name.[0] with '$' -> Variable String | _ -> Variable Integer - -let literal : S.pos -> t T.literal list -> t = - fun pos values -> - ignore pos; - let init = None in - let typed = - List.fold_left values ~init ~f:(fun state -> function - | T.Text t -> ( - (* Tranform the type, but keep the information is it’s a raw data +module Expression = struct + type t = Variable of type_of | Raw of type_of [@@deriving show, eq] + type t' = t + + let v = Fun.id + let get_type : t -> type_of = function Raw r -> r | Variable r -> r + + let map : t -> type_of -> t = + fun t type_of -> + match t with Raw _ -> Raw type_of | Variable _ -> Variable type_of + + let get_nature : t -> t -> type_of -> t = + fun t1 t2 type_of -> + match (t1, t2) with + | Variable _, _ -> Variable type_of + | _, Variable _ -> Variable type_of + | Raw _, Raw _ -> Raw type_of + + let integer : ctx:Qsp_syntax.S.extract_context -> S.pos -> string -> t = + fun ~ctx _ _ -> + ignore ctx; + Raw Integer + + let ident : ctx:Qsp_syntax.S.extract_context -> (S.pos, 'any) S.variable -> t + = + fun ~ctx var -> + ignore ctx; + match var.name.[0] with '$' -> Variable String | _ -> Variable Integer + + let literal : + ctx:Qsp_syntax.S.extract_context -> S.pos -> t T.literal list -> t = + fun ~ctx pos values -> + ignore ctx; + ignore pos; + let init = None in + let typed = + List.fold_left values ~init ~f:(fun state -> function + | T.Text t -> ( + (* Tranform the type, but keep the information is it’s a raw data or a variable one *) - let nature = Option.value ~default:(Raw Integer) state in - match (Option.map get_type state, int_of_string_opt t) with - | None, Some _ - | Some Integer, Some _ - | Some NumericString, Some _ - | Some Bool, Some _ -> - Some (map nature NumericString) - | _, _ -> - if String.equal "" t then - (* If the text is empty, ignore it *) - state - else Some (map nature String)) - | T.Expression t -> ( - let nature = Option.value ~default:(Raw Integer) state in - match (Option.map get_type state, get_type t) with - | None, Integer | Some NumericString, Integer -> - Some (get_nature nature t NumericString) - | _ -> Some (map nature String))) - in - let result = Option.value ~default:(Raw String) typed in - result - -let uoperator : S.pos -> T.uoperator -> t -> t = - fun pos operator t -> - ignore pos; - match operator with Add -> t | Neg | No -> Raw Integer - -let boperator : S.pos -> T.boperator -> t -> t -> t = - fun pos operator t1 t2 -> - ignore pos; - match operator with - | T.Plus -> ( - match (get_type t1, get_type t2) with - | Integer, Integer -> get_nature t1 t2 Integer - | String, _ -> get_nature t1 t2 String - | _, String -> get_nature t1 t2 String - | (_ as t), Bool -> get_nature t1 t2 t - | Bool, (_ as t) -> get_nature t1 t2 t - | (_ as t), NumericString -> get_nature t1 t2 t - | NumericString, (_ as t) -> get_nature t1 t2 t) - | T.Eq | T.Neq -> get_nature t1 t2 Bool - | T.Mod | T.Minus | T.Product | T.Div -> get_nature t1 t2 Integer - | T.And | T.Or -> get_nature t1 t2 Bool - | Lt | Gte | Lte | Gt -> get_nature t1 t2 Bool - -let function_ : S.pos -> T.function_ -> t list -> t = - fun pos function_ params -> - ignore pos; - match function_ with - | Dyneval | Dyneval' -> Variable NumericString - | Arrcomp | Arrpos | Arrsize | Countobj | Desc | Getobj | Instr | Isplay -> - Variable Integer - | Desc' | Getobj' -> Variable String - | Func | Func' -> Variable NumericString - | Iif | Iif' -> ( match params with _ :: t :: _ -> t | _ -> Raw Bool) - | Input | Input' -> Variable NumericString - | Isnum -> Raw Bool - | Lcase | Lcase' | Ucase | Ucase' -> Raw String - | Len -> Variable Integer - | Loc -> Variable Bool - | Max | Max' | Min | Min' -> ( - match params with - | [] -> Raw Bool - | Raw String :: [] | Variable String :: [] -> Variable NumericString - | hd :: _ -> hd) - | Mid | Mid' -> Variable String - | Msecscount -> Variable Integer - | Rand -> Variable Integer - | Replace -> Variable String - | Replace' -> Variable String - | Rgb -> Variable Integer - | Rnd -> Variable Integer - | Selact -> Variable String - | Str | Str' -> Raw String - | Strcomp -> Raw Bool - | Strfind -> Variable String - | Strfind' -> Variable String - | Strpos -> Variable Integer - | Trim -> Variable String - | Trim' -> Variable String - | Val -> Variable Integer + let nature = Option.value ~default:(Raw Integer) state in + match (Option.map get_type state, int_of_string_opt t) with + | None, Some _ + | Some Integer, Some _ + | Some NumericString, Some _ + | Some Bool, Some _ -> + Some (map nature NumericString) + | _, _ -> + if String.equal "" t then + (* If the text is empty, ignore it *) + state + else Some (map nature String)) + | T.Expression t -> ( + let nature = Option.value ~default:(Raw Integer) state in + match (Option.map get_type state, get_type t) with + | None, Integer | Some NumericString, Integer -> + Some (get_nature nature t NumericString) + | _ -> Some (map nature String))) + in + let result = Option.value ~default:(Raw String) typed in + result + + let uoperator : + ctx:Qsp_syntax.S.extract_context -> S.pos -> T.uoperator -> t -> t = + fun ~ctx pos operator t -> + ignore ctx; + ignore pos; + match operator with Add -> t | Neg | No -> Raw Integer + + let boperator : + ctx:Qsp_syntax.S.extract_context -> S.pos -> T.boperator -> t -> t -> t = + fun ~ctx pos operator t1 t2 -> + ignore ctx; + ignore pos; + match operator with + | T.Plus -> ( + match (get_type t1, get_type t2) with + | Integer, Integer -> get_nature t1 t2 Integer + | String, _ -> get_nature t1 t2 String + | _, String -> get_nature t1 t2 String + | (_ as t), Bool -> get_nature t1 t2 t + | Bool, (_ as t) -> get_nature t1 t2 t + | (_ as t), NumericString -> get_nature t1 t2 t + | NumericString, (_ as t) -> get_nature t1 t2 t) + | T.Eq | T.Neq -> get_nature t1 t2 Bool + | T.Mod | T.Minus | T.Product | T.Div -> get_nature t1 t2 Integer + | T.And | T.Or -> get_nature t1 t2 Bool + | Lt | Gte | Lte | Gt -> get_nature t1 t2 Bool + + let function_ : + ctx:Qsp_syntax.S.extract_context -> S.pos -> T.function_ -> t list -> t = + fun ~ctx pos function_ params -> + ignore ctx; + ignore pos; + match function_ with + | Dyneval | Dyneval' -> Variable NumericString + | Arrcomp | Arrpos | Arrsize | Countobj | Desc | Getobj | Instr | Isplay -> + Variable Integer + | Desc' | Getobj' -> Variable String + | Func | Func' -> Variable NumericString + | Iif | Iif' -> ( match params with _ :: t :: _ -> t | _ -> Raw Bool) + | Input | Input' -> Variable NumericString + | Isnum -> Raw Bool + | Lcase | Lcase' | Ucase | Ucase' -> Raw String + | Len -> Variable Integer + | Loc -> Variable Bool + | Max | Max' | Min | Min' -> ( + match params with + | [] -> Raw Bool + | Raw String :: [] | Variable String :: [] -> Variable NumericString + | hd :: _ -> hd) + | Mid | Mid' -> Variable String + | Msecscount -> Variable Integer + | Rand -> Variable Integer + | Replace -> Variable String + | Replace' -> Variable String + | Rgb -> Variable Integer + | Rnd -> Variable Integer + | Selact -> Variable String + | Str | Str' -> Raw String + | Strcomp -> Raw Bool + | Strfind -> Variable String + | Strfind' -> Variable String + | Strpos -> Variable Integer + | Trim -> Variable String + | Trim' -> Variable String + | Val -> Variable Integer +end + +module A = struct + let identifier = "get_types" + let description = "Identify the type for an expression" + let is_global = true + let active = ref false + let depends = [] + + type ex = Qsp_syntax.Identifier.t + type context = unit + + let initialize () = () + + module Expression = Expression + + module Instruction = struct + type t = unit + type t' = unit + + include + Default.Instruction + (Expression) + (struct + type t = unit + + let default = () + let fold seq = Seq.iter (fun _ -> ()) seq + end) + + let v = Fun.id + end + + module Location = struct + type t = unit + type instruction = Instruction.t' + + let location : context -> S.pos -> instruction list -> t = + fun context pos instr -> + ignore context; + ignore pos; + List.iter instr ~f:(fun _ -> ()) + + let v : t -> Report.t list = fun _ -> [] + end + + let finalize context = + ignore context; + [] +end + +let expression_id = Type.Id.make () +let ex = Qsp_syntax.Identifier.build ~expression_id (module A) diff --git a/lib/checks/get_type.mli b/lib/checks/get_type.mli new file mode 100644 index 0000000..476059b --- /dev/null +++ b/lib/checks/get_type.mli @@ -0,0 +1,25 @@ +type type_of = + | Integer (** A numeric value *) + | Bool (** A boolean, not a real type *) + | String (** String value *) + | NumericString (** String containing a numeric value *) +[@@deriving show, eq] + +module Expression : sig + type t = Variable of type_of | Raw of type_of [@@deriving show, eq] + type t' = t + + include Qsp_syntax.S.Expression with type t := t and type t' := t' + + val ident : + ctx:Qsp_syntax.S.extract_context -> + (Qsp_syntax.S.pos, 'any) Qsp_syntax.S.variable -> + t + + val get_type : t -> type_of +end + +val expression_id : Expression.t Type.Id.t +(** Type identifier for the expression in this module *) + +val ex : Qsp_syntax.Identifier.t diff --git a/lib/checks/locations.ml b/lib/checks/locations.ml index 8e5f500..3a5ddf5 100644 --- a/lib/checks/locations.ml +++ b/lib/checks/locations.ml @@ -20,6 +20,9 @@ let identifier = "locations" let description = "Ensure every call points to an existing location" let is_global = true let active = ref true +let depends = [] + +type ex = Qsp_syntax.Identifier.t type t = { locations : LocationSet.t; @@ -89,8 +92,11 @@ module Expression = struct let v : t -> t' = Fun.id (* Extract the litteral if this is a simple text *) - let literal : S.pos -> t' T.literal list -> t' = - fun _ ll -> match ll with Text lit :: [] -> Some lit | _ -> None + let literal : + ctx:Qsp_syntax.S.extract_context -> S.pos -> t' T.literal list -> t' = + fun ~ctx _ ll -> + ignore ctx; + match ll with Text lit :: [] -> Some lit | _ -> None end module Instruction = struct diff --git a/lib/checks/nested_strings.ml b/lib/checks/nested_strings.ml index 51c5258..d4a7947 100644 --- a/lib/checks/nested_strings.ml +++ b/lib/checks/nested_strings.ml @@ -7,80 +7,77 @@ let identifier = "escaped_string" let description = "Check for unnecessary use of expression encoded in string" let is_global = false let active = ref true +let depends = [ Get_type.ex ] +type ex = Qsp_syntax.Identifier.t type context = unit let initialize = Fun.id let finalize () = [] -module Expression = Compose.TypeBuilder.Make (struct - type t = Report.t list +module Expression = struct + type t = { type_of : Get_type.Expression.t; report : Report.t list } type t' = Report.t list - let v : Get_type.t Lazy.t * t -> t' = snd + let v : t -> t' = fun t -> t.report (** Identify the expressions reprented as string. That’s here that the report are added. All the rest of the module only push thoses warning to the top level. *) let literal : - S.pos -> (Get_type.t Lazy.t * t) T.literal list -> Get_type.t Lazy.t -> t - = - fun pos content _type_of -> + ctx:Qsp_syntax.S.extract_context -> S.pos -> t T.literal list -> t = + fun ~ctx pos content -> + let type_of = Option.get (ctx.f Get_type.expression_id) in match content with - | [ T.Expression (t', _); T.Text "" ] -> ( - match Get_type.get_type (Lazy.force t') with - | Get_type.Integer -> [] + | [ T.Expression t; T.Text "" ] -> ( + match Get_type.Expression.get_type t.type_of with + | Get_type.Integer -> { type_of; report = [] } | _ -> let msg = Report.debug pos "This expression can be simplified" in - [ msg ]) - | _ -> [] + { type_of; report = [ msg ] }) + | _ -> { type_of; report = [] } - let ident : - (S.pos, Get_type.t Lazy.t * t) S.variable -> Get_type.t Lazy.t -> t = - fun variable _type_of -> - match variable.index with None -> [] | Some (_, t) -> t + let ident : ctx:Qsp_syntax.S.extract_context -> (S.pos, t) S.variable -> t = + fun ~ctx variable -> + let type_of = Option.get (ctx.f Get_type.expression_id) in + match variable.index with None -> { type_of; report = [] } | Some t -> t - let integer : S.pos -> string -> Get_type.t Lazy.t -> t = - fun pos t _type_of -> + let integer : ctx:Qsp_syntax.S.extract_context -> S.pos -> string -> t = + fun ~ctx pos t -> ignore pos; ignore t; - [] + let type_of = Option.get (ctx.f Get_type.expression_id) in + { type_of; report = [] } let function_ : - S.pos -> - T.function_ -> - (Get_type.t Lazy.t * t) list -> - Get_type.t Lazy.t -> - t = - fun pos f expressions _type_of -> + ctx:Qsp_syntax.S.extract_context -> S.pos -> T.function_ -> t list -> t = + fun ~ctx pos f expressions -> + let type_of = Option.get (ctx.f Get_type.expression_id) in ignore pos; ignore f; let exprs = List.fold_left ~init:[] expressions ~f:(fun acc el -> - List.rev_append (snd el) acc) + List.rev_append el.report acc) in - exprs + { type_of; report = exprs } let uoperator : - S.pos -> T.uoperator -> Get_type.t Lazy.t * t -> Get_type.t Lazy.t -> t = - fun pos op r _type_of -> + ctx:Qsp_syntax.S.extract_context -> S.pos -> T.uoperator -> t -> t = + fun ~ctx pos op r -> + let type_of = Option.get (ctx.f Get_type.expression_id) in ignore op; ignore pos; - snd r + { r with type_of } let boperator : - S.pos -> - T.boperator -> - Get_type.t Lazy.t * t -> - Get_type.t Lazy.t * t -> - Get_type.t Lazy.t -> - t = - fun pos op (_, r1) (_, r2) _type_of -> + ctx:Qsp_syntax.S.extract_context -> S.pos -> T.boperator -> t -> t -> t = + fun ~ctx pos op r1 r2 -> + let type_of = Option.get (ctx.f Get_type.expression_id) in ignore pos; ignore op; - r1 @ r2 -end) + { type_of; report = r1.report @ r2.report } +end module Instruction : S.Instruction with type t' = Report.t list and type expression = Expression.t' = diff --git a/lib/checks/nested_strings.mli b/lib/checks/nested_strings.mli index 1ef2e33..01e373a 100644 --- a/lib/checks/nested_strings.mli +++ b/lib/checks/nested_strings.mli @@ -1,3 +1,3 @@ -include Qsp_syntax.S.Analyzer +include Qsp_syntax.Analyzer.T with type ex = Qsp_syntax.Identifier.t (** The module [Nested_strings] report errors for each unnecessary raw string encoded inside a string expression *) diff --git a/lib/checks/type_of.ml b/lib/checks/type_of.ml index 42f9a2d..243c8b3 100644 --- a/lib/checks/type_of.ml +++ b/lib/checks/type_of.ml @@ -12,12 +12,15 @@ type context = unit let initialize = Fun.id let finalize () = [] +let depends = [ Get_type.ex ] + +type ex = Qsp_syntax.Identifier.t module Helper = struct - type argument_repr = { pos : S.pos; t : Get_type.t } + type argument_repr = { pos : S.pos; t : Get_type.Expression.t } module DynType = struct - type nonrec t = Get_type.t -> Get_type.t + type nonrec t = Get_type.Expression.t -> Get_type.Expression.t (** Dynamic type is a type unknown during the code. For example, the equality operator accept either Integer or String, but @@ -143,35 +146,35 @@ module Helper = struct msg :: report end -type t' = { result : Get_type.t Lazy.t; pos : S.pos } +type t' = { result : Get_type.Expression.t; pos : S.pos } -let arg_of_repr : Get_type.t Lazy.t -> S.pos -> Helper.argument_repr = - fun type_of pos -> { pos; t = Lazy.force type_of } +let arg_of_repr : Get_type.Expression.t -> S.pos -> Helper.argument_repr = + fun type_of pos -> { pos; t = type_of } -module TypedExpression = struct +module Expression = struct type nonrec t' = t' * Report.t list - type state = { pos : S.pos } + type state = { pos : S.pos; type_of : Get_type.Expression.t } type t = state * Report.t list - let v : Get_type.t Lazy.t * t -> t' = - fun (type_of, (t, r)) -> ({ result = type_of; pos = t.pos }, r) + let v : t -> t' = fun (t, r) -> ({ result = t.type_of; pos = t.pos }, r) (** The variable has type string when starting with a '$' *) - let ident : - (S.pos, Get_type.t Lazy.t * t) S.variable -> Get_type.t Lazy.t -> t = - fun var _type_of -> + let ident : ctx:Qsp_syntax.S.extract_context -> (S.pos, t) S.variable -> t = + fun ~ctx var -> (* Extract the error from the index *) let report = match var.index with | None -> [] | Some (_, expr) -> - let _, r = expr in + let r = expr in r in - ({ pos = var.pos }, report) + let type_of = Option.get (ctx.f Get_type.expression_id) in + ({ pos = var.pos; type_of }, report) - let integer : S.pos -> string -> Get_type.t Lazy.t -> t = - fun pos value _type_of -> + let integer : ctx:Qsp_syntax.S.extract_context -> S.pos -> string -> t = + fun ~ctx pos value -> + let type_of = Option.get (ctx.f Get_type.expression_id) in let int_value = int_of_string_opt value in let report = @@ -181,42 +184,36 @@ module TypedExpression = struct | None -> Report.error pos "Invalid integer value" :: [] in - ({ pos }, report) + ({ pos; type_of }, report) let literal : - S.pos -> (Get_type.t Lazy.t * t) T.literal list -> Get_type.t Lazy.t -> t - = - fun pos values type_of -> - ignore type_of; + ctx:Qsp_syntax.S.extract_context -> S.pos -> t T.literal list -> t = + fun ~ctx pos values -> + let type_of = Option.get (ctx.f Get_type.expression_id) in let init = [] in let report = List.fold_left values ~init ~f:(fun report -> function | T.Text _ -> report - | T.Expression (_, t) -> + | T.Expression t -> let report = List.rev_append (snd t) report in report) in - ({ pos }, report) + ({ pos; type_of }, report) let function_ : - S.pos -> - T.function_ -> - (Get_type.t Lazy.t * t) list -> - Get_type.t Lazy.t -> - t = - fun pos function_ params _type_of -> + ctx:Qsp_syntax.S.extract_context -> S.pos -> T.function_ -> t list -> t = + fun ~ctx pos function_ params -> + let type_of = Option.get (ctx.f Get_type.expression_id) in (* Accumulate the expressions and get the results, the report is given in the differents arguments, and we build a list with the type of the parameters. *) let types, report = - List.fold_left params ~init:([], []) - ~f:(fun (types, report) (type_of, param) -> - ignore type_of; + List.fold_left params ~init:([], []) ~f:(fun (types, report) param -> let t, r = param in - let arg = arg_of_repr type_of t.pos in + let arg = arg_of_repr t.type_of t.pos in (arg :: types, r @ report)) in - let types = List.rev types and default = { pos } in + let types = List.rev types and default = { pos; type_of } in match function_ with | Arrcomp | Arrpos | Arrsize | Countobj | Desc | Dyneval | Getobj | Instr @@ -229,7 +226,7 @@ module TypedExpression = struct let expected = Helper.[ Fixed Bool; Dynamic d; Dynamic d ] in let report = Helper.compare_args pos expected types report in (* Extract the type for the expression *) - ({ pos }, report) + ({ pos; type_of }, report) | Input | Input' -> (* Input should check the result if the variable is a num and raise a message in this case.*) @@ -257,7 +254,7 @@ module TypedExpression = struct (* All the arguments must have the same type *) let expected = Helper.[ Variable (Dynamic d) ] in let report = Helper.compare_args pos expected types report in - ({ pos }, report) + ({ pos; type_of }, report) | Mid | Mid' -> let expected = Helper.[ Fixed String; Variable (Fixed Integer) ] in let report = Helper.compare_args pos expected types report in @@ -292,29 +289,25 @@ module TypedExpression = struct (** Unary operator like [-123] or [+'Text']*) let uoperator : - S.pos -> T.uoperator -> Get_type.t Lazy.t * t -> Get_type.t Lazy.t -> t = - fun pos operator t1 type_of -> - ignore type_of; - let type_of, (t, report) = t1 in + ctx:Qsp_syntax.S.extract_context -> S.pos -> T.uoperator -> t -> t = + fun ~ctx pos operator t1 -> + let t, report = t1 in match operator with | Add -> (t, report) | Neg | No -> - let types = [ arg_of_repr type_of t.pos ] in + let types = [ arg_of_repr t.type_of t.pos ] in let expected = Helper.[ Fixed Integer ] in let report = Helper.compare_args pos expected types report in - ({ pos }, report) + let type_of = Option.get (ctx.f Get_type.expression_id) in + ({ pos; type_of }, report) let boperator : - S.pos -> - T.boperator -> - Get_type.t Lazy.t * t -> - Get_type.t Lazy.t * t -> - Get_type.t Lazy.t -> - t = - fun pos operator (type_1, t1) (type_2, t2) type_of -> - ignore type_of; + ctx:Qsp_syntax.S.extract_context -> S.pos -> T.boperator -> t -> t -> t = + fun ~ctx pos operator t1 t2 -> let t1, report1 = t1 in let t2, report2 = t2 in + let type_1 = t1.type_of and type_2 = t2.type_of in + let type_of = Option.get (ctx.f Get_type.expression_id) in let report = report1 @ report2 in @@ -327,7 +320,7 @@ module TypedExpression = struct When concatenating, it’s allowed to add an integer and a number. *) - ({ pos }, report) + ({ pos; type_of }, report) | T.Eq | T.Neq | Lt | Gte | Lte | Gt -> (* If the expression is '' or 0, we accept the comparaison as if instead of raising a warning *) @@ -345,26 +338,24 @@ module TypedExpression = struct report | report -> report in - ({ pos }, report) + ({ pos; type_of }, report) | T.Mod | T.Minus | T.Product | T.Div -> (* Operation over number *) let expected = Helper.[ Fixed Integer; Fixed Integer ] in let report = Helper.compare_args pos expected types report in - ({ pos }, report) + ({ pos; type_of }, report) | T.And | T.Or -> (* Operation over booleans *) let expected = Helper.[ Fixed Bool; Fixed Bool ] in let report = Helper.compare_args pos expected types report in - ({ pos }, report) + ({ pos; type_of }, report) end -module Expression = Compose.TypeBuilder.Make (TypedExpression) - module Instruction = struct type t = Report.t list type t' = Report.t list - let v : t -> t' = fun local_report -> local_report + let v : t -> t' = Fun.id type expression = Expression.t' @@ -444,17 +435,21 @@ module Instruction = struct let report = List.rev_append report' report in - match (op, Get_type.get_type (Lazy.force right_expression.result)) with + match (op, Get_type.Expression.get_type right_expression.result) with | T.Eq', Get_type.Integer -> (* Assigning an intger is allowed in a string variable, but raise a warning. *) - let var_type = Lazy.from_val (Get_type.ident variable) in + let var_type = + Get_type.Expression.ident ~ctx:{ f = (fun _ -> None) } variable + in let op1 = arg_of_repr var_type variable.pos in let expected = Helper.[ Fixed Integer ] in Helper.compare_args ~strict:true ~level:Report.Warn pos expected [ op1 ] report | _, _ -> ( - let var_type = Lazy.from_val (Get_type.ident variable) in + let var_type = + Get_type.Expression.ident ~ctx:{ f = (fun _ -> None) } variable + in let op1 = arg_of_repr var_type variable.pos in let op2 = arg_of_repr right_expression.result right_expression.pos in diff --git a/lib/checks/type_of.mli b/lib/checks/type_of.mli index de0f8f9..f2be559 100644 --- a/lib/checks/type_of.mli +++ b/lib/checks/type_of.mli @@ -1,7 +1,7 @@ -include Qsp_syntax.S.Analyzer +include Qsp_syntax.Analyzer.T with type ex = Qsp_syntax.Identifier.t (** The module [type_of] populate the report with differents inconsistency - errors in the types. + errors in the types. - - 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. *) + - 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. *) diff --git a/lib/checks/write_only.ml b/lib/checks/write_only.ml index e2c3d7e..2d78b59 100644 --- a/lib/checks/write_only.ml +++ b/lib/checks/write_only.ml @@ -15,6 +15,9 @@ let description = "Check variables never read" let active = ref false let is_global = true +let depends = [] + +type ex = Qsp_syntax.Identifier.t module StringMap = Hashtbl.Make (String) module Set = Set.Make (String) @@ -76,13 +79,16 @@ module Expression = struct let default _ map = ignore map end) - let ident : (S.pos, t) S.variable -> t = - fun variable filename map -> + let ident : ctx:Qsp_syntax.S.extract_context -> (S.pos, t) S.variable -> t = + fun ~ctx variable filename map -> + ignore ctx; (* Update the map and set the read flag *) set_readed variable.pos variable.name filename map - let literal : S.pos -> t T.literal list -> t = - fun pos l filename map -> + let literal : + ctx:Qsp_syntax.S.extract_context -> S.pos -> t T.literal list -> t = + fun ~ctx pos l filename map -> + ignore ctx; List.iter l ~f:(function | T.Text t -> set_readed pos ~update_only:true (String.uppercase_ascii t) filename @@ -91,13 +97,22 @@ module Expression = struct (* When the string contains an expression evaluate it *) exprs filename map) - let function_ : S.pos -> T.function_ -> t list -> t = - fun _ _ exprs filename map -> List.iter ~f:(fun v -> v filename map) exprs + let function_ : + ctx:Qsp_syntax.S.extract_context -> S.pos -> T.function_ -> t list -> t = + fun ~ctx _ _ exprs filename map -> + ignore ctx; + List.iter ~f:(fun v -> v filename map) exprs - let uoperator : S.pos -> T.uoperator -> t -> t = fun _ _ t map -> t map + let uoperator : + ctx:Qsp_syntax.S.extract_context -> S.pos -> T.uoperator -> t -> t = + fun ~ctx _ _ t map -> + ignore ctx; + t map - let boperator : S.pos -> T.boperator -> t -> t -> t = - fun _ _ t1 t2 filename map -> + let boperator : + ctx:Qsp_syntax.S.extract_context -> S.pos -> T.boperator -> t -> t -> t = + fun ~ctx _ _ t1 t2 filename map -> + ignore ctx; t1 filename map; t2 filename map end |