@@ -29,6 +29,9 @@ class ErrorInfo:
2929 # The line number related to this error within file.
3030 line = 0 # -1 if unknown
3131
32+ # The column number related to this error with file.
33+ column = 0 # -1 if unknown
34+
3235 # Either 'error' or 'note'.
3336 severity = ''
3437
@@ -42,13 +45,14 @@ class ErrorInfo:
4245 only_once = False
4346
4447 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 :
4750 self .import_ctx = import_ctx
4851 self .file = file
4952 self .type = typ
5053 self .function_or_member = function_or_member
5154 self .line = line
55+ self .column = column
5256 self .severity = severity
5357 self .message = message
5458 self .blocker = blocker
@@ -92,7 +96,11 @@ class Errors:
9296 # Set to True to suppress "In function "foo":" messages.
9397 suppress_error_context = False # type: bool
9498
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 :
96104 self .error_info = []
97105 self .import_ctx = []
98106 self .type_name = [None ]
@@ -101,9 +109,10 @@ def __init__(self, suppress_error_context: bool = False) -> None:
101109 self .used_ignored_lines = defaultdict (set )
102110 self .only_once_messages = set ()
103111 self .suppress_error_context = suppress_error_context
112+ self .show_column_numbers = show_column_numbers
104113
105114 def copy (self ) -> 'Errors' :
106- new = Errors (self .suppress_error_context )
115+ new = Errors (self .suppress_error_context , self . show_column_numbers )
107116 new .file = self .file
108117 new .import_ctx = self .import_ctx [:]
109118 new .type_name = self .type_name [:]
@@ -169,7 +178,7 @@ def set_import_context(self, ctx: List[Tuple[str, int]]) -> None:
169178 """Replace the entire import context with a new value."""
170179 self .import_ctx = ctx [:]
171180
172- def report (self , line : int , message : str , blocker : bool = False ,
181+ def report (self , line : int , column : int , message : str , blocker : bool = False ,
173182 severity : str = 'error' , file : str = None , only_once : bool = False ) -> None :
174183 """Report message at the given line using the current error context.
175184
@@ -187,7 +196,7 @@ def report(self, line: int, message: str, blocker: bool = False,
187196 if file is None :
188197 file = self .file
189198 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 ,
191200 blocker , only_once )
192201 self .add_error_info (info )
193202
@@ -210,7 +219,7 @@ def generate_unused_ignore_notes(self) -> None:
210219 for line in ignored_lines - self .used_ignored_lines [file ]:
211220 # Don't use report since add_error_info will ignore the error!
212221 info = ErrorInfo (self .import_context (), file , None , None ,
213- line , 'note' , "unused 'type: ignore' comment" ,
222+ line , - 1 , 'note' , "unused 'type: ignore' comment" ,
214223 False , False )
215224 self .error_info .append (info )
216225
@@ -245,10 +254,13 @@ def messages(self) -> List[str]:
245254 a = [] # type: List[str]
246255 errors = self .render_messages (self .sort_messages (self .error_info ))
247256 errors = self .remove_duplicates (errors )
248- for file , line , severity , message in errors :
257+ for file , line , column , severity , message in errors :
249258 s = ''
250259 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 :
252264 srcloc = '{}:{}' .format (file , line )
253265 else :
254266 srcloc = file
@@ -258,16 +270,17 @@ def messages(self) -> List[str]:
258270 a .append (s )
259271 return a
260272
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 ,
262274 str , str ]]:
263275 """Translate the messages into a sequence of tuples.
264276
265- Each tuple is of form (path, line, message. The rendered
277+ Each tuple is of form (path, line, col, message. The rendered
266278 sequence includes information about error contexts. The path
267279 item may be None. If the line item is negative, the line
268280 number is not defined for the tuple.
269281 """
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)
271284
272285 prev_import_context = [] # type: List[Tuple[str, int]]
273286 prev_function_or_member = None # type: str
@@ -290,7 +303,7 @@ def render_messages(self, errors: List[ErrorInfo]) -> List[Tuple[str, int,
290303 # Remove prefix to ignore from path (if present) to
291304 # simplify path.
292305 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 )))
294307 i -= 1
295308
296309 file = self .simplify_path (e .file )
@@ -302,27 +315,27 @@ def render_messages(self, errors: List[ErrorInfo]) -> List[Tuple[str, int,
302315 e .type != prev_type ):
303316 if e .function_or_member is None :
304317 if e .type is None :
305- result .append ((file , - 1 , 'note' , 'At top level:' ))
318+ result .append ((file , - 1 , - 1 , 'note' , 'At top level:' ))
306319 else :
307- result .append ((file , - 1 , 'note' , 'In class "{}":' .format (
320+ result .append ((file , - 1 , - 1 , 'note' , 'In class "{}":' .format (
308321 e .type )))
309322 else :
310323 if e .type is None :
311- result .append ((file , - 1 , 'note' ,
324+ result .append ((file , - 1 , - 1 , 'note' ,
312325 'In function "{}":' .format (
313326 e .function_or_member )))
314327 else :
315- result .append ((file , - 1 , 'note' ,
328+ result .append ((file , - 1 , - 1 , 'note' ,
316329 'In member "{}" of class "{}":' .format (
317330 e .function_or_member , e .type )))
318331 elif e .type != prev_type :
319332 if e .type is None :
320- result .append ((file , - 1 , 'note' , 'At top level:' ))
333+ result .append ((file , - 1 , - 1 , 'note' , 'At top level:' ))
321334 else :
322- result .append ((file , - 1 , 'note' ,
335+ result .append ((file , - 1 , - 1 , 'note' ,
323336 'In class "{}":' .format (e .type )))
324337
325- result .append ((file , e .line , e .severity , e .message ))
338+ result .append ((file , e .line , e .column , e . severity , e .message ))
326339
327340 prev_import_context = e .import_ctx
328341 prev_function_or_member = e .function_or_member
@@ -348,22 +361,23 @@ def sort_messages(self, errors: List[ErrorInfo]) -> List[ErrorInfo]:
348361 i += 1
349362 i += 1
350363
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 ) )
353366 result .extend (a )
354367 return result
355368
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 ]]:
358371 """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]]
360373 i = 0
361374 while i < len (errors ):
362375 dup = False
363376 j = i - 1
364377 while (j >= 0 and errors [j ][0 ] == errors [i ][0 ] and
365378 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
367381 dup = True
368382 break
369383 j -= 1
0 commit comments