2023-06-28 16:57:52 +03:00
|
|
|
(* This file is part of the Catala compiler, a specification language for tax
|
|
|
|
and social benefits computation rules. Copyright (C) 2023 Inria,
|
|
|
|
contributors: Denis Merigoux <denis.merigoux@inria.fr>, Louis Gesbert
|
|
|
|
<louis.gesbert@inria.fr>
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
|
|
use this file except in compliance with the License. You may obtain a copy of
|
|
|
|
the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
License for the specific language governing permissions and limitations under
|
|
|
|
the License. *)
|
|
|
|
|
2023-05-30 16:41:49 +03:00
|
|
|
(** Error formatting and helper functions *)
|
|
|
|
|
2023-05-30 16:51:44 +03:00
|
|
|
(**{1 Terminal formatting}*)
|
|
|
|
|
2023-06-07 19:10:50 +03:00
|
|
|
(* Adds handling of color tags in the formatter *)
|
2023-06-08 13:06:27 +03:00
|
|
|
let color_formatter ppf =
|
2023-06-07 19:10:50 +03:00
|
|
|
Ocolor_format.prettify_formatter ppf;
|
|
|
|
ppf
|
|
|
|
|
|
|
|
(* Sets handling of tags in the formatter to ignore them (don't print any color
|
|
|
|
codes) *)
|
|
|
|
let unstyle_formatter ppf =
|
|
|
|
Format.pp_set_mark_tags ppf false;
|
|
|
|
ppf
|
2023-06-07 19:15:14 +03:00
|
|
|
|
2023-06-07 19:10:50 +03:00
|
|
|
(* SIDE EFFECT AT MODULE LOAD: this turns on handling of tags in
|
|
|
|
[Format.sprintf] etc. functions (ignoring them) *)
|
|
|
|
let () = ignore (unstyle_formatter Format.str_formatter)
|
2023-06-08 13:06:27 +03:00
|
|
|
|
2023-06-07 19:10:50 +03:00
|
|
|
(* Note: we could do the same for std_formatter, err_formatter... but we'd
|
|
|
|
rather promote the use of the formatting functions of this module and the
|
|
|
|
below std_ppf / err_ppf *)
|
|
|
|
|
Generate tests reports from 'clerk test'
This is a proper replacement for the previous shell-based placeholder hack.
Here is a summary:
- `clerk runtest` (normally run by ninja) is much extended:
* besides generating the test@out file, it checks individual tests for success
and can write a report file containing their status, and the positions for
their (expected/current) outputs (this uses `Marshal`)
* it now handles out-tests directly in addition to inline-tests, for which
it generates the separate output file ; they are included in the report
- ninja is now tasked with building all the test reports (which shouldn't fail);
for directories, individual reports are concatenated (as before).
Removing intermediate report rules, and out-test rules means that the ninja
file is much simplified.
- then, clerk takes back control, reads the final reports and formats them in a
user-friendly way. Printing the reports may imply running `diff` internally.
In particular, the commands to easily reproduce each test are provided.
Resetting the test results if required is also done directly by clerk, at this
stage.
A few switches are available to customise the output, but I am waiting for some
feedback before deciding what to make available from the CLI.
The `clerk report` command is available to manually explore test reports, but
normally the processing is done directly at the end of `clerk test` (i.e. ninja
will no longer call that command)
2024-06-14 22:05:19 +03:00
|
|
|
let terminal_columns, set_terminal_width_function =
|
|
|
|
let get_cols = ref (fun () -> 80) in
|
|
|
|
(fun () -> !get_cols ()), fun f -> get_cols := f
|
|
|
|
|
2024-04-26 16:40:55 +03:00
|
|
|
let has_color_raw ~(tty : bool Lazy.t) =
|
2024-03-15 16:23:30 +03:00
|
|
|
match Global.options.color with
|
|
|
|
| Global.Never -> false
|
2023-06-07 19:10:50 +03:00
|
|
|
| Always -> true
|
2024-04-26 16:40:55 +03:00
|
|
|
| Auto -> Lazy.force tty
|
|
|
|
|
|
|
|
let has_color oc =
|
|
|
|
has_color_raw ~tty:(lazy Unix.(isatty (descr_of_out_channel oc)))
|
2023-06-07 19:10:50 +03:00
|
|
|
|
2023-06-08 13:06:27 +03:00
|
|
|
(* Here we create new formatters to stderr/stdout that remain separate from the
|
|
|
|
ones used by [Format.printf] / [Format.eprintf] (which remain unchanged) *)
|
2023-06-07 19:10:50 +03:00
|
|
|
|
2023-06-08 13:06:27 +03:00
|
|
|
let formatter_of_out_channel oc =
|
2024-04-26 16:40:55 +03:00
|
|
|
let tty = lazy Unix.(isatty (descr_of_out_channel oc)) in
|
|
|
|
let ppf =
|
2024-05-04 18:45:22 +03:00
|
|
|
lazy
|
|
|
|
(let ppf = Format.formatter_of_out_channel oc in
|
|
|
|
if has_color_raw ~tty then color_formatter ppf else unstyle_formatter ppf)
|
2024-04-26 16:40:55 +03:00
|
|
|
in
|
2024-05-04 18:45:22 +03:00
|
|
|
fun () ->
|
|
|
|
let ppf = Lazy.force ppf in
|
2024-04-26 16:40:55 +03:00
|
|
|
if Lazy.force tty then Format.pp_set_margin ppf (terminal_columns ());
|
2024-05-04 18:45:22 +03:00
|
|
|
ppf
|
|
|
|
|
|
|
|
let std_ppf = formatter_of_out_channel stdout
|
|
|
|
let err_ppf = formatter_of_out_channel stderr
|
2023-06-07 19:10:50 +03:00
|
|
|
|
2024-05-04 18:45:22 +03:00
|
|
|
let ignore_ppf =
|
|
|
|
let ppf = lazy (Format.make_formatter (fun _ _ _ -> ()) (fun () -> ())) in
|
|
|
|
fun () -> Lazy.force ppf
|
2023-06-07 19:10:50 +03:00
|
|
|
|
|
|
|
let unformat (f : Format.formatter -> unit) : string =
|
|
|
|
let buf = Buffer.create 1024 in
|
|
|
|
let ppf = unstyle_formatter (Format.formatter_of_buffer buf) in
|
|
|
|
Format.pp_set_margin ppf max_int;
|
|
|
|
(* We won't print newlines anyways, but better not have them in the first
|
|
|
|
place (this wouldn't remove cuts in a vbox for example) *)
|
|
|
|
let out_funs = Format.pp_get_formatter_out_functions ppf () in
|
|
|
|
Format.pp_set_formatter_out_functions ppf
|
|
|
|
{
|
|
|
|
out_funs with
|
|
|
|
Format.out_newline = (fun () -> out_funs.out_string " " 0 1);
|
|
|
|
Format.out_indent = (fun _ -> ());
|
|
|
|
};
|
|
|
|
f ppf;
|
|
|
|
Format.pp_print_flush ppf ();
|
|
|
|
Buffer.contents buf
|
|
|
|
|
2024-06-18 16:10:29 +03:00
|
|
|
let pad n s ppf = Pos.pad_fmt n s ppf
|
|
|
|
|
2023-06-07 19:10:50 +03:00
|
|
|
(**{2 Message types and output helpers *)
|
2023-05-30 16:51:44 +03:00
|
|
|
|
2024-04-09 14:30:01 +03:00
|
|
|
type level = Error | Warning | Debug | Log | Result
|
2023-06-07 19:10:50 +03:00
|
|
|
|
|
|
|
let get_ppf = function
|
2024-05-04 18:45:22 +03:00
|
|
|
| Result -> std_ppf ()
|
|
|
|
| Debug when not Global.options.debug -> ignore_ppf ()
|
|
|
|
| Warning when Global.options.disable_warnings -> ignore_ppf ()
|
|
|
|
| Error | Log | Debug | Warning -> err_ppf ()
|
2023-06-07 19:10:50 +03:00
|
|
|
|
|
|
|
(**{3 Markers}*)
|
2023-05-30 16:51:44 +03:00
|
|
|
|
2023-06-07 19:10:50 +03:00
|
|
|
let print_time_marker =
|
|
|
|
let time : float ref = ref (Unix.gettimeofday ()) in
|
|
|
|
fun ppf () ->
|
2023-05-30 16:51:44 +03:00
|
|
|
let new_time = Unix.gettimeofday () in
|
|
|
|
let old_time = !time in
|
|
|
|
time := new_time;
|
|
|
|
let delta = (new_time -. old_time) *. 1000. in
|
2024-06-26 11:41:59 +03:00
|
|
|
if delta > 50. then Format.fprintf ppf " @{<bold;black>%.0fms@}" delta
|
2023-06-07 19:10:50 +03:00
|
|
|
|
2024-06-19 18:21:57 +03:00
|
|
|
let pp_marker ?extra_label target ppf =
|
2023-06-07 19:10:50 +03:00
|
|
|
let open Ocolor_types in
|
|
|
|
let tags, str =
|
|
|
|
match target with
|
2024-05-03 13:20:09 +03:00
|
|
|
| Debug -> [Bold; Fg (C4 magenta)], "DEBUG"
|
|
|
|
| Error -> [Bold; Fg (C4 red)], "ERROR"
|
|
|
|
| Warning -> [Bold; Fg (C4 yellow)], "WARNING"
|
|
|
|
| Result -> [Bold; Fg (C4 green)], "RESULT"
|
|
|
|
| Log -> [Bold; Fg (C4 black)], "LOG"
|
2023-06-07 19:10:50 +03:00
|
|
|
in
|
2024-06-19 18:21:57 +03:00
|
|
|
let str =
|
|
|
|
match extra_label with
|
|
|
|
| None -> str
|
|
|
|
| Some lbl -> Printf.sprintf "%s %s" str lbl
|
|
|
|
in
|
2023-06-07 19:10:50 +03:00
|
|
|
Format.pp_open_stag ppf (Ocolor_format.Ocolor_styles_tag tags);
|
|
|
|
Format.pp_print_string ppf str;
|
2024-06-26 11:41:59 +03:00
|
|
|
Format.pp_close_stag ppf ();
|
|
|
|
if target = Debug then print_time_marker ppf ()
|
2023-05-30 16:51:44 +03:00
|
|
|
|
|
|
|
(**{2 Printers}*)
|
|
|
|
|
2023-05-30 16:41:49 +03:00
|
|
|
(** {1 Message content} *)
|
|
|
|
|
2023-05-30 17:22:05 +03:00
|
|
|
module Content = struct
|
2023-06-07 19:10:50 +03:00
|
|
|
type message = Format.formatter -> unit
|
|
|
|
type position = { pos_message : message option; pos : Pos.t }
|
2023-05-30 17:22:05 +03:00
|
|
|
|
2023-06-19 18:08:16 +03:00
|
|
|
type message_element =
|
|
|
|
| MainMessage of message
|
|
|
|
| Position of position
|
2023-07-09 18:58:07 +03:00
|
|
|
| Suggestion of string list
|
2024-04-09 14:30:01 +03:00
|
|
|
| Outcome of message
|
2023-06-19 14:59:56 +03:00
|
|
|
|
2023-06-19 18:08:16 +03:00
|
|
|
type t = message_element list
|
2023-06-07 19:15:14 +03:00
|
|
|
|
2023-06-19 18:08:16 +03:00
|
|
|
let of_message (message : message) : t = [MainMessage message]
|
2024-04-09 14:30:01 +03:00
|
|
|
let of_result (message : message) : t = [Outcome message]
|
2023-06-19 18:08:16 +03:00
|
|
|
let prepend_message (content : t) prefix : t = MainMessage prefix :: content
|
2023-06-13 21:10:42 +03:00
|
|
|
|
2023-06-19 18:08:16 +03:00
|
|
|
let to_internal_error (content : t) : t =
|
|
|
|
let internal_error_prefix ppf =
|
|
|
|
Format.pp_print_string ppf
|
|
|
|
"Internal Error, please report to \
|
|
|
|
https://github.com/CatalaLang/catala/issues."
|
|
|
|
in
|
|
|
|
prepend_message content internal_error_prefix
|
2023-06-13 21:10:42 +03:00
|
|
|
|
2023-07-09 18:58:07 +03:00
|
|
|
let add_suggestion (content : t) (suggestion : string list) =
|
2023-06-19 18:08:16 +03:00
|
|
|
content @ [Suggestion suggestion]
|
2023-06-13 21:10:42 +03:00
|
|
|
|
2024-04-10 19:39:30 +03:00
|
|
|
let add_position (content : t) ?(message : message option) (position : Pos.t)
|
|
|
|
=
|
2024-01-17 18:03:20 +03:00
|
|
|
content @ [Position { pos = position; pos_message = message }]
|
|
|
|
|
2023-06-19 18:08:16 +03:00
|
|
|
let of_string (s : string) : t =
|
2024-04-10 18:33:19 +03:00
|
|
|
[MainMessage (fun ppf -> Format.pp_print_text ppf s)]
|
2023-06-19 18:08:16 +03:00
|
|
|
|
2024-06-19 18:21:57 +03:00
|
|
|
let basic_msg ?(pp_marker = pp_marker) ppf target content =
|
2024-05-03 13:20:09 +03:00
|
|
|
Format.pp_open_vbox ppf 0;
|
|
|
|
Format.pp_print_list
|
|
|
|
~pp_sep:(fun ppf () -> Format.fprintf ppf "@,@,")
|
|
|
|
(fun ppf -> function
|
|
|
|
| Position pos ->
|
|
|
|
Option.iter
|
|
|
|
(fun msg -> Format.fprintf ppf "@[<hov>%t@]@," msg)
|
|
|
|
pos.pos_message;
|
|
|
|
Pos.format_loc_text ppf pos.pos
|
|
|
|
| MainMessage msg ->
|
|
|
|
Format.fprintf ppf "@[<hov 2>[%t] %t@]" (pp_marker target) msg
|
|
|
|
| Outcome msg ->
|
|
|
|
Format.fprintf ppf "@[<hov>[%t]@ %t@]" (pp_marker target) msg
|
|
|
|
| Suggestion suggestions_list -> Suggestions.format ppf suggestions_list)
|
|
|
|
ppf content;
|
|
|
|
Format.pp_close_box ppf ();
|
|
|
|
Format.pp_print_newline ppf ()
|
|
|
|
|
2024-06-19 18:21:57 +03:00
|
|
|
let fancy_msg ?(pp_marker = pp_marker) ppf target content =
|
2024-05-03 13:20:09 +03:00
|
|
|
let ppf_out_fcts = Format.pp_get_formatter_out_functions ppf () in
|
|
|
|
let restore_ppf () =
|
|
|
|
Format.pp_print_flush ppf ();
|
|
|
|
Format.pp_set_formatter_out_functions ppf ppf_out_fcts
|
|
|
|
in
|
|
|
|
let getcolorstr pp =
|
|
|
|
let buf = Buffer.create 17 in
|
|
|
|
let ppfb = Format.formatter_of_buffer buf in
|
|
|
|
Format.pp_set_formatter_stag_functions ppfb
|
|
|
|
(Format.pp_get_formatter_stag_functions ppf ());
|
|
|
|
Format.pp_set_mark_tags ppfb (Format.pp_get_mark_tags ppf ());
|
|
|
|
pp ppfb;
|
|
|
|
Format.pp_print_flush ppfb ();
|
|
|
|
Buffer.contents buf
|
|
|
|
in
|
|
|
|
(* The following adds a blue line on the left *)
|
|
|
|
Format.pp_set_formatter_out_functions ppf
|
|
|
|
{
|
|
|
|
ppf_out_fcts with
|
|
|
|
out_indent =
|
|
|
|
(fun n ->
|
|
|
|
let lead =
|
|
|
|
getcolorstr (fun ppf -> Format.fprintf ppf "@{<blue>@<1>%s@}" "│")
|
|
|
|
in
|
|
|
|
if n >= 1 then ppf_out_fcts.out_string lead 0 (String.length lead);
|
|
|
|
if n >= 2 then ppf_out_fcts.out_indent (n - 1));
|
|
|
|
};
|
|
|
|
Format.pp_open_vbox ppf 1;
|
|
|
|
Format.fprintf ppf "@{<blue>@<2>%s[%t]@<2>%s@}" "┌─" (pp_marker target) "─";
|
|
|
|
(* Returns true when a finaliser is needed *)
|
|
|
|
let print_elt ppf ?(islast = false) = function
|
|
|
|
| MainMessage msg ->
|
|
|
|
Format.fprintf ppf "@,@[<v 2>@,@[<hov>%t@]@]" msg;
|
|
|
|
if islast then Format.pp_print_cut ppf ();
|
|
|
|
true
|
|
|
|
| Position pos -> (
|
|
|
|
Format.pp_print_cut ppf ();
|
|
|
|
Option.iter
|
|
|
|
(fun msg -> Format.fprintf ppf "@[<v 1>@,@[<hov 2>%t@]@]" msg)
|
|
|
|
pos.pos_message;
|
|
|
|
Format.pp_print_break ppf 0 (-1);
|
|
|
|
let pr_head, pr_context, pr_legal = Pos.format_loc_text_parts pos.pos in
|
|
|
|
Format.pp_open_vbox ppf 2;
|
|
|
|
Format.fprintf ppf "@{<blue>@<1>%s@}%t" "├" pr_head;
|
|
|
|
pr_context ppf;
|
|
|
|
Format.pp_close_box ppf ();
|
|
|
|
match pr_legal with
|
|
|
|
| None -> true
|
|
|
|
| Some pr_legal ->
|
|
|
|
Format.pp_print_break ppf 0 (-1);
|
|
|
|
if islast then (
|
|
|
|
restore_ppf ();
|
|
|
|
Format.pp_open_vbox ppf 3;
|
|
|
|
Format.fprintf ppf "@{<blue>@<3>%s@}%t" "└─ " pr_legal)
|
|
|
|
else (
|
|
|
|
Format.pp_open_vbox ppf 3;
|
|
|
|
Format.fprintf ppf "@{<blue>@<3>%s@}%t" "├─ " pr_legal);
|
|
|
|
Format.pp_close_box ppf ();
|
|
|
|
not islast)
|
|
|
|
| Outcome msg ->
|
2024-05-03 16:12:52 +03:00
|
|
|
Format.fprintf ppf "@;<0 1>@[<v 1>@[<hov 2>%t@]@]" msg;
|
2024-05-03 13:20:09 +03:00
|
|
|
true
|
|
|
|
| Suggestion suggestions_list ->
|
|
|
|
Format.fprintf ppf "@,@[<v 1>@,@[<hov 2>%a@]@]" Suggestions.format
|
|
|
|
suggestions_list;
|
|
|
|
true
|
|
|
|
in
|
|
|
|
let rec print_lines ppf = function
|
|
|
|
| [elt] ->
|
|
|
|
let finalise = print_elt ppf ~islast:true elt in
|
|
|
|
Format.pp_close_box ppf ();
|
|
|
|
if finalise then Format.fprintf ppf "@,@{<blue>@<2>%s@}" "└─"
|
|
|
|
| elt :: r ->
|
|
|
|
let _ = print_elt ppf elt in
|
|
|
|
print_lines ppf r
|
|
|
|
| [] ->
|
|
|
|
Format.pp_close_box ppf ();
|
|
|
|
Format.pp_print_cut ppf ()
|
|
|
|
in
|
|
|
|
print_lines ppf content;
|
|
|
|
Format.pp_close_box ppf ();
|
|
|
|
restore_ppf ();
|
|
|
|
Format.pp_print_newline ppf ()
|
|
|
|
|
2024-06-19 18:21:57 +03:00
|
|
|
let emit ?(pp_marker = pp_marker) (content : t) (target : level) : unit =
|
2024-03-15 16:23:30 +03:00
|
|
|
match Global.options.message_format with
|
2024-05-03 13:20:09 +03:00
|
|
|
| Global.Human -> (
|
2023-06-19 18:08:16 +03:00
|
|
|
let ppf = get_ppf target in
|
2024-05-03 13:20:09 +03:00
|
|
|
match target with
|
2024-06-19 18:21:57 +03:00
|
|
|
| Debug | Log -> basic_msg ~pp_marker ppf target content
|
|
|
|
| Result | Warning | Error -> fancy_msg ~pp_marker ppf target content)
|
2024-03-15 16:23:30 +03:00
|
|
|
| Global.GNU ->
|
2023-07-12 14:57:58 +03:00
|
|
|
(* The top message doesn't come with a position, which is not something
|
|
|
|
the GNU standard allows. So we look the position list and put the top
|
|
|
|
message everywhere there is not a more precise message. If we can't
|
|
|
|
find a position without a more precise message, we just take the first
|
|
|
|
position in the list to pair with the message. *)
|
|
|
|
let ppf = get_ppf target in
|
|
|
|
Format.pp_print_list ~pp_sep:Format.pp_print_newline
|
|
|
|
(fun ppf elt ->
|
2023-07-12 17:32:55 +03:00
|
|
|
let pos, message =
|
|
|
|
match elt with
|
|
|
|
| MainMessage m ->
|
|
|
|
let pos =
|
|
|
|
List.find_map
|
|
|
|
(function
|
|
|
|
| Position { pos_message = None; pos } -> Some pos
|
|
|
|
| _ -> None)
|
|
|
|
content
|
|
|
|
|> function
|
|
|
|
| None ->
|
|
|
|
List.find_map
|
|
|
|
(function
|
|
|
|
| Position { pos_message = _; pos } -> Some pos
|
|
|
|
| _ -> None)
|
|
|
|
content
|
|
|
|
| some -> some
|
|
|
|
in
|
2024-04-29 17:09:38 +03:00
|
|
|
pos, Some m
|
|
|
|
| Position { pos_message; pos } -> Some pos, pos_message
|
|
|
|
| Outcome m -> None, Some m
|
|
|
|
| Suggestion sl -> None, Some (fun ppf -> Suggestions.format ppf sl)
|
2023-07-12 17:32:55 +03:00
|
|
|
in
|
|
|
|
Option.iter
|
|
|
|
(fun pos ->
|
|
|
|
Format.fprintf ppf "@{<blue>%s@}: " (Pos.to_string_short pos))
|
|
|
|
pos;
|
2024-05-03 13:20:09 +03:00
|
|
|
Format.fprintf ppf "[%t]" (pp_marker target);
|
2024-04-29 17:09:38 +03:00
|
|
|
match message with
|
|
|
|
| Some message ->
|
|
|
|
Format.pp_print_char ppf ' ';
|
|
|
|
Format.pp_print_string ppf (unformat message)
|
|
|
|
| None -> ())
|
2023-07-12 14:57:58 +03:00
|
|
|
ppf content;
|
|
|
|
Format.pp_print_newline ppf ()
|
2024-06-19 18:21:57 +03:00
|
|
|
|
2024-06-20 11:52:57 +03:00
|
|
|
let emit_n (target : level) = function
|
|
|
|
| [content] -> emit content target
|
|
|
|
| contents ->
|
|
|
|
let ppf = get_ppf target in
|
|
|
|
let len = List.length contents in
|
|
|
|
List.iteri
|
|
|
|
(fun i c ->
|
|
|
|
if i > 0 then Format.pp_print_newline ppf ();
|
|
|
|
let extra_label = Printf.sprintf "(%d/%d)" (succ i) len in
|
|
|
|
let pp_marker ?extra_label:_ = pp_marker ~extra_label in
|
|
|
|
emit ~pp_marker c target)
|
|
|
|
contents
|
2024-06-19 18:21:57 +03:00
|
|
|
|
|
|
|
let emit (content : t) (target : level) = emit content target
|
2023-05-30 17:22:05 +03:00
|
|
|
end
|
2023-05-30 16:41:49 +03:00
|
|
|
|
2023-06-07 19:10:50 +03:00
|
|
|
open Content
|
|
|
|
|
2023-05-30 16:41:49 +03:00
|
|
|
(** {1 Error exception} *)
|
|
|
|
|
2023-05-30 17:22:05 +03:00
|
|
|
exception CompilerError of Content.t
|
2024-06-17 16:36:38 +03:00
|
|
|
exception CompilerErrors of Content.t list
|
2023-05-30 16:41:49 +03:00
|
|
|
|
|
|
|
(** {1 Error printing} *)
|
|
|
|
|
2024-04-09 14:30:01 +03:00
|
|
|
type ('a, 'b) emitter =
|
|
|
|
?header:Content.message ->
|
|
|
|
?internal:bool ->
|
|
|
|
?pos:Pos.t ->
|
|
|
|
?pos_msg:Content.message ->
|
2024-04-09 20:08:29 +03:00
|
|
|
?extra_pos:(string * Pos.t) list ->
|
|
|
|
?fmt_pos:(Content.message * Pos.t) list ->
|
2024-05-03 13:20:09 +03:00
|
|
|
?outcome:Content.message list ->
|
2024-04-09 14:30:01 +03:00
|
|
|
?suggestion:string list ->
|
|
|
|
('a, Format.formatter, unit, 'b) format4 ->
|
|
|
|
'a
|
|
|
|
|
2024-04-10 19:39:30 +03:00
|
|
|
let make
|
|
|
|
?header
|
|
|
|
?(internal = false)
|
|
|
|
?pos
|
|
|
|
?pos_msg
|
|
|
|
?extra_pos
|
|
|
|
?fmt_pos
|
2024-05-03 13:20:09 +03:00
|
|
|
?(outcome = [])
|
2024-04-10 17:11:14 +03:00
|
|
|
?(suggestion = [])
|
2024-04-10 19:39:30 +03:00
|
|
|
~cont
|
|
|
|
~level =
|
2024-05-03 16:13:08 +03:00
|
|
|
match level with
|
|
|
|
| Debug when not Global.options.debug ->
|
2024-05-04 18:45:22 +03:00
|
|
|
Format.ikfprintf (fun _ -> cont [] level) (ignore_ppf ())
|
2024-05-03 16:13:08 +03:00
|
|
|
| Warning when Global.options.disable_warnings ->
|
2024-05-04 18:45:22 +03:00
|
|
|
Format.ikfprintf (fun _ -> cont [] level) (ignore_ppf ())
|
2024-05-03 16:13:08 +03:00
|
|
|
| _ ->
|
|
|
|
Format.kdprintf
|
|
|
|
@@ fun message ->
|
|
|
|
let t =
|
|
|
|
match level with Result -> of_result message | _ -> of_message message
|
|
|
|
in
|
|
|
|
let t = match header with Some h -> prepend_message t h | None -> t in
|
|
|
|
let t = if internal then to_internal_error t else t in
|
|
|
|
let t =
|
|
|
|
match outcome with [] -> t | o -> t @ List.map (fun o -> Outcome o) o
|
|
|
|
in
|
|
|
|
let t =
|
|
|
|
match pos with Some p -> add_position t ?message:pos_msg p | None -> t
|
|
|
|
in
|
|
|
|
let t =
|
|
|
|
match extra_pos with
|
|
|
|
| Some pl ->
|
|
|
|
List.fold_left
|
|
|
|
(fun t (message, p) ->
|
|
|
|
let message =
|
|
|
|
if message = "" then None
|
|
|
|
else Some (fun ppf -> Format.pp_print_text ppf message)
|
|
|
|
in
|
|
|
|
add_position t ?message p)
|
|
|
|
t pl
|
|
|
|
| None -> t
|
|
|
|
in
|
|
|
|
let t =
|
|
|
|
match fmt_pos with
|
|
|
|
| Some pl ->
|
|
|
|
List.fold_left
|
|
|
|
(fun t (message, p) ->
|
|
|
|
let message = if message == ignore then None else Some message in
|
|
|
|
add_position t ?message p)
|
|
|
|
t pl
|
|
|
|
| None -> t
|
|
|
|
in
|
|
|
|
let t = match suggestion with [] -> t | s -> add_suggestion t s in
|
|
|
|
cont t level
|
2024-04-09 14:30:01 +03:00
|
|
|
|
|
|
|
let debug = make ~level:Debug ~cont:emit
|
|
|
|
let log = make ~level:Log ~cont:emit
|
|
|
|
let result = make ~level:Result ~cont:emit
|
2024-05-03 16:04:56 +03:00
|
|
|
let results r = emit (List.flatten (List.map of_result r)) Result
|
2024-04-09 14:30:01 +03:00
|
|
|
let warning = make ~level:Warning ~cont:emit
|
|
|
|
let error = make ~level:Error ~cont:(fun m _ -> raise (CompilerError m))
|
2024-06-17 16:36:38 +03:00
|
|
|
|
|
|
|
(* Multiple errors handling *)
|
2024-06-17 18:38:27 +03:00
|
|
|
|
|
|
|
type global_errors = {
|
|
|
|
mutable errors : t list option;
|
|
|
|
mutable stop_on_error : bool;
|
|
|
|
}
|
|
|
|
|
|
|
|
let global_errors = { errors = None; stop_on_error = false }
|
2024-06-17 16:36:38 +03:00
|
|
|
|
|
|
|
let delayed_error x =
|
|
|
|
make ~level:Error ~cont:(fun m _ ->
|
2024-06-17 18:38:27 +03:00
|
|
|
if global_errors.stop_on_error then raise (CompilerError m);
|
|
|
|
match global_errors.errors with
|
2024-06-17 16:36:38 +03:00
|
|
|
| None ->
|
2024-06-18 17:26:59 +03:00
|
|
|
error ~internal:true
|
2024-06-17 16:36:38 +03:00
|
|
|
"delayed error called outside scope: encapsulate using \
|
|
|
|
'with_delayed_errors' first"
|
|
|
|
| Some l ->
|
2024-06-17 18:38:27 +03:00
|
|
|
global_errors.errors <- Some (m :: l);
|
2024-06-17 16:36:38 +03:00
|
|
|
x)
|
|
|
|
|
2024-06-17 18:38:27 +03:00
|
|
|
let with_delayed_errors
|
|
|
|
?(stop_on_error = Global.options.stop_on_error)
|
|
|
|
(f : unit -> 'a) : 'a =
|
|
|
|
(match global_errors.errors with
|
|
|
|
| None -> global_errors.errors <- Some []
|
2024-06-18 17:26:59 +03:00
|
|
|
| Some _ ->
|
|
|
|
error ~internal:true
|
|
|
|
"delayed error called outside scope: encapsulate using \
|
|
|
|
'with_delayed_errors' first");
|
2024-06-17 18:38:27 +03:00
|
|
|
global_errors.stop_on_error <- stop_on_error;
|
2024-06-17 16:36:38 +03:00
|
|
|
let r = f () in
|
2024-06-17 18:38:27 +03:00
|
|
|
match global_errors.errors with
|
2024-06-18 17:26:59 +03:00
|
|
|
| None -> error ~internal:true "intertwined delayed error scope"
|
2024-06-17 16:36:38 +03:00
|
|
|
| Some [] ->
|
2024-06-17 18:38:27 +03:00
|
|
|
global_errors.errors <- None;
|
2024-06-17 16:36:38 +03:00
|
|
|
r
|
2024-06-19 18:21:57 +03:00
|
|
|
| Some [err] ->
|
|
|
|
global_errors.errors <- None;
|
|
|
|
raise (CompilerError err)
|
2024-06-17 16:36:38 +03:00
|
|
|
| Some errs ->
|
2024-06-17 18:38:27 +03:00
|
|
|
global_errors.errors <- None;
|
2024-06-17 16:36:38 +03:00
|
|
|
raise (CompilerErrors (List.rev errs))
|