@@ -81,29 +81,40 @@ class FileRange:
8181 """Stores the coordinates of a span on a single line within a file.
8282
8383 Attributes:
84- line : the line number
85- start_column : the (inclusive) column where the span starts
86- end_column : the (inclusive) column where the span ends
84+ content : line str
85+ start_byte : the (inclusive) byte offset the span starts
86+ end_byte : the (inclusive) byte offset the span ends
8787 """
8888
89- line : int
90- start_column : int
91- end_column : int
89+ content : str
90+ start_byte : int
91+ end_byte : int
9292
9393 def __init__ (
9494 self , content : str , start_byte : int , end_byte : int
9595 ): # pylint: disable=g-doc-args
96- """Derives a span's coordinates based on a string and start/end bytes.
96+ """
97+ Stores the coordinates of a span based on a string and start/end bytes.
9798
9899 `start_byte` and `end_byte` are assumed to be on the same line.
99100 """
100- content_before_span = content [:start_byte ]
101- self .line = content_before_span .count ("\n " ) + 1
102- self .start_column = start_byte - content_before_span .rfind ("\n " )
103- self .end_column = self .start_column + (end_byte - start_byte - 1 )
101+ self .content = content
102+ self .start_byte = start_byte
103+ self .end_byte = end_byte
104104
105- def __str__ (self ) -> str :
106- return f"{ self .line } :{ self .start_column } -{ self .end_column } "
105+ def as_str (self ):
106+ """
107+ Derives span from line and coordinates.
108+
109+ start_column: the (inclusive) column where the span starts
110+ end_column: the (inclusive) column where the span ends
111+ """
112+ content_before_span = self .content [: self .start_byte ]
113+ line = content_before_span .count ("\n " ) + 1
114+ start_column = self .start_byte - content_before_span .rfind ("\n " )
115+ end_column = start_column + (self .end_byte - self .start_byte - 1 )
116+
117+ return f"{ line } :{ start_column } -{ end_column } "
107118
108119
109120class Diagnostic :
@@ -134,7 +145,7 @@ def __init__(
134145 self .fix = fix
135146
136147 def __str__ (self ) -> str :
137- return f"{ self .filepath } :" + str ( self .filerange ) + f": { self .summary ()} "
148+ return f"{ self .filepath } :" + self .filerange . as_str ( ) + f": { self .summary ()} "
138149
139150 def summary (self ) -> str :
140151 return (
@@ -228,7 +239,8 @@ def find_best_match(typo):
228239 )
229240
230241 potential_directives = find_potential_directives (content )
231-
242+ # Cache score and best_match to skip recalculating.
243+ score_and_best_match_for_potential_directive = dict ()
232244 for filerange , potential_directive in potential_directives :
233245 # TODO(bchetioui): match count directives more finely. We skip directives
234246 # starting with 'CHECK-COUNT-' for the moment as they require more complex
@@ -244,7 +256,16 @@ def find_best_match(typo):
244256 if len (potential_directive ) > max (map (len , all_directives )) + threshold :
245257 continue
246258
247- score , best_match = find_best_match (potential_directive )
259+ if potential_directive not in score_and_best_match_for_potential_directive :
260+ score , best_match = find_best_match (potential_directive )
261+ score_and_best_match_for_potential_directive [potential_directive ] = (
262+ score ,
263+ best_match ,
264+ )
265+ else :
266+ score , best_match = score_and_best_match_for_potential_directive [
267+ potential_directive
268+ ]
248269 if score == 0 : # This is an actual directive, ignore.
249270 continue
250271 elif score <= threshold and best_match not in _ignore :
0 commit comments