Exemple #1
0
def main(files: List[str],
         draw_cfg: str = "",
         binary: str = "../output/bin/iec_checker"):
    for f in files:
        if not os.path.isfile(f):
            continue
        checker_warnings, rc = run_checker(f, binary)
        if rc != 0:
            print(f'Report for {f}:')
            for w in checker_warnings:
                print(f'{w}')
            continue

        dump_name = f'{f}.dump.json'
        plugins_warnings = []
        with DumpManager(dump_name) as dm:
            plugins_warnings = dm.run_all_inspections()
            if draw_cfg:
                cfg_plotter = CFGPlotter(dm.scheme.cfgs)
                cfg_plotter.save_file(draw_cfg)

        print(f'Report for {f}:')
        if checker_warnings or plugins_warnings:
            for w in checker_warnings:
                print(f'{w}')
            for p in plugins_warnings:
                print(f'{w}')
        else:
            print('No errors found!')
Exemple #2
0
def test_no_parser_errors():
    f = os.path.join('./test/plcopen/example.xml')
    fdump = f'{f}.dump.json'
    checker_warnings, rc = run_checker(f, '-input-format', 'xml')
    assert rc == 0, f"Incorrect exit code for {f}"
    with DumpManager(fdump):
        pass
Exemple #3
0
def test_cfg_func_call_statement():
    fdump = f'stdin.dump.json'
    checker_warnings, rc = check_program(
        """
        PROGRAM test_func_call
        VAR j : INT := 0; END_VAR
        j := fn0(INVAL := 19);
        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) == 1
        # NOTE: I'm not sure about evaluation order for expressions in function
        # paramters. Need check how does it implemented in the modern IDEs.
        # Need revisit Mario de Sousa's paper for this topic.
        # j := fn0(INVAL := 19)
        #   INVAL := 19
        #   j := 0
        #   fn0()
        # P.S. This is a good idea for the additional inspection.
        assert bbs[0].id == 0
        assert bbs[0].type == "BBExit"
        assert bbs[0].preds == set()
        assert bbs[0].succs == set()
Exemple #4
0
def test_cfg_single_bb_from_linear_statements_sequence():
    fdump = f'stdin.dump.json'
    checker_warnings, rc = check_program(
        """
        PROGRAM linear
        VAR a : INT; END_VAR
        a := 1;
        a := 2;
        a := 3;
        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
        # a := 2
        # a := 3
        assert bbs[0].id == 0
        assert bbs[0].type == "BBExit"
        assert bbs[0].preds == set()
        assert bbs[0].succs == set()
Exemple #5
0
def test_cfg_function_calls_in_condition_stmt():
    fdump = f'stdin.dump.json'
    checker_warnings, rc = check_program(
        """
        PROGRAM func_calls
        VAR a : INT; END_VAR
        IF fn1(a1 := 19) AND fn2(a1 := 35, a2 := 40) THEN
            a := 30;
        END_IF;
        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) == 2
        # if .. then
        # and
        # fn1()
        # a := 19
        # fn2()
        # a := 35
        # a := 40
        assert len(bbs[0].stmt_ids) == 7
Exemple #6
0
def test_cfg_single_if_statement_bb_has_exit_type():
    fdump = f'stdin.dump.json'
    checker_warnings, rc = check_program(
        """
        PROGRAM f
        VAR a : INT := 0; END_VAR
        IF a < 16 THEN
            a := 1;
        END_IF;
        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) == 2
        # if .. then
        # a < 16
        assert bbs[0].id == 0
        assert bbs[0].type == "BBExit"
        assert bbs[0].preds == set()
        assert bbs[0].succs == {1}
        # a := 1
        assert bbs[1].id == 1
        assert bbs[1].type == "BBExit"
        assert bbs[1].preds == {0}
        assert bbs[1].succs == set()
Exemple #7
0
def test_direct_variables():
    f = './test/st/good/direct-variables.st'
    fdump = f'{f}.dump.json'
    checker_warnings, rc = run_checker(f)
    assert rc == 0
    with DumpManager(fdump) as dm:
        _ = dm.scheme  # TODO
Exemple #8
0
def test_cfg_return_statement():
    fdump = f'stdin.dump.json'
    checker_warnings, rc = check_program(
        """
        FUNCTION test_return : INT
        VAR A : INT; END_VAR
        A := 0;
        RETURN;
        A := 1;
        A := 42;
        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) == 2
        # a := 0
        # RETURN
        assert bbs[0].id == 0
        assert bbs[0].type == "BBExit"
        assert bbs[0].preds == set()
        assert bbs[0].succs == set()
Exemple #9
0
def test_cfa_dead_code_in_the_loops():
    fdump = f'stdin.dump.json'
    warns, rc = check_program(
        """
        PROGRAM dead_code_in_the_loops
        VAR a : INT; i : INT; END_VAR
        WHILE i < 10 DO
          IF i = 5 THEN
            i := i + 1;
            EXIT;
            i := 19; (* UnreachableCode error *)
            i := 42; (* No additional warnings *)
            i := 42;
          ELSIF i = 6 THEN
            CONTINUE;
            i := 3; (* UnreachableCode error *)
            i := 44; (* No additional warnings *)
            i := 19;
          END_IF;
          i := i + 2;
        END_WHILE;
        i := 0;
        END_PROGRAM
        """.replace('\n', ''))
    assert rc == 0
    assert len(filter_warns(warns, 'UnreachableCode')) == 2
    with DumpManager(fdump) as dm:
        scheme = dm.scheme
        assert scheme
def test_l10():
    f = 'st/plcopen-l10.st'
    fdump = f'{f}.dump.json'
    checker_warnings, rc = run_checker(f)
    assert rc == 0
    checker_warnings.count('PLCOPEN-L10') == 3
    with DumpManager(fdump):
        pass
def test_cp9():
    f = 'st/plcopen-cp9.st'
    fdump = f'{f}.dump.json'
    warns, rc = run_checker(f)
    assert rc == 0
    assert len(filter_warns(warns, 'PLCOPEN-CP9')) == 2
    with DumpManager(fdump):
        pass
Exemple #12
0
def test_zerodiv():
    f = 'st/zero-division.st'
    fdump = f'{f}.dump.json'
    checker_warnings, rc = run_checker(f)
    assert rc == 0
    checker_warnings.count('ZeroDivision') == 2
    with DumpManager(fdump):
        pass
def test_cp25():
    f = 'st/plcopen-cp25.st'
    fdump = f'{f}.dump.json'
    checker_warnings, rc = run_checker(f)
    assert rc == 0
    checker_warnings.count('PLCOPEN-CP25') == 2
    with DumpManager(fdump):
        pass
Exemple #14
0
def test_parser_errors():
    for fname in os.listdir('./test/st/bad/'):
        f = os.path.join('./test/st/bad/', fname)
        fdump = f'{f}.dump.json'
        checker_warnings, rc = run_checker(f)
        assert rc == 1, f"Incorrect exit code for {f}"
        assert len(checker_warnings) > 0
        with DumpManager(fdump):
            pass
Exemple #15
0
def test_no_parser_errors():
    for fname in os.listdir('./test/st/good/'):
        if not fname.endswith('.st'):
            continue
        f = os.path.join('./test/st/good/', fname)
        fdump = f'{f}.dump.json'
        checker_warnings, rc = run_checker(f)
        assert rc == 0, f"Incorrect exit code for {f}"
        with DumpManager(fdump):
            pass
Exemple #16
0
def test_cfg_continue():
    fdump = f'stdin.dump.json'
    checker_warnings, rc = check_program(
        """
        PROGRAM test_exit
        VAR a : INT; i : INT; END_VAR
        WHILE i < 10 DO
          IF i = 5 THEN
            i := i + 1;
            CONTINUE;
          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) == 5
        # while
        # i < 10
        assert bbs[0].id == 0
        assert bbs[0].type == "BBEntry"
        assert bbs[0].preds == {2, 3}
        assert bbs[0].succs == {1, 4}
        # 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
        # continue
        assert bbs[2].id == 2
        assert bbs[2].type == "BB"
        assert bbs[2].preds == {1}
        assert bbs[2].succs == {0}
        assert len(bbs[2].stmt_ids) == 2
        # i := i + 2
        assert bbs[3].id == 3
        assert bbs[3].type == "BB"
        assert bbs[3].preds == {1}
        assert bbs[3].succs == {0}
        # i := 0
        assert bbs[4].id == 4
        assert bbs[4].type == "BBExit"
        assert bbs[4].preds == {0}
        assert bbs[4].succs == set()
Exemple #17
0
def test_cfg_while_nested_statements():
    fdump = f'stdin.dump.json'
    checker_warnings, rc = check_program(
        """
        PROGRAM test_cfg_while_nested_statements
        VAR a : INT; i : INT; END_VAR
        WHILE i < 10 DO
          a := 1;
          IF i > 10 THEN
            a := 2;
          END_IF;
          a := 3;
        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) == 5
        # while
        # i < 10
        assert bbs[0].id == 0
        assert bbs[0].type == "BBEntry"
        assert bbs[0].preds == {3}
        assert bbs[0].succs == {1, 4}
        # a := 1
        # if
        # if > 10
        assert bbs[1].id == 1
        assert bbs[1].type == "BB"
        assert bbs[1].preds == {0}
        assert bbs[1].succs == {2, 3}
        # a := 2
        assert bbs[2].id == 2
        assert bbs[2].type == "BB"
        assert bbs[2].preds == {1}
        assert bbs[2].succs == {3}
        # a := 3
        assert bbs[3].id == 3
        assert bbs[3].type == "BB"
        assert bbs[3].preds == {1, 2}
        assert bbs[3].succs == {0}
        # i := 0
        assert bbs[4].id == 4
        assert bbs[4].type == "BBExit"
        assert bbs[4].preds == {0}
        assert bbs[4].succs == set()
Exemple #18
0
def test_lexing_error():
    f = './test/st/bad/lexing-error.st'
    fdump = f'{f}.dump.json'
    checker_warnings, rc = run_checker(f)
    assert rc == 1
    assert len(checker_warnings) == 1
    cv = checker_warnings[0]
    assert cv.id == 'LexingError'
    assert cv.linenr == 9
    assert cv.column == 6
    with DumpManager(fdump):
        pass
def test_initialization_literal():
    f = 'st/declaration-analysis.st'
    fdump = f'{f}.dump.json'
    checker_warnings, rc = run_checker(f)
    assert rc == 0
    assert len(checker_warnings) == 3
    cv = checker_warnings[0]
    assert cv.id == 'OutOfBounds'
    # assert cv.linenr == 8
    # assert cv.column == 31
    with DumpManager(fdump):
        pass
def test_n3():
    f = 'st/plcopen-n3.st'
    fdump = f'{f}.dump.json'
    checker_warnings, rc = run_checker(f)
    assert rc == 0
    assert len(checker_warnings) >= 1
    cv = checker_warnings[0]
    assert cv.id == 'PLCOPEN-N3'
    assert cv.linenr == 6
    assert cv.column == 7
    with DumpManager(fdump):
        pass
def test_l17():
    f = 'st/plcopen-l17.st'
    fdump = f'{f}.dump.json'
    checker_warnings, rc = run_checker(f)
    assert rc == 0
    assert len(checker_warnings) >= 1
    cv = checker_warnings[1]
    assert cv.id == 'PLCOPEN-L17'
    assert cv.linenr == 10
    assert cv.column == 4
    with DumpManager(fdump):
        pass
def test_cp13():
    f = './test/st/plcopen-cp13.st'
    fdump = f'{f}.dump.json'
    checker_warnings, rc = run_checker(f)
    assert rc == 0
    assert len(checker_warnings) >= 1
    cv = checker_warnings[0]
    assert cv.id == 'PLCOPEN-CP13'
    assert cv.linenr == 8
    assert cv.column == 30
    with DumpManager(fdump):
        pass
Exemple #23
0
def test_array_types():
    fdump = f'stdin.dump.json'
    checker_warnings, rc = check_program("""
        TYPE BITS: ARRAY [0..7] OF BOOL; 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 == 'BITS'
        assert ty.type == 'Array'
Exemple #24
0
def test_ref_types():
    fdump = f'stdin.dump.json'
    checker_warnings, rc = check_program("""
        TYPE myRef: REF_TO INT; 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 == 'MYREF'
        assert ty.type == 'Ref'
Exemple #25
0
def test_enum_types():
    fdump = f'stdin.dump.json'
    checker_warnings, rc = check_program("""
        TYPE
          Traffic_Light: (Red, Amber, Green);
        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 == 'TRAFFIC_LIGHT'
        assert ty.type == 'Enum'
Exemple #26
0
def test_statements_order():
    """Test that POU statements are arranged in the correct order."""
    fdump = f'stdin.dump.json'
    checker_warnings, rc = check_program("""
        PROGRAM p
        VAR a : INT; i : INT; END_VAR
        a := 1;
        i := 22;
        a := 16#42;
        END_PROGRAM
        """.replace('\n', ''))
    assert rc == 0
    with DumpManager(fdump) as dm:
        scheme = dm.scheme
        assert scheme
        assert len(scheme.programs) == 1
Exemple #27
0
def test_cfg_if_else():
    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;
        ELSE
            i := 42;
        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) == 4
        # 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 == {3}
        # else
        # i := 42
        assert bbs[2].id == 2
        assert bbs[2].type == "BB"
        assert bbs[2].preds == {0}
        assert bbs[2].succs == {3}
        # i := 0
        assert bbs[3].id == 3
        assert bbs[3].type == "BBExit"
        assert bbs[3].preds == {1, 2}
        assert bbs[3].succs == set()
def test_unused_local_variable():
    fdump = f'stdin.dump.json'
    warns, rc = check_program("""
        PROGRAM p
        VAR
          a : INT;
          b : INT;
          c : INT;
        END_VAR
        b := 1 + c;
        END_PROGRAM
        """.replace('\n', ''))
    assert rc == 0
    assert len(filter_warns(warns, 'UnusedVariable')) == 1
    with DumpManager(fdump) as dm:
        scheme = dm.scheme
        assert scheme
Exemple #29
0
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()
Exemple #30
0
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'