diff --git a/slither/solc_parsing/solidity_types/type_parsing.py b/slither/solc_parsing/solidity_types/type_parsing.py index c5b11b33f3..54faf65f2f 100644 --- a/slither/solc_parsing/solidity_types/type_parsing.py +++ b/slither/solc_parsing/solidity_types/type_parsing.py @@ -295,13 +295,24 @@ def parse_type( all_structuress = [c.structures for c in scope.contracts.values()] all_structures = [item for sublist in all_structuress for item in sublist] all_structures += structures_direct_access + # Include types from accessible (imported) scopes + for accessible in scope.accessible_scopes: + all_structures += list(accessible.structures.values()) + for c in accessible.contracts.values(): + all_structures += c.structures enums_direct_access = [] all_enumss = [c.enums for c in scope.contracts.values()] all_enums = [item for sublist in all_enumss for item in sublist] all_enums += scope.enums.values() - - contracts = scope.contracts.values() + for accessible in scope.accessible_scopes: + all_enums += list(accessible.enums.values()) + for c in accessible.contracts.values(): + all_enums += c.enums + + contracts = list(scope.contracts.values()) + for accessible in scope.accessible_scopes: + contracts += list(accessible.contracts.values()) functions = list(scope.functions) renaming = scope.renaming @@ -326,12 +337,23 @@ def parse_type( all_structuress = [c.structures for c in scope.contracts.values()] all_structures = [item for sublist in all_structuress for item in sublist] all_structures += scope.structures.values() + # Include types from accessible (imported) scopes + for accessible in scope.accessible_scopes: + all_structures += list(accessible.structures.values()) + for c in accessible.contracts.values(): + all_structures += c.structures enums_direct_access += contract.enums enums_direct_access += scope.enums.values() all_enumss = [c.enums for c in scope.contracts.values()] all_enums = [item for sublist in all_enumss for item in sublist] all_enums += scope.enums.values() - contracts = scope.contracts.values() + for accessible in scope.accessible_scopes: + all_enums += list(accessible.enums.values()) + for c in accessible.contracts.values(): + all_enums += c.enums + contracts = list(scope.contracts.values()) + for accessible in scope.accessible_scopes: + contracts += list(accessible.contracts.values()) functions = contract.functions + contract.modifiers renaming = scope.renaming diff --git a/tests/unit/core/test_data/imported_struct_in_library/Consumer.sol b/tests/unit/core/test_data/imported_struct_in_library/Consumer.sol new file mode 100644 index 0000000000..b3026371ef --- /dev/null +++ b/tests/unit/core/test_data/imported_struct_in_library/Consumer.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Structs} from "./Structs.sol"; + +contract Consumer { + Structs.ShiftChanges public currentChanges; + Structs.Nested public nestedData; + + function updateShift(uint256 oldShift, uint256 newShift) external { + currentChanges = Structs.ShiftChanges(oldShift, newShift); + } + + function getChanges() external view returns (uint256, uint256) { + return (currentChanges.oldShift, currentChanges.newShift); + } +} diff --git a/tests/unit/core/test_data/imported_struct_in_library/Structs.sol b/tests/unit/core/test_data/imported_struct_in_library/Structs.sol new file mode 100644 index 0000000000..7110d44bd4 --- /dev/null +++ b/tests/unit/core/test_data/imported_struct_in_library/Structs.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +library Structs { + struct ShiftChanges { + uint256 oldShift; + uint256 newShift; + } + + struct Nested { + ShiftChanges changes; + address updater; + } +} diff --git a/tests/unit/core/test_imported_struct.py b/tests/unit/core/test_imported_struct.py new file mode 100644 index 0000000000..8ee35ce94f --- /dev/null +++ b/tests/unit/core/test_imported_struct.py @@ -0,0 +1,24 @@ +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" +IMPORTED_STRUCT_TEST_DATA_DIR = Path(TEST_DATA_DIR, "imported_struct_in_library") + + +# https://github.com/crytic/slither/issues/2954 +def test_imported_struct_from_library(solc_binary_path) -> None: + """Test that structs defined in an imported library are resolved correctly.""" + solc_path = solc_binary_path("0.8.0") + standard_json = SolcStandardJson() + for source_file in IMPORTED_STRUCT_TEST_DATA_DIR.rglob("**/*.sol"): + standard_json.add_source_file(Path(source_file).as_posix()) + compilation = CryticCompile(standard_json, solc=solc_path) + sl = Slither(compilation, disallow_partial=True) + + # Verify the struct types were resolved correctly + consumer = next(c for c in sl.contracts if c.name == "Consumer") + changes_var = next(v for v in consumer.state_variables if v.name == "currentChanges") + assert "ShiftChanges" in str(changes_var.type)