diff --git a/slither/core/declarations/__init__.py b/slither/core/declarations/__init__.py index f341187518..f6e902e06f 100644 --- a/slither/core/declarations/__init__.py +++ b/slither/core/declarations/__init__.py @@ -20,3 +20,4 @@ from .custom_error_contract import CustomErrorContract from .custom_error_top_level import CustomErrorTopLevel from .custom_error import CustomError +from .solidity_import_placeholder import SolidityImportPlaceHolder diff --git a/slither/visitors/slithir/expression_to_slithir.py b/slither/visitors/slithir/expression_to_slithir.py index 0c439151b9..931dc82ae8 100644 --- a/slither/visitors/slithir/expression_to_slithir.py +++ b/slither/visitors/slithir/expression_to_slithir.py @@ -2,6 +2,7 @@ from typing import Union, List, TYPE_CHECKING, Any from slither.core import expressions +from slither.core.scope.scope import FileScope from slither.core.declarations import ( Function, SolidityVariable, @@ -11,6 +12,8 @@ EnumContract, EnumTopLevel, Enum, + SolidityImportPlaceHolder, + Import, Structure, ) from slither.core.expressions import ( @@ -600,12 +603,76 @@ def _post_member_access(self, expression: MemberAccess) -> None: set_val(expression, expr.enums_as_dict[str(expression)]) return + if isinstance(expr, (SolidityImportPlaceHolder, Import)): + scope = ( + expr.import_directive.scope + if isinstance(expr, SolidityImportPlaceHolder) + else expr.scope + ) + if self._check_elem_in_scope(expression.member_name, scope, expression): + return + val_ref = ReferenceVariable(self._node) member = Member(expr, Constant(expression.member_name), val_ref) member.set_expression(expression) self._result.append(member) set_val(expression, val_ref) + def _check_elem_in_scope(self, elem: str, scope: FileScope, expression: MemberAccess) -> bool: + if elem in scope.renaming: + self._check_elem_in_scope(scope.renaming[elem], scope, expression) + return True + + if elem in scope.contracts: + set_val(expression, scope.contracts[elem]) + return True + + if elem in scope.structures: + set_val(expression, scope.structures[elem]) + return True + + if elem in scope.variables: + set_val(expression, scope.variables[elem]) + return True + + if elem in scope.enums: + set_val(expression, scope.enums[elem]) + return True + + if elem in scope.type_aliases: + set_val(expression, scope.type_aliases[elem]) + return True + + for import_directive in scope.imports: + if elem == import_directive.alias: + set_val(expression, import_directive) + return True + + for custom_error in scope.custom_errors: + if custom_error.name == elem: + set_val(expression, custom_error) + return True + + if str(expression.type).startswith("function "): + # This is needed to handle functions overloading + signature_to_seaarch = ( + str(expression.type) + .replace("function ", elem) + .replace("pure ", "") + .replace("view ", "") + .replace("struct ", "") + .replace("enum ", "") + .replace(" memory", "") + .split(" returns", maxsplit=1)[0] + ) + + for function in scope.functions: + if signature_to_seaarch == function.full_name: + set_val(expression, function) + return True + + return False + def _post_new_array(self, expression: NewArray) -> None: val = TemporaryVariable(self._node) operation = TmpNewArray(expression.array_type, val) diff --git a/tests/e2e/solc_parsing/test_ast_parsing.py b/tests/e2e/solc_parsing/test_ast_parsing.py index 37f6d7274b..bc57dc51b5 100644 --- a/tests/e2e/solc_parsing/test_ast_parsing.py +++ b/tests/e2e/solc_parsing/test_ast_parsing.py @@ -459,6 +459,7 @@ def make_version(minor: int, patch_min: int, patch_max: int) -> List[str]: ["0.6.9", "0.7.6", "0.8.16"], ), Test("user_defined_operators-0.8.19.sol", ["0.8.19"]), + Test("aliasing/main.sol", ["0.8.19"]), Test("type-aliases.sol", ["0.8.19"]), Test("enum-max-min.sol", ["0.8.19"]), ] diff --git a/tests/e2e/solc_parsing/test_data/aliasing/l1.sol b/tests/e2e/solc_parsing/test_data/aliasing/l1.sol new file mode 100644 index 0000000000..86cb0bd5fa --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/aliasing/l1.sol @@ -0,0 +1,24 @@ +import "./l2.sol" as L2; +import {MyErr as MyImportError} from "./l3.sol"; + +type fd is uint; +error MyError(); +uint constant qwe = 34; + +struct SS { + uint g; +} + +enum MyEnum { + A, + B +} + +function tpf(MyEnum p) returns(uint) {return 4;} +function tpf(fd p) pure returns(uint) {return 4;} +function tpf(uint e, SS memory g) view returns(uint) {return 4;} + +library MyC { + function callme() public {} + +} diff --git a/tests/e2e/solc_parsing/test_data/aliasing/l2.sol b/tests/e2e/solc_parsing/test_data/aliasing/l2.sol new file mode 100644 index 0000000000..14d3b5fbad --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/aliasing/l2.sol @@ -0,0 +1 @@ +function l() {} \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/aliasing/l3.sol b/tests/e2e/solc_parsing/test_data/aliasing/l3.sol new file mode 100644 index 0000000000..1c92842a98 --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/aliasing/l3.sol @@ -0,0 +1 @@ +error MyErr(); \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/aliasing/main.sol b/tests/e2e/solc_parsing/test_data/aliasing/main.sol new file mode 100644 index 0000000000..c725b96c53 --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/aliasing/main.sol @@ -0,0 +1,20 @@ +import "./l1.sol" as R; + +contract Tp { + + function re(R.SS calldata param1, R.MyEnum param2) public { + R.MyEnum a = R.MyEnum.A; + R.SS memory b = R.SS(R.qwe); + R.MyC.callme(); + R.tpf(2, param1); + R.tpf(param2); + R.tpf(R.fd.wrap(4)); + R.L2.l(); + revert R.MyImportError(); + } + + function re2() public { + revert R.MyError(); + } + +} \ No newline at end of file diff --git a/tests/e2e/solc_parsing/test_data/compile/aliasing/main.sol-0.8.19-compact.zip b/tests/e2e/solc_parsing/test_data/compile/aliasing/main.sol-0.8.19-compact.zip new file mode 100644 index 0000000000..cbfa8d909e Binary files /dev/null and b/tests/e2e/solc_parsing/test_data/compile/aliasing/main.sol-0.8.19-compact.zip differ diff --git a/tests/e2e/solc_parsing/test_data/expected/aliasing/main.sol-0.8.19-compact.json b/tests/e2e/solc_parsing/test_data/expected/aliasing/main.sol-0.8.19-compact.json new file mode 100644 index 0000000000..4aab8998ce --- /dev/null +++ b/tests/e2e/solc_parsing/test_data/expected/aliasing/main.sol-0.8.19-compact.json @@ -0,0 +1,9 @@ +{ + "MyC": { + "callme()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n}\n" + }, + "Tp": { + "re(SS,MyEnum)": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: NEW VARIABLE 1\n\"];\n1->2;\n2[label=\"Node Type: NEW VARIABLE 2\n\"];\n2->3;\n3[label=\"Node Type: EXPRESSION 3\n\"];\n3->4;\n4[label=\"Node Type: EXPRESSION 4\n\"];\n4->5;\n5[label=\"Node Type: EXPRESSION 5\n\"];\n5->6;\n6[label=\"Node Type: EXPRESSION 6\n\"];\n6->7;\n7[label=\"Node Type: EXPRESSION 7\n\"];\n7->8;\n8[label=\"Node Type: EXPRESSION 8\n\"];\n}\n", + "re2()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file