def test_cfg_return_statement_inside_branch(): fdump = f'stdin.dump.json' checker_warnings, rc = check_program( """ FUNCTION test_return_nested : INT VAR i : INT; a : INT; END_VAR IF i = 0 THEN a := a + 1; RETURN; a := a + 2; END_IF; a := a + 3; END_FUNCTION """.replace('\n', '')) assert rc == 0 with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme assert len(scheme.functions) == 1 assert len(scheme.cfgs) == 1 cfg = scheme.cfgs[0] bbs = cfg.basic_blocks assert len(bbs) == 4 # if # i = 0 assert bbs[0].id == 0 assert bbs[0].type == "BBEntry" assert bbs[0].preds == set() assert bbs[0].succs == {1, 3} # a := a + 1 # RETURN assert bbs[1].id == 1 assert bbs[1].type == "BBExit" assert bbs[1].preds == {0} assert bbs[1].succs == set() # a := a + 2 assert bbs[2].id == 2 assert bbs[2].type == "BB" assert bbs[2].preds == set() assert bbs[2].succs == {3} # a := a + 3 assert bbs[3].id == 3 assert bbs[3].type == "BBExit" assert bbs[3].preds == {0, 2} assert bbs[3].succs == set()
def test_struct_types(): fdump = f'stdin.dump.json' checker_warnings, rc = check_program(""" TYPE Cooler: STRUCT Temp: INT; Cooling: TOF; END_STRUCT; END_TYPE """.replace('\n', '')) assert rc == 0 with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme assert len(scheme.types) == 1 ty = scheme.types[0] assert ty.name == 'COOLER' assert ty.type == 'Struct'
def test_cfa_multiple_pous(): fdump = f'stdin.dump.json' warns, rc = check_program(""" FUNCTION dead_code_after_return_1 : INT VAR some_var : INT; END_VAR RETURN; some_var := SQRT(16#42); (* UnreachableCode error *) END_FUNCTION FUNCTION dead_code_after_return_2 : INT VAR some_var : INT; END_VAR RETURN; some_var := SQRT(16#42); (* UnreachableCode error *) END_FUNCTION """.replace('\n', '')) assert rc == 0 assert len(filter_warns(warns, 'PLCOPEN-CP2')) == 2 with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme
def test_array_initialized_list(): fdump = f'stdin.dump.json' warns, rc = check_program(""" TYPE BITS: ARRAY [1..2, 1..3] OF BOOL := [0,0,0,0,0,0,1,1,1]; END_TYPE PROGRAM test_p VAR ARR1: ARRAY [1..2, 1..3] OF BOOL := [0,0,0,0,0,0,1,1,1]; END_VAR ARR1[1] := 19; END_PROGRAM """.replace('\n', '')) assert rc == 0 assert len(warns) >= 2 oob_warns = filter_warns(warns, 'OutOfBounds') assert len(oob_warns) == 2 for w in oob_warns: assert w.id == 'OutOfBounds' assert '3 values will be lost' in w.msg with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme assert len(scheme.types) == 1
def test_cfa_dead_code_top_statements(): fdump = f'stdin.dump.json' warns, rc = check_program(""" FUNCTION test_dead_code_to_stmts : INT VAR counter : INT := 0; some_var : INT; END_VAR counter := counter + 1; counter := 2 + 2; RETURN; some_var := SQRT(16#42); (* UnreachableCode error *) some_var := 16#42; (* No additional warnings *) some_var := 19; END_FUNCTION """.replace('\n', '')) assert rc == 0 assert len(warns) >= 1 assert len(filter_warns(warns, 'PLCOPEN-CP2')) == 1 with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme
def test_cfg_single_continue_in_the_first_bb(): fdump = f'stdin.dump.json' checker_warnings, rc = check_program( """ PROGRAM test_continue VAR a : INT; i : INT; END_VAR WHILE i < 10 DO IF i = 5 THEN CONTINUE; END_IF; i := i + 2; END_WHILE; i := 0; END_PROGRAM """.replace('\n', '')) assert rc == 0 with DumpManager(fdump) as dm: bbs = dm.scheme.cfgs[0].basic_blocks assert len(bbs) == 5 assert bbs[0].preds == {2, 3} assert bbs[2].succs == {0} assert len(bbs[2].stmt_ids) == 1
def test_cfg_repeat_statement(): fdump = f'stdin.dump.json' checker_warnings, rc = check_program( """ PROGRAM test_repeat VAR j : INT := 0; END_VAR REPEAT j := j + 2; UNTIL j < 100 END_REPEAT; j := 0; END_PROGRAM """.replace('\n', '')) assert rc == 0 with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme assert len(scheme.programs) == 1 assert len(scheme.cfgs) == 1 cfg = scheme.cfgs[0] bbs = cfg.basic_blocks assert len(bbs) == 3 # repeat assert bbs[0].id == 0 assert bbs[0].type == "BBEntry" assert bbs[0].preds == {1} assert bbs[0].succs == {1} # j := j + 2 # until # j < 100 assert bbs[1].id == 1 assert bbs[1].type == "BB" assert bbs[1].preds == {0} assert bbs[1].succs == {0, 2} # j := 0 assert bbs[2].id == 2 assert bbs[2].type == "BBExit" assert bbs[2].preds == {1} assert bbs[2].succs == set()
def test_cfg_for_statement(): fdump = f'stdin.dump.json' checker_warnings, rc = check_program( """ PROGRAM test_for VAR a : INT; i : INT; END_VAR FOR i := 0 TO 10 BY 2 DO a := a + i; a := a + 1; END_FOR; i := 0; END_PROGRAM """.replace('\n', '')) assert rc == 0 with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme assert len(scheme.programs) == 1 assert len(scheme.cfgs) == 1 cfg = scheme.cfgs[0] bbs = cfg.basic_blocks assert len(bbs) == 3 # for # i := 0 assert bbs[0].id == 0 assert bbs[0].type == "BBEntry" assert bbs[0].preds == {1} assert bbs[0].succs == {1, 2} # a := a + i # a := a + 1 assert bbs[1].id == 1 assert bbs[1].type == "BB" assert bbs[1].preds == {0} assert bbs[1].succs == {0} # i := 0 assert bbs[2].id == 2 assert bbs[2].type == "BBExit" assert bbs[2].preds == {0} assert bbs[2].succs == set()
def test_cfg_while_statement(): fdump = f'stdin.dump.json' checker_warnings, rc = check_program( """ PROGRAM test_while VAR a : INT; i : INT; END_VAR WHILE i <= 10 DO a := i + 2; i := i - 1; END_WHILE; i := 0; END_PROGRAM """.replace('\n', '')) assert rc == 0 with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme assert len(scheme.programs) == 1 assert len(scheme.cfgs) == 1 cfg = scheme.cfgs[0] bbs = cfg.basic_blocks assert len(bbs) == 3 # while # i <= 10 assert bbs[0].id == 0 assert bbs[0].type == "BBEntry" assert bbs[0].preds == {1} assert bbs[0].succs == {1, 2} # a := i + 2 # i := i - 1 assert bbs[1].id == 1 assert bbs[1].type == "BB" assert bbs[1].preds == {0} assert bbs[1].succs == {0} # i := 0 assert bbs[2].id == 2 assert bbs[2].type == "BBExit" assert bbs[2].preds == {0} assert bbs[2].succs == set()
def test_cfg_single_return_statement(): fdump = f'stdin.dump.json' checker_warnings, rc = check_program( """ FUNCTION test_single_return : INT VAR A : INT; END_VAR RETURN; END_FUNCTION """.replace('\n', '')) assert rc == 0 with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme assert len(scheme.functions) == 1 assert len(scheme.cfgs) == 1 cfg = scheme.cfgs[0] bbs = cfg.basic_blocks assert len(bbs) == 1 # RETURN assert bbs[0].id == 0 assert bbs[0].type == "BBExit" assert bbs[0].preds == set() assert bbs[0].succs == set()
def test_cfg_single_bb_has_exit_type(): fdump = f'stdin.dump.json' checker_warnings, rc = check_program( """ PROGRAM f VAR a : INT; END_VAR a := 1; END_PROGRAM """.replace('\n', '')) assert rc == 0 with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme assert len(scheme.programs) == 1 assert len(scheme.cfgs) == 1 cfg = scheme.cfgs[0] bbs = cfg.basic_blocks assert len(bbs) == 1 # a := 1 assert bbs[0].id == 0 assert bbs[0].type == "BBExit" assert bbs[0].preds == set() assert bbs[0].succs == set()
def test_cfg_no_condition_self_reference(): fdump = f'stdin.dump.json' checker_warnings, rc = check_program( """ PROGRAM p VAR a : INT; i : INT; END_VAR a := 1; IF a > 1 THEN i := 1; END_IF; a := 2; END_PROGRAM """.replace('\n', '')) assert rc == 0 with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme and len(scheme.cfgs) == 1 cfg = scheme.cfgs[0] bbs = cfg.basic_blocks assert len(bbs) == 3 # a := 1 # if ... then # a > 1 assert bbs[0].preds == set()
def test_use_define_array(): fdump = f'stdin.dump.json' warns, rc = check_program(""" PROGRAM test_arr_len VAR ARR1: ARRAY [1..2] OF BOOL; END_VAR ARR1[0] := 19; (* error *) ARR1[1] := 19; (* no false positive *) ARR1[2] := 19; (* no false positive *) ARR1[3] := 19; (* error *) ARR1[2,1] := 19; (* error *) END_PROGRAM """.replace('\n', '')) assert rc == 0 assert len(warns) >= 3 ws = filter_warns(warns, 'OutOfBounds') assert len(ws) == 3 assert "index 0 is out" in ws[0].msg assert "index 3 is out" in ws[1].msg assert "addressed to 2 dimension" in ws[2].msg with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme
def test_cfg_exit_continue(): fdump = f'stdin.dump.json' checker_warnings, rc = check_program( """ PROGRAM test_exit_continue VAR a : INT; i : INT; END_VAR WHILE i <= 10 DO IF i = 5 THEN i := i + 1; EXIT; ELSIF i = 6 THEN CONTINUE; i := 3; END_IF; i := i + 2; END_WHILE; i := 0; END_PROGRAM """.replace('\n', '')) assert rc == 0 with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme assert len(scheme.programs) == 1 assert len(scheme.cfgs) == 1 cfg = scheme.cfgs[0] bbs = cfg.basic_blocks assert len(bbs) == 8 # while # i <= 10 assert bbs[0].id == 0 assert bbs[0].type == "BBEntry" assert bbs[0].preds == {4, 6} assert bbs[0].succs == {1, 7} # if # i = 5 assert bbs[1].id == 1 assert bbs[1].type == "BB" assert bbs[1].preds == {0} assert bbs[1].succs == {2, 3} # i := i + 1 # exit assert bbs[2].id == 2 assert bbs[2].type == "BB" assert bbs[2].preds == {1} assert bbs[2].succs == {7} assert len(bbs[2].stmt_ids) == 2 # elsif # i = 6 assert bbs[3].id == 3 assert bbs[3].type == "BB" assert bbs[3].preds == {1} assert bbs[3].succs == {4, 6} # continue assert bbs[4].id == 4 assert bbs[4].type == "BB" assert bbs[4].preds == {3} assert bbs[4].succs == {0} # i := 3 assert bbs[5].id == 5 assert bbs[5].type == "BB" assert bbs[5].preds == set() assert bbs[5].succs == {6} # i := i + 2 assert bbs[6].id == 6 assert bbs[6].type == "BB" assert bbs[6].preds == {5, 3} assert bbs[6].succs == {0} # i := 0 assert bbs[7].id == 7 assert bbs[7].type == "BBExit" assert bbs[7].preds == {0, 2} assert bbs[7].succs == set()
def test_cfg_case_statement(): fdump = f'stdin.dump.json' checker_warnings, rc = check_program( """ PROGRAM case_test VAR a : INT; END_VAR CASE a OF 1 : a := 3; 2 : a := 5; 3,4 : a := 42; ELSE a := 19; a := 20; END_CASE; a := 0; END_PROGRAM """.replace('\n', '')) assert rc == 0 with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme assert len(scheme.programs) == 1 assert len(scheme.cfgs) == 1 cfg = scheme.cfgs[0] bbs = cfg.basic_blocks assert len(bbs) == 9 # (switch) case .. of # a assert bbs[0].id == 0 assert bbs[0].type == "BBEntry" assert bbs[0].preds == set() assert bbs[0].succs == {1} # (case) 1 assert bbs[1].id == 1 assert bbs[1].type == "BB" assert bbs[1].preds == {0} assert bbs[1].succs == {2, 3} # a := 3 assert bbs[2].id == 2 assert bbs[2].type == "BB" assert bbs[2].preds == {1} assert bbs[2].succs == {8} # (case) 2 assert bbs[3].id == 3 assert bbs[3].type == "BB" assert bbs[3].preds == {1} assert bbs[3].succs == {4, 5} # a := 5 assert bbs[4].id == 4 assert bbs[4].type == "BB" assert bbs[4].preds == {3} assert bbs[4].succs == {8} # (case) 3 # (case) 4 assert bbs[5].id == 5 assert bbs[5].type == "BB" assert bbs[5].preds == {3} assert bbs[5].succs == {6, 7} # a := 42 assert bbs[6].id == 6 assert bbs[6].type == "BB" assert bbs[6].preds == {5} assert bbs[6].succs == {8} # (else) # a := 19 # a := 20 assert bbs[7].id == 7 assert bbs[7].type == "BB" assert bbs[7].preds == {5} assert bbs[7].succs == {8} # a := 0 assert bbs[8].id == 8 assert bbs[8].type == "BBExit" assert bbs[8].preds == {2, 4, 6, 7} assert bbs[8].succs == set()
def test_cfg_if_elsif(): fdump = f'stdin.dump.json' checker_warnings, rc = check_program( """ PROGRAM f VAR a : INT; i : INT; END_VAR a := 1; IF a > 1 THEN i := 1; ELSIF a > 2 THEN i := 2; ELSIF a > 3 THEN i := 3; END_IF; i := 0; END_PROGRAM """.replace('\n', '')) assert rc == 0 with DumpManager(fdump) as dm: scheme = dm.scheme assert scheme assert len(scheme.programs) == 1 assert len(scheme.cfgs) == 1 cfg = scheme.cfgs[0] bbs = cfg.basic_blocks assert len(bbs) == 7 # a := 1 # if ... then # a > 1 assert bbs[0].id == 0 assert bbs[0].type == "BBEntry" assert bbs[0].preds == set() assert bbs[0].succs == {1, 2} # i := 1 assert bbs[1].id == 1 assert bbs[1].type == "BB" assert bbs[1].preds == {0} assert bbs[1].succs == {6} # elsif ... then # a > 2 assert bbs[2].id == 2 assert bbs[2].type == "BB" assert bbs[2].preds == {0} assert bbs[2].succs == {3, 4} # i := 2 assert bbs[3].id == 3 assert bbs[3].type == "BB" assert bbs[3].preds == {2} assert bbs[3].succs == {6} # elsif ... then # a > 3 assert bbs[4].id == 4 assert bbs[4].type == "BB" assert bbs[4].preds == {2} assert bbs[4].succs == {5, 6} # i := 3 assert bbs[5].id == 5 assert bbs[5].type == "BB" assert bbs[5].preds == {4} assert bbs[5].succs == {6} # i := 0 assert bbs[6].id == 6 assert bbs[6].type == "BBExit" assert bbs[6].preds == {1, 3, 4, 5} assert bbs[6].succs == set()