5757
5858FUZZ_EXEC_FUNC = re .compile (r'^\[fuzz-exec\] calling (?P<name>\S*)$' )
5959
60+ ANNOTATION_RE = re .compile (r'^\s*\(\@.*' )
61+
6062
6163def indentKindName (match ):
6264 # Return the indent, kind, and name from an ITEM_RE match
@@ -127,6 +129,31 @@ def find_end(module, start):
127129 return end
128130
129131
132+ def find_annotations (module , start ):
133+ # Search backward to find the start of the line containing the first of the
134+ # annotations preceding `start`, if any.
135+ depth = 0
136+ annotation = start
137+ for i in range (start - 1 , - 1 , - 1 ):
138+ if module [i ] == ')' :
139+ depth += 1
140+ elif module [i ] == '(' :
141+ depth -= 1
142+ if depth == 0 :
143+ if module [i + 1 ] == '@' :
144+ # Found the start of a new annotation.
145+ annotation = i
146+ else :
147+ # Found something that isn't an annotation.
148+ break
149+ # Look for the start of the line containin the first annoation.
150+ for i in range (annotation - 1 , - 1 , - 1 ):
151+ if module [i ] == '\n ' :
152+ return i + 1
153+ # The annotation should not have been on the first line of the module
154+ assert False
155+
156+
130157def split_modules (text ):
131158 # Return a list of strings; one for each module
132159 module_starts = [match .start () for match in MODULE_RE .finditer (text )]
@@ -150,8 +177,9 @@ def parse_output_modules(text):
150177 items = []
151178 for match in ITEM_RE .finditer (module ):
152179 _ , kind , name = indentKindName (match )
180+ start = find_annotations (module , match .start ())
153181 end = find_end (module , match .end (1 ))
154- lines = module [match . start () :end ].split ('\n ' )
182+ lines = module [start :end ].split ('\n ' )
155183 items .append (((kind , name ), lines ))
156184 modules .append (items )
157185 return modules
@@ -277,7 +305,12 @@ def pad(line):
277305 # Remove extra newlines at the end of modules
278306 input_modules = [m [:- 1 ] for m in input_modules [:- 1 ]] + [input_modules [- 1 ]]
279307
308+ # Collect annotation lines as we see them so we can put them after any
309+ # checks we generate.
310+ annotation_lines = []
311+
280312 for module_idx in range (len (input_modules )):
313+ assert len (annotation_lines ) == 0
281314 output = command_output [module_idx ] \
282315 if module_idx < len (command_output ) else {}
283316
@@ -286,8 +319,16 @@ def pad(line):
286319 if check_line_re .match (line ):
287320 continue
288321
322+ # Collect annotations to emit later once we know what they should
323+ # attach to.
324+ if ANNOTATION_RE .match (line ):
325+ annotation_lines .append (line )
326+ continue
327+
289328 match = ITEM_RE .match (line )
290329 if not match :
330+ output_lines .extend (annotation_lines )
331+ annotation_lines = []
291332 output_lines .append (line )
292333 continue
293334
@@ -313,6 +354,8 @@ def pad(line):
313354 emit_checks (indent , prefix , lines )
314355 if name and (kind , name ) == kind_name :
315356 break
357+ output_lines .extend (annotation_lines )
358+ annotation_lines = []
316359 output_lines .append (line )
317360
318361 # Output any remaining checks for each prefix
0 commit comments