| 745 | feature.class="st">""" |
| 746 | |
| 747 | def lint(self, start=None): |
| 748 | froms = self.froms |
| 749 | if not froms: |
| 750 | return None, None |
| 751 | |
| 752 | edges = set(self.edges) |
| 753 | the_rest = set(froms) |
| 754 | |
| 755 | if start is not None: |
| 756 | start_with = start |
| 757 | the_rest.remove(start_with) |
| 758 | else: |
| 759 | start_with = the_rest.pop() |
| 760 | |
| 761 | stack = collections.deque([start_with]) |
| 762 | |
| 763 | while stack and the_rest: |
| 764 | node = stack.popleft() |
| 765 | the_rest.discard(node) |
| 766 | |
| 767 | class="cm"># comparison of nodes in edges here is based on hash equality, as |
| 768 | class="cm"># there are class="st">"annotated" elements that match the non-annotated ones. |
| 769 | class="cm"># to remove the need for in-python hash() calls, use native |
| 770 | class="cm"># containment routines (e.g. class="st">"node in edge", class="st">"edge.index(node)") |
| 771 | to_remove = {edge for edge in edges if node in edge} |
| 772 | |
| 773 | class="cm"># appendleft the node in each edge that is not |
| 774 | class="cm"># the one that matched. |
| 775 | stack.extendleft(edge[not edge.index(node)] for edge in to_remove) |
| 776 | edges.difference_update(to_remove) |
| 777 | |
| 778 | class="cm"># FROMS left over? boom |
| 779 | if the_rest: |
| 780 | return the_rest, start_with |
| 781 | else: |
| 782 | return None, None |
| 783 | |
| 784 | def warn(self, stmt_type=class="st">"SELECT"): |
| 785 | the_rest, start_with = self.lint() |