| 711 | feature.""" |
| 712 | |
| 713 | def lint(self, start=None): |
| 714 | froms = self.froms |
| 715 | if not froms: |
| 716 | return None, None |
| 717 | |
| 718 | edges = set(self.edges) |
| 719 | the_rest = set(froms) |
| 720 | |
| 721 | if start is not None: |
| 722 | start_with = start |
| 723 | the_rest.remove(start_with) |
| 724 | else: |
| 725 | start_with = the_rest.pop() |
| 726 | |
| 727 | stack = collections.deque([start_with]) |
| 728 | |
| 729 | while stack and the_rest: |
| 730 | node = stack.popleft() |
| 731 | the_rest.discard(node) |
| 732 | |
| 733 | # comparison of nodes in edges here is based on hash equality, as |
| 734 | # there are "annotated" elements that match the non-annotated ones. |
| 735 | # to remove the need for in-python hash() calls, use native |
| 736 | # containment routines (e.g. "node in edge", "edge.index(node)") |
| 737 | to_remove = {edge for edge in edges if node in edge} |
| 738 | |
| 739 | # appendleft the node in each edge that is not |
| 740 | # the one that matched. |
| 741 | stack.extendleft(edge[not edge.index(node)] for edge in to_remove) |
| 742 | edges.difference_update(to_remove) |
| 743 | |
| 744 | # FROMS left over? boom |
| 745 | if the_rest: |
| 746 | return the_rest, start_with |
| 747 | else: |
| 748 | return None, None |
| 749 | |
| 750 | def warn(self, stmt_type="SELECT"): |
| 751 | the_rest, start_with = self.lint() |