Merge pull request #2320 from crytic/fix/inheritance-renaming

fix: support renaming in base inheritance and base constructor calls
pull/2146/head
alpharush 9 months ago committed by GitHub
commit 942adb176d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 57
      slither/solc_parsing/slither_compilation_unit_solc.py
  2. 4
      tests/unit/core/test_data/inheritance_with_renaming/a.sol
  3. 4
      tests/unit/core/test_data/inheritance_with_renaming/b.sol
  4. 3
      tests/unit/core/test_data/inheritance_with_renaming/c.sol
  5. 36
      tests/unit/core/test_inheritance.py

@ -432,61 +432,56 @@ Please rename it, this name is reserved for Slither's internals"""
self._contracts_by_id[contract.id] = contract self._contracts_by_id[contract.id] = contract
self._compilation_unit.contracts.append(contract) self._compilation_unit.contracts.append(contract)
def resolve_remapping_and_renaming(contract_parser: ContractSolc, want: str) -> Contract:
contract_name = contract_parser.remapping[want]
if contract_name in contract_parser.underlying_contract.file_scope.renaming:
contract_name = contract_parser.underlying_contract.file_scope.renaming[
contract_name
]
target = contract_parser.underlying_contract.file_scope.get_contract_from_name(
contract_name
)
if target == contract_parser.underlying_contract:
raise InheritanceResolutionError(
"Could not resolve contract inheritance. This is likely caused by an import renaming that collides with existing names (see https://github.com/crytic/slither/issues/1758)."
f"\n Try changing `contract {target}` ({target.source_mapping}) to a unique name."
)
assert target, f"Contract {contract_name} not found"
return target
# Update of the inheritance # Update of the inheritance
for contract_parser in self._underlying_contract_to_parser.values(): for contract_parser in self._underlying_contract_to_parser.values():
# remove the first elem in linearizedBaseContracts as it is the contract itself
ancestors = [] ancestors = []
fathers = [] fathers = []
father_constructors = [] father_constructors = []
# try:
# Resolve linearized base contracts.
missing_inheritance = None missing_inheritance = None
# Resolve linearized base contracts.
# Remove the first elem in linearizedBaseContracts as it is the contract itself.
for i in contract_parser.linearized_base_contracts[1:]: for i in contract_parser.linearized_base_contracts[1:]:
if i in contract_parser.remapping: if i in contract_parser.remapping:
contract_name = contract_parser.remapping[i] target = resolve_remapping_and_renaming(contract_parser, i)
if contract_name in contract_parser.underlying_contract.file_scope.renaming:
contract_name = contract_parser.underlying_contract.file_scope.renaming[
contract_name
]
target = contract_parser.underlying_contract.file_scope.get_contract_from_name(
contract_name
)
if target == contract_parser.underlying_contract:
raise InheritanceResolutionError(
"Could not resolve contract inheritance. This is likely caused by an import renaming that collides with existing names (see https://github.com/crytic/slither/issues/1758)."
f"\n Try changing `contract {target}` ({target.source_mapping}) to a unique name."
)
assert target, f"Contract {contract_name} not found"
ancestors.append(target) ancestors.append(target)
elif i in self._contracts_by_id: elif i in self._contracts_by_id:
ancestors.append(self._contracts_by_id[i]) ancestors.append(self._contracts_by_id[i])
else: else:
missing_inheritance = i missing_inheritance = i
# Resolve immediate base contracts # Resolve immediate base contracts.
for i in contract_parser.baseContracts: for i in contract_parser.baseContracts:
if i in contract_parser.remapping: if i in contract_parser.remapping:
fathers.append( target = resolve_remapping_and_renaming(contract_parser, i)
contract_parser.underlying_contract.file_scope.get_contract_from_name( fathers.append(target)
contract_parser.remapping[i]
)
# self._compilation_unit.get_contract_from_name(contract_parser.remapping[i])
)
elif i in self._contracts_by_id: elif i in self._contracts_by_id:
fathers.append(self._contracts_by_id[i]) fathers.append(self._contracts_by_id[i])
else: else:
missing_inheritance = i missing_inheritance = i
# Resolve immediate base constructor calls # Resolve immediate base constructor calls.
for i in contract_parser.baseConstructorContractsCalled: for i in contract_parser.baseConstructorContractsCalled:
if i in contract_parser.remapping: if i in contract_parser.remapping:
father_constructors.append( target = resolve_remapping_and_renaming(contract_parser, i)
contract_parser.underlying_contract.file_scope.get_contract_from_name( father_constructors.append(target)
contract_parser.remapping[i]
)
# self._compilation_unit.get_contract_from_name(contract_parser.remapping[i])
)
elif i in self._contracts_by_id: elif i in self._contracts_by_id:
father_constructors.append(self._contracts_by_id[i]) father_constructors.append(self._contracts_by_id[i])
else: else:

@ -0,0 +1,4 @@
import {B as Base} from "./b.sol";
contract A is Base(address(0)) {
constructor (address x) {}
}

@ -0,0 +1,4 @@
import {C as C2} from "./c.sol";
contract B is C2 {
constructor (address x) C2(x) {}
}

@ -0,0 +1,3 @@
contract C {
constructor (address) {}
}

@ -0,0 +1,36 @@
from pathlib import Path
from crytic_compile import CryticCompile
from crytic_compile.platform.solc_standard_json import SolcStandardJson
from slither import Slither
TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data" / "inheritance_with_renaming"
# https://github.com/crytic/slither/issues/2304
def test_inheritance_with_renaming(solc_binary_path) -> None:
solc_path = solc_binary_path("0.8.15")
standard_json = SolcStandardJson()
for source_file in Path(TEST_DATA_DIR).rglob("*.sol"):
print(source_file)
standard_json.add_source_file(Path(source_file).as_posix())
compilation = CryticCompile(standard_json, solc=solc_path)
slither = Slither(compilation)
a = slither.get_contract_from_name("A")[0]
b = slither.get_contract_from_name("B")[0]
c = slither.get_contract_from_name("C")[0]
assert len(a.immediate_inheritance) == 1
assert a.immediate_inheritance[0] == b
assert len(a.inheritance) == 2
assert a.inheritance[0] == b
assert a.inheritance[1] == c
assert len(a.explicit_base_constructor_calls) == 1
a_base_constructor_call = a.explicit_base_constructor_calls[0]
assert a_base_constructor_call == b.constructor
assert len(b.inheritance) == 1
assert b.inheritance[0] == c
assert len(b.immediate_inheritance) == 1
assert b.immediate_inheritance[0] == c
assert len(b.explicit_base_constructor_calls) == 0
Loading…
Cancel
Save