@@ -29,6 +29,9 @@ class ErrorInfo:
29
29
# The line number related to this error within file.
30
30
line = 0 # -1 if unknown
31
31
32
+ # The column number related to this error with file.
33
+ column = 0 # -1 if unknown
34
+
32
35
# Either 'error' or 'note'.
33
36
severity = ''
34
37
@@ -42,13 +45,14 @@ class ErrorInfo:
42
45
only_once = False
43
46
44
47
def __init__ (self , import_ctx : List [Tuple [str , int ]], file : str , typ : str ,
45
- function_or_member : str , line : int , severity : str , message : str ,
46
- blocker : bool , only_once : bool ) -> None :
48
+ function_or_member : str , line : int , column : int , severity : str ,
49
+ message : str , blocker : bool , only_once : bool ) -> None :
47
50
self .import_ctx = import_ctx
48
51
self .file = file
49
52
self .type = typ
50
53
self .function_or_member = function_or_member
51
54
self .line = line
55
+ self .column = column
52
56
self .severity = severity
53
57
self .message = message
54
58
self .blocker = blocker
@@ -92,7 +96,11 @@ class Errors:
92
96
# Set to True to suppress "In function "foo":" messages.
93
97
suppress_error_context = False # type: bool
94
98
95
- def __init__ (self , suppress_error_context : bool = False ) -> None :
99
+ # Set to True to show column numbers in error messages
100
+ show_column_numbers = False # type: bool
101
+
102
+ def __init__ (self , suppress_error_context : bool = False ,
103
+ show_column_numbers : bool = False ) -> None :
96
104
self .error_info = []
97
105
self .import_ctx = []
98
106
self .type_name = [None ]
@@ -101,9 +109,10 @@ def __init__(self, suppress_error_context: bool = False) -> None:
101
109
self .used_ignored_lines = defaultdict (set )
102
110
self .only_once_messages = set ()
103
111
self .suppress_error_context = suppress_error_context
112
+ self .show_column_numbers = show_column_numbers
104
113
105
114
def copy (self ) -> 'Errors' :
106
- new = Errors (self .suppress_error_context )
115
+ new = Errors (self .suppress_error_context , self . show_column_numbers )
107
116
new .file = self .file
108
117
new .import_ctx = self .import_ctx [:]
109
118
new .type_name = self .type_name [:]
@@ -169,7 +178,7 @@ def set_import_context(self, ctx: List[Tuple[str, int]]) -> None:
169
178
"""Replace the entire import context with a new value."""
170
179
self .import_ctx = ctx [:]
171
180
172
- def report (self , line : int , message : str , blocker : bool = False ,
181
+ def report (self , line : int , column : int , message : str , blocker : bool = False ,
173
182
severity : str = 'error' , file : str = None , only_once : bool = False ) -> None :
174
183
"""Report message at the given line using the current error context.
175
184
@@ -187,7 +196,7 @@ def report(self, line: int, message: str, blocker: bool = False,
187
196
if file is None :
188
197
file = self .file
189
198
info = ErrorInfo (self .import_context (), file , type ,
190
- self .function_or_member [- 1 ], line , severity , message ,
199
+ self .function_or_member [- 1 ], line , column , severity , message ,
191
200
blocker , only_once )
192
201
self .add_error_info (info )
193
202
@@ -210,7 +219,7 @@ def generate_unused_ignore_notes(self) -> None:
210
219
for line in ignored_lines - self .used_ignored_lines [file ]:
211
220
# Don't use report since add_error_info will ignore the error!
212
221
info = ErrorInfo (self .import_context (), file , None , None ,
213
- line , 'note' , "unused 'type: ignore' comment" ,
222
+ line , - 1 , 'note' , "unused 'type: ignore' comment" ,
214
223
False , False )
215
224
self .error_info .append (info )
216
225
@@ -245,10 +254,13 @@ def messages(self) -> List[str]:
245
254
a = [] # type: List[str]
246
255
errors = self .render_messages (self .sort_messages (self .error_info ))
247
256
errors = self .remove_duplicates (errors )
248
- for file , line , severity , message in errors :
257
+ for file , line , column , severity , message in errors :
249
258
s = ''
250
259
if file is not None :
251
- if line is not None and line >= 0 :
260
+ if self .show_column_numbers and line is not None and line >= 0 \
261
+ and column is not None and column >= 0 :
262
+ srcloc = '{}:{}:{}' .format (file , line , column )
263
+ elif line is not None and line >= 0 :
252
264
srcloc = '{}:{}' .format (file , line )
253
265
else :
254
266
srcloc = file
@@ -258,16 +270,17 @@ def messages(self) -> List[str]:
258
270
a .append (s )
259
271
return a
260
272
261
- def render_messages (self , errors : List [ErrorInfo ]) -> List [Tuple [str , int ,
273
+ def render_messages (self , errors : List [ErrorInfo ]) -> List [Tuple [str , int , int ,
262
274
str , str ]]:
263
275
"""Translate the messages into a sequence of tuples.
264
276
265
- Each tuple is of form (path, line, message. The rendered
277
+ Each tuple is of form (path, line, col, message. The rendered
266
278
sequence includes information about error contexts. The path
267
279
item may be None. If the line item is negative, the line
268
280
number is not defined for the tuple.
269
281
"""
270
- result = [] # type: List[Tuple[str, int, str, str]] # (path, line, severity, message)
282
+ result = [] # type: List[Tuple[str, int, int, str, str]]
283
+ # (path, line, column, severity, message)
271
284
272
285
prev_import_context = [] # type: List[Tuple[str, int]]
273
286
prev_function_or_member = None # type: str
@@ -290,7 +303,7 @@ def render_messages(self, errors: List[ErrorInfo]) -> List[Tuple[str, int,
290
303
# Remove prefix to ignore from path (if present) to
291
304
# simplify path.
292
305
path = remove_path_prefix (path , self .ignore_prefix )
293
- result .append ((None , - 1 , 'note' , fmt .format (path , line )))
306
+ result .append ((None , - 1 , - 1 , 'note' , fmt .format (path , line )))
294
307
i -= 1
295
308
296
309
file = self .simplify_path (e .file )
@@ -302,27 +315,27 @@ def render_messages(self, errors: List[ErrorInfo]) -> List[Tuple[str, int,
302
315
e .type != prev_type ):
303
316
if e .function_or_member is None :
304
317
if e .type is None :
305
- result .append ((file , - 1 , 'note' , 'At top level:' ))
318
+ result .append ((file , - 1 , - 1 , 'note' , 'At top level:' ))
306
319
else :
307
- result .append ((file , - 1 , 'note' , 'In class "{}":' .format (
320
+ result .append ((file , - 1 , - 1 , 'note' , 'In class "{}":' .format (
308
321
e .type )))
309
322
else :
310
323
if e .type is None :
311
- result .append ((file , - 1 , 'note' ,
324
+ result .append ((file , - 1 , - 1 , 'note' ,
312
325
'In function "{}":' .format (
313
326
e .function_or_member )))
314
327
else :
315
- result .append ((file , - 1 , 'note' ,
328
+ result .append ((file , - 1 , - 1 , 'note' ,
316
329
'In member "{}" of class "{}":' .format (
317
330
e .function_or_member , e .type )))
318
331
elif e .type != prev_type :
319
332
if e .type is None :
320
- result .append ((file , - 1 , 'note' , 'At top level:' ))
333
+ result .append ((file , - 1 , - 1 , 'note' , 'At top level:' ))
321
334
else :
322
- result .append ((file , - 1 , 'note' ,
335
+ result .append ((file , - 1 , - 1 , 'note' ,
323
336
'In class "{}":' .format (e .type )))
324
337
325
- result .append ((file , e .line , e .severity , e .message ))
338
+ result .append ((file , e .line , e .column , e . severity , e .message ))
326
339
327
340
prev_import_context = e .import_ctx
328
341
prev_function_or_member = e .function_or_member
@@ -348,22 +361,23 @@ def sort_messages(self, errors: List[ErrorInfo]) -> List[ErrorInfo]:
348
361
i += 1
349
362
i += 1
350
363
351
- # Sort the errors specific to a file according to line number.
352
- a = sorted (errors [i0 :i ], key = lambda x : x .line )
364
+ # Sort the errors specific to a file according to line number and column .
365
+ a = sorted (errors [i0 :i ], key = lambda x : ( x .line , x . column ) )
353
366
result .extend (a )
354
367
return result
355
368
356
- def remove_duplicates (self , errors : List [Tuple [str , int , str , str ]]
357
- ) -> List [Tuple [str , int , str , str ]]:
369
+ def remove_duplicates (self , errors : List [Tuple [str , int , int , str , str ]]
370
+ ) -> List [Tuple [str , int , int , str , str ]]:
358
371
"""Remove duplicates from a sorted error list."""
359
- res = [] # type: List[Tuple[str, int, str, str]]
372
+ res = [] # type: List[Tuple[str, int, int, str, str]]
360
373
i = 0
361
374
while i < len (errors ):
362
375
dup = False
363
376
j = i - 1
364
377
while (j >= 0 and errors [j ][0 ] == errors [i ][0 ] and
365
378
errors [j ][1 ] == errors [i ][1 ]):
366
- if errors [j ] == errors [i ]:
379
+ if (errors [j ][3 ] == errors [i ][3 ] and
380
+ errors [j ][4 ] == errors [i ][4 ]): # ignore column
367
381
dup = True
368
382
break
369
383
j -= 1
0 commit comments