123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- open Extlib_leftovers
- open Globals
- open Common
- open CompilationContext
- let resolve_source file l1 p1 l2 p2 =
- if l1 = l2 && p1 = p2 && l1 = 1 && p1 = 1 then []
- else begin
- let ch = open_in_bin file in
- let curline = ref 1 in
- let lines = ref [] in
- let rec loop p line =
- let inc i line =
- if (!curline >= l1) && (!curline <= l2) then lines := (!curline, line) :: !lines;
- incr curline;
- (i, "")
- in
- let input_char_or_done ch line =
- try input_char ch with End_of_file -> begin
- ignore(inc 0 line);
- raise End_of_file
- end
- in
- let read_char line = match input_char_or_done ch line with
- | '\n' -> inc 1 line
- | '\r' ->
- ignore(input_char_or_done ch line);
- inc 2 line
- | c -> begin
- let line = ref (line ^ (String.make 1 c)) in
- let rec skip n =
- if n > 0 then begin
- let c = input_char_or_done ch !line in
- line := !line ^ (String.make 1 c);
- skip (n - 1)
- end
- in
- let code = int_of_char c in
- if code < 0xC0 then ()
- else if code < 0xE0 then skip 1
- else if code < 0xF0 then skip 2
- else skip 3;
- (1, !line)
- end
- in
- let (delta, line) = read_char line in
- loop (p + delta) line
- in
- try loop 0 ""; with End_of_file -> close_in ch;
- List.rev !lines
- end
- let error_printer file line = Printf.sprintf "%s:%d:" file line
- type error_context = {
- absolute_positions : bool;
- mutable last_positions : pos IntMap.t;
- mutable max_lines : int IntMap.t;
- mutable gutter : int IntMap.t;
- mutable previous : (pos * MessageSeverity.t * int) option;
- }
- let create_error_context absolute_positions = {
- absolute_positions = absolute_positions;
- last_positions = IntMap.empty;
- max_lines = IntMap.empty;
- gutter = IntMap.empty;
- previous = None;
- }
- let compiler_pretty_message_string com ectx cm =
- match cm.cm_message with
- (* Filter some messages that don't add much when using this message renderer *)
- | "End of overload failure reasons" -> None
- | _ -> begin
- ectx.last_positions <- (IntMap.add cm.cm_depth cm.cm_pos ectx.last_positions);
- let is_null_pos = cm.cm_pos = null_pos || cm.cm_pos.pmin = -1 in
- let is_unknown_file f = f = "" || f = "?" in
- (* Extract informations from position *)
- let l1, p1, l2, p2, epos, lines =
- if is_null_pos then begin
- let epos = if is_unknown_file cm.cm_pos.pfile then "(unknown position)" else cm.cm_pos.pfile in
- (-1, -1, -1, -1, epos, [])
- end else try begin
- let l1, p1, l2, p2 = Lexer.get_pos_coords cm.cm_pos in
- let lines = resolve_source cm.cm_pos.pfile l1 p1 l2 p2 in
- let epos =
- if lines = [] then cm.cm_pos.pfile
- else if ectx.absolute_positions then TPrinting.Printer.s_pos cm.cm_pos
- else Lexer.get_error_pos error_printer cm.cm_pos
- in
- (l1, p1, l2, p2, epos, lines)
- end with Not_found | Sys_error _ ->
- (1, 1, 1, 1, cm.cm_pos.pfile, [])
- in
- (* If 4 lines or less, display all; if more, crop the middle *)
- let lines = match lines with
- | _ :: (_ :: (_ :: (_ :: []))) -> lines
- | hd :: (_ :: (_ :: (_ :: l))) ->
- let _,line = hd in
- let indent = ref 0 in
- let found = ref false in
- while (not !found) && (!indent < (String.length line - 1)) do
- found := not (Lexer.is_whitespace (String.unsafe_get line !indent));
- indent := !indent + 1
- done;
- [hd; (0, (String.make (!indent+1) ' ') ^ "[...]"); List.hd (List.rev l)]
- | _ -> lines
- in
- let parent_pos =
- if cm.cm_depth = 0 then null_pos
- else (try IntMap.find (cm.cm_depth-1) ectx.last_positions with Not_found -> null_pos)
- in
- let prev_pos,prev_sev,prev_nl = match ectx.previous with
- | None -> (None, None, 0)
- | Some (p, sev, depth) -> (Some p, Some sev, depth)
- in
- let sev_changed = prev_sev = None || Some cm.cm_severity <> prev_sev in
- let pos_changed = (prev_pos = None || cm.cm_pos <> Option.get prev_pos || (cm.cm_depth <> prev_nl && cm.cm_depth <> prev_nl + 1)) && (parent_pos = null_pos || cm.cm_pos <> parent_pos) in
- let file_changed = prev_pos = None || (pos_changed && match (cm.cm_pos.pfile, (Option.get prev_pos).pfile) with
- | (f1, f2) when (is_unknown_file f1) && (is_unknown_file f2) -> false
- | (f1, f2) -> f1 <> f2
- ) in
- let display_heading = cm.cm_depth = 0 || sev_changed || file_changed in
- let has_source = match lines with | [] -> false | _ -> true in
- let display_source = has_source && (cm.cm_depth = 0 || sev_changed || pos_changed) in
- let display_pos_marker = (not is_null_pos) && has_source && (cm.cm_depth = 0 || sev_changed || pos_changed) in
- let gutter_len = (try String.length (Printf.sprintf "%d" (IntMap.find cm.cm_depth ectx.max_lines)) with Not_found -> 0) + 2 in
- let no_color = Define.defined com.defines Define.MessageNoColor in
- let c_reset = if no_color then "" else "\x1b[0m" in
- let c_bold = if no_color then "" else "\x1b[1m" in
- let c_dim = if no_color then "" else "\x1b[2m" in
- let (c_sev, c_sev_bg) = if no_color then ("", "") else match cm.cm_severity with
- | MessageSeverity.Warning -> ("\x1b[33m", "\x1b[30;43m")
- | Information | Hint -> ("\x1b[34m", "\x1b[30;44m")
- | Error -> ("\x1b[31m", "\x1b[30;41m")
- in
- let sev_label = if cm.cm_depth > 0 then " -> " else Printf.sprintf
- (if no_color then "[%s]" else " %s ")
- (match cm.cm_severity with
- | MessageSeverity.Warning -> "WARNING"
- | Information -> "INFO"
- | Hint -> "HINT"
- | Error -> "ERROR"
- ) in
- let out = ref "" in
- if display_heading then
- out := Printf.sprintf "%s%s%s\n\n"
- (* Severity heading *)
- (c_sev_bg ^ sev_label ^ c_reset ^ " ")
- (* Macro context indicator *)
- (if cm.cm_from_macro then c_sev ^ "(macro) " ^ c_reset else "")
- (* File + line pointer *)
- epos;
- (* Macros can send all sorts of bad positions; avoid failing too hard *)
- let safe_sub s pos len =
- if len < 0 then ""
- else
- let pos = if pos < 0 then 0 else pos in
- let slen = String.length s in
- if pos >= slen then ""
- else
- let len = if (pos + len) > slen then slen - pos else len in
- try String.sub s pos len with
- (* Should not happen anymore, but still better than a crash if I missed some case... *)
- | Invalid_argument _ -> (Printf.sprintf "[%s;%i;%i]" s pos len)
- in
- (* Error source *)
- if display_source then out := List.fold_left (fun out (l, line) ->
- let nb_len = String.length (string_of_int l) in
- let gutter = gutter_len - nb_len - 1 in
- (* Replace tabs with 1 space to avoid column misalignments *)
- let line = String.concat " " (ExtString.String.nsplit line "\t") in
- let len = String.length line in
- out ^ Printf.sprintf "%s%s | %s\n"
- (* left-padded line number *)
- (if gutter < 1 then "" else String.make gutter ' ')
- (if l = 0 then "-" else Printf.sprintf "%d" l)
- (* Source code at that line *)
- (
- if l = 0 then
- c_dim ^ line ^ c_reset
- else if l1 = l2 then
- (if p1 > 1 then c_dim ^ (safe_sub line 0 (p1-1)) else "")
- ^ c_reset ^ c_bold ^ (safe_sub line (p1-1) (p2-p1))
- ^ c_reset ^ c_dim ^ (safe_sub line (p2-1) (len - p2 + 1))
- ^ c_reset
- else begin
- (if (l = l1) then
- c_dim ^ (safe_sub line 0 (p1-1))
- ^ c_reset ^ c_bold ^ (safe_sub line (p1-1) (len-p1+1))
- ^ c_reset
- else if (l = l2) then
- c_bold ^ (safe_sub line 0 (p2-1))
- ^ c_reset ^ c_dim ^ (safe_sub line (p2-1) (len-p2+1))
- ^ c_reset
- else c_bold ^ line ^ c_reset)
- end
- )
- ) !out lines;
- (* Error position marker *)
- if display_pos_marker then
- out := Printf.sprintf "%s%s|%s\n"
- !out
- (String.make gutter_len ' ')
- (if l1 = l2 then String.make p1 ' ' ^ c_sev ^ String.make (if p1 = p2 then 1 else p2-p1) '^' ^ c_reset else "");
- (* Error message *)
- out := List.fold_left (fun out str -> Printf.sprintf "%s%s| %s\n"
- out
- (String.make gutter_len ' ')
- (* Remove "... " prefix *)
- (if (ExtString.String.starts_with str "... ") then String.sub str 4 ((String.length str) - 4) else str)
- ) !out (ExtString.String.nsplit cm.cm_message "\n");
- ectx.previous <- Some ((if is_null_pos then null_pos else cm.cm_pos), cm.cm_severity, cm.cm_depth);
- ectx.gutter <- (IntMap.add cm.cm_depth gutter_len ectx.gutter);
- (* Indent sub errors *)
- let rec indent ?(acc=0) depth =
- if depth = 0 then acc
- else indent ~acc:(acc + try IntMap.find (depth-1) ectx.gutter with Not_found -> 3) (depth-1)
- in
- Some (
- if cm.cm_depth > 0 then String.concat "\n" (List.map (fun str -> match str with
- | "" -> ""
- | _ -> (String.make (indent cm.cm_depth) ' ') ^ str
- ) (ExtString.String.nsplit !out "\n"))
- else !out
- )
- end
- let compiler_message_string ectx cm =
- let str = match cm.cm_severity with
- | MessageSeverity.Warning -> "Warning : " ^ cm.cm_message
- | Information | Error | Hint -> cm.cm_message
- in
- if cm.cm_pos = null_pos then
- Some str
- else begin
- let epos =
- if ectx.absolute_positions then TPrinting.Printer.s_pos cm.cm_pos
- else Lexer.get_error_pos error_printer cm.cm_pos
- in
- let str =
- let lines =
- match (ExtString.String.nsplit str "\n") with
- | first :: rest -> first :: List.map Error.compl_msg rest
- | l -> l
- in
- String.concat ("\n" ^ epos ^ " : ") lines
- in
- Some (Printf.sprintf "%s : %s" epos str)
- end
- let compiler_indented_message_string ectx cm =
- match cm.cm_message with
- (* Filter some messages that don't add much when using this message renderer *)
- | "End of overload failure reasons" -> None
- | _ ->
- let str = match cm.cm_severity with
- | MessageSeverity.Warning -> "Warning : " ^ cm.cm_message
- | Information -> "Info : " ^ cm.cm_message
- | Error | Hint -> cm.cm_message
- in
- if cm.cm_pos = null_pos then
- Some str
- else begin
- let epos =
- if ectx.absolute_positions then TPrinting.Printer.s_pos cm.cm_pos
- else Lexer.get_error_pos error_printer cm.cm_pos
- in
- let lines =
- match (ExtString.String.nsplit str "\n") with
- | first :: rest -> (cm.cm_depth, first) :: List.map (fun msg -> (cm.cm_depth+1, msg)) rest
- | l -> [(cm.cm_depth, List.hd l)]
- in
- let rm_prefix str = if (ExtString.String.starts_with str "... ") then String.sub str 4 ((String.length str) - 4) else str in
- Some (String.concat "\n" (List.map (fun (depth, msg) -> (String.make (depth*2) ' ') ^ epos ^ " : " ^ (rm_prefix msg)) lines))
- end
- let get_max_line max_lines messages =
- List.fold_left (fun max_lines cm ->
- let _,_,l2,_ = Lexer.get_pos_coords cm.cm_pos in
- let old = try IntMap.find cm.cm_depth max_lines with Not_found -> 0 in
- if l2 > old then IntMap.add cm.cm_depth l2 max_lines
- else max_lines
- ) max_lines messages
- let display_source_at com p =
- let absolute_positions = Define.defined com.defines Define.MessageAbsolutePositions in
- let ectx = create_error_context absolute_positions in
- let msg = make_compiler_message "" p 0 MessageKind.DKCompilerMessage MessageSeverity.Information in
- ectx.max_lines <- get_max_line ectx.max_lines [msg];
- match compiler_pretty_message_string com ectx msg with
- | None -> ()
- | Some s -> prerr_endline s
- exception ConfigError of string
- let get_formatter com def default =
- let format_mode = Define.defined_value_safe ~default com.defines def in
- match format_mode with
- | "pretty" -> compiler_pretty_message_string com
- | "indent" -> compiler_indented_message_string
- | "classic" -> compiler_message_string
- | m -> begin
- let def = Define.get_define_key def in
- raise (ConfigError (Printf.sprintf "Invalid message reporting mode: \"%s\", expected classic | pretty | indent (for -D %s)." m def))
- end
- let print_error (err : Error.error) =
- let ret = ref "" in
- Error.recurse_error (fun depth err ->
- ret := !ret ^ (Lexer.get_error_pos (Printf.sprintf "%s:%d: ") err.err_pos) ^ (Error.error_msg err.err_message) ^ "\n"
- ) err;
- !ret
- let format_messages com messages =
- let absolute_positions = Define.defined com.defines Define.MessageAbsolutePositions in
- let ectx = create_error_context absolute_positions in
- ectx.max_lines <- get_max_line ectx.max_lines messages;
- let message_formatter = get_formatter com Define.MessageReporting "pretty" in
- let lines = List.rev (
- List.fold_left (fun lines cm -> match (message_formatter ectx cm) with
- | None -> lines
- | Some str -> str :: lines
- ) [] messages
- ) in
- ExtLib.String.join "\n" lines
- let display_messages ctx on_message = begin
- let absolute_positions = Define.defined ctx.com.defines Define.MessageAbsolutePositions in
- let ectx = create_error_context absolute_positions in
- ectx.max_lines <- get_max_line ectx.max_lines ctx.messages;
- let error msg =
- ctx.has_error <- true;
- on_message MessageSeverity.Error msg
- in
- let get_formatter _ def default =
- try get_formatter ctx.com def default
- with | ConfigError s ->
- error s;
- compiler_message_string
- in
- let message_formatter = get_formatter ctx.com Define.MessageReporting "pretty" in
- let log_formatter = get_formatter ctx.com Define.MessageLogFormat "indent" in
- let log_messages = ref (Define.defined ctx.com.defines Define.MessageLogFile) in
- let log_message = ref None in
- let close_logs = ref None in
- if !log_messages then begin
- try begin
- let buf = Rbuffer.create 16000 in
- let file = Define.defined_value ctx.com.defines Define.MessageLogFile in
- let chan =
- Path.mkdir_from_path file;
- open_out_bin file
- in
- log_message := (Some (fun msg ->
- match (log_formatter ectx msg) with
- | None -> ()
- | Some str -> Rbuffer.add_string buf (str ^ "\n")));
- close_logs := (Some (fun () ->
- Rbuffer.output_buffer chan buf;
- Rbuffer.clear buf;
- close_out chan
- ));
- end with
- | Failure e | Sys_error e -> begin
- let def = Define.get_define_key Define.MessageLogFile in
- error (Printf.sprintf "Error opening log file: %s. Logging to file disabled (-D %s)" e def);
- log_messages := false;
- end
- end;
- List.iter (fun cm ->
- if !log_messages then (Option.get !log_message) cm;
- match (message_formatter ectx cm) with
- | None -> ()
- | Some str -> on_message cm.cm_severity str
- ) (List.rev ctx.messages);
- if !log_messages then (Option.get !close_logs) ();
- end
|