slither.vyper_parsing.type_parsing

 1from typing import Union
 2from slither.core.solidity_types.elementary_type import (
 3    ElementaryType,
 4    ElementaryTypeName,
 5)  # TODO rename solidity type
 6from slither.core.solidity_types.array_type import ArrayType
 7from slither.core.solidity_types.mapping_type import MappingType
 8from slither.core.solidity_types.user_defined_type import UserDefinedType
 9from slither.core.declarations import FunctionContract, Contract
10from slither.vyper_parsing.ast.types import Name, Subscript, Call, Index, Tuple
11from slither.solc_parsing.exceptions import ParsingError
12
13# pylint: disable=too-many-branches,too-many-return-statements,import-outside-toplevel,too-many-locals
14def parse_type(
15    annotation: Union[Name, Subscript, Call, Tuple],
16    caller_context: Union[FunctionContract, Contract],
17):
18    from slither.vyper_parsing.expressions.expression_parsing import parse_expression
19
20    if isinstance(caller_context, FunctionContract):
21        contract = caller_context.contract
22    else:
23        contract = caller_context
24
25    assert isinstance(annotation, (Name, Subscript, Call, Tuple))
26
27    if isinstance(annotation, Name):
28        name = annotation.id
29        lname = name.lower()  # map `String` to string
30        if lname in ElementaryTypeName:
31            return ElementaryType(lname)
32
33        if name in contract.structures_as_dict:
34            return UserDefinedType(contract.structures_as_dict[name])
35
36        if name in contract.enums_as_dict:
37            return UserDefinedType(contract.enums_as_dict[name])
38
39        if name in contract.file_scope.contracts:
40            return UserDefinedType(contract.file_scope.contracts[name])
41
42        if name in contract.file_scope.structures:
43            return UserDefinedType(contract.file_scope.structures[name])
44    elif isinstance(annotation, Subscript):
45        assert isinstance(annotation.slice, Index)
46        # This is also a strange construct... https://github.com/vyperlang/vyper/issues/3577
47        if isinstance(annotation.slice.value, Tuple):
48            assert isinstance(annotation.value, Name)
49            if annotation.value.id == "DynArray":
50                type_ = parse_type(annotation.slice.value.elements[0], caller_context)
51                length = parse_expression(annotation.slice.value.elements[1], caller_context)
52                return ArrayType(type_, length)
53            if annotation.value.id == "HashMap":
54                type_from = parse_type(annotation.slice.value.elements[0], caller_context)
55                type_to = parse_type(annotation.slice.value.elements[1], caller_context)
56
57                return MappingType(type_from, type_to)
58
59        elif isinstance(annotation.value, Subscript):
60            type_ = parse_type(annotation.value, caller_context)
61
62        elif isinstance(annotation.value, Name):
63            # TODO it is weird that the ast_type is `Index` when it's a type annotation and not an expression, so we grab the value. https://github.com/vyperlang/vyper/issues/3577
64            type_ = parse_type(annotation.value, caller_context)
65            if annotation.value.id == "String":
66                # This is an elementary type
67                return type_
68
69        length = parse_expression(annotation.slice.value, caller_context)
70        return ArrayType(type_, length)
71
72    elif isinstance(annotation, Call):
73        # TODO event variable represented as Call https://github.com/vyperlang/vyper/issues/3579
74        return parse_type(annotation.args[0], caller_context)
75
76    elif isinstance(annotation, Tuple):
77        # Vyper has tuple types like python x = f() where f() -> (y,z)
78        # and tuple elements can be unpacked like x[0]: y and x[1]: z.
79        # We model these as a struct and unpack each index into a field
80        # e.g. accessing the 0th element is translated as x._0
81        from slither.core.declarations.structure import Structure
82        from slither.core.variables.structure_variable import StructureVariable
83
84        st = Structure(caller_context.compilation_unit)
85        st.set_offset("-1:-1:-1", caller_context.compilation_unit)
86        st.name = "FAKE_TUPLE"
87        for idx, elem_info in enumerate(annotation.elements):
88            elem = StructureVariable()
89            elem.type = parse_type(elem_info, caller_context)
90            elem.name = f"_{idx}"
91            elem.set_structure(st)
92            elem.set_offset("-1:-1:-1", caller_context.compilation_unit)
93            st.elems[elem.name] = elem
94            st.add_elem_in_order(elem.name)
95            st.name += elem.name
96
97        return UserDefinedType(st)
98
99    raise ParsingError(f"Type name not found {name} context {caller_context}")
 15def parse_type(
 16    annotation: Union[Name, Subscript, Call, Tuple],
 17    caller_context: Union[FunctionContract, Contract],
 18):
 19    from slither.vyper_parsing.expressions.expression_parsing import parse_expression
 20
 21    if isinstance(caller_context, FunctionContract):
 22        contract = caller_context.contract
 23    else:
 24        contract = caller_context
 25
 26    assert isinstance(annotation, (Name, Subscript, Call, Tuple))
 27
 28    if isinstance(annotation, Name):
 29        name = annotation.id
 30        lname = name.lower()  # map `String` to string
 31        if lname in ElementaryTypeName:
 32            return ElementaryType(lname)
 33
 34        if name in contract.structures_as_dict:
 35            return UserDefinedType(contract.structures_as_dict[name])
 36
 37        if name in contract.enums_as_dict:
 38            return UserDefinedType(contract.enums_as_dict[name])
 39
 40        if name in contract.file_scope.contracts:
 41            return UserDefinedType(contract.file_scope.contracts[name])
 42
 43        if name in contract.file_scope.structures:
 44            return UserDefinedType(contract.file_scope.structures[name])
 45    elif isinstance(annotation, Subscript):
 46        assert isinstance(annotation.slice, Index)
 47        # This is also a strange construct... https://github.com/vyperlang/vyper/issues/3577
 48        if isinstance(annotation.slice.value, Tuple):
 49            assert isinstance(annotation.value, Name)
 50            if annotation.value.id == "DynArray":
 51                type_ = parse_type(annotation.slice.value.elements[0], caller_context)
 52                length = parse_expression(annotation.slice.value.elements[1], caller_context)
 53                return ArrayType(type_, length)
 54            if annotation.value.id == "HashMap":
 55                type_from = parse_type(annotation.slice.value.elements[0], caller_context)
 56                type_to = parse_type(annotation.slice.value.elements[1], caller_context)
 57
 58                return MappingType(type_from, type_to)
 59
 60        elif isinstance(annotation.value, Subscript):
 61            type_ = parse_type(annotation.value, caller_context)
 62
 63        elif isinstance(annotation.value, Name):
 64            # TODO it is weird that the ast_type is `Index` when it's a type annotation and not an expression, so we grab the value. https://github.com/vyperlang/vyper/issues/3577
 65            type_ = parse_type(annotation.value, caller_context)
 66            if annotation.value.id == "String":
 67                # This is an elementary type
 68                return type_
 69
 70        length = parse_expression(annotation.slice.value, caller_context)
 71        return ArrayType(type_, length)
 72
 73    elif isinstance(annotation, Call):
 74        # TODO event variable represented as Call https://github.com/vyperlang/vyper/issues/3579
 75        return parse_type(annotation.args[0], caller_context)
 76
 77    elif isinstance(annotation, Tuple):
 78        # Vyper has tuple types like python x = f() where f() -> (y,z)
 79        # and tuple elements can be unpacked like x[0]: y and x[1]: z.
 80        # We model these as a struct and unpack each index into a field
 81        # e.g. accessing the 0th element is translated as x._0
 82        from slither.core.declarations.structure import Structure
 83        from slither.core.variables.structure_variable import StructureVariable
 84
 85        st = Structure(caller_context.compilation_unit)
 86        st.set_offset("-1:-1:-1", caller_context.compilation_unit)
 87        st.name = "FAKE_TUPLE"
 88        for idx, elem_info in enumerate(annotation.elements):
 89            elem = StructureVariable()
 90            elem.type = parse_type(elem_info, caller_context)
 91            elem.name = f"_{idx}"
 92            elem.set_structure(st)
 93            elem.set_offset("-1:-1:-1", caller_context.compilation_unit)
 94            st.elems[elem.name] = elem
 95            st.add_elem_in_order(elem.name)
 96            st.name += elem.name
 97
 98        return UserDefinedType(st)
 99
100    raise ParsingError(f"Type name not found {name} context {caller_context}")