-
Notifications
You must be signed in to change notification settings - Fork 63
Expand file tree
/
Copy pathLog_.ml
More file actions
254 lines (227 loc) · 8.58 KB
/
Log_.ml
File metadata and controls
254 lines (227 loc) · 8.58 KB
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
open Common
module Color = struct
let color_enabled = lazy (Unix.isatty Unix.stdout)
let forceColor = ref false
let get_color_enabled () = !forceColor || Lazy.force color_enabled
type color = Red | Yellow | Magenta | Cyan
type style = FG of color | Bold | Dim
let code_of_style = function
| FG Red -> "31"
| FG Yellow -> "33"
| FG Magenta -> "35"
| FG Cyan -> "36"
| Bold -> "1"
| Dim -> "2"
let getStringTag s =
match s with
| Format.String_tag s -> s
| _ -> ""
let style_of_tag s =
match s |> getStringTag with
| "error" -> [Bold; FG Red]
| "warning" -> [Bold; FG Magenta]
| "info" -> [Bold; FG Yellow]
| "dim" -> [Dim]
| "filename" -> [FG Cyan]
| _ -> []
let ansi_of_tag s =
let l = style_of_tag s in
let s = String.concat ";" (List.map code_of_style l) in
"\027[" ^ s ^ "m"
let reset_lit = "\027[0m"
let setOpenCloseTag openTag closeTag =
{
Format.mark_open_stag = openTag;
mark_close_stag = closeTag;
print_open_stag = (fun _ -> ());
print_close_stag = (fun _ -> ());
}
let color_functions =
setOpenCloseTag
(fun s -> if get_color_enabled () then ansi_of_tag s else "")
(fun _ -> if get_color_enabled () then reset_lit else "")
let setup () =
Format.pp_set_mark_tags Format.std_formatter true;
Format.pp_set_formatter_stag_functions Format.std_formatter color_functions;
if not (get_color_enabled ()) then Misc.Color.setup (Some Never);
(* Print a dummy thing once in the beginning, as otherwise flushing does not work. *)
Location.print_loc Format.str_formatter Location.none
let error ppf s = Format.fprintf ppf "@{<error>%s@}" s
let info ppf s = Format.fprintf ppf "@{<info>%s@}" s
end
module Loc = struct
let print_loc ppf (loc : Location.t) =
(* Change the range so it's on a single line.
In this way, the line number is clickable in vscode. *)
let startChar = loc.loc_start.pos_cnum - loc.loc_start.pos_bol in
let endChar = startChar + loc.loc_end.pos_cnum - loc.loc_start.pos_cnum in
let line = loc.loc_start.pos_lnum in
let processPos char (pos : Lexing.position) : Lexing.position =
{
pos_lnum = line;
pos_bol = 0;
pos_cnum = char;
pos_fname =
(let open Filename in
match is_implicit pos.pos_fname with
| _ when !Cli.ci -> basename pos.pos_fname
| true -> concat (Sys.getcwd ()) pos.pos_fname
| false -> pos.pos_fname);
}
in
Location.print_loc ppf
{
loc with
loc_start = loc.loc_start |> processPos startChar;
loc_end = loc.loc_end |> processPos endChar;
}
let print ppf (loc : Location.t) = Format.fprintf ppf "@[%a@]" print_loc loc
end
let log x = Format.fprintf Format.std_formatter x
let item x =
Format.fprintf Format.std_formatter " ";
Format.fprintf Format.std_formatter x
let missingRaiseInfoToText {missingAnnotations; locFull} =
let missingTxt =
Format.asprintf "%a" (Exceptions.pp ~exnTable:None) missingAnnotations
in
if !Cli.json then
EmitJson.emitAnnotate ~action:"Add @raises annotation"
~pos:(EmitJson.locToPos locFull)
~text:(Format.asprintf "@raises(%s)\\n" missingTxt)
else ""
let logAdditionalInfo ~(description : description) =
match description with
| DeadWarning {lineAnnotation; shouldWriteLineAnnotation} ->
if shouldWriteLineAnnotation then
WriteDeadAnnotations.lineAnnotationToString lineAnnotation
else ""
| ExceptionAnalysisMissing missingRaiseInfo ->
missingRaiseInfoToText missingRaiseInfo
| _ -> ""
let missingRaiseInfoToMessage {exnTable; exnName; missingAnnotations; raiseSet}
=
let raisesTxt =
Format.asprintf "%a" (Exceptions.pp ~exnTable:(Some exnTable)) raiseSet
in
let missingTxt =
Format.asprintf "%a" (Exceptions.pp ~exnTable:None) missingAnnotations
in
Format.asprintf
"@{<info>%s@} might raise %s and is not annotated with @raises(%s)" exnName
raisesTxt missingTxt
let descriptionToMessage (description : description) =
match description with
| Circular {message} -> message
| DeadModule {message} -> message
| DeadOptional {message} -> message
| DeadWarning {path; message} ->
Format.asprintf "@{<info>%s@} %s" path message
| ExceptionAnalysis {message} -> message
| ExceptionAnalysisMissing missingRaiseInfo ->
missingRaiseInfoToMessage missingRaiseInfo
| Termination {message} -> message
let descriptionToName (description : description) =
match description with
| Circular _ -> Issues.warningDeadAnalysisCycle
| DeadModule _ -> Issues.warningDeadModule
| DeadOptional {deadOptional = WarningUnusedArgument} ->
Issues.warningUnusedArgument
| DeadOptional {deadOptional = WarningRedundantOptionalArgument} ->
Issues.warningRedundantOptionalArgument
| DeadWarning {deadWarning = WarningDeadException} ->
Issues.warningDeadException
| DeadWarning {deadWarning = WarningDeadType} -> Issues.warningDeadType
| DeadWarning {deadWarning = WarningDeadValue} -> Issues.warningDeadValue
| DeadWarning {deadWarning = WarningDeadValueWithSideEffects} ->
Issues.warningDeadValueWithSideEffects
| DeadWarning {deadWarning = IncorrectDeadAnnotation} ->
Issues.incorrectDeadAnnotation
| ExceptionAnalysis _ -> Issues.exceptionAnalysis
| ExceptionAnalysisMissing _ -> Issues.exceptionAnalysis
| Termination {termination = ErrorHygiene} -> Issues.errorHygiene
| Termination {termination = ErrorNotImplemented} ->
Issues.errorNotImplemented
| Termination {termination = ErrorTermination} -> Issues.errorTermination
| Termination {termination = TerminationAnalysisInternal} ->
Issues.terminationAnalysisInternal
let logIssue ~(issue : issue) =
let open Format in
let loc = issue.loc in
if !Cli.json then
let file = Json.escape loc.loc_start.pos_fname in
let startLine = loc.loc_start.pos_lnum - 1 in
let startCharacter = loc.loc_start.pos_cnum - loc.loc_start.pos_bol in
let endLine = loc.loc_end.pos_lnum - 1 in
let endCharacter = loc.loc_end.pos_cnum - loc.loc_start.pos_bol in
let message = Json.escape (descriptionToMessage issue.description) in
Format.asprintf "%a%s%s"
(fun ppf () ->
EmitJson.emitItem ~ppf ~name:issue.name
~kind:
(match issue.severity with
| Warning -> "warning"
| Error -> "error")
~file
~range:(startLine, startCharacter, endLine, endCharacter)
~message)
()
(logAdditionalInfo ~description:issue.description)
(if !Cli.json then EmitJson.emitClose () else "")
else
let color =
match issue.severity with
| Warning -> Color.info
| Error -> Color.error
in
asprintf "@. %a@. %a@. %s%s@." color issue.name Loc.print issue.loc
(descriptionToMessage issue.description)
(logAdditionalInfo ~description:issue.description)
module Stats = struct
let issues = ref []
let addIssue (issue : issue) = issues := issue :: !issues
let clear () = issues := []
let getSortedIssues () =
let counters2 = Hashtbl.create 1 in
!issues
|> List.iter (fun (issue : issue) ->
let counter =
match Hashtbl.find_opt counters2 issue.name with
| Some counter -> counter
| None ->
let counter = ref 0 in
Hashtbl.add counters2 issue.name counter;
counter
in
incr counter);
let issues, nIssues =
Hashtbl.fold
(fun name cnt (issues, nIssues) ->
((name, cnt) :: issues, nIssues + !cnt))
counters2 ([], 0)
in
(issues |> List.sort (fun (n1, _) (n2, _) -> String.compare n1 n2), nIssues)
let report () =
!issues |> List.rev
|> List.iter (fun issue -> logIssue ~issue |> print_string);
let sortedIssues, nIssues = getSortedIssues () in
if not !Cli.json then (
if sortedIssues <> [] then item "@.";
item "Analysis reported %d issues%s@." nIssues
(match sortedIssues with
| [] -> ""
| _ :: _ ->
" ("
^ (sortedIssues
|> List.map (fun (name, cnt) -> name ^ ":" ^ string_of_int !cnt)
|> String.concat ", ")
^ ")"))
end
let logIssue ~forStats ~severity ~(loc : Location.t) description =
let name = descriptionToName description in
if Suppress.filter loc.loc_start then
if forStats then Stats.addIssue {name; severity; loc; description}
let warning ?(forStats = true) ~loc description =
description |> logIssue ~severity:Warning ~forStats ~loc
let error ~loc description =
description |> logIssue ~severity:Error ~forStats:true ~loc