9
9
from mypy .scope import Scope
10
10
from mypy .options import Options
11
11
from mypy .version import __version__ as mypy_version
12
+ from mypy .errorcodes import ErrorCode
13
+ from mypy import errorcodes as codes
12
14
13
15
T = TypeVar ('T' )
14
16
allowed_duplicates = ['@overload' , 'Got:' , 'Expected:' ] # type: Final
@@ -45,6 +47,9 @@ class ErrorInfo:
45
47
# The error message.
46
48
message = ''
47
49
50
+ # The error code.
51
+ code = None # type: Optional[ErrorCode]
52
+
48
53
# If True, we should halt build after the file that generated this error.
49
54
blocker = False
50
55
@@ -68,6 +73,7 @@ def __init__(self,
68
73
column : int ,
69
74
severity : str ,
70
75
message : str ,
76
+ code : Optional [ErrorCode ],
71
77
blocker : bool ,
72
78
only_once : bool ,
73
79
origin : Optional [Tuple [str , int , int ]] = None ,
@@ -81,12 +87,23 @@ def __init__(self,
81
87
self .column = column
82
88
self .severity = severity
83
89
self .message = message
90
+ self .code = code
84
91
self .blocker = blocker
85
92
self .only_once = only_once
86
93
self .origin = origin or (file , line , line )
87
94
self .target = target
88
95
89
96
97
+ # Type used internally to represent errors:
98
+ # (path, line, column, severity, message, code)
99
+ ErrorTuple = Tuple [Optional [str ],
100
+ int ,
101
+ int ,
102
+ str ,
103
+ str ,
104
+ Optional [ErrorCode ]]
105
+
106
+
90
107
class Errors :
91
108
"""Container for compile errors.
92
109
@@ -111,8 +128,9 @@ class Errors:
111
128
# Path to current file.
112
129
file = '' # type: str
113
130
114
- # Ignore errors on these lines of each file.
115
- ignored_lines = None # type: Dict[str, Set[int]]
131
+ # Ignore some errors on these lines of each file
132
+ # (path -> line -> error-codes)
133
+ ignored_lines = None # type: Dict[str, Dict[int, List[str]]]
116
134
117
135
# Lines on which an error was actually ignored.
118
136
used_ignored_lines = None # type: Dict[str, Set[int]]
@@ -135,10 +153,13 @@ class Errors:
135
153
target_module = None # type: Optional[str]
136
154
scope = None # type: Optional[Scope]
137
155
138
- def __init__ (self , show_error_context : bool = False ,
139
- show_column_numbers : bool = False ) -> None :
156
+ def __init__ (self ,
157
+ show_error_context : bool = False ,
158
+ show_column_numbers : bool = False ,
159
+ show_error_codes : bool = False ) -> None :
140
160
self .show_error_context = show_error_context
141
161
self .show_column_numbers = show_column_numbers
162
+ self .show_error_codes = show_error_codes
142
163
self .initialize ()
143
164
144
165
def initialize (self ) -> None :
@@ -197,7 +218,7 @@ def set_file(self, file: str,
197
218
self .scope = scope
198
219
199
220
def set_file_ignored_lines (self , file : str ,
200
- ignored_lines : Set [int ],
221
+ ignored_lines : Dict [int , List [ str ] ],
201
222
ignore_all : bool = False ) -> None :
202
223
self .ignored_lines [file ] = ignored_lines
203
224
if ignore_all :
@@ -226,6 +247,8 @@ def report(self,
226
247
line : int ,
227
248
column : Optional [int ],
228
249
message : str ,
250
+ code : Optional [ErrorCode ] = None ,
251
+ * ,
229
252
blocker : bool = False ,
230
253
severity : str = 'error' ,
231
254
file : Optional [str ] = None ,
@@ -237,7 +260,9 @@ def report(self,
237
260
238
261
Args:
239
262
line: line number of error
263
+ column: column number of error
240
264
message: message to report
265
+ code: error code (defaults to 'misc' for 'error' severity)
241
266
blocker: if True, don't continue analysis after this error
242
267
severity: 'error' or 'note'
243
268
file: if non-None, override current file as context
@@ -267,8 +292,11 @@ def report(self,
267
292
if end_line is None :
268
293
end_line = origin_line
269
294
295
+ if severity == 'error' and code is None :
296
+ code = codes .MISC
297
+
270
298
info = ErrorInfo (self .import_context (), file , self .current_module (), type ,
271
- function , line , column , severity , message ,
299
+ function , line , column , severity , message , code ,
272
300
blocker , only_once ,
273
301
origin = (self .file , origin_line , end_line ),
274
302
target = self .current_target ())
@@ -293,7 +321,7 @@ def add_error_info(self, info: ErrorInfo) -> None:
293
321
# Check each line in this context for "type: ignore" comments.
294
322
# line == end_line for most nodes, so we only loop once.
295
323
for scope_line in range (line , end_line + 1 ):
296
- if scope_line in self .ignored_lines [file ]:
324
+ if self . is_ignored_error ( scope_line , info , self .ignored_lines [file ]) :
297
325
# Annotation requests us to ignore all errors on this line.
298
326
self .used_ignored_lines [file ].add (scope_line )
299
327
return
@@ -305,6 +333,16 @@ def add_error_info(self, info: ErrorInfo) -> None:
305
333
self .only_once_messages .add (info .message )
306
334
self ._add_error_info (file , info )
307
335
336
+ def is_ignored_error (self , line : int , info : ErrorInfo , ignores : Dict [int , List [str ]]) -> bool :
337
+ if line not in ignores :
338
+ return False
339
+ elif not ignores [line ]:
340
+ # Empty list means that we ignore all errors
341
+ return True
342
+ elif info .code :
343
+ return info .code .code in ignores [line ]
344
+ return False
345
+
308
346
def clear_errors_in_targets (self , path : str , targets : Set [str ]) -> None :
309
347
"""Remove errors in specific fine-grained targets within a file."""
310
348
if path in self .error_info_map :
@@ -319,11 +357,11 @@ def clear_errors_in_targets(self, path: str, targets: Set[str]) -> None:
319
357
def generate_unused_ignore_errors (self , file : str ) -> None :
320
358
ignored_lines = self .ignored_lines [file ]
321
359
if not self .is_typeshed_file (file ) and file not in self .ignored_files :
322
- for line in ignored_lines - self .used_ignored_lines [file ]:
360
+ for line in set ( ignored_lines ) - self .used_ignored_lines [file ]:
323
361
# Don't use report since add_error_info will ignore the error!
324
362
info = ErrorInfo (self .import_context (), file , self .current_module (), None ,
325
363
None , line , - 1 , 'error' , "unused 'type: ignore' comment" ,
326
- False , False )
364
+ None , False , False )
327
365
self ._add_error_info (file , info )
328
366
329
367
def is_typeshed_file (self , file : str ) -> bool :
@@ -373,7 +411,7 @@ def format_messages(self, error_info: List[ErrorInfo]) -> List[str]:
373
411
a = [] # type: List[str]
374
412
errors = self .render_messages (self .sort_messages (error_info ))
375
413
errors = self .remove_duplicates (errors )
376
- for file , line , column , severity , message in errors :
414
+ for file , line , column , severity , message , code in errors :
377
415
s = ''
378
416
if file is not None :
379
417
if self .show_column_numbers and line >= 0 and column >= 0 :
@@ -385,6 +423,8 @@ def format_messages(self, error_info: List[ErrorInfo]) -> List[str]:
385
423
s = '{}: {}: {}' .format (srcloc , severity , message )
386
424
else :
387
425
s = message
426
+ if self .show_error_codes and code :
427
+ s = '{} [{}]' .format (s , code .code )
388
428
a .append (s )
389
429
return a
390
430
@@ -420,18 +460,16 @@ def targets(self) -> Set[str]:
420
460
for info in errs
421
461
if info .target )
422
462
423
- def render_messages (self , errors : List [ ErrorInfo ]) -> List [ Tuple [ Optional [ str ], int , int ,
424
- str , str ] ]:
463
+ def render_messages (self ,
464
+ errors : List [ ErrorInfo ]) -> List [ ErrorTuple ]:
425
465
"""Translate the messages into a sequence of tuples.
426
466
427
- Each tuple is of form (path, line, col, severity, message).
467
+ Each tuple is of form (path, line, col, severity, message, code ).
428
468
The rendered sequence includes information about error contexts.
429
469
The path item may be None. If the line item is negative, the
430
470
line number is not defined for the tuple.
431
471
"""
432
- result = [] # type: List[Tuple[Optional[str], int, int, str, str]]
433
- # (path, line, column, severity, message)
434
-
472
+ result = [] # type: List[ErrorTuple]
435
473
prev_import_context = [] # type: List[Tuple[str, int]]
436
474
prev_function_or_member = None # type: Optional[str]
437
475
prev_type = None # type: Optional[str]
@@ -455,7 +493,7 @@ def render_messages(self, errors: List[ErrorInfo]) -> List[Tuple[Optional[str],
455
493
# Remove prefix to ignore from path (if present) to
456
494
# simplify path.
457
495
path = remove_path_prefix (path , self .ignore_prefix )
458
- result .append ((None , - 1 , - 1 , 'note' , fmt .format (path , line )))
496
+ result .append ((None , - 1 , - 1 , 'note' , fmt .format (path , line ), None ))
459
497
i -= 1
460
498
461
499
file = self .simplify_path (e .file )
@@ -467,27 +505,27 @@ def render_messages(self, errors: List[ErrorInfo]) -> List[Tuple[Optional[str],
467
505
e .type != prev_type ):
468
506
if e .function_or_member is None :
469
507
if e .type is None :
470
- result .append ((file , - 1 , - 1 , 'note' , 'At top level:' ))
508
+ result .append ((file , - 1 , - 1 , 'note' , 'At top level:' , None ))
471
509
else :
472
510
result .append ((file , - 1 , - 1 , 'note' , 'In class "{}":' .format (
473
- e .type )))
511
+ e .type ), None ))
474
512
else :
475
513
if e .type is None :
476
514
result .append ((file , - 1 , - 1 , 'note' ,
477
515
'In function "{}":' .format (
478
- e .function_or_member )))
516
+ e .function_or_member ), None ))
479
517
else :
480
518
result .append ((file , - 1 , - 1 , 'note' ,
481
519
'In member "{}" of class "{}":' .format (
482
- e .function_or_member , e .type )))
520
+ e .function_or_member , e .type ), None ))
483
521
elif e .type != prev_type :
484
522
if e .type is None :
485
- result .append ((file , - 1 , - 1 , 'note' , 'At top level:' ))
523
+ result .append ((file , - 1 , - 1 , 'note' , 'At top level:' , None ))
486
524
else :
487
525
result .append ((file , - 1 , - 1 , 'note' ,
488
- 'In class "{}":' .format (e .type )))
526
+ 'In class "{}":' .format (e .type ), None ))
489
527
490
- result .append ((file , e .line , e .column , e .severity , e .message ))
528
+ result .append ((file , e .line , e .column , e .severity , e .message , e . code ))
491
529
492
530
prev_import_context = e .import_ctx
493
531
prev_function_or_member = e .function_or_member
@@ -518,10 +556,9 @@ def sort_messages(self, errors: List[ErrorInfo]) -> List[ErrorInfo]:
518
556
result .extend (a )
519
557
return result
520
558
521
- def remove_duplicates (self , errors : List [Tuple [Optional [str ], int , int , str , str ]]
522
- ) -> List [Tuple [Optional [str ], int , int , str , str ]]:
559
+ def remove_duplicates (self , errors : List [ErrorTuple ]) -> List [ErrorTuple ]:
523
560
"""Remove duplicates from a sorted error list."""
524
- res = [] # type: List[Tuple[Optional[str], int, int, str, str] ]
561
+ res = [] # type: List[ErrorTuple ]
525
562
i = 0
526
563
while i < len (errors ):
527
564
dup = False
0 commit comments