From d973133204813a4bd6d491646fe16b19f7c62763 Mon Sep 17 00:00:00 2001 From: Judy Wu Date: Fri, 6 Oct 2023 22:22:32 -0400 Subject: [PATCH] bug: address issue #2107 add contract id in inheritance graph printer --- slither/printers/abstract_printer.py | 2 +- .../printers/inheritance/inheritance_graph.py | 7 +-- tests/e2e/printers/__init__.py | 0 .../test_data/test_contract_names/A.sol | 4 ++ .../test_data/test_contract_names/B.sol | 8 ++++ .../test_data/test_contract_names/B2.sol | 7 +++ .../test_data/test_contract_names/C.sol | 7 +++ tests/e2e/printers/test_printers.py | 45 +++++++++++++++++++ 8 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 tests/e2e/printers/__init__.py create mode 100644 tests/e2e/printers/test_data/test_contract_names/A.sol create mode 100644 tests/e2e/printers/test_data/test_contract_names/B.sol create mode 100644 tests/e2e/printers/test_data/test_contract_names/B2.sol create mode 100644 tests/e2e/printers/test_data/test_contract_names/C.sol create mode 100644 tests/e2e/printers/test_printers.py diff --git a/slither/printers/abstract_printer.py b/slither/printers/abstract_printer.py index 5d0a9f4941..166cc55f0a 100644 --- a/slither/printers/abstract_printer.py +++ b/slither/printers/abstract_printer.py @@ -20,7 +20,7 @@ class AbstractPrinter(metaclass=abc.ABCMeta): WIKI = "" - def __init__(self, slither: "Slither", logger: Logger) -> None: + def __init__(self, slither: "Slither", logger: Optional[Logger]) -> None: self.slither = slither self.contracts = slither.contracts self.filename = slither.filename diff --git a/slither/printers/inheritance/inheritance_graph.py b/slither/printers/inheritance/inheritance_graph.py index 95022c0679..a16ce273af 100644 --- a/slither/printers/inheritance/inheritance_graph.py +++ b/slither/printers/inheritance/inheritance_graph.py @@ -100,10 +100,11 @@ def _summary(self, contract): # Add arrows (number them if there is more than one path so we know order of declaration for inheritance). if len(contract.immediate_inheritance) == 1: - ret += f"{contract.name} -> {contract.immediate_inheritance[0]};\n" + immediate_inheritance = contract.immediate_inheritance[0] + ret += f"c{contract.id}_{contract.name} -> c{immediate_inheritance.id}_{immediate_inheritance};\n" else: for i, immediate_inheritance in enumerate(contract.immediate_inheritance): - ret += f'{contract.name} -> {immediate_inheritance} [ label="{i + 1}" ];\n' + ret += f'c{contract.id}_{contract.name} -> c{immediate_inheritance.id}_{immediate_inheritance} [ label="{i + 1}" ];\n' # Functions visibilities = ["public", "external"] @@ -151,7 +152,7 @@ def _summary(self, contract): indirect_shadowing_information = self._get_indirect_shadowing_information(contract) # Build the node label - ret += f'{contract.name}[shape="box"' + ret += f'c{contract.id}_{contract.name}[shape="box"' ret += 'label=< ' ret += f'' if public_functions: diff --git a/tests/e2e/printers/__init__.py b/tests/e2e/printers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/e2e/printers/test_data/test_contract_names/A.sol b/tests/e2e/printers/test_data/test_contract_names/A.sol new file mode 100644 index 0000000000..f187ba0d39 --- /dev/null +++ b/tests/e2e/printers/test_data/test_contract_names/A.sol @@ -0,0 +1,4 @@ +contract A { + function a_main() public pure {} +} + diff --git a/tests/e2e/printers/test_data/test_contract_names/B.sol b/tests/e2e/printers/test_data/test_contract_names/B.sol new file mode 100644 index 0000000000..db9f210b7d --- /dev/null +++ b/tests/e2e/printers/test_data/test_contract_names/B.sol @@ -0,0 +1,8 @@ +import "./A.sol"; + +contract B is A { + function b_main() public pure { + a_main(); + } +} + diff --git a/tests/e2e/printers/test_data/test_contract_names/B2.sol b/tests/e2e/printers/test_data/test_contract_names/B2.sol new file mode 100644 index 0000000000..b9f602896b --- /dev/null +++ b/tests/e2e/printers/test_data/test_contract_names/B2.sol @@ -0,0 +1,7 @@ +import "./A.sol"; + +contract B is A { + function b2_main() public pure { + a_main(); + } +} diff --git a/tests/e2e/printers/test_data/test_contract_names/C.sol b/tests/e2e/printers/test_data/test_contract_names/C.sol new file mode 100644 index 0000000000..90bc35df39 --- /dev/null +++ b/tests/e2e/printers/test_data/test_contract_names/C.sol @@ -0,0 +1,7 @@ +import "./A.sol"; + +contract C is A { + function c_main() public pure { + a_main(); + } +} diff --git a/tests/e2e/printers/test_printers.py b/tests/e2e/printers/test_printers.py new file mode 100644 index 0000000000..45e0b0b76a --- /dev/null +++ b/tests/e2e/printers/test_printers.py @@ -0,0 +1,45 @@ +import re +from collections import Counter +from pathlib import Path + +from crytic_compile import CryticCompile +from crytic_compile.platform.solc_standard_json import SolcStandardJson + +from slither import Slither +from slither.printers.inheritance.inheritance_graph import PrinterInheritanceGraph + + +TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data" + + +def test_inheritance_printer(solc_binary_path) -> None: + solc_path = solc_binary_path("0.8.0") + standard_json = SolcStandardJson() + standard_json.add_source_file( + Path(TEST_DATA_DIR, "test_contract_names", "A.sol").as_posix() + ) + standard_json.add_source_file( + Path(TEST_DATA_DIR, "test_contract_names", "B.sol").as_posix() + ) + standard_json.add_source_file( + Path(TEST_DATA_DIR, "test_contract_names", "B2.sol").as_posix() + ) + standard_json.add_source_file( + Path(TEST_DATA_DIR, "test_contract_names", "C.sol").as_posix() + ) + compilation = CryticCompile(standard_json, solc=solc_path) + slither = Slither(compilation) + printer = PrinterInheritanceGraph(slither=slither, logger=None) + + content = "" + for contract in slither.contracts: + content += printer._summary(contract) + + pattern = re.compile(r"(?:c\d+_)?(\w+ -> )(?:c\d+_)(\w+)") + matches = re.findall(pattern, content) + relations = ["".join(m) for m in matches] + + counter = Counter(relations) + + assert counter["B -> A"] == 2 + assert counter["C -> A"] == 1
{contract.name}