from pathlib import Path from slither import Slither TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data" def test_overrides(solc_binary_path) -> None: # pylint: disable=too-many-statements,too-many-locals solc_path = solc_binary_path("0.8.15") slither = Slither(Path(TEST_DATA_DIR, "virtual_overrides.sol").as_posix(), solc=solc_path) test = slither.get_contract_from_name("Test")[0] test_virtual_func = test.get_function_from_full_name("myVirtualFunction()") assert test_virtual_func.is_virtual assert not test_virtual_func.is_override x = test.get_functions_overridden_by(test_virtual_func) assert len(x) == 0 x = test_virtual_func.overridden_by assert len(x) == 5 assert set(i.canonical_name for i in x) == set( ["A.myVirtualFunction()", "C.myVirtualFunction()", "X.myVirtualFunction()"] ) a = slither.get_contract_from_name("A")[0] a_virtual_func = a.get_function_from_full_name("myVirtualFunction()") assert a_virtual_func.is_virtual assert a_virtual_func.is_override x = a.get_functions_overridden_by(a_virtual_func) assert len(x) == 2 assert set(i.canonical_name for i in x) == set(["Test.myVirtualFunction()"]) b = slither.get_contract_from_name("B")[0] b_virtual_func = b.get_function_from_full_name("myVirtualFunction()") assert not b_virtual_func.is_virtual assert b_virtual_func.is_override x = b.get_functions_overridden_by(b_virtual_func) assert len(x) == 2 assert set(i.canonical_name for i in x) == set(["A.myVirtualFunction()"]) assert len(b_virtual_func.overridden_by) == 0 c = slither.get_contract_from_name("C")[0] c_virtual_func = c.get_function_from_full_name("myVirtualFunction()") assert not c_virtual_func.is_virtual assert c_virtual_func.is_override x = c.get_functions_overridden_by(c_virtual_func) assert len(x) == 2 # C should not override B as they are distinct leaves in the inheritance tree assert set(i.canonical_name for i in x) == set(["Test.myVirtualFunction()"]) y = slither.get_contract_from_name("Y")[0] y_virtual_func = y.get_function_from_full_name("myVirtualFunction()") assert y_virtual_func.is_virtual assert not y_virtual_func.is_override x = y_virtual_func.overridden_by assert len(x) == 1 assert x[0].canonical_name == "Z.myVirtualFunction()" z = slither.get_contract_from_name("Z")[0] z_virtual_func = z.get_function_from_full_name("myVirtualFunction()") assert z_virtual_func.is_virtual assert z_virtual_func.is_override x = z.get_functions_overridden_by(z_virtual_func) assert len(x) == 4 assert set(i.canonical_name for i in x) == set( ["Y.myVirtualFunction()", "X.myVirtualFunction()"] ) k = slither.get_contract_from_name("K")[0] k_virtual_func = k.get_function_from_full_name("a()") assert not k_virtual_func.is_virtual assert k_virtual_func.is_override assert len(k_virtual_func.overrides) == 3 x = k_virtual_func.overrides assert set(i.canonical_name for i in x) == set(["I.a()"]) i = slither.get_contract_from_name("I")[0] i_virtual_func = i.get_function_from_full_name("a()") assert i_virtual_func.is_virtual assert not i_virtual_func.is_override assert len(i_virtual_func.overrides) == 0 x = i_virtual_func.overridden_by assert len(x) == 1 assert x[0].canonical_name == "K.a()" def test_virtual_override_references_and_implementations(solc_binary_path) -> None: solc_path = solc_binary_path("0.8.15") file = Path(TEST_DATA_DIR, "virtual_overrides.sol").as_posix() slither = Slither(file, solc=solc_path) funcs = slither.offset_to_objects(file, 29) assert len(funcs) == 1 func = funcs.pop() assert func.canonical_name == "Test.myVirtualFunction()" assert {(x.start, x.end) for x in slither.offset_to_implementations(file, 29)} == { (20, 73), (102, 164), (274, 328), (357, 419), } funcs = slither.offset_to_objects(file, 111) assert len(funcs) == 1 func = funcs.pop() assert func.canonical_name == "A.myVirtualFunction()" # A.myVirtualFunction() is implemented in A and also overridden in B assert {(x.start, x.end) for x in slither.offset_to_implementations(file, 111)} == { (102, 164), (190, 244), } # X is inherited by Z and Z.myVirtualFunction() overrides X.myVirtualFunction() assert {(x.start, x.end) for x in slither.offset_to_references(file, 341)} == { (514, 515), (570, 571), } # The reference to X in inheritance specifier is the definition of Z assert {(x.start, x.end) for x in slither.offset_to_definitions(file, 514)} == {(341, 343)} # The reference to X in the function override specifier is the definition of Z assert {(x.start, x.end) for x in slither.offset_to_definitions(file, 570)} == {(341, 343)} # Y is inherited by Z and Z.myVirtualFunction() overrides Y.myVirtualFunction() assert {(x.start, x.end) for x in slither.offset_to_references(file, 432)} == { (511, 512), (567, 568), } # The reference to Y in inheritance specifier is the definition of Z assert {(x.start, x.end) for x in slither.offset_to_definitions(file, 511)} == {(432, 434)} # The reference to Y in the function override specifier is the definition of Z assert {(x.start, x.end) for x in slither.offset_to_definitions(file, 567)} == {(432, 434)} # Name is abstract and has no implementation. It is inherited and implemented by Name2 assert {(x.start, x.end) for x in slither.offset_to_implementations(file, 612)} == {(657, 718)} def test_virtual_is_implemented(solc_binary_path): solc_path = solc_binary_path("0.8.15") file = Path(TEST_DATA_DIR, "virtual_overrides.sol").as_posix() slither = Slither(file, solc=solc_path) test2 = slither.get_contract_from_name("Test2")[0] f = test2.get_function_from_full_name("f()") assert f.is_virtual assert not f.is_implemented a2 = slither.get_contract_from_name("A2")[0] f = a2.get_function_from_full_name("f()") assert f.is_virtual assert f.is_implemented # Test.2f() is not implemented, but A2 inherits from Test2 and overrides f() assert {(x.start, x.end) for x in slither.offset_to_implementations(file, 759)} == {(809, 853)}