ControlFlowGraph { public Set<CfgBlock> blocks() { return blocks; } ControlFlowGraph(ImmutableSet<CfgBlock> blocks, CfgBlock start, PhpCfgEndBlock end); @VisibleForTesting static ControlFlowGraph build(BlockTree body); @CheckForNull static ControlFlowGraph build(Tree tree, CheckContext context); CfgBlock start(); CfgBlock end(); Set<CfgBlock> blocks(); static final Set<Tree.Kind> KINDS_WITH_CONTROL_FLOW; }
@Test public void not_remove_empty_with_many_successors() { ControlFlowGraph cfg = cfgForBlock("" + "try {" + " tryBody( succ = [catchBody] );" + " throw $e;" + "} catch (Type $e) {" + " catchBody1( succ = [finallyBody] );" + "}"); for (CfgBlock cfgBlock : cfg.blocks()) { if (cfgBlock.syntacticSuccessor() != null) { assertThat(cfg.blocks()).contains(cfgBlock.syntacticSuccessor()); } } }
@Test public void try_catch() { ControlFlowGraph actualCfg = cfgForBlock("" + "try {" + " tryBody( succ = [catchBody, _empty] );" + "} catch(Exception $e) {" + " catchBody( succ = [_empty] );" + "}" + "after( succ = [END]);"); ExpectedCfgStructure expectedCfgStructure = ExpectedCfgStructure.parse(actualCfg.blocks(), expected -> { expected.createEmptyBlockExpectation() .withPredecessorIds("tryBody", "catchBody") .withSuccessorsIds("after", "END"); return expected; }); new Validator(expectedCfgStructure).assertCfg(actualCfg); }
@Test public void try_return_catch() { ControlFlowGraph actualCfg = cfgForBlock("" + "try {" + " tryBody(succ = [_empty], pred = [], syntSucc = _empty);" + " return;" + "} catch (Exception $e) {" + " catchBody(succ=[_empty], pred = [_empty]);" + "}" + "after( succ = [END], pred = [_empty]);"); ExpectedCfgStructure expectedCfgStructure = ExpectedCfgStructure.parse(actualCfg.blocks(), expected -> { expected.createEmptyBlockExpectation() .withPredecessorIds("tryBody") .withSuccessorsIds("catchBody", "_empty"); expected.createEmptyBlockExpectation() .withPredecessorIds("catchBody", "_empty") .withSuccessorsIds("after", "END"); return expected; }); new Validator(expectedCfgStructure).assertCfg(actualCfg); }
@Test public void try_return_catch_finally() { ControlFlowGraph actualCfg = cfgForBlock("" + "try {" + " tryBody(succ = [_empty], syntSucc = _empty);" + " return;" + "} catch (Exception $e) {" + " catchBody1(succ=[finallyBody], pred = [_empty]);" + "} catch (Exception $e) {" + " catchBody2(succ=[finallyBody], pred = [_empty]);" + "} finally {" + " finallyBody(succ=[after,END], pred = [catchBody1,catchBody2,_empty]);" + "}" + "after(succ = [END], pred = [finallyBody]);"); ExpectedCfgStructure expectedCfgStructure = ExpectedCfgStructure.parse(actualCfg.blocks(), expected -> { expected.createEmptyBlockExpectation() .withPredecessorIds("tryBody") .withSuccessorsIds("catchBody1", "catchBody2", "finallyBody"); return expected; }); new Validator(expectedCfgStructure).assertCfg(actualCfg); }
@Test public void nested_try_return_catch_finally() { ControlFlowGraph actualCfg = cfgForBlock("" + "try { " + " try {" + " tryBody(succ = [_empty], syntSucc = _empty);" + " return;" + " } catch (Exception $e) {" + " catchBody(succ=[finallyBody], pred = [_empty]);" + " } finally {" + " finallyBody(succ=[afterInnerTry, outerFinallyBody], pred = [catchBody,_empty]);" + " }" + " afterInnerTry(succ = [outerFinallyBody], pred = [finallyBody]);" + "} finally {" + " outerFinallyBody(succ=[END], pred = [afterInnerTry, finallyBody]);" + "}"); ExpectedCfgStructure expectedCfgStructure = ExpectedCfgStructure.parse(actualCfg.blocks(), expected -> { expected.createEmptyBlockExpectation() .withPredecessorIds("tryBody") .withSuccessorsIds("catchBody", "finallyBody"); return expected; }); new Validator(expectedCfgStructure).assertCfg(actualCfg); }
@Test public void throw_inside_try_catch_finally() { ControlFlowGraph actualCfg = cfgForBlock("" + "try {" + " tryBody( succ = [catchBody1], syntSucc = dead );" + " throw $e;" + " dead( succ = [catchBody1, catchBody2, finallyBody] );" + "} catch (Type1 $e) {" + " catchBody1( succ = [END], syntSucc = finallyBody );" + " throw $e;" + "} catch (Type2 $e) {" + " catchBody2( succ = [finallyBody] );" + "} finally {" + " finallyBody( succ = [END], syntSucc = _empty );" + " throw $e;" + "}" + "after( succ = [END]);"); ExpectedCfgStructure expectedCfgStructure = ExpectedCfgStructure.parse(actualCfg.blocks(), expected -> { expected.createEmptyBlockExpectation() .withSuccessorsIds("after", "END"); return expected; }); new Validator(expectedCfgStructure).assertCfg(actualCfg); }
@Test public void throw_inside_nested_try_catch_finally() { ControlFlowGraph actualCfg = cfgForBlock("" + "try {" + " try {" + " innerTryBody( succ = [catchBody1], syntSucc = dead );" + " throw $e;" + " dead( succ = [catchBody1, _empty]);" + " } catch (Type $e) {" + " catchBody1( succ = [_empty] );" + " }" + " outerTryBody( succ = [catchBody2, _empty] );" + "} catch (Type $e) {" + " catchBody2( succ = [_empty] );" + "}"); ExpectedCfgStructure expectedCfg = ExpectedCfgStructure.parse(actualCfg.blocks(), expected -> { expected.createEmptyBlockExpectation() .withPredecessorIds("catchBody1", "dead") .withSuccessorsIds("outerTryBody", "_empty"); expected.createEmptyBlockExpectation() .withPredecessorIds("outerTryBody", "catchBody2", "_empty") .withSuccessorsIds("END"); return expected; }); new Validator(expectedCfg).assertCfg(actualCfg); }
@Test public void while_try_break_catch() { ControlFlowGraph actualCfg = cfgForBlock("" + "while (cond(succ=[tryBody, after])) {" + " try {" + " tryBody(succ=[_empty], syntSucc=_empty);" + " break;" + " } catch (Exception $e) {" + " catchBody(succ = [_empty]);" + " }" + " afterTry(succ = [cond]);" + "}" + "after(succ=[END]);"); ExpectedCfgStructure expectedCfgStructure = ExpectedCfgStructure.parse(actualCfg.blocks(), expected -> { expected.createEmptyBlockExpectation() .withPredecessorIds("tryBody") .withSuccessorsIds("catchBody", "_empty"); expected.createEmptyBlockExpectation() .withPredecessorIds("catchBody", "_empty") .withSuccessorsIds("afterTry", "END"); return expected; }); new Validator(expectedCfgStructure).assertCfg(actualCfg); }
@Test public void while_try_continue_catch() { ControlFlowGraph actualCfg = cfgForBlock("" + "while (cond(succ=[tryBody, after])) {" + " try {" + " tryBody(succ=[_empty], syntSucc=_empty);" + " continue;" + " } catch (Exception $e) {" + " catchBody(succ = [_empty]);" + " }" + "}" + "after(succ=[END]);"); ExpectedCfgStructure expectedCfgStructure = ExpectedCfgStructure.parse(actualCfg.blocks(), expected -> { expected.createEmptyBlockExpectation() .withPredecessorIds("tryBody") .withSuccessorsIds("catchBody", "_empty"); expected.createEmptyBlockExpectation() .withPredecessorIds("catchBody", "_empty") .withSuccessorsIds("cond", "END"); return expected; }); new Validator(expectedCfgStructure).assertCfg(actualCfg); }
@Test public void while_try_break_catch_finally() { ControlFlowGraph actualCfg = cfgForBlock("" + "while (cond(succ=[tryBody, after])) {" + " try {" + " tryBody(succ=[_empty], syntSucc=_empty);" + " break;" + " } catch (Exception $e) {" + " catchBody(succ = [finallyBody]);" + " } finally {" + " finallyBody(succ = [cond, END]);" + " }" + "}" + "after(succ=[END]);"); ExpectedCfgStructure expectedCfgStructure = ExpectedCfgStructure.parse(actualCfg.blocks(), expected -> { expected.createEmptyBlockExpectation() .withPredecessorIds("tryBody") .withSuccessorsIds("catchBody", "finallyBody"); return expected; }); new Validator(expectedCfgStructure).assertCfg(actualCfg); }