@@ -58,6 +58,9 @@ class ErrorInfo:
58
58
# Only report this particular messages once per program.
59
59
only_once = False
60
60
61
+ # Do not remove duplicate copies of this message (ignored if only_once is True).
62
+ allow_dups = False
63
+
61
64
# Actual origin of the error message as tuple (path, line number, end line number)
62
65
# If end line number is unknown, use line number.
63
66
origin : Tuple [str , int , int ]
@@ -82,6 +85,7 @@ def __init__(self,
82
85
code : Optional [ErrorCode ],
83
86
blocker : bool ,
84
87
only_once : bool ,
88
+ allow_dups : bool ,
85
89
origin : Optional [Tuple [str , int , int ]] = None ,
86
90
target : Optional [str ] = None ) -> None :
87
91
self .import_ctx = import_ctx
@@ -96,17 +100,19 @@ def __init__(self,
96
100
self .code = code
97
101
self .blocker = blocker
98
102
self .only_once = only_once
103
+ self .allow_dups = allow_dups
99
104
self .origin = origin or (file , line , line )
100
105
self .target = target
101
106
102
107
103
108
# Type used internally to represent errors:
104
- # (path, line, column, severity, message, code)
109
+ # (path, line, column, severity, message, allow_dups, code)
105
110
ErrorTuple = Tuple [Optional [str ],
106
111
int ,
107
112
int ,
108
113
str ,
109
114
str ,
115
+ bool ,
110
116
Optional [ErrorCode ]]
111
117
112
118
@@ -290,6 +296,7 @@ def report(self,
290
296
severity : str = 'error' ,
291
297
file : Optional [str ] = None ,
292
298
only_once : bool = False ,
299
+ allow_dups : bool = False ,
293
300
origin_line : Optional [int ] = None ,
294
301
offset : int = 0 ,
295
302
end_line : Optional [int ] = None ) -> None :
@@ -304,6 +311,7 @@ def report(self,
304
311
severity: 'error' or 'note'
305
312
file: if non-None, override current file as context
306
313
only_once: if True, only report this exact message once per build
314
+ allow_dups: if True, allow duplicate copies of this message (ignored if only_once)
307
315
origin_line: if non-None, override current context as origin
308
316
end_line: if non-None, override current context as end
309
317
"""
@@ -333,7 +341,7 @@ def report(self,
333
341
334
342
info = ErrorInfo (self .import_context (), file , self .current_module (), type ,
335
343
function , line , column , severity , message , code ,
336
- blocker , only_once ,
344
+ blocker , only_once , allow_dups ,
337
345
origin = (self .file , origin_line , end_line ),
338
346
target = self .current_target ())
339
347
self .add_error_info (info )
@@ -410,6 +418,7 @@ def report_hidden_errors(self, info: ErrorInfo) -> None:
410
418
code = None ,
411
419
blocker = False ,
412
420
only_once = True ,
421
+ allow_dups = False ,
413
422
origin = info .origin ,
414
423
target = info .target ,
415
424
)
@@ -456,7 +465,7 @@ def generate_unused_ignore_errors(self, file: str) -> None:
456
465
# Don't use report since add_error_info will ignore the error!
457
466
info = ErrorInfo (self .import_context (), file , self .current_module (), None ,
458
467
None , line , - 1 , 'error' , 'unused "type: ignore" comment' ,
459
- None , False , False )
468
+ None , False , False , False )
460
469
self ._add_error_info (file , info )
461
470
462
471
def num_messages (self ) -> int :
@@ -515,7 +524,7 @@ def format_messages(self, error_info: List[ErrorInfo],
515
524
error_info = [info for info in error_info if not info .hidden ]
516
525
errors = self .render_messages (self .sort_messages (error_info ))
517
526
errors = self .remove_duplicates (errors )
518
- for file , line , column , severity , message , code in errors :
527
+ for file , line , column , severity , message , allow_dups , code in errors :
519
528
s = ''
520
529
if file is not None :
521
530
if self .show_column_numbers and line >= 0 and column >= 0 :
@@ -590,7 +599,7 @@ def render_messages(self,
590
599
errors : List [ErrorInfo ]) -> List [ErrorTuple ]:
591
600
"""Translate the messages into a sequence of tuples.
592
601
593
- Each tuple is of form (path, line, col, severity, message, code).
602
+ Each tuple is of form (path, line, col, severity, message, allow_dups, code).
594
603
The rendered sequence includes information about error contexts.
595
604
The path item may be None. If the line item is negative, the
596
605
line number is not defined for the tuple.
@@ -619,7 +628,8 @@ def render_messages(self,
619
628
# Remove prefix to ignore from path (if present) to
620
629
# simplify path.
621
630
path = remove_path_prefix (path , self .ignore_prefix )
622
- result .append ((None , - 1 , - 1 , 'note' , fmt .format (path , line ), None ))
631
+ result .append ((None , - 1 , - 1 , 'note' ,
632
+ fmt .format (path , line ), e .allow_dups , None ))
623
633
i -= 1
624
634
625
635
file = self .simplify_path (e .file )
@@ -631,27 +641,27 @@ def render_messages(self,
631
641
e .type != prev_type ):
632
642
if e .function_or_member is None :
633
643
if e .type is None :
634
- result .append ((file , - 1 , - 1 , 'note' , 'At top level:' , None ))
644
+ result .append ((file , - 1 , - 1 , 'note' , 'At top level:' , e . allow_dups , None ))
635
645
else :
636
646
result .append ((file , - 1 , - 1 , 'note' , 'In class "{}":' .format (
637
- e .type ), None ))
647
+ e .type ), e . allow_dups , None ))
638
648
else :
639
649
if e .type is None :
640
650
result .append ((file , - 1 , - 1 , 'note' ,
641
651
'In function "{}":' .format (
642
- e .function_or_member ), None ))
652
+ e .function_or_member ), e . allow_dups , None ))
643
653
else :
644
654
result .append ((file , - 1 , - 1 , 'note' ,
645
655
'In member "{}" of class "{}":' .format (
646
- e .function_or_member , e .type ), None ))
656
+ e .function_or_member , e .type ), e . allow_dups , None ))
647
657
elif e .type != prev_type :
648
658
if e .type is None :
649
- result .append ((file , - 1 , - 1 , 'note' , 'At top level:' , None ))
659
+ result .append ((file , - 1 , - 1 , 'note' , 'At top level:' , e . allow_dups , None ))
650
660
else :
651
661
result .append ((file , - 1 , - 1 , 'note' ,
652
- 'In class "{}":' .format (e .type ), None ))
662
+ 'In class "{}":' .format (e .type ), e . allow_dups , None ))
653
663
654
- result .append ((file , e .line , e .column , e .severity , e .message , e .code ))
664
+ result .append ((file , e .line , e .column , e .severity , e .message , e .allow_dups , e . code ))
655
665
656
666
prev_import_context = e .import_ctx
657
667
prev_function_or_member = e .function_or_member
@@ -691,23 +701,25 @@ def remove_duplicates(self, errors: List[ErrorTuple]) -> List[ErrorTuple]:
691
701
# Use slightly special formatting for member conflicts reporting.
692
702
conflicts_notes = False
693
703
j = i - 1
694
- while j >= 0 and errors [j ][0 ] == errors [i ][0 ]:
695
- if errors [j ][4 ].strip () == 'Got:' :
696
- conflicts_notes = True
697
- j -= 1
698
- j = i - 1
699
- while (j >= 0 and errors [j ][0 ] == errors [i ][0 ] and
700
- errors [j ][1 ] == errors [i ][1 ]):
701
- if (errors [j ][3 ] == errors [i ][3 ] and
702
- # Allow duplicate notes in overload conflicts reporting.
703
- not ((errors [i ][3 ] == 'note' and
704
- errors [i ][4 ].strip () in allowed_duplicates )
705
- or (errors [i ][4 ].strip ().startswith ('def ' ) and
706
- conflicts_notes )) and
707
- errors [j ][4 ] == errors [i ][4 ]): # ignore column
708
- dup = True
709
- break
710
- j -= 1
704
+ # Find duplicates, unless duplicates are allowed.
705
+ if not errors [i ][5 ]:
706
+ while j >= 0 and errors [j ][0 ] == errors [i ][0 ]:
707
+ if errors [j ][4 ].strip () == 'Got:' :
708
+ conflicts_notes = True
709
+ j -= 1
710
+ j = i - 1
711
+ while (j >= 0 and errors [j ][0 ] == errors [i ][0 ] and
712
+ errors [j ][1 ] == errors [i ][1 ]):
713
+ if (errors [j ][3 ] == errors [i ][3 ] and
714
+ # Allow duplicate notes in overload conflicts reporting.
715
+ not ((errors [i ][3 ] == 'note' and
716
+ errors [i ][4 ].strip () in allowed_duplicates )
717
+ or (errors [i ][4 ].strip ().startswith ('def ' ) and
718
+ conflicts_notes )) and
719
+ errors [j ][4 ] == errors [i ][4 ]): # ignore column
720
+ dup = True
721
+ break
722
+ j -= 1
711
723
if not dup :
712
724
res .append (errors [i ])
713
725
i += 1
0 commit comments