407 if byte_incr: |
407 if byte_incr: |
408 if line_num != last_line_num: |
408 if line_num != last_line_num: |
409 yield (byte_num, line_num) |
409 yield (byte_num, line_num) |
410 last_line_num = line_num |
410 last_line_num = line_num |
411 byte_num += byte_incr |
411 byte_num += byte_incr |
|
412 if env.PYVERSION >= (3, 6) and line_incr >= 0x80: |
|
413 line_incr -= 0x100 |
412 line_num += line_incr |
414 line_num += line_incr |
413 if line_num != last_line_num: |
415 if line_num != last_line_num: |
414 yield (byte_num, line_num) |
416 yield (byte_num, line_num) |
415 |
417 |
416 def _find_statements(self): |
418 def _find_statements(self): |
501 def __init__(self, body): |
503 def __init__(self, body): |
502 self.body = body |
504 self.body = body |
503 self.lineno = body[0].lineno |
505 self.lineno = body[0].lineno |
504 |
506 |
505 |
507 |
|
508 # TODO: some add_arcs methods here don't add arcs, they return them. Rename them. |
|
509 # TODO: the cause messages have too many commas. |
|
510 # TODO: Shouldn't the cause messages join with "and" instead of "or"? |
|
511 |
506 class AstArcAnalyzer(object): |
512 class AstArcAnalyzer(object): |
507 """Analyze source text with an AST to find executable code paths.""" |
513 """Analyze source text with an AST to find executable code paths.""" |
508 |
514 |
509 @contract(text='unicode', statements=set) |
515 @contract(text='unicode', statements=set) |
510 def __init__(self, text, statements, multiline): |
516 def __init__(self, text, statements, multiline): |
542 node_name = node.__class__.__name__ |
548 node_name = node.__class__.__name__ |
543 code_object_handler = getattr(self, "_code_object__" + node_name, None) |
549 code_object_handler = getattr(self, "_code_object__" + node_name, None) |
544 if code_object_handler is not None: |
550 if code_object_handler is not None: |
545 code_object_handler(node) |
551 code_object_handler(node) |
546 |
552 |
|
553 @contract(start=int, end=int) |
547 def add_arc(self, start, end, smsg=None, emsg=None): |
554 def add_arc(self, start, end, smsg=None, emsg=None): |
548 """Add an arc, including message fragments to use if it is missing.""" |
555 """Add an arc, including message fragments to use if it is missing.""" |
549 if self.debug: # pragma: debugging |
556 if self.debug: # pragma: debugging |
550 print("\nAdding arc: ({}, {}): {!r}, {!r}".format(start, end, smsg, emsg)) |
557 print("\nAdding arc: ({}, {}): {!r}, {!r}".format(start, end, smsg, emsg)) |
551 print(short_stack(limit=6)) |
558 print(short_stack(limit=6)) |
570 if handler is not None: |
577 if handler is not None: |
571 return handler(node) |
578 return handler(node) |
572 else: |
579 else: |
573 return node.lineno |
580 return node.lineno |
574 |
581 |
|
582 def _line_decorated(self, node): |
|
583 """Compute first line number for things that can be decorated (classes and functions).""" |
|
584 lineno = node.lineno |
|
585 if env.PYBEHAVIOR.trace_decorated_def: |
|
586 if node.decorator_list: |
|
587 lineno = node.decorator_list[0].lineno |
|
588 return lineno |
|
589 |
575 def _line__Assign(self, node): |
590 def _line__Assign(self, node): |
576 return self.line_for_node(node.value) |
591 return self.line_for_node(node.value) |
|
592 |
|
593 _line__ClassDef = _line_decorated |
577 |
594 |
578 def _line__Dict(self, node): |
595 def _line__Dict(self, node): |
579 # Python 3.5 changed how dict literals are made. |
596 # Python 3.5 changed how dict literals are made. |
580 if env.PYVERSION >= (3, 5) and node.keys: |
597 if env.PYVERSION >= (3, 5) and node.keys: |
581 if node.keys[0] is not None: |
598 if node.keys[0] is not None: |
584 # Unpacked dict literals `{**{'a':1}}` have None as the key, |
601 # Unpacked dict literals `{**{'a':1}}` have None as the key, |
585 # use the value in that case. |
602 # use the value in that case. |
586 return node.values[0].lineno |
603 return node.values[0].lineno |
587 else: |
604 else: |
588 return node.lineno |
605 return node.lineno |
|
606 |
|
607 _line__FunctionDef = _line_decorated |
589 |
608 |
590 def _line__List(self, node): |
609 def _line__List(self, node): |
591 if node.elts: |
610 if node.elts: |
592 return self.line_for_node(node.elts[0]) |
611 return self.line_for_node(node.elts[0]) |
593 else: |
612 else: |
688 node = missing_fn(node) |
707 node = missing_fn(node) |
689 else: |
708 else: |
690 node = None |
709 node = None |
691 return node |
710 return node |
692 |
711 |
|
712 # Missing nodes: _missing__* |
|
713 # |
|
714 # Entire statements can be optimized away by Python. They will appear in |
|
715 # the AST, but not the bytecode. These functions are called (by |
|
716 # find_non_missing_node) to find a node to use instead of the missing |
|
717 # node. They can return None if the node should truly be gone. |
|
718 |
693 def _missing__If(self, node): |
719 def _missing__If(self, node): |
694 # If the if-node is missing, then one of its children might still be |
720 # If the if-node is missing, then one of its children might still be |
695 # here, but not both. So return the first of the two that isn't missing. |
721 # here, but not both. So return the first of the two that isn't missing. |
696 # Use a NodeList to hold the clauses as a single node. |
722 # Use a NodeList to hold the clauses as a single node. |
697 non_missing = self.find_non_missing_node(NodeList(node.body)) |
723 non_missing = self.find_non_missing_node(NodeList(node.body)) |
715 return None |
741 return None |
716 if len(non_missing_children) == 1: |
742 if len(non_missing_children) == 1: |
717 return non_missing_children[0] |
743 return non_missing_children[0] |
718 return NodeList(non_missing_children) |
744 return NodeList(non_missing_children) |
719 |
745 |
|
746 def _missing__While(self, node): |
|
747 body_nodes = self.find_non_missing_node(NodeList(node.body)) |
|
748 if not body_nodes: |
|
749 return None |
|
750 # Make a synthetic While-true node. |
|
751 new_while = ast.While() |
|
752 new_while.lineno = body_nodes.lineno |
|
753 new_while.test = ast.Name() |
|
754 new_while.test.lineno = body_nodes.lineno |
|
755 new_while.test.id = "True" |
|
756 new_while.body = body_nodes.body |
|
757 new_while.orelse = None |
|
758 return new_while |
|
759 |
720 def is_constant_expr(self, node): |
760 def is_constant_expr(self, node): |
721 """Is this a compile-time constant?""" |
761 """Is this a compile-time constant?""" |
722 node_name = node.__class__.__name__ |
762 node_name = node.__class__.__name__ |
723 if node_name in ["NameConstant", "Num"]: |
763 if node_name in ["Constant", "NameConstant", "Num"]: |
724 return "Num" |
764 return "Num" |
725 elif node_name == "Name": |
765 elif node_name == "Name": |
726 if node.id in ["True", "False", "None", "__debug__"]: |
766 if node.id in ["True", "False", "None", "__debug__"]: |
727 return "Name" |
767 return "Name" |
728 return None |
768 return None |
803 |
843 |
804 |
844 |
805 # Handlers: _handle__* |
845 # Handlers: _handle__* |
806 # |
846 # |
807 # Each handler deals with a specific AST node type, dispatched from |
847 # Each handler deals with a specific AST node type, dispatched from |
808 # add_arcs. Each deals with a particular kind of node type, and returns |
848 # add_arcs. Handlers return the set of exits from that node, and can |
809 # the set of exits from that node. These functions mirror the Python |
849 # also call self.add_arc to record arcs they find. These functions mirror |
810 # semantics of each syntactic construct. See the docstring for add_arcs to |
850 # the Python semantics of each syntactic construct. See the docstring |
811 # understand the concept of exits from a node. |
851 # for add_arcs to understand the concept of exits from a node. |
812 |
852 |
813 @contract(returns='ArcStarts') |
853 @contract(returns='ArcStarts') |
814 def _handle__Break(self, node): |
854 def _handle__Break(self, node): |
815 here = self.line_for_node(node) |
855 here = self.line_for_node(node) |
816 break_start = ArcStart(here, cause="the break on line {lineno} wasn't executed") |
856 break_start = ArcStart(here, cause="the break on line {lineno} wasn't executed") |
818 return set() |
858 return set() |
819 |
859 |
820 @contract(returns='ArcStarts') |
860 @contract(returns='ArcStarts') |
821 def _handle_decorated(self, node): |
861 def _handle_decorated(self, node): |
822 """Add arcs for things that can be decorated (classes and functions).""" |
862 """Add arcs for things that can be decorated (classes and functions).""" |
823 last = self.line_for_node(node) |
863 main_line = last = node.lineno |
824 if node.decorator_list: |
864 if node.decorator_list: |
|
865 if env.PYBEHAVIOR.trace_decorated_def: |
|
866 last = None |
825 for dec_node in node.decorator_list: |
867 for dec_node in node.decorator_list: |
826 dec_start = self.line_for_node(dec_node) |
868 dec_start = self.line_for_node(dec_node) |
827 if dec_start != last: |
869 if last is not None and dec_start != last: |
828 self.add_arc(last, dec_start) |
870 self.add_arc(last, dec_start) |
829 last = dec_start |
871 last = dec_start |
|
872 if env.PYBEHAVIOR.trace_decorated_def: |
|
873 self.add_arc(last, main_line) |
|
874 last = main_line |
830 # The definition line may have been missed, but we should have it |
875 # The definition line may have been missed, but we should have it |
831 # in `self.statements`. For some constructs, `line_for_node` is |
876 # in `self.statements`. For some constructs, `line_for_node` is |
832 # not what we'd think of as the first line in the statement, so map |
877 # not what we'd think of as the first line in the statement, so map |
833 # it to the first one. |
878 # it to the first one. |
834 if node.body: |
879 if node.body: |
966 ) |
1011 ) |
967 |
1012 |
968 final_exits = self.add_body_arcs(node.finalbody, prev_starts=final_from) |
1013 final_exits = self.add_body_arcs(node.finalbody, prev_starts=final_from) |
969 |
1014 |
970 if try_block.break_from: |
1015 if try_block.break_from: |
971 self.process_break_exits( |
1016 if env.PYBEHAVIOR.finally_jumps_back: |
972 self._combine_finally_starts(try_block.break_from, final_exits) |
1017 for break_line in try_block.break_from: |
973 ) |
1018 lineno = break_line.lineno |
|
1019 cause = break_line.cause.format(lineno=lineno) |
|
1020 for final_exit in final_exits: |
|
1021 self.add_arc(final_exit.lineno, lineno, cause) |
|
1022 breaks = try_block.break_from |
|
1023 else: |
|
1024 breaks = self._combine_finally_starts(try_block.break_from, final_exits) |
|
1025 self.process_break_exits(breaks) |
|
1026 |
974 if try_block.continue_from: |
1027 if try_block.continue_from: |
975 self.process_continue_exits( |
1028 if env.PYBEHAVIOR.finally_jumps_back: |
976 self._combine_finally_starts(try_block.continue_from, final_exits) |
1029 for continue_line in try_block.continue_from: |
977 ) |
1030 lineno = continue_line.lineno |
|
1031 cause = continue_line.cause.format(lineno=lineno) |
|
1032 for final_exit in final_exits: |
|
1033 self.add_arc(final_exit.lineno, lineno, cause) |
|
1034 continues = try_block.continue_from |
|
1035 else: |
|
1036 continues = self._combine_finally_starts(try_block.continue_from, final_exits) |
|
1037 self.process_continue_exits(continues) |
|
1038 |
978 if try_block.raise_from: |
1039 if try_block.raise_from: |
979 self.process_raise_exits( |
1040 self.process_raise_exits( |
980 self._combine_finally_starts(try_block.raise_from, final_exits) |
1041 self._combine_finally_starts(try_block.raise_from, final_exits) |
981 ) |
1042 ) |
|
1043 |
982 if try_block.return_from: |
1044 if try_block.return_from: |
983 self.process_return_exits( |
1045 if env.PYBEHAVIOR.finally_jumps_back: |
984 self._combine_finally_starts(try_block.return_from, final_exits) |
1046 for return_line in try_block.return_from: |
985 ) |
1047 lineno = return_line.lineno |
|
1048 cause = return_line.cause.format(lineno=lineno) |
|
1049 for final_exit in final_exits: |
|
1050 self.add_arc(final_exit.lineno, lineno, cause) |
|
1051 returns = try_block.return_from |
|
1052 else: |
|
1053 returns = self._combine_finally_starts(try_block.return_from, final_exits) |
|
1054 self.process_return_exits(returns) |
986 |
1055 |
987 if exits: |
1056 if exits: |
988 # The finally clause's exits are only exits for the try block |
1057 # The finally clause's exits are only exits for the try block |
989 # as a whole if the try block had some exits to begin with. |
1058 # as a whole if the try block had some exits to begin with. |
990 exits = final_exits |
1059 exits = final_exits |