Skip to content
← Back

src/logging/diagnostics_store.ghul

1
namespace Logging is
2
use System.Exception;
3
4
use Collections.Map;
5
use Collections.MutableMap;
6
use Collections.List;
7
use Collections.MutableList;
8
use Collections.Iterable;
9
10
use Collections.MAP;
11
use Collections.LIST;
12
use Collections.STACK;
13
14
use IO.TextWriter;
15
16
use Ghul.Pipes;
17
18
use Source.LOCATION;
19
20
struct DIAGNOSTIC_MESSAGE is
21
is_analysis: bool;
22
is_compile_expressions: bool;
23
severity: DiagnosticSeverity;
24
location: LOCATION;
25
code: string?;
26
text: string;
27
// Secondary text for an INLAY carrier. For a narrowing-introduction
28
// carrier this holds only the narrowed-to type; the hover sentence
29
// is assembled at read-out. For a kill carrier it holds the full
30
// hover text. Null for every other diagnostic.
31
detail: string?;
32
is_hint: bool => (severity == DiagnosticSeverity.HINT);
33
is_inlay: bool => (severity == DiagnosticSeverity.INLAY);
34
is_fatal: bool => (severity == DiagnosticSeverity.FATAL \/ severity == DiagnosticSeverity.EXCEPTION);
35
36
init(
37
is_analysis: bool,
38
is_compile_expressions: bool,
39
severity: DiagnosticSeverity,
40
location: LOCATION,
41
text: string
42
) is
43
self.is_analysis = is_analysis;
44
self.is_compile_expressions = is_compile_expressions;
45
self.severity = severity;
46
self.location = location;
47
self.code = null;
48
self.text = text;
49
self.detail = null;
50
si
51
52
init(
53
is_analysis: bool,
54
is_compile_expressions: bool,
55
severity: DiagnosticSeverity,
56
location: LOCATION,
57
code: string?,
58
text: string
59
) is
60
self.is_analysis = is_analysis;
61
self.is_compile_expressions = is_compile_expressions;
62
self.severity = severity;
63
self.location = location;
64
self.code = code;
65
self.text = text;
66
self.detail = null;
67
si
68
69
init(
70
is_analysis: bool,
71
is_compile_expressions: bool,
72
severity: DiagnosticSeverity,
73
location: LOCATION,
74
code: string?,
75
text: string,
76
detail: string?
77
) is
78
self.is_analysis = is_analysis;
79
self.is_compile_expressions = is_compile_expressions;
80
self.severity = severity;
81
self.location = location;
82
self.code = code;
83
self.text = text;
84
self.detail = detail;
85
si
86
87
to_string() -> string => "{severity} {location} {text}";
88
si
89
90
trait DiagnosticFormatter is
91
want_filter_error_cascades: bool => false;
92
need_clear_errors: bool => false;
93
format(diagnostic: DIAGNOSTIC_MESSAGE) -> string?;
94
si
95
96
class TAB_DELIMITED_DIAGNOSTIC_FORMATTER: DiagnosticFormatter is
97
want_filter_error_cascades: bool => true;
98
need_clear_errors: bool => true;
99
100
init() is
101
si
102
103
format(diagnostic: DIAGNOSTIC_MESSAGE) -> string? is
104
// Inlay carriers are never published as diagnostics; they reach
105
// the client only through the INLAY_HINTS query.
106
if diagnostic.is_inlay then
107
return null;
108
fi
109
110
let location = diagnostic.location;
111
let severity =
112
if diagnostic.is_fatal then
113
cast int(DiagnosticSeverity.HINT);
114
else
115
cast int(diagnostic.severity);
116
fi;
117
118
let message = diagnostic.text;
119
let code = if diagnostic.code? then diagnostic.code; else ""; fi;
120
121
let s = string.format(
122
"{{0}}\t{{1}}\t{{2}}\t{{3}}\t{{4}}\t{{5}}\t{{6}}\t{{7}}",
123
[
124
location.file_name,
125
location.start_line,
126
location.start_column,
127
location.end_line,
128
location.end_column+1,
129
cast int(severity),
130
message,
131
code
132
]
133
).replace('\n', ' ');
134
135
return s;
136
si
137
si
138
139
class HUMAN_READABLE_DIAGNOSTIC_FORMATTER: DiagnosticFormatter is
140
init() is
141
si
142
143
format(diagnostic: DIAGNOSTIC_MESSAGE) -> string? is
144
if diagnostic.is_hint then
145
return null;
146
fi
147
148
let location = diagnostic.location;
149
let severity = diagnostic.severity.to_string().to_lower();
150
let message =
151
if diagnostic.code? then
152
"[{diagnostic.code}] {diagnostic.text}";
153
else
154
diagnostic.text;
155
fi;
156
157
let s = string.format(
158
"{{0}}: {{1}},{{2}}..{{3}},{{4}}: {{5}}: {{6}}",
159
[
160
location.file_name,
161
location.start_line,
162
location.start_column,
163
location.end_line,
164
location.end_column+1,
165
severity,
166
message
167
]
168
);
169
170
return s;
171
si
172
si
173
174
class MSBUILD_DIAGNOSTIC_FORMATTER: DiagnosticFormatter is
175
init() is
176
si
177
178
format(diagnostic: Logging.DIAGNOSTIC_MESSAGE) -> string is
179
let location = diagnostic.location;
180
let severity = diagnostic.severity;
181
let message = diagnostic.text;
182
183
let s = string.format(
184
"{{0}}({{1}},{{2}}): {{3}}: {{4}}",
185
[
186
location.file_name,
187
location.start_line,
188
location.start_column,
189
severity,
190
message
191
]
192
);
193
194
return s;
195
si
196
si
197
198
class DIAGNOSTICS_LIST is
199
_path: string;
200
_dirty: bool;
201
_diagnostics: LIST[DIAGNOSTIC_MESSAGE];
202
203
is_poisoned: bool;
204
has_consumed_any: bool;
205
has_consumed_error: bool;
206
207
any_errors: bool => _diagnostics |> any(d => d.severity == DiagnosticSeverity.ERROR);
208
any_warnings: bool => _diagnostics |> any(d => d.severity == DiagnosticSeverity.WARN);
209
210
count: int => _diagnostics.count;
211
error_count: int => _diagnostics |> filter(d => d.severity == DiagnosticSeverity.ERROR) |> count();
212
syntax_error_count: int => _diagnostics |> filter(d => !d.is_analysis /\ d.severity == DiagnosticSeverity.ERROR) |> count();
213
analysis_count: int => _diagnostics |> filter(d => d.is_analysis) |> count();
214
215
diagnostics: List[DIAGNOSTIC_MESSAGE] => _diagnostics;
216
217
init(path: string) is
218
_path = path;
219
_diagnostics = LIST();
220
si
221
222
add(diagnostic_message: DIAGNOSTIC_MESSAGE) is
223
_dirty = true;
224
225
if diagnostic_message.severity == DiagnosticSeverity.FATAL \/ diagnostic_message.severity == DiagnosticSeverity.EXCEPTION then
226
is_poisoned = true;
227
fi
228
229
_diagnostics.add(diagnostic_message);
230
si
231
232
mark_poisoned() is
233
is_poisoned = true;
234
si
235
236
mark_consumed_any() is
237
has_consumed_any = true;
238
si
239
240
mark_consumed_error() is
241
has_consumed_error = true;
242
si
243
244
clear_consumed_any() is
245
has_consumed_any = false;
246
si
247
248
clear_consumed_error() is
249
has_consumed_error = false;
250
si
251
252
clear(analysis_only: bool) is
253
if analysis_only == false then
254
_dirty = true;
255
_diagnostics.clear();
256
else
257
let n = LIST[DIAGNOSTIC_MESSAGE](_diagnostics.count);
258
259
for d in _diagnostics do
260
if !d.is_analysis then
261
n.add(d);
262
fi
263
od
264
265
_diagnostics = n;
266
fi
267
si
268
269
clear_global_declaration_diagnostics() is
270
let n = LIST[DIAGNOSTIC_MESSAGE](_diagnostics.count);
271
272
for d in _diagnostics do
273
if !d.is_analysis \/ d.is_compile_expressions then
274
n.add(d);
275
fi
276
od
277
278
_diagnostics = n;
279
si
280
281
// The complement of clear_global_declaration_diagnostics: drop
282
// only the expression-level analysis diagnostics, keeping parse
283
// and declaration-level ones. Used before an on-demand
284
// compile-expressions re-walk of one file, which re-reports the
285
// expression diagnostics but re-runs no earlier pass.
286
clear_expression_diagnostics() is
287
let n = LIST[DIAGNOSTIC_MESSAGE](_diagnostics.count);
288
289
for d in _diagnostics do
290
if !d.is_analysis \/ !d.is_compile_expressions then
291
n.add(d);
292
fi
293
od
294
295
_diagnostics = n;
296
si
297
298
write_all_diagnostics(writer: TextWriter, formatter: DiagnosticFormatter) is
299
let written_any mut = false;
300
301
for diagnostic in _diagnostics do
302
let formatted = formatter.format(diagnostic);
303
304
if formatted? then
305
written_any = true;
306
writer.write(formatted);
307
writer.write("\n");
308
fi
309
od
310
311
if !written_any /\ formatter.need_clear_errors then
312
// clear errors in the client
313
writer.write(_path);
314
writer.write("\n");
315
fi
316
si
317
318
write_filtered_diagnostics(writer: TextWriter, formatter: DiagnosticFormatter) is
319
if _diagnostics |> filter(d => !d.is_analysis) |> count() == 0 then
320
if formatter.need_clear_errors then
321
// clear errors in the client
322
writer.write(_path);
323
writer.write("\n");
324
fi
325
326
return;
327
fi
328
329
// only write the first 15 syntax diagnostics per source file, on the assumption that
330
// the first few are probably the root cause of the whole error cascade. Note this
331
// relies on errors being written in the order they are discovered, which is guaranteed
332
// for syntax errors, but not for analysis errors.
333
for diagnostic in _diagnostics |> filter(d => !d.is_analysis) |> take(15) do
334
let formatted = formatter.format(diagnostic);
335
336
if formatted? then
337
writer.write(formatted);
338
writer.write("\n");
339
fi
340
od
341
si
342
si
343
344
class DIAGNOSTICS_STATE is
345
count: int => _diagnostics_by_source_path.values |> reduce(0, (r, d) => r + d.count);
346
is_poisoned: bool => _diagnostics_by_source_path.values |> any(d => d.is_poisoned);
347
has_consumed_error: bool;
348
has_consumed_any: bool;
349
any_errors: bool => _diagnostics_by_source_path.values |> any(d => d.any_errors);
350
any_warnings: bool => _diagnostics_by_source_path.values |> any(d => d.any_warnings);
351
352
paths_with_errors: Iterable[string] is
353
let result = LIST[string]();
354
355
for pair in _diagnostics_by_source_path do
356
if pair.value.any_errors then
357
result.add(pair.key);
358
fi
359
od
360
361
return result;
362
si
363
364
// A parse error typically produces a flood of downstream semantic
365
// errors; when one is present and the total error count is high,
366
// the filtered writer shows only the pre-analysis diagnostics on
367
// the assumption that the rest are noise. Both arms count errors
368
// only: warnings and hints are not cascade evidence, and a project
369
// can carry standing warnings (e.g. name-convention warnings, which
370
// are logged pre-analysis) without losing its semantic diagnostics.
371
is_possible_error_cascade: bool =>
372
(_diagnostics_by_source_path.values |> any(d => d.syntax_error_count > 0)) /\
373
(_diagnostics_by_source_path.values |> reduce(0, (r, d) => r + d.error_count) > 30);
374
375
_diagnostics_by_source_path: MutableMap[string, DIAGNOSTICS_LIST];
376
377
init() is
378
_diagnostics_by_source_path = MAP[string, DIAGNOSTICS_LIST]();
379
si
380
381
clear() is
382
for i in _diagnostics_by_source_path.values do
383
i.clear(false);
384
od
385
386
init();
387
si
388
389
clear(source_path: string, analysis_only: bool) is
390
get_diagnostics_list(source_path).clear(analysis_only);
391
si
392
393
clear_global_declaration_diagnostics(source_path: string) is
394
get_diagnostics_list(source_path).clear_global_declaration_diagnostics();
395
si
396
397
clear_expression_diagnostics(source_path: string) is
398
get_diagnostics_list(source_path).clear_expression_diagnostics();
399
si
400
401
get_diagnostics_list(source_path: string) -> DIAGNOSTICS_LIST is
402
let diagnostics_for_path: DIAGNOSTICS_LIST mut;
403
404
if !_diagnostics_by_source_path.try_get_value(source_path, diagnostics_for_path ref) then
405
diagnostics_for_path = DIAGNOSTICS_LIST(source_path);
406
407
_diagnostics_by_source_path.add(source_path, diagnostics_for_path);
408
fi
409
410
return diagnostics_for_path;
411
si
412
413
add_diagnostic_message(source_path: string, message: DIAGNOSTIC_MESSAGE) is
414
get_diagnostics_list(source_path).add(message);
415
si
416
417
mark_poisoned(source_path: string) is
418
get_diagnostics_list(source_path).mark_poisoned();
419
si
420
421
mark_consumed_error() is
422
has_consumed_error = true;
423
si
424
425
mark_consumed_any() is
426
has_consumed_any = true;
427
si
428
429
clear_consumed_error() is
430
for i in _diagnostics_by_source_path.values do
431
i.clear_consumed_error();
432
od
433
si
434
435
clear_consumed_any() is
436
for i in _diagnostics_by_source_path.values do
437
i.clear_consumed_any();
438
od
439
si
440
441
merge(state: DIAGNOSTICS_STATE) is
442
for i in state._diagnostics_by_source_path do
443
add_diagnostic_messages(i.key, i.value.diagnostics);
444
445
if i.value.is_poisoned then
446
mark_poisoned(i.key);
447
fi
448
od
449
450
if state.has_consumed_error then
451
mark_consumed_error();
452
fi
453
454
if state.has_consumed_any then
455
mark_consumed_any();
456
fi
457
si
458
459
add_diagnostic_messages(source_path: string, diagnostics: Iterable[DIAGNOSTIC_MESSAGE]) is
460
let list = get_diagnostics_list(source_path);
461
462
for d in diagnostics do
463
list.add(d);
464
od
465
si
466
467
write_all_diagnostics(writer: TextWriter, formatter: DiagnosticFormatter) is
468
for list in _diagnostics_by_source_path.values do
469
list.write_all_diagnostics(writer, formatter);
470
od
471
si
472
473
write_filtered_diagnostics(writer: TextWriter, formatter: DiagnosticFormatter) is
474
for list in _diagnostics_by_source_path.values do
475
list.write_filtered_diagnostics(writer, formatter);
476
od
477
si
478
si
479
480
class DIAGNOSTICS_STORE: Logger is
481
_states: STACK[DIAGNOSTICS_STATE];
482
// The bottom, never-speculated diagnostics state. Lexer errors are
483
// written straight here (see lexer_error).
484
_base_state: DIAGNOSTICS_STATE;
485
_suppressed_codes: Collections.SET[string];
486
_suppression_regions: SUPPRESSION_REGIONS;
487
_all_warnings_are_errors: bool;
488
_error_codes: Collections.SET[string];
489
_hint_codes: Collections.SET[string];
490
_info_codes: Collections.SET[string];
491
492
is_poisoned: bool => _states.peek().is_poisoned;
493
has_consumed_error: bool => _states.peek().has_consumed_error;
494
has_consumed_any: bool => _states.peek().has_consumed_any;
495
496
error_count: int => _states.peek().count;
497
any_errors: bool => _states.peek().any_errors;
498
any_warnings: bool => _states.peek().any_warnings;
499
paths_with_errors: Iterable[string] => _states.peek().paths_with_errors;
500
501
is_clean: bool => !any_errors /\ !has_consumed_error /\ !has_consumed_any;
502
503
is_analysis: bool public;
504
is_compile_expressions: bool public;
505
506
_open_files: Collections.SET[string];
507
508
depth: int => _states.count;
509
510
init() is
511
_states = STACK();
512
_base_state = DIAGNOSTICS_STATE();
513
_states.push(_base_state);
514
_suppressed_codes = Collections.SET[string]();
515
_suppression_regions = SUPPRESSION_REGIONS();
516
_error_codes = Collections.SET[string]();
517
_hint_codes = Collections.SET[string]();
518
_info_codes = Collections.SET[string]();
519
_open_files = Collections.SET[string]();
520
si
521
522
set_open_files(paths: Collections.Iterable[string]) -> bool is
523
let previous = _open_files;
524
_open_files = Collections.SET[string]();
525
526
let has_new_path mut = false;
527
528
for path in paths do
529
_open_files.add(path);
530
531
if !previous.contains(path) then
532
has_new_path = true;
533
fi
534
od
535
536
return has_new_path \/ _open_files.count != previous.count;
537
si
538
539
is_file_open(path: string?) -> bool => path? /\ _open_files.contains(path);
540
541
suppress(code: string) is
542
_suppressed_codes.add(code);
543
si
544
545
is_suppressed(code: string?) -> bool =>
546
code? /\ _suppressed_codes.contains(code);
547
548
set_all_warnings_are_errors(value: bool) is
549
_all_warnings_are_errors = value;
550
si
551
552
promote_to_error(code: string) is
553
_error_codes.add(code);
554
si
555
556
is_promoted_to_error(code: string?) -> bool =>
557
_all_warnings_are_errors \/ (code? /\ _error_codes.contains(code));
558
559
demote_to_hint(code: string) is
560
_hint_codes.add(code);
561
si
562
563
is_demoted_to_hint(code: string?) -> bool =>
564
code? /\ _hint_codes.contains(code);
565
566
demote_to_info(code: string) is
567
_info_codes.add(code);
568
si
569
570
is_demoted_to_info(code: string?) -> bool =>
571
code? /\ _info_codes.contains(code);
572
573
want_hint_for(location: LOCATION?) -> bool =>
574
is_analysis /\ location? /\ is_file_open(location.file_name);
575
576
register_suppression_region(region: LOCATION, code: string) is
577
_suppression_regions.register(region, code);
578
si
579
580
clear_suppression_regions(path: string) is
581
_suppression_regions.clear(path);
582
si
583
584
clear_suppression_regions() is
585
_suppression_regions.clear();
586
si
587
588
is_suppressed(code: string?, location: LOCATION) -> bool =>
589
code? /\
590
(_suppressed_codes.contains(code) \/ _suppression_regions.contains(code, location));
591
592
start_analysis() is
593
is_analysis = true;
594
si
595
596
end_analysis() is
597
is_analysis = false;
598
is_compile_expressions = false;
599
si
600
601
set_is_compiling_expressions(value: bool) is
602
is_compile_expressions = value;
603
si
604
605
only: DIAGNOSTICS_STATE is
606
assert _states.count == 1 else "expected exactly one stacked diagnostics state, found {_states.count}";
607
608
return _states.peek();
609
si
610
611
top: DIAGNOSTICS_STATE is
612
assert _states.count >= 1 else "expected at least one stacked diagnostics state";
613
614
return _states.peek();
615
si
616
617
pop() -> DIAGNOSTICS_STATE is
618
assert _states.count >= 1 else "expected at least one stacked diagnostics state";
619
620
return _states.pop();
621
si
622
623
clear(source_path: string, analysis_only: bool) is
624
only.clear(source_path, analysis_only);
625
si
626
627
clear_global_declaration_diagnostics(source_path: string) is
628
only.clear_global_declaration_diagnostics(source_path);
629
si
630
631
clear_expression_diagnostics(source_path: string) is
632
only.clear_expression_diagnostics(source_path);
633
si
634
635
speculate() is
636
_states.push(DIAGNOSTICS_STATE());
637
si
638
639
roll_back() -> DIAGNOSTICS_STATE =>
640
pop();
641
642
commit() is
643
let to_merge = pop();
644
645
top.merge(to_merge);
646
si
647
648
mark() -> int => _states.count;
649
release(mark: int) is
650
while _states.count > mark do
651
pop();
652
od
653
si
654
655
speculate_then_commit() -> LOGGER_SPECULATE_THEN_COMMIT =>
656
LOGGER_SPECULATE_THEN_COMMIT(self);
657
658
speculate_then_backtrack() -> LOGGER_SPECULATE_THEN_BACKTRACK =>
659
LOGGER_SPECULATE_THEN_BACKTRACK(self);
660
661
mark_then_release() -> MARK_THEN_RELEASE =>
662
MARK_THEN_RELEASE(self);
663
664
merge(state: DIAGNOSTICS_STATE) is
665
top.merge(state);
666
si
667
668
write_all_diagnostics(writer: TextWriter, formatter: DiagnosticFormatter) is
669
let state = only;
670
671
if formatter.want_filter_error_cascades /\ state.is_possible_error_cascade then
672
state.write_filtered_diagnostics(writer, formatter);
673
else
674
state.write_all_diagnostics(writer, formatter);
675
fi
676
si
677
678
poison(location: LOCATION) is
679
top.mark_poisoned(location.file_name);
680
si
681
682
mark_consumed_error() is
683
top.mark_consumed_error();
684
si
685
686
mark_consumed_any() is
687
top.mark_consumed_any();
688
si
689
690
mark_consumed_any_if(consumed: bool) is
691
if consumed then
692
top.mark_consumed_any();
693
fi
694
si
695
696
clear_consumed_any() is
697
top.clear_consumed_any();
698
si
699
700
clear_consumed_error() is
701
top.clear_consumed_error();
702
si
703
704
exception(location: LOCATION, exception: Exception, message: string) is
705
debug_always("{location} exception: {exception.to_string().replace_line_endings(" ")}");
706
707
if _states.count == 0 then
708
debug_always("diagnostics store: exception depth {_states.count}: {location}: {message}: {exception}");
709
fi
710
711
top.add_diagnostic_message(location.file_name, DIAGNOSTIC_MESSAGE(is_analysis, is_compile_expressions, DiagnosticSeverity.EXCEPTION, location, "{message}: {exception}"));
712
si
713
714
fatal(location: LOCATION, message: string) is
715
debug_always("{location}: fatal: {message}");
716
717
debug_always("diagnostics store: fatal depth {_states.count}: {location}: {message}");
718
719
top.add_diagnostic_message(location.file_name, DIAGNOSTIC_MESSAGE(is_analysis, is_compile_expressions, DiagnosticSeverity.FATAL, location, message));
720
si
721
722
error(location: LOCATION, message: string) is
723
top.add_diagnostic_message(location.file_name, DIAGNOSTIC_MESSAGE(is_analysis, is_compile_expressions, DiagnosticSeverity.ERROR, location, message));
724
si
725
726
// A lexical error comes from the tokenizer, which reads each source
727
// position exactly once — parser speculation replays buffered tokens,
728
// it never re-lexes. So a lexer error is produced once no matter how
729
// the parser speculates over those tokens. It is written to the base
730
// state rather than the current speculative one, so a parse that reads
731
// the tokens speculatively and then backtracks does not roll it back
732
// and lose it. Unlike a parse error — which the parser regenerates on
733
// every re-walk and so must roll back to avoid duplicates — a lexer
734
// error cannot duplicate, because nothing regenerates it.
735
lexer_error(location: LOCATION, message: string) is
736
_base_state.add_diagnostic_message(location.file_name, DIAGNOSTIC_MESSAGE(is_analysis, is_compile_expressions, DiagnosticSeverity.ERROR, location, message));
737
si
738
739
warn(location: LOCATION, message: string) is
740
let severity =
741
if _all_warnings_are_errors then
742
DiagnosticSeverity.ERROR
743
else
744
DiagnosticSeverity.WARN
745
fi;
746
top.add_diagnostic_message(location.file_name, DIAGNOSTIC_MESSAGE(is_analysis, is_compile_expressions, severity, location, message));
747
si
748
749
warn(location: LOCATION, code: string, message: string) is
750
if is_suppressed(code, location) then
751
return;
752
fi
753
754
if is_demoted_to_hint(code) then
755
// Emitted as an editor-only hint under the same gate as a
756
// native hint: dropped in batch compilation, surfaced in
757
// analysis mode only for a file the client has open.
758
if !want_hint_for(location) then
759
return;
760
fi
761
762
top.add_diagnostic_message(location.file_name, DIAGNOSTIC_MESSAGE(is_analysis, is_compile_expressions, DiagnosticSeverity.HINT, location, code, message));
763
764
return;
765
fi
766
767
if is_demoted_to_info(code) then
768
// Unlike a hint, info stays a normal batch-visible
769
// diagnostic; it is not subject to the editor-only gate.
770
top.add_diagnostic_message(location.file_name, DIAGNOSTIC_MESSAGE(is_analysis, is_compile_expressions, DiagnosticSeverity.INFO, location, code, message));
771
772
return;
773
fi
774
775
let severity =
776
if is_promoted_to_error(code) then
777
DiagnosticSeverity.ERROR
778
else
779
DiagnosticSeverity.WARN
780
fi;
781
782
top.add_diagnostic_message(location.file_name, DIAGNOSTIC_MESSAGE(is_analysis, is_compile_expressions, severity, location, code, message));
783
si
784
785
info(location: LOCATION, message: string) is
786
top.add_diagnostic_message(location.file_name, DIAGNOSTIC_MESSAGE(is_analysis, is_compile_expressions, DiagnosticSeverity.INFO, location, message));
787
si
788
789
info(location: LOCATION, code: string, message: string) is
790
if is_suppressed(code, location) then
791
return;
792
fi
793
794
top.add_diagnostic_message(location.file_name, DIAGNOSTIC_MESSAGE(is_analysis, is_compile_expressions, DiagnosticSeverity.INFO, location, code, message));
795
si
796
797
hint(location: LOCATION, message: string) is
798
top.add_diagnostic_message(location.file_name, DIAGNOSTIC_MESSAGE(is_analysis, is_compile_expressions, DiagnosticSeverity.HINT, location, message));
799
si
800
801
hint(location: LOCATION, code: string, message: string) is
802
if is_suppressed(code, location) then
803
return;
804
fi
805
806
top.add_diagnostic_message(location.file_name, DIAGNOSTIC_MESSAGE(is_analysis, is_compile_expressions, DiagnosticSeverity.HINT, location, code, message));
807
si
808
809
inlay(location: LOCATION, code: string, label: string, detail: string?) is
810
if is_suppressed(code, location) then
811
return;
812
fi
813
814
top.add_diagnostic_message(location.file_name, DIAGNOSTIC_MESSAGE(is_analysis, is_compile_expressions, DiagnosticSeverity.INLAY, location, code, label, detail));
815
si
816
817
inlays_for(path: string) -> Collections.Iterable[DIAGNOSTIC_MESSAGE] =>
818
top.get_diagnostics_list(path).diagnostics |> filter(d => d.is_inlay);
819
820
poison(location: LOCATION, message: string) is
821
top.add_diagnostic_message(location.file_name, DIAGNOSTIC_MESSAGE(is_analysis, is_compile_expressions, DiagnosticSeverity.FATAL, location, message));
822
top.mark_poisoned(location.file_name);
823
si
824
825
write_poison_messages() is
826
if is_poisoned then
827
debug_always("internal compiler error");
828
IO.Std.error.flush();
829
fi
830
si
831
si
832
si