Source file reporting.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
let opt_warnings = ref true
let opt_all_warnings = ref false
let opt_backtrace_length = ref 10
type pos_or_loc = Loc of Parse_ast.l | Pos of Lexing.position
let fix_endline str = if str.[String.length str - 1] = '\n' then String.sub str 0 (String.length str - 1) else str
let print_err_internal hint p_l m1 m2 =
let open Error_format in
prerr_endline (m1 ^ ":");
begin
match p_l with
| Loc l -> format_message (Location ("", hint, l, Line (fix_endline m2))) err_formatter
| Pos p -> format_message (Location ("", hint, Parse_ast.Range (p, p), Line (fix_endline m2))) err_formatter
end
let loc_to_string l =
let open Error_format in
let b = Buffer.create 160 in
format_message (Location ("", None, l, Line "")) (buffer_formatter b);
Buffer.contents b
let rec simp_loc = function
| Parse_ast.Unknown -> None
| Parse_ast.Unique (_, l) -> simp_loc l
| Parse_ast.Generated l -> simp_loc l
| Parse_ast.Hint (_, l1, l2) -> begin match simp_loc l1 with None -> simp_loc l2 | pos -> pos end
| Parse_ast.Range (p1, p2) -> Some (p1, p2)
let loc_range_to_src (p1 : Lexing.position) (p2 : Lexing.position) =
(fun contents -> String.sub contents p1.pos_cnum (p2.pos_cnum - p1.pos_cnum)) (Util.read_whole_file p1.pos_fname)
let rec map_loc_range f = function
| Parse_ast.Unknown -> Parse_ast.Unknown
| Parse_ast.Unique (n, l) -> Parse_ast.Unique (n, map_loc_range f l)
| Parse_ast.Generated l -> Parse_ast.Generated (map_loc_range f l)
| Parse_ast.Hint (hint, l1, l2) -> Parse_ast.Hint (hint, l1, map_loc_range f l2)
| Parse_ast.Range (p1, p2) ->
let p1, p2 = f p1 p2 in
Parse_ast.Range (p1, p2)
let rec loc_file = function
| Parse_ast.Unknown -> None
| Parse_ast.Unique (_, l) -> loc_file l
| Parse_ast.Generated l -> loc_file l
| Parse_ast.Hint (_, _, l) -> loc_file l
| Parse_ast.Range (p1, _) -> Some p1.pos_fname
let rec start_loc = function
| Parse_ast.Unknown -> Parse_ast.Unknown
| Parse_ast.Unique (u, l) -> Parse_ast.Unique (u, start_loc l)
| Parse_ast.Generated l -> Parse_ast.Generated (start_loc l)
| Parse_ast.Hint (hint, l1, l2) -> Parse_ast.Hint (hint, start_loc l1, start_loc l2)
| Parse_ast.Range (p1, _) -> Parse_ast.Range (p1, p1)
let short_loc_to_string l =
match simp_loc l with
| None -> "unknown location"
| Some (p1, p2) ->
Printf.sprintf "%s:%d.%d-%d.%d" p1.pos_fname p1.pos_lnum (p1.pos_cnum - p1.pos_bol) p2.pos_lnum
(p2.pos_cnum - p2.pos_bol)
let print_err l m1 m2 = print_err_internal None (Loc l) m1 m2
type error =
| Err_general of Parse_ast.l * string
| Err_unreachable of Parse_ast.l * (string * int * int * int) * Printexc.raw_backtrace * string
| Err_todo of Parse_ast.l * string
| Err_syntax of Lexing.position * string
| Err_syntax_loc of Parse_ast.l * string
| Err_lex of Lexing.position * string
| Err_type of Parse_ast.l * string option * string
let issues = "\nPlease report this as an issue on GitHub at https://github.com/rems-project/sail/issues"
let dest_err ?(interactive = false) = function
| Err_general (l, m) -> (Util.("Error" |> yellow |> clear), None, Loc l, m)
| Err_unreachable (l, (file, line, _, _), backtrace, m) ->
if interactive then ("Error", None, Loc l, m)
else
( Printf.sprintf "Internal error: Unreachable code (at \"%s\" line %d)" file line,
None,
Loc l,
m ^ "\n\n" ^ Printexc.raw_backtrace_to_string backtrace ^ issues
)
| Err_todo (l, m) -> ("Todo", None, Loc l, m)
| Err_syntax (p, m) -> (Util.("Syntax error" |> yellow |> clear), None, Pos p, m)
| Err_syntax_loc (l, m) -> (Util.("Syntax error" |> yellow |> clear), None, Loc l, m)
| Err_lex (p, s) -> (Util.("Lexical error" |> yellow |> clear), None, Pos p, s)
| Err_type (l, hint, m) -> (Util.("Type error" |> yellow |> clear), hint, Loc l, m)
exception Fatal_error of error
let err_todo l m = Fatal_error (Err_todo (l, m))
let err_unreachable l ocaml_pos m =
let backtrace = Printexc.get_callstack !opt_backtrace_length in
Fatal_error (Err_unreachable (l, ocaml_pos, backtrace, m))
let err_general l m = Fatal_error (Err_general (l, m))
let err_typ ?hint l m = Fatal_error (Err_type (l, hint, m))
let err_syntax p m = Fatal_error (Err_syntax (p, m))
let err_syntax_loc l m = Fatal_error (Err_syntax_loc (l, m))
let err_lex p m = Fatal_error (Err_lex (p, m))
let unreachable l pos msg = raise (err_unreachable l pos msg)
let forbid_errors ocaml_pos f x =
try f x with
| Fatal_error (Err_general (l, m)) -> raise (err_unreachable l ocaml_pos m)
| Fatal_error (Err_todo (l, m)) -> raise (err_unreachable l ocaml_pos m)
| Fatal_error (Err_syntax (p, m)) -> raise (err_unreachable (Range (p, p)) ocaml_pos m)
| Fatal_error (Err_syntax_loc (l, m)) -> raise (err_unreachable l ocaml_pos m)
| Fatal_error (Err_lex (p, m)) -> raise (err_unreachable (Range (p, p)) ocaml_pos m)
| Fatal_error (Err_type (l, _, m)) -> raise (err_unreachable l ocaml_pos m)
let print_error ?(interactive = false) e =
let m1, hint, pos_l, m2 = dest_err ~interactive e in
print_err_internal hint pos_l m1 m2
let print_type_error ?hint l msg = print_err_internal hint (Loc l) Util.("Type error" |> yellow |> clear) msg
module StringSet = Set.Make (String)
let pos_compare p1 p2 =
let open Lexing in
match String.compare p1.pos_fname p2.pos_fname with
| 0 -> begin
match compare p1.pos_lnum p2.pos_lnum with
| 0 -> begin match compare p1.pos_bol p2.pos_bol with 0 -> compare p1.pos_cnum p2.pos_cnum | n -> n end
| n -> n
end
| n -> n
module Range = struct
type t = Lexing.position * Lexing.position
let compare (p1, p2) (p3, p4) =
let c = pos_compare p1 p3 in
if c = 0 then pos_compare p2 p4 else c
end
module RangeMap = Map.Make (Range)
let ignored_files = ref StringSet.empty
let suppress_warnings_for_file f = ignored_files := StringSet.add f !ignored_files
let seen_warnings = ref RangeMap.empty
let once_from_warnings = ref StringSet.empty
let suppressed_warnings = ref 0
let suppressed_warning_info () =
if !suppressed_warnings > 0 then (
prerr_endline
(Util.("Warning" |> yellow |> clear)
^ ": " ^ string_of_int !suppressed_warnings
^ " warnings have been suppressed. Use --all-warnings to display them."
);
suppressed_warnings := 0
)
let warn ?once_from short_str l explanation =
let already_shown =
match once_from with
| Some (file, lnum, cnum, enum) when not !opt_all_warnings ->
let key = Printf.sprintf "%d:%d:%d:%s" lnum cnum enum file in
if StringSet.mem key !once_from_warnings then (
incr suppressed_warnings;
true
)
else (
once_from_warnings := StringSet.add key !once_from_warnings;
false
)
| _ -> false
in
if !opt_warnings && not already_shown then (
match simp_loc l with
| Some (p1, p2) when not (StringSet.mem p1.pos_fname !ignored_files) ->
let shorts = RangeMap.find_opt (p1, p2) !seen_warnings |> Option.value ~default:[] in
if not (List.exists (fun s -> s = short_str) shorts) then (
prerr_endline
(Util.("Warning" |> yellow |> clear)
^ ": " ^ short_str
^ (if short_str <> "" then " " else "")
^ loc_to_string l ^ explanation ^ "\n"
);
seen_warnings := RangeMap.add (p1, p2) (short_str :: shorts) !seen_warnings
)
| _ -> prerr_endline (Util.("Warning" |> yellow |> clear) ^ ": " ^ short_str ^ "\n" ^ explanation ^ "\n")
)
let format_warn ?once_from short_str l explanation =
let already_shown =
match once_from with
| Some (file, lnum, cnum, enum) when not !opt_all_warnings ->
let key = Printf.sprintf "%d:%d:%d:%s" lnum cnum enum file in
if StringSet.mem key !once_from_warnings then (
incr suppressed_warnings;
true
)
else (
once_from_warnings := StringSet.add key !once_from_warnings;
false
)
| _ -> false
in
if !opt_warnings && not already_shown then (
match simp_loc l with
| Some (p1, p2) when not (StringSet.mem p1.pos_fname !ignored_files) ->
let shorts = RangeMap.find_opt (p1, p2) !seen_warnings |> Option.value ~default:[] in
if not (List.exists (fun s -> s = short_str) shorts) then (
let open Error_format in
prerr_endline (Util.("Warning" |> yellow |> clear) ^ ": " ^ short_str);
format_message (Location ("", None, l, explanation)) err_formatter;
seen_warnings := RangeMap.add (p1, p2) (short_str :: shorts) !seen_warnings
)
| _ -> prerr_endline (Util.("Warning" |> yellow |> clear) ^ ": " ^ short_str ^ "\n")
)
let simple_warn str = warn str Parse_ast.Unknown ""
let get_sail_dir default_sail_dir =
match Sys.getenv_opt "SAIL_DIR" with
| Some path -> path
| None ->
if Sys.file_exists default_sail_dir then default_sail_dir
else
raise
(err_general Parse_ast.Unknown
("Sail share directory " ^ default_sail_dir
^ " does not exist. Make sure Sail is installed correctly, or try setting the SAIL_DIR environment variable"
)
)
let system_checked ?loc:(l = Parse_ast.Unknown) cmd =
match Unix.system cmd with
| WEXITED 0 -> ()
| WEXITED n -> raise (err_general l (Printf.sprintf "Command %s failed with exit code %d" cmd n))
| WSTOPPED n -> raise (err_general l (Printf.sprintf "Command %s stopped by signal %d" cmd n))
| WSIGNALED n -> raise (err_general l (Printf.sprintf "Command %s killed by signal %d" cmd n))
module Position = struct
open Lexing
let trim_position str p =
let len = String.length str in
let rec go n p =
if n < len && (str.[n] = ' ' || str.[n] = '\t') then go (n + 1) { p with pos_cnum = p.pos_cnum + 1 }
else if n < len && str.[n] = '\n' then
go (n + 1) { p with pos_lnum = p.pos_lnum + 1; pos_bol = p.pos_cnum + 1; pos_cnum = p.pos_cnum + 1 }
else p
in
go 0 p
let string_positions trim after str p =
let str, p = if trim then (String.trim str, trim_position str p) else (str, p) in
let lnum = ref p.pos_lnum in
let bol = ref p.pos_bol in
let cnum = ref p.pos_cnum in
for i = 0 to String.length str - 1 do
if str.[i] = '\n' then (
incr lnum;
incr cnum;
bol := !cnum
)
else incr cnum
done;
(p, { p with pos_lnum = !lnum; pos_bol = !bol; pos_cnum = !cnum + after })
let advance_position ?(after = 0) ~trim str p = snd (string_positions trim after str p)
let string_location ~trim ~start:s str =
let s, e = string_positions trim 0 str s in
Parse_ast.Range (s, e)
end