ControlFlowGraph { @Override public String toString() { StringBuilder sb = new StringBuilder(); Function<Collection<CfgBlock>, List<CfgBlock>> sortedByDisplayString = list -> list.stream() .sorted(Comparator.comparing(Object::toString)) .collect(Collectors.toList()); List<CfgBlock> sortedBlocks = sortedByDisplayString.apply(blocks); int graphNodeId = 0; Map<CfgBlock, Integer> graphNodeIds = new HashMap<>(); for (CfgBlock block : sortedBlocks) { graphNodeIds.put(block, graphNodeId); sb.append(graphNodeId).append("[label=\"").append(block.toString()).append("\"];"); graphNodeId++; } for (CfgBlock block : sortedBlocks) { int id = graphNodeIds.get(block); for (CfgBlock successor : sortedByDisplayString.apply(block.successors())) { sb.append(id).append("->").append(graphNodeIds.get(successor)).append(";"); } if (block.syntacticSuccessor() != null) { sb.append(id).append("->").append(graphNodeIds.get(block.syntacticSuccessor())).append("[style=dotted];"); } } return sb.toString(); } ControlFlowGraph(Set<CfgBlock> blocks, CfgBlock start, CfgBlock end); @CheckForNull static ControlFlowGraph build(FunctionDef functionDef, PythonFile file); @CheckForNull static ControlFlowGraph build(FileInput fileInput, PythonFile file); CfgBlock start(); CfgBlock end(); Set<CfgBlock> blocks(); @Override String toString();  }
@Test public void CFG_toString() { ControlFlowGraph cfg = cfg("" + "if x:", " return 1", "else:", " foo()", " return 2" ); assertThat(cfg.toString()).isEqualTo("" + "0[label=\"2:2:NAME\"];" + "1[label=\"3:6:RETURN_STMT\"];" + "2[label=\"5:6:EXPRESSION_STMT;RETURN_STMT\"];" + "3[label=\"END\"];" + "0->1;0->2;1->3;1->3[style=dotted];2->3;2->3[style=dotted];"); }