Skip to content

Commit

Permalink
Merge pull request #2153 from crytic/fix-inheritance-printer-rebase
Browse files Browse the repository at this point in the history
Fix inheritance printer rebase
  • Loading branch information
0xalpharush authored Jun 4, 2024
2 parents dc767b1 + 8615b19 commit ee7023c
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 4 deletions.
8 changes: 8 additions & 0 deletions slither/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,14 @@ def parse_args(
default=defaults_flag_in_config["printers_to_run"],
)

group_printer.add_argument(
"--include-interfaces",
help="Include interfaces from inheritance-graph printer",
action="store_true",
dest="include_interfaces",
default=False,
)

group_detector.add_argument(
"--list-detectors",
help="List available detectors",
Expand Down
20 changes: 18 additions & 2 deletions slither/printers/inheritance/inheritance_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,21 @@ def _summary(self, contract):
"""
ret = ""

# Remove contracts that have "mock" in the name and if --include-interfaces in False (default)
# removes inherited interfaces
inheritance = [
i
for i in contract.immediate_inheritance
if "mock" not in i.name.lower()
and (not i.is_interface or self.slither.include_interfaces)
]

# 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:
if len(inheritance) == 1:
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):
for i, immediate_inheritance in enumerate(inheritance):
ret += f'c{contract.id}_{contract.name} -> c{immediate_inheritance.id}_{immediate_inheritance} [ label="{i + 1}" ];\n'

# Functions
Expand All @@ -113,6 +122,7 @@ def _summary(self, contract):
for f in contract.functions
if not f.is_constructor
and not f.is_constructor_variables
and not f.is_virtual
and f.contract_declarer == contract
and f.visibility in visibilities
]
Expand Down Expand Up @@ -195,6 +205,12 @@ def output(self, filename):

content = 'digraph "" {\n'
for c in self.contracts:
if (
"mock" in c.name.lower()
or c.is_library
or (c.is_interface and not self.slither.include_interfaces)
):
continue
content += self._summary(c) + "\n"
content += "}"

Expand Down
4 changes: 3 additions & 1 deletion slither/slither.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,12 @@ def __init__(self, target: Union[str, CryticCompile], **kwargs) -> None:
if printers_to_run == "echidna":
self.skip_data_dependency = True

# Used in inheritance-graph printer
self.include_interfaces = kwargs.get("include_interfaces", False)

self._init_parsing_and_analyses(kwargs.get("skip_analyze", False))

def _init_parsing_and_analyses(self, skip_analyze: bool) -> None:

for parser in self._parsers:
try:
parser.parse_contracts()
Expand Down
16 changes: 15 additions & 1 deletion tests/e2e/printers/test_data/test_contract_names/C.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
import "./A.sol";

contract C is A {
interface MyInterfaceX {
function count() external view returns (uint256);

function increment() external;
}

contract C is A, MyInterfaceX {
function c_main() public pure {
a_main();
}

function count() external view override returns (uint256){
return 1;
}

function increment() external override {

}
}
12 changes: 12 additions & 0 deletions tests/e2e/printers/test_printers.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,15 @@ def test_inheritance_printer(solc_binary_path) -> None:

assert counter["B -> A"] == 2
assert counter["C -> A"] == 1

# Lets also test the include/exclude interface behavior
# Check that the interface is not included
assert "MyInterfaceX" not in content

slither.include_interfaces = True
output = printer.output("test_printer.dot")
content = output.elements[0]["name"]["content"]
assert "MyInterfaceX" in content

# Remove test generated files
Path("test_printer.dot").unlink(missing_ok=True)

0 comments on commit ee7023c

Please sign in to comment.