Coverage for pysource_minimize/_minimize_structure.py: 86%
442 statements
« prev ^ index » next coverage.py v6.5.0, created at 2024-03-25 08:14 +0100
« prev ^ index » next coverage.py v6.5.0, created at 2024-03-25 08:14 +0100
1import ast
2import sys
4from ._minimize_base import arguments
5from ._minimize_base import coverage_required
6from ._minimize_base import MinimizeBase
7from ._minimize_base import ValueWrapper
10def walk_until(node, stop=()):
11 if isinstance(node, list):
12 for e in node:
13 yield from walk_until(e, stop)
14 return
15 yield node
16 for child in ast.iter_child_nodes(node):
17 if not isinstance(child, stop): 17 ↛ 16line 17 didn't jump to line 16, because the condition on line 17 was never false
18 yield from walk_until(child)
21class MinimizeStructure(MinimizeBase):
22 def minimize(self, o):
23 if (
24 sys.version_info >= (3, 8)
25 and isinstance(o, (ast.expr, ast.stmt))
26 and hasattr(o, "type_comment")
27 ):
28 self.try_attr(o, "type_comment", None)
30 if isinstance(o, ast.expr):
31 return self.minimize_expr(o)
32 elif isinstance(o, ast.stmt):
33 return self.minimize_stmt(o)
34 elif sys.version_info < (3, 9) and isinstance(o, ast.slice):
35 return self.minimize_expr(o)
36 elif isinstance(o, list):
37 return self.minimize_list(o, self.minimize)
38 elif isinstance(o, ast.arg): 38 ↛ 40line 38 didn't jump to line 40, because the condition on line 38 was never false
39 return self.minimize_arg(o)
40 elif isinstance(o, ast.pattern):
41 pass
42 elif isinstance(o, ValueWrapper):
43 pass
44 else:
45 raise TypeError(type(o))
47 def minimize_comprehension(self, comp):
48 self.minimize_expr(comp.iter)
49 self.minimize_list(comp.ifs, terminal=self.minimize_expr)
51 def minimize_arg(self, arg: ast.arg):
52 self.minimize_optional(arg.annotation)
54 def minimize_expr(self, node):
55 if isinstance(node, ast.BoolOp):
56 remaining = self.minimize_list(node.values, self.minimize_expr, 1)
57 if len(remaining) == 1: 57 ↛ exitline 57 didn't return from function 'minimize_expr', because the condition on line 57 was never false
58 self.try_only(node, remaining[0])
60 elif isinstance(node, ast.Compare):
61 if self.try_only(node, node.left): 61 ↛ 62line 61 didn't jump to line 62, because the condition on line 61 was never true
62 self.minimize(node.left)
63 return
65 for comp in node.comparators:
66 if self.try_only(node, comp):
67 self.minimize(comp)
68 return
70 self.minimize_lists( 70 ↛ exitline 70 didn't jump to the function exit
71 (node.ops, node.comparators), (lambda _: None, self.minimize)
72 )
74 elif isinstance(node, ast.Subscript):
75 self.try_only_minimize(node, node.value, node.slice)
77 elif isinstance(node, ast.FormattedValue):
78 if isinstance(node.format_spec, ast.JoinedStr):
79 # work around for https://github.com/python/cpython/issues/110309
80 spec = [
81 v
82 for v in node.format_spec.values
83 if not (isinstance(v, ast.Constant) and v.value == "")
84 ]
85 if len(spec) == 1 and self.try_only(node, spec[0]): 85 ↛ 86line 85 didn't jump to line 86, because the condition on line 85 was never true
86 self.minimize(spec[0])
87 return
89 if not self.try_none(node.format_spec):
90 self.minimize(node.format_spec)
92 self.minimize_expr(node.value)
94 elif isinstance(node, ast.JoinedStr):
95 for v in node.values:
96 if isinstance(v, ast.FormattedValue) and self.try_only(node, v.value):
97 self.minimize(v.value)
98 return
99 if (
100 isinstance(v, ast.FormattedValue)
101 and v.format_spec
102 and self.try_only(node, v.format_spec)
103 ):
104 self.minimize(v.format_spec)
105 return
107 self.minimize(node.values)
108 # todo minimize values
110 elif isinstance(node, ast.Slice):
111 if self.try_only(node, node.lower, node.upper, node.step):
112 return
114 for child in (node.lower, node.upper, node.step): 114 ↛ exitline 114 didn't return from function 'minimize_expr', because the loop on line 114 didn't complete
115 if not self.try_none(child): 115 ↛ 116line 115 didn't jump to line 116, because the condition on line 115 was never true
116 self.minimize(child)
118 elif isinstance(node, ast.ExtSlice): 118 ↛ 119line 118 didn't jump to line 119, because the condition on line 118 was never true
119 self.minimize_list(node.dims, minimal=1)
121 elif isinstance(node, ast.Index):
122 self.minimize(node.value)
124 elif isinstance(node, ast.Lambda):
125 if self.try_only_minimize(node, node.body):
126 return
128 if self.minimize_args_of(node):
129 return
131 elif isinstance(node, ast.UnaryOp):
132 self.try_only_minimize(node, node.operand)
133 elif isinstance(node, ast.BinOp):
134 self.try_only_minimize(node, node.left, node.right)
135 elif isinstance(node, ast.Attribute):
136 if not self.try_node(node, ast.Name(id="something", ctx=ast.Load())):
137 self.try_only_minimize(node, node.value)
138 elif isinstance(node, ast.IfExp):
139 self.try_only_minimize(node, node.test, node.body, node.orelse)
140 elif isinstance(node, ast.Await):
141 self.try_only_minimize(node, node.value)
142 elif isinstance(node, ast.Yield):
143 if node.value is None:
144 if self.try_node(node, ast.Constant(value=None)): 144 ↛ 146line 144 didn't jump to line 146, because the condition on line 144 was never false
145 return
146 self.try_only_minimize(node, node.value)
147 elif isinstance(node, ast.YieldFrom):
148 self.try_only_minimize(node, node.value)
149 elif isinstance(node, ast.Dict):
150 remaining = self.minimize_lists(
151 (node.keys, node.values), (self.minimize, self.minimize)
152 )
153 if len(remaining) == 1:
154 if self.try_only(node, remaining[0][0]):
155 return
157 if self.try_only(node, remaining[0][1]): 157 ↛ exitline 157 didn't return from function 'minimize_expr', because the condition on line 157 was never false
158 return
160 elif isinstance(node, (ast.Set)):
161 remaining = self.minimize_list(node.elts, self.minimize, 1)
162 # TODO: min size 1?
163 if len(remaining) == 1: 163 ↛ exitline 163 didn't return from function 'minimize_expr', because the condition on line 163 was never false
164 self.try_only(node, remaining[0])
165 elif isinstance(node, (ast.List, ast.Tuple)):
166 remaining = self.minimize_list(node.elts, self.minimize, 1)
167 # TODO: min size 1?
168 if len(remaining) == 1: 168 ↛ exitline 168 didn't return from function 'minimize_expr', because the condition on line 168 was never false
169 self.try_only(node, remaining[0])
170 elif isinstance(node, ast.Name):
171 pass
172 elif isinstance(node, ast.Constant):
173 pass
174 elif isinstance(node, ast.Index): 174 ↛ 175line 174 didn't jump to line 175, because the condition on line 174 was never true
175 self.minimize(node.value)
176 elif sys.version_info < (3, 8) and isinstance( 176 ↛ 179line 176 didn't jump to line 179, because the condition on line 176 was never true
177 node, (ast.Str, ast.Bytes, ast.Num, ast.NameConstant, ast.Ellipsis)
178 ):
179 pass
180 elif isinstance(node, ast.Starred):
181 self.try_only_minimize(node, node.value)
182 elif isinstance(node, ast.Call):
183 for e in [
184 node.func,
185 *[kw.value for kw in node.keywords],
186 *[
187 arg.value if isinstance(arg, ast.Starred) else arg
188 for arg in node.args
189 ],
190 ]:
191 if self.try_only(node, e):
192 self.minimize(e)
193 return
195 self.minimize(node.args)
196 self.minimize_list( 196 ↛ exitline 196 didn't jump to the function exit
197 node.keywords, terminal=lambda kw: self.minimize(kw.value)
198 )
200 elif isinstance(
201 node, (ast.ListComp, ast.SetComp, ast.GeneratorExp, ast.DictComp)
202 ):
203 for gen in node.generators:
204 if self.try_only(node, gen.target):
205 self.minimize_expr(gen.target)
206 return
208 if self.try_only(node, gen.iter):
209 self.minimize_expr(gen.iter)
210 return
212 for if_ in gen.ifs:
213 if self.try_only(node, if_):
214 self.minimize_expr(if_)
215 return
217 if isinstance(node, ast.DictComp):
218 if self.try_only_minimize(node, node.key, node.value): 218 ↛ 224line 218 didn't jump to line 224, because the condition on line 218 was never false
219 return
220 else:
221 if self.try_only_minimize(node, node.elt):
222 return
224 self.minimize_list(node.generators, self.minimize_comprehension, 1)
226 elif isinstance(node, ast.NamedExpr):
227 self.try_only_minimize(node, node.target, node.value)
228 else:
229 assert False, "expression is not handled %s" % (node)
231 def minimize_optional(self, node):
232 if not self.try_none(node):
233 self.minimize(node)
235 if sys.version_info >= (3, 10):
237 def minimize_match_case(self, c: ast.match_case):
238 def minimize_pattern(pattern):
239 if isinstance(pattern, ast.MatchSequence): 239 ↛ 240line 239 didn't jump to line 240, because the condition on line 239 was never true
240 self.minimize_list(pattern.patterns, minimize_pattern)
241 elif isinstance(pattern, ast.MatchOr): 241 ↛ 242line 241 didn't jump to line 242, because the condition on line 241 was never true
242 self.minimize_list(pattern.patterns, minimize_pattern, 1)
244 elif isinstance(pattern, ast.MatchAs): 244 ↛ 245line 244 didn't jump to line 245, because the condition on line 244 was never true
245 if pattern.pattern:
246 self.try_only(pattern, pattern.pattern)
247 elif isinstance(pattern, ast.MatchMapping): 247 ↛ 248line 247 didn't jump to line 248, because the condition on line 247 was never true
248 self.minimize_lists(
249 (pattern.keys, pattern.patterns),
250 (self.minimize, minimize_pattern),
251 )
252 elif isinstance(pattern, ast.MatchClass):
253 self.minimize(pattern.cls)
254 self.minimize_list(pattern.patterns, minimize_pattern)
255 self.minimize_lists((pattern.kwd_attrs, pattern.kwd_patterns))
257 self.minimize(c.body)
259 if not self.try_none(c.guard): 259 ↛ 260line 259 didn't jump to line 260, because the condition on line 259 was never true
260 self.minimize(c.guard)
262 minimize_pattern(c.pattern)
264 def minimize_args_of(self, func):
265 args = func.args
267 for child in [
268 *[arg.annotation for arg in arguments(func)],
269 *func.args.defaults,
270 *func.args.kw_defaults,
271 getattr(func, "returns", None),
272 ]:
273 if child != None and self.try_only(func, child):
274 self.minimize(child)
275 return True
277 all_args = []
278 if sys.version_info >= (3, 8): 278 ↛ 280line 278 didn't jump to line 280, because the condition on line 278 was never false
279 all_args += args.posonlyargs
280 all_args += args.args
282 split = len(all_args) - len(args.defaults)
283 self.minimize_list(all_args[:split])
284 remaining = self.minimize_lists(
285 (all_args[split:], args.defaults), (self.minimize, lambda a: None)
286 )
288 remove_defaults = True
289 for _, default in remaining:
290 if remove_defaults: 290 ↛ 296line 290 didn't jump to line 296, because the condition on line 290 was never false
291 if default is not None: 291 ↛ 289line 291 didn't jump to line 289, because the condition on line 291 was never false
292 if not self.try_without([default]): 292 ↛ 289, 292 ↛ 2932 missed branches: 1) line 292 didn't jump to line 289, because the condition on line 292 was never false, 2) line 292 didn't jump to line 293, because the condition on line 292 was never true
293 self.minimize(default)
294 remove_defaults = False
295 else:
296 self.minimize(default)
298 remaining = self.minimize_lists(
299 (args.kwonlyargs, args.kw_defaults), (self.minimize, lambda a: None)
300 )
301 for _, default in remaining:
302 self.minimize_optional(default)
304 self.minimize_optional(args.vararg)
305 self.minimize_optional(args.kwarg)
307 return False
309 def minimize_stmt(self, node):
310 if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
311 if self.try_only_minimize(node, node.decorator_list): 311 ↛ 312line 311 didn't jump to line 312, because the condition on line 311 was never true
312 return
314 self.minimize_list(node.body)
315 body = self.get_ast(node).body
317 if not any( 317 ↛ 342line 317 didn't jump to line 342, because the condition on line 317 was never false
318 isinstance(
319 n,
320 (
321 ast.Return,
322 ast.Yield,
323 ast.YieldFrom,
324 ast.Await,
325 ast.AsyncFor,
326 ast.AsyncWith,
327 ),
328 )
329 for n in walk_until(
330 body,
331 (
332 ast.GeneratorExp,
333 ast.FunctionDef,
334 ast.ClassDef,
335 ast.AsyncFunctionDef,
336 ),
337 )
338 ):
339 if self.try_only(node, node.body):
340 return
342 if self.minimize_args_of(node):
343 return
345 if sys.version_info >= (3, 12): 345 ↛ 356line 345 didn't jump to line 356, because the condition on line 345 was never false
346 for p in node.type_params: 346 ↛ 354line 346 didn't jump to line 354, because the loop on line 346 didn't complete
347 if ( 347 ↛ 346line 347 didn't jump to line 346
348 isinstance(p, ast.TypeVar)
349 and p.bound is not None
350 and self.try_only(node, p.bound)
351 ):
352 self.minimize(p.bound)
353 return
354 self.minimize_list(node.type_params, self.minimize_type_param)
356 if node.returns:
357 if not self.try_none(node.returns):
358 self.minimize_expr(node.returns)
360 elif isinstance(node, ast.ClassDef):
361 if self.try_only_minimize(node, node.decorator_list): 361 ↛ 362line 361 didn't jump to line 362, because the condition on line 361 was never true
362 return
364 if self.try_only_minimize(node, node.body): 364 ↛ 365line 364 didn't jump to line 365, because the condition on line 364 was never true
365 return
367 if sys.version_info >= (3, 12):
368 for p in node.type_params:
369 if ( 369 ↛ 368line 369 didn't jump to line 368
370 isinstance(p, ast.TypeVar)
371 and p.bound is not None
372 and self.try_only(node, p.bound)
373 ):
374 self.minimize(p.bound)
375 return
376 self.minimize_list(node.type_params, self.minimize_type_param)
378 for e in [
379 *[kw.value for kw in node.keywords],
380 *[
381 arg.value if isinstance(arg, ast.Starred) else arg
382 for arg in node.bases
383 ],
384 ]:
385 if self.try_only(node, e):
386 self.minimize(e)
387 return
389 self.minimize(node.bases)
390 self.minimize_list(
391 node.keywords, terminal=lambda kw: self.minimize(kw.value)
392 )
393 return
395 elif isinstance(node, ast.Return):
396 self.try_only_minimize(node, node.value)
398 elif isinstance(node, ast.Delete):
399 for t in node.targets: 399 ↛ 404line 399 didn't jump to line 404, because the loop on line 399 didn't complete
400 if self.try_only(node, t):
401 self.minimize(t)
402 return
404 self.minimize_list(node.targets, self.minimize, 1)
406 elif isinstance(node, ast.Assign):
407 self.try_only_minimize(node, node.value, node.targets)
409 elif isinstance(node, ast.AugAssign):
410 self.try_only_minimize(node, node.target, node.value)
412 elif isinstance(node, ast.AnnAssign):
413 for child in [node.target, node.value, node.annotation]:
414 if child is not None and self.try_only(node, child):
415 self.minimize(child)
416 return
418 if not self.try_node(
419 node,
420 ast.Assign(
421 targets=[node.target],
422 value=node.value,
423 **(dict(type_comment="") if sys.version_info >= (3, 8) else {}),
424 ),
425 ):
426 self.minimize(node.target)
427 self.minimize_optional(node.value)
428 self.minimize(node.annotation)
430 elif isinstance(node, (ast.For, ast.AsyncFor)):
431 if self.try_only(node, node.target):
432 self.minimize(node.target)
433 return
435 self.minimize_list(node.body)
436 body = self.get_ast(node)
437 if not any( 437 ↛ 443line 437 didn't jump to line 443, because the condition on line 437 was never false
438 isinstance(n, (ast.Break, ast.Continue)) for n in ast.walk(body)
439 ):
440 if self.try_only(node, node.body):
441 return
443 self.try_only_minimize(node, node.iter, node.orelse)
444 self.minimize(node.target)
446 elif isinstance(node, ast.While):
447 self.minimize_list(node.body)
448 body = self.get_ast(node)
449 if not any( 449 ↛ 455line 449 didn't jump to line 455, because the condition on line 449 was never false
450 isinstance(n, (ast.Break, ast.Continue)) for n in ast.walk(body)
451 ):
452 if self.try_only(node, node.body):
453 return
455 self.try_only_minimize(node, node.test, node.orelse)
457 elif isinstance(node, (ast.Break, ast.Continue)): 457 ↛ 458line 457 didn't jump to line 458, because the condition on line 457 was never true
458 pass
460 elif isinstance(node, ast.If):
461 self.try_only_minimize(node, node.test, node.body, node.orelse)
463 elif isinstance(node, (ast.With, ast.AsyncWith)):
464 if self.try_only_minimize(node, node.body):
465 return
467 for item in node.items:
468 if self.try_only(node, item.context_expr):
469 self.minimize(item.context_expr)
470 return
472 if item.optional_vars is not None and self.try_only(
473 node, item.optional_vars
474 ):
475 self.minimize(item.optional_vars)
476 return
478 def minimize_item(item: ast.withitem):
479 self.minimize(item.context_expr)
480 self.minimize_optional(item.optional_vars)
482 self.minimize_list(node.items, minimize_item, minimal=1)
484 elif sys.version_info >= (3, 10) and isinstance(node, ast.Match):
485 if self.try_only_minimize(node, node.subject):
486 return
488 for case_ in node.cases:
489 for e in [case_.guard, case_.body]:
490 if e is not None and self.try_only(node, e):
491 self.minimize(e)
492 return
494 if isinstance(case_.pattern, ast.MatchValue):
495 if self.try_only(node, case_.pattern.value):
496 self.minimize(case_.pattern.value)
497 return
499 self.minimize_list(node.cases, self.minimize_match_case, 1)
501 elif isinstance(node, ast.Raise):
502 if node.exc and self.try_only(node, node.exc):
503 self.minimize(node.exc)
504 return
506 if node.cause and not self.try_only(node, node.cause):
507 self.minimize_optional(node.cause)
508 # cause requires exc
509 # `raise from cause` is not valid
510 if self.get_ast(node).cause: 510 ↛ 513line 510 didn't jump to line 513, because the condition on line 510 was never false
511 self.minimize(node.exc)
512 else:
513 coverage_required()
514 self.minimize_optional(node.exc)
516 elif isinstance(node, ast.Try) or (
517 sys.version_info >= (3, 11) and isinstance(node, ast.TryStar)
518 ):
519 try_star = sys.version_info >= (3, 11) and isinstance(node, ast.TryStar)
521 if self.try_only(node, node.body):
522 self.minimize(node.body)
523 return
525 if node.orelse and self.try_only(node, node.orelse):
526 self.minimize(node.orelse)
527 return
529 if node.finalbody and self.try_only(node, node.finalbody):
530 self.minimize(node.finalbody)
531 return
533 for h in node.handlers:
534 if self.try_only(node, h.body):
535 self.minimize(h.body)
536 return
537 if h.type is not None and self.try_only(node, h.type):
538 self.minimize(h.type)
539 return
541 def minimize_except_handler(handler):
542 self.minimize_list(handler.body)
544 if not handler.name and not try_star:
545 self.minimize_optional(handler.type)
546 elif handler.type is not None: 546 ↛ 549line 546 didn't jump to line 549, because the condition on line 546 was never false
547 self.minimize(handler.type)
549 if handler.name:
550 self.try_attr(handler, "name", None)
552 self.minimize_list(
553 node.handlers, minimize_except_handler, 1 # 0 if node.finalbody else 1
554 )
556 self.minimize(node.body)
557 self.minimize(node.orelse)
558 self.minimize(node.finalbody)
560 # if try_star and self.try_node(
561 # node,
562 # ast.Try(
563 # body=node.body,
564 # handlers=node.handlers,
565 # orelse=node.orelse,
566 # finalbody=node.finalbody,
567 # ),
568 # ):
569 # return
571 elif isinstance(node, ast.Assert):
572 if node.msg:
573 if self.try_only(node, node.msg):
574 self.minimize(node.msg)
575 return
577 if not self.try_none(node.msg): 577 ↛ 580line 577 didn't jump to line 580, because the condition on line 577 was never false
578 self.minimize(node.msg)
580 if self.try_only_minimize(node, node.test): 580 ↛ 581line 580 didn't jump to line 581, because the condition on line 580 was never true
581 return
583 elif isinstance(node, (ast.Import, ast.ImportFrom)): 583 ↛ 584line 583 didn't jump to line 584, because the condition on line 583 was never true
584 self.minimize_list(node.names, lambda e: None, 1)
586 elif isinstance(node, (ast.Global, ast.Nonlocal)):
587 self.minimize_list(node.names)
589 elif isinstance(node, ast.Expr):
590 self.minimize_expr(node.value)
592 elif isinstance(node, ast.Module):
593 self.minimize(node.body)
594 if sys.version_info >= (3, 8): 594 ↛ exitline 594 didn't return from function 'minimize_stmt', because the condition on line 594 was never false
595 self.minimize_list(node.type_ignores, lambda e: None) 595 ↛ exitline 595 didn't run the lambda on line 595
596 elif sys.version_info >= (3, 12) and isinstance(node, ast.TypeAlias): 596 ↛ 609line 596 didn't jump to line 609, because the condition on line 596 was never false
597 for p in node.type_params:
598 if ( 598 ↛ 597line 598 didn't jump to line 597
599 isinstance(p, ast.TypeVar)
600 and p.bound is not None
601 and self.try_only(node, p.bound)
602 ):
603 self.minimize(p.bound)
604 return
605 if self.try_only_minimize(node, node.name, node.value): 605 ↛ 607line 605 didn't jump to line 607, because the condition on line 605 was never false
606 return
607 self.minimize_list(node.type_params, self.minimize_type_param)
609 elif isinstance(node, ast.Pass):
610 pass
611 else:
612 raise TypeError(node) # Stmt
614 def minimize_type_param(self, node):
615 assert sys.version_info >= (3, 12)
616 if isinstance(node, ast.TypeVar):
617 self.minimize_optional(node.bound)
619 def minimize_lists(self, lists, terminals=None, minimal=0):
620 if terminals is None:
621 terminals = [self.minimize for _ in lists]
623 lists = list(zip(*lists))
624 max_remove = len(lists) - minimal
626 import itertools
628 def try_without(l):
629 return self.try_without(itertools.chain.from_iterable(l))
631 def wo(l):
632 nonlocal max_remove
634 if max_remove < len(l) or not try_without(l):
635 devide(l)
636 else:
637 max_remove -= len(l)
639 def devide(l):
640 nonlocal max_remove, remaining
641 if not l:
642 return
644 if len(l) == 1:
645 if max_remove >= 1 and try_without(l): 645 ↛ 646line 645 didn't jump to line 646, because the condition on line 645 was never true
646 max_remove -= 1
647 else:
648 remaining.append(l[0])
649 else:
650 mid = len(l) // 2
652 # remove in reverse order
653 # this is a good heuristic, because it removes the usage before the definition
654 wo(l[mid:])
655 wo(l[:mid])
657 remaining = []
658 devide(lists)
660 for nodes in remaining:
661 for terminal, node in zip(terminals, nodes):
662 terminal(node)
664 return remaining
666 def minimize_list(self, stmts, terminal=None, minimal=0):
667 if terminal is None:
668 terminal = self.minimize
670 # result= self.minimize_lists((stmts,),(terminal,),minimal=0)
671 # return [e[0] for e in result]
673 stmts = list(stmts)
674 max_remove = len(stmts) - minimal
676 def wo(l):
677 nonlocal max_remove
679 if max_remove < len(l) or not self.try_without(l):
680 devide(l)
681 else:
682 max_remove -= len(l)
684 def devide(l):
685 nonlocal max_remove, remaining
686 if not l:
687 return
689 if len(l) == 1:
690 if max_remove >= 1 and self.try_without(l):
691 max_remove -= 1
692 else:
693 remaining.append(l[0])
694 else:
695 mid = len(l) // 2
697 # remove in reverse order
698 # this is a good heuristic, because it removes the usage before the definition
699 wo(l[mid:])
700 wo(l[:mid])
702 remaining = []
703 devide(stmts)
705 for node in remaining:
706 terminal(node)
708 return remaining