Browse Source

Merge pull request #728 from crytic/dev-top-level-elements

Improve top level objects support
Feist Josselin 2 months ago
parent
commit
7d3bf292ad
100 changed files with 1267 additions and 452 deletions
  1. 2 0
      slither/core/declarations/__init__.py
  2. 15 14
      slither/core/declarations/contract.py
  3. 2 14
      slither/core/declarations/enum.py
  4. 17 0
      slither/core/declarations/enum_contract.py
  5. 6 0
      slither/core/declarations/enum_top_level.py
  6. 19 61
      slither/core/declarations/function.py
  7. 106 0
      slither/core/declarations/function_contract.py
  8. 80 0
      slither/core/declarations/function_top_level.py
  9. 11 0
      slither/core/declarations/import_directive.py
  10. 2 2
      slither/core/declarations/modifier.py
  11. 35 2
      slither/core/declarations/solidity_variables.py
  12. 1 10
      slither/core/declarations/structure.py
  13. 12 0
      slither/core/declarations/structure_contract.py
  14. 6 0
      slither/core/declarations/structure_top_level.py
  15. 5 0
      slither/core/declarations/top_level.py
  16. 28 13
      slither/core/slither_core.py
  17. 36 0
      slither/core/variables/top_level_variable.py
  18. 24 19
      slither/printers/summary/slithir.py
  19. 3 3
      slither/slither.py
  20. 28 20
      slither/slithir/convert.py
  21. 4 1
      slither/slithir/operations/internal_call.py
  22. 63 52
      slither/solc_parsing/declarations/contract.py
  23. 15 6
      slither/solc_parsing/declarations/function.py
  24. 9 2
      slither/solc_parsing/declarations/modifier.py
  25. 19 10
      slither/solc_parsing/declarations/structure.py
  26. 56 0
      slither/solc_parsing/declarations/structure_top_level.py
  27. 206 78
      slither/solc_parsing/expressions/expression_parsing.py
  28. 126 61
      slither/solc_parsing/slitherSolc.py
  29. 136 48
      slither/solc_parsing/solidity_types/type_parsing.py
  30. 15 0
      slither/solc_parsing/variables/top_level_variable.py
  31. 8 4
      slither/solc_parsing/yul/parse_yul.py
  32. 1 2
      tests/ast-parsing/expected/struct-0.6.0-compact.json
  33. 1 2
      tests/ast-parsing/expected/struct-0.6.1-compact.json
  34. 1 2
      tests/ast-parsing/expected/struct-0.6.10-compact.json
  35. 1 2
      tests/ast-parsing/expected/struct-0.6.11-compact.json
  36. 1 2
      tests/ast-parsing/expected/struct-0.6.12-compact.json
  37. 1 2
      tests/ast-parsing/expected/struct-0.6.2-compact.json
  38. 1 2
      tests/ast-parsing/expected/struct-0.6.3-compact.json
  39. 1 2
      tests/ast-parsing/expected/struct-0.6.4-compact.json
  40. 1 2
      tests/ast-parsing/expected/struct-0.6.5-compact.json
  41. 1 2
      tests/ast-parsing/expected/struct-0.6.6-compact.json
  42. 1 2
      tests/ast-parsing/expected/struct-0.6.7-compact.json
  43. 1 2
      tests/ast-parsing/expected/struct-0.6.8-compact.json
  44. 1 2
      tests/ast-parsing/expected/struct-0.6.9-compact.json
  45. 1 2
      tests/ast-parsing/expected/struct-0.7.0-compact.json
  46. 1 2
      tests/ast-parsing/expected/struct-0.7.1-compact.json
  47. 1 2
      tests/ast-parsing/expected/struct-0.7.2-compact.json
  48. 3 0
      tests/ast-parsing/expected/top-level-0.4.0-legacy.json
  49. 3 0
      tests/ast-parsing/expected/top-level-0.4.1-legacy.json
  50. 3 0
      tests/ast-parsing/expected/top-level-0.4.10-legacy.json
  51. 3 0
      tests/ast-parsing/expected/top-level-0.4.11-legacy.json
  52. 3 0
      tests/ast-parsing/expected/top-level-0.4.12-compact.json
  53. 3 0
      tests/ast-parsing/expected/top-level-0.4.12-legacy.json
  54. 3 0
      tests/ast-parsing/expected/top-level-0.4.13-compact.json
  55. 3 0
      tests/ast-parsing/expected/top-level-0.4.13-legacy.json
  56. 3 0
      tests/ast-parsing/expected/top-level-0.4.14-compact.json
  57. 3 0
      tests/ast-parsing/expected/top-level-0.4.14-legacy.json
  58. 3 0
      tests/ast-parsing/expected/top-level-0.4.15-compact.json
  59. 3 0
      tests/ast-parsing/expected/top-level-0.4.15-legacy.json
  60. 3 0
      tests/ast-parsing/expected/top-level-0.4.16-compact.json
  61. 3 0
      tests/ast-parsing/expected/top-level-0.4.16-legacy.json
  62. 3 0
      tests/ast-parsing/expected/top-level-0.4.17-compact.json
  63. 3 0
      tests/ast-parsing/expected/top-level-0.4.17-legacy.json
  64. 3 0
      tests/ast-parsing/expected/top-level-0.4.18-compact.json
  65. 3 0
      tests/ast-parsing/expected/top-level-0.4.18-legacy.json
  66. 3 0
      tests/ast-parsing/expected/top-level-0.4.19-compact.json
  67. 3 0
      tests/ast-parsing/expected/top-level-0.4.19-legacy.json
  68. 3 0
      tests/ast-parsing/expected/top-level-0.4.2-legacy.json
  69. 3 0
      tests/ast-parsing/expected/top-level-0.4.20-compact.json
  70. 3 0
      tests/ast-parsing/expected/top-level-0.4.20-legacy.json
  71. 3 0
      tests/ast-parsing/expected/top-level-0.4.21-compact.json
  72. 3 0
      tests/ast-parsing/expected/top-level-0.4.21-legacy.json
  73. 3 0
      tests/ast-parsing/expected/top-level-0.4.22-compact.json
  74. 3 0
      tests/ast-parsing/expected/top-level-0.4.22-legacy.json
  75. 3 0
      tests/ast-parsing/expected/top-level-0.4.23-compact.json
  76. 3 0
      tests/ast-parsing/expected/top-level-0.4.23-legacy.json
  77. 3 0
      tests/ast-parsing/expected/top-level-0.4.24-compact.json
  78. 3 0
      tests/ast-parsing/expected/top-level-0.4.24-legacy.json
  79. 3 0
      tests/ast-parsing/expected/top-level-0.4.25-compact.json
  80. 3 0
      tests/ast-parsing/expected/top-level-0.4.25-legacy.json
  81. 3 0
      tests/ast-parsing/expected/top-level-0.4.26-compact.json
  82. 3 0
      tests/ast-parsing/expected/top-level-0.4.26-legacy.json
  83. 3 0
      tests/ast-parsing/expected/top-level-0.4.3-legacy.json
  84. 3 0
      tests/ast-parsing/expected/top-level-0.4.4-legacy.json
  85. 3 0
      tests/ast-parsing/expected/top-level-0.4.5-legacy.json
  86. 3 0
      tests/ast-parsing/expected/top-level-0.4.6-legacy.json
  87. 3 0
      tests/ast-parsing/expected/top-level-0.4.7-legacy.json
  88. 3 0
      tests/ast-parsing/expected/top-level-0.4.8-legacy.json
  89. 3 0
      tests/ast-parsing/expected/top-level-0.4.9-legacy.json
  90. 3 0
      tests/ast-parsing/expected/top-level-0.5.0-compact.json
  91. 3 0
      tests/ast-parsing/expected/top-level-0.5.0-legacy.json
  92. 3 0
      tests/ast-parsing/expected/top-level-0.5.1-compact.json
  93. 3 0
      tests/ast-parsing/expected/top-level-0.5.1-legacy.json
  94. 3 0
      tests/ast-parsing/expected/top-level-0.5.10-compact.json
  95. 3 0
      tests/ast-parsing/expected/top-level-0.5.10-legacy.json
  96. 3 0
      tests/ast-parsing/expected/top-level-0.5.11-compact.json
  97. 3 0
      tests/ast-parsing/expected/top-level-0.5.11-legacy.json
  98. 3 0
      tests/ast-parsing/expected/top-level-0.5.12-compact.json
  99. 3 0
      tests/ast-parsing/expected/top-level-0.5.12-legacy.json
  100. 0 0
      tests/ast-parsing/expected/top-level-0.5.13-compact.json

+ 2 - 0
slither/core/declarations/__init__.py

@@ -11,3 +11,5 @@ from .solidity_variables import (
     SolidityFunction,
 )
 from .structure import Structure
+from .enum_contract import EnumContract
+from .structure_contract import StructureContract

+ 15 - 14
slither/core/declarations/contract.py

@@ -25,8 +25,7 @@ from slither.utils.tests_pattern import is_test_contract
 # pylint: disable=too-many-lines,too-many-instance-attributes,import-outside-toplevel,too-many-nested-blocks
 if TYPE_CHECKING:
     from slither.utils.type_helpers import LibraryCallType, HighLevelCallType, InternalCallType
-    from slither.core.declarations import Enum, Event, Modifier
-    from slither.core.declarations import Structure
+    from slither.core.declarations import Enum, Event, Modifier, EnumContract, StructureContract
     from slither.slithir.variables.variable import SlithIRVariable
     from slither.core.variables.variable import Variable
     from slither.core.variables.state_variable import StateVariable
@@ -51,8 +50,8 @@ class Contract(ChildSlither, SourceMapping):  # pylint: disable=too-many-public-
         # contract B is A(1) { ..
         self._explicit_base_constructor_calls: List["Contract"] = []
 
-        self._enums: Dict[str, "Enum"] = {}
-        self._structures: Dict[str, "Structure"] = {}
+        self._enums: Dict[str, "EnumContract"] = {}
+        self._structures: Dict[str, "StructureContract"] = {}
         self._events: Dict[str, "Event"] = {}
         self._variables: Dict[str, "StateVariable"] = {}
         self._variables_ordered: List["StateVariable"] = []
@@ -135,28 +134,28 @@ class Contract(ChildSlither, SourceMapping):  # pylint: disable=too-many-public-
     ###################################################################################
 
     @property
-    def structures(self) -> List["Structure"]:
+    def structures(self) -> List["StructureContract"]:
         """
         list(Structure): List of the structures
         """
         return list(self._structures.values())
 
     @property
-    def structures_inherited(self) -> List["Structure"]:
+    def structures_inherited(self) -> List["StructureContract"]:
         """
         list(Structure): List of the inherited structures
         """
         return [s for s in self.structures if s.contract != self]
 
     @property
-    def structures_declared(self) -> List["Structure"]:
+    def structures_declared(self) -> List["StructureContract"]:
         """
         list(Structues): List of the structures declared within the contract (not inherited)
         """
         return [s for s in self.structures if s.contract == self]
 
     @property
-    def structures_as_dict(self) -> Dict[str, "Structure"]:
+    def structures_as_dict(self) -> Dict[str, "StructureContract"]:
         return self._structures
 
     # endregion
@@ -167,25 +166,25 @@ class Contract(ChildSlither, SourceMapping):  # pylint: disable=too-many-public-
     ###################################################################################
 
     @property
-    def enums(self) -> List["Enum"]:
+    def enums(self) -> List["EnumContract"]:
         return list(self._enums.values())
 
     @property
-    def enums_inherited(self) -> List["Enum"]:
+    def enums_inherited(self) -> List["EnumContract"]:
         """
         list(Enum): List of the inherited enums
         """
         return [e for e in self.enums if e.contract != self]
 
     @property
-    def enums_declared(self) -> List["Enum"]:
+    def enums_declared(self) -> List["EnumContract"]:
         """
         list(Enum): List of the enums declared within the contract (not inherited)
         """
         return [e for e in self.enums if e.contract == self]
 
     @property
-    def enums_as_dict(self) -> Dict[str, "Enum"]:
+    def enums_as_dict(self) -> Dict[str, "EnumContract"]:
         return self._enums
 
     # endregion
@@ -1090,11 +1089,13 @@ class Contract(ChildSlither, SourceMapping):  # pylint: disable=too-many-public-
         self._is_incorrectly_parsed = incorrect
 
     def add_constructor_variables(self):
+        from slither.core.declarations.function_contract import FunctionContract
+
         if self.state_variables:
             for (idx, variable_candidate) in enumerate(self.state_variables):
                 if variable_candidate.expression and not variable_candidate.is_constant:
 
-                    constructor_variable = Function()
+                    constructor_variable = FunctionContract(self.slither)
                     constructor_variable.set_function_type(FunctionType.CONSTRUCTOR_VARIABLES)
                     constructor_variable.set_contract(self)
                     constructor_variable.set_contract_declarer(self)
@@ -1120,7 +1121,7 @@ class Contract(ChildSlither, SourceMapping):  # pylint: disable=too-many-public-
             for (idx, variable_candidate) in enumerate(self.state_variables):
                 if variable_candidate.expression and variable_candidate.is_constant:
 
-                    constructor_variable = Function()
+                    constructor_variable = FunctionContract(self.slither)
                     constructor_variable.set_function_type(
                         FunctionType.CONSTRUCTOR_CONSTANT_VARIABLES
                     )

+ 2 - 14
slither/core/declarations/enum.py

@@ -1,13 +1,9 @@
-from typing import List, TYPE_CHECKING
+from typing import List
 
 from slither.core.source_mapping.source_mapping import SourceMapping
-from slither.core.children.child_contract import ChildContract
 
-if TYPE_CHECKING:
-    from slither.core.declarations import Contract
 
-
-class Enum(ChildContract, SourceMapping):
+class Enum(SourceMapping):
     def __init__(self, name: str, canonical_name: str, values: List[str]):
         super().__init__()
         self._name = name
@@ -26,13 +22,5 @@ class Enum(ChildContract, SourceMapping):
     def values(self) -> List[str]:
         return self._values
 
-    def is_declared_by(self, contract: "Contract") -> bool:
-        """
-        Check if the element is declared by the contract
-        :param contract:
-        :return:
-        """
-        return self.contract == contract
-
     def __str__(self):
         return self.name

+ 17 - 0
slither/core/declarations/enum_contract.py

@@ -0,0 +1,17 @@
+from typing import TYPE_CHECKING
+
+from slither.core.children.child_contract import ChildContract
+from slither.core.declarations import Enum
+
+if TYPE_CHECKING:
+    from slither.core.declarations import Contract
+
+
+class EnumContract(Enum, ChildContract):
+    def is_declared_by(self, contract: "Contract") -> bool:
+        """
+        Check if the element is declared by the contract
+        :param contract:
+        :return:
+        """
+        return self.contract == contract

+ 6 - 0
slither/core/declarations/enum_top_level.py

@@ -0,0 +1,6 @@
+from slither.core.declarations import Enum
+from slither.core.declarations.top_level import TopLevel
+
+
+class EnumTopLevel(Enum, TopLevel):
+    pass

+ 19 - 61
slither/core/declarations/function.py

@@ -2,13 +2,12 @@
     Function module
 """
 import logging
+from abc import ABCMeta, abstractmethod
 from collections import namedtuple
 from enum import Enum
 from itertools import groupby
 from typing import Dict, TYPE_CHECKING, List, Optional, Set, Union, Callable, Tuple
 
-from slither.core.children.child_contract import ChildContract
-from slither.core.children.child_inheritance import ChildInheritance
 from slither.core.declarations.solidity_variables import (
     SolidityFunction,
     SolidityVariable,
@@ -24,7 +23,6 @@ from slither.core.solidity_types import UserDefinedType
 from slither.core.solidity_types.type import Type
 from slither.core.source_mapping.source_mapping import SourceMapping
 from slither.core.variables.local_variable import LocalVariable
-
 from slither.core.variables.state_variable import StateVariable
 from slither.utils.utils import unroll
 
@@ -46,6 +44,7 @@ if TYPE_CHECKING:
     from slither.slithir.operations import Operation
     from slither.slither import Slither
     from slither.core.cfg.node import NodeType
+    from slither.core.slither_core import SlitherCore
 
 LOGGER = logging.getLogger("Function")
 ReacheableNode = namedtuple("ReacheableNode", ["node", "ir"])
@@ -103,14 +102,12 @@ def _filter_state_variables_written(expressions: List["Expression"]):
     return ret
 
 
-class Function(
-    ChildContract, ChildInheritance, SourceMapping
-):  # pylint: disable=too-many-public-methods
+class Function(metaclass=ABCMeta):  # pylint: disable=too-many-public-methods
     """
     Function class
     """
 
-    def __init__(self):
+    def __init__(self, slither: "SlitherCore"):
         super().__init__()
         self._scope: List[str] = []
         self._name: Optional[str] = None
@@ -206,6 +203,8 @@ class Function(
         self._canonical_name: Optional[str] = None
         self._is_protected: Optional[bool] = None
 
+        self._slither: "SlitherCore" = slither
+
     ###################################################################################
     ###################################################################################
     # region General properties
@@ -260,20 +259,13 @@ class Function(
         return self._full_name
 
     @property
+    @abstractmethod
     def canonical_name(self) -> str:
         """
         str: contract.func_name(type1,type2)
         Return the function signature without the return values
         """
-        if self._canonical_name is None:
-            name, parameters, _ = self.signature
-            self._canonical_name = (
-                ".".join([self.contract_declarer.name] + self._scope + [name])
-                + "("
-                + ",".join(parameters)
-                + ")"
-            )
-        return self._canonical_name
+        return ""
 
     @property
     def contains_assembly(self) -> bool:
@@ -320,16 +312,12 @@ class Function(
         return self._can_reenter
 
     @property
-    def slither(self) -> "Slither":
-        return self.contract.slither
+    def slither(self) -> "SlitherCore":
+        return self._slither
 
-    def is_declared_by(self, contract: "Contract") -> bool:
-        """
-        Check if the element is declared by the contract
-        :param contract:
-        :return:
-        """
-        return self.contract_declarer == contract
+    @slither.setter
+    def slither(self, sl: "SlitherCore"):
+        self._slither = sl
 
     # endregion
     ###################################################################################
@@ -978,16 +966,9 @@ class Function(
     ###################################################################################
 
     @property
+    @abstractmethod
     def functions_shadowed(self) -> List["Function"]:
-        """
-            Return the list of functions shadowed
-        Returns:
-            list(core.Function)
-
-        """
-        candidates = [c.functions_declared for c in self.contract.inheritance]
-        candidates = [candidate for sublist in candidates for candidate in sublist]
-        return [f for f in candidates if f.full_name == self.full_name]
+        pass
 
     # endregion
     ###################################################################################
@@ -1400,25 +1381,11 @@ class Function(
         """
         return variable in self.variables_written
 
+    @abstractmethod
     def get_summary(
         self,
     ) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]:
-        """
-            Return the function summary
-        Returns:
-            (str, str, str, list(str), list(str), listr(str), list(str), list(str);
-            contract_name, name, visibility, modifiers, vars read, vars written, internal_calls, external_calls_as_expressions
-        """
-        return (
-            self.contract_declarer.name,
-            self.full_name,
-            self.visibility,
-            [str(x) for x in self.modifiers],
-            [str(x) for x in self.state_variables_read + self.solidity_variables_read],
-            [str(x) for x in self.state_variables_written],
-            [str(x) for x in self.internal_calls],
-            [str(x) for x in self.external_calls_as_expressions],
-        )
+        pass
 
     def is_protected(self) -> bool:
         """
@@ -1684,18 +1651,9 @@ class Function(
         self._analyze_read_write()
         self._analyze_calls()
 
+    @abstractmethod
     def generate_slithir_ssa(self, all_ssa_state_variables_instances):
-        from slither.slithir.utils.ssa import add_ssa_ir, transform_slithir_vars_to_ssa
-        from slither.core.dominators.utils import (
-            compute_dominance_frontier,
-            compute_dominators,
-        )
-
-        compute_dominators(self.nodes)
-        compute_dominance_frontier(self.nodes)
-        transform_slithir_vars_to_ssa(self)
-        if not self.contract.is_incorrectly_constructed:
-            add_ssa_ir(self, all_ssa_state_variables_instances)
+        pass
 
     def update_read_write_using_ssa(self):
         for node in self.nodes:

+ 106 - 0
slither/core/declarations/function_contract.py

@@ -0,0 +1,106 @@
+"""
+    Function module
+"""
+from typing import TYPE_CHECKING, List, Tuple
+
+from slither.core.children.child_contract import ChildContract
+from slither.core.children.child_inheritance import ChildInheritance
+from slither.core.source_mapping.source_mapping import SourceMapping
+from slither.core.declarations import Function
+
+# pylint: disable=import-outside-toplevel,too-many-instance-attributes,too-many-statements,too-many-lines
+
+if TYPE_CHECKING:
+    from slither.core.declarations import Contract
+
+
+class FunctionContract(Function, ChildContract, ChildInheritance, SourceMapping):
+    @property
+    def canonical_name(self) -> str:
+        """
+        str: contract.func_name(type1,type2)
+        Return the function signature without the return values
+        """
+        if self._canonical_name is None:
+            name, parameters, _ = self.signature
+            self._canonical_name = (
+                ".".join([self.contract_declarer.name] + self._scope + [name])
+                + "("
+                + ",".join(parameters)
+                + ")"
+            )
+        return self._canonical_name
+
+    def is_declared_by(self, contract: "Contract") -> bool:
+        """
+        Check if the element is declared by the contract
+        :param contract:
+        :return:
+        """
+        return self.contract_declarer == contract
+
+    # endregion
+    ###################################################################################
+    ###################################################################################
+    # region Functions
+    ###################################################################################
+    ###################################################################################
+
+    @property
+    def functions_shadowed(self) -> List["Function"]:
+        """
+            Return the list of functions shadowed
+        Returns:
+            list(core.Function)
+
+        """
+        candidates = [c.functions_declared for c in self.contract.inheritance]
+        candidates = [candidate for sublist in candidates for candidate in sublist]
+        return [f for f in candidates if f.full_name == self.full_name]
+
+    # endregion
+    ###################################################################################
+    ###################################################################################
+    # region Summary information
+    ###################################################################################
+    ###################################################################################
+
+    def get_summary(
+        self,
+    ) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]:
+        """
+            Return the function summary
+        Returns:
+            (str, str, str, list(str), list(str), listr(str), list(str), list(str);
+            contract_name, name, visibility, modifiers, vars read, vars written, internal_calls, external_calls_as_expressions
+        """
+        return (
+            self.contract_declarer.name,
+            self.full_name,
+            self.visibility,
+            [str(x) for x in self.modifiers],
+            [str(x) for x in self.state_variables_read + self.solidity_variables_read],
+            [str(x) for x in self.state_variables_written],
+            [str(x) for x in self.internal_calls],
+            [str(x) for x in self.external_calls_as_expressions],
+        )
+
+    # endregion
+    ###################################################################################
+    ###################################################################################
+    # region SlithIr and SSA
+    ###################################################################################
+    ###################################################################################
+
+    def generate_slithir_ssa(self, all_ssa_state_variables_instances):
+        from slither.slithir.utils.ssa import add_ssa_ir, transform_slithir_vars_to_ssa
+        from slither.core.dominators.utils import (
+            compute_dominance_frontier,
+            compute_dominators,
+        )
+
+        compute_dominators(self.nodes)
+        compute_dominance_frontier(self.nodes)
+        transform_slithir_vars_to_ssa(self)
+        if not self.contract.is_incorrectly_constructed:
+            add_ssa_ir(self, all_ssa_state_variables_instances)

+ 80 - 0
slither/core/declarations/function_top_level.py

@@ -0,0 +1,80 @@
+"""
+    Function module
+"""
+from typing import List, Tuple
+
+from slither.core.declarations import Function
+from slither.core.declarations.top_level import TopLevel
+from slither.core.source_mapping.source_mapping import SourceMapping
+
+
+class FunctionTopLevel(Function, TopLevel, SourceMapping):
+    @property
+    def canonical_name(self) -> str:
+        """
+        str: contract.func_name(type1,type2)
+        Return the function signature without the return values
+        """
+        if self._canonical_name is None:
+            name, parameters, _ = self.signature
+            self._canonical_name = ".".join(self._scope + [name]) + "(" + ",".join(parameters) + ")"
+        return self._canonical_name
+
+    # endregion
+    ###################################################################################
+    ###################################################################################
+    # region Functions
+    ###################################################################################
+    ###################################################################################
+
+    @property
+    def functions_shadowed(self) -> List["Function"]:
+        return []
+
+    # endregion
+    ###################################################################################
+    ###################################################################################
+    # region Summary information
+    ###################################################################################
+    ###################################################################################
+
+    def get_summary(
+        self,
+    ) -> Tuple[str, str, str, List[str], List[str], List[str], List[str], List[str]]:
+        """
+            Return the function summary
+        Returns:
+            (str, str, str, list(str), list(str), listr(str), list(str), list(str);
+            contract_name, name, visibility, modifiers, vars read, vars written, internal_calls, external_calls_as_expressions
+        """
+        return (
+            "",
+            self.full_name,
+            self.visibility,
+            [str(x) for x in self.modifiers],
+            [str(x) for x in self.state_variables_read + self.solidity_variables_read],
+            [str(x) for x in self.state_variables_written],
+            [str(x) for x in self.internal_calls],
+            [str(x) for x in self.external_calls_as_expressions],
+        )
+
+    # endregion
+    ###################################################################################
+    ###################################################################################
+    # region SlithIr and SSA
+    ###################################################################################
+    ###################################################################################
+
+    def generate_slithir_ssa(self, all_ssa_state_variables_instances):
+        # pylint: disable=import-outside-toplevel
+        from slither.slithir.utils.ssa import add_ssa_ir, transform_slithir_vars_to_ssa
+        from slither.core.dominators.utils import (
+            compute_dominance_frontier,
+            compute_dominators,
+        )
+
+        compute_dominators(self.nodes)
+        compute_dominance_frontier(self.nodes)
+        transform_slithir_vars_to_ssa(self)
+
+        add_ssa_ir(self, all_ssa_state_variables_instances)

+ 11 - 0
slither/core/declarations/import_directive.py

@@ -1,3 +1,5 @@
+from typing import Optional
+
 from slither.core.source_mapping.source_mapping import SourceMapping
 
 
@@ -5,10 +7,19 @@ class Import(SourceMapping):
     def __init__(self, filename: str):
         super().__init__()
         self._filename = filename
+        self._alias: Optional[str] = None
 
     @property
     def filename(self) -> str:
         return self._filename
 
+    @property
+    def alias(self) -> Optional[str]:
+        return self._alias
+
+    @alias.setter
+    def alias(self, a: str):
+        self._alias = a
+
     def __str__(self):
         return self.filename

+ 2 - 2
slither/core/declarations/modifier.py

@@ -1,8 +1,8 @@
 """
     Modifier module
 """
-from .function import Function
+from .function_contract import FunctionContract
 
 
-class Modifier(Function):
+class Modifier(FunctionContract):
     pass

+ 35 - 2
slither/core/declarations/solidity_variables.py

@@ -1,10 +1,13 @@
 # https://solidity.readthedocs.io/en/v0.4.24/units-and-global-variables.html
-from typing import List, Dict, Union
+from typing import List, Dict, Union, TYPE_CHECKING
 
 from slither.core.context.context import Context
 from slither.core.solidity_types import ElementaryType, TypeInformation
 from slither.exceptions import SlitherException
 
+if TYPE_CHECKING:
+    from slither.core.declarations import Import
+
 SOLIDITY_VARIABLES = {
     "now": "uint256",
     "this": "address",
@@ -31,7 +34,6 @@ SOLIDITY_VARIABLES_COMPOSED = {
     "tx.origin": "address",
 }
 
-
 SOLIDITY_FUNCTIONS: Dict[str, List[str]] = {
     "gasleft()": ["uint256"],
     "assert(bool)": [],
@@ -181,3 +183,34 @@ class SolidityFunction:
 
     def __hash__(self):
         return hash(self.name)
+
+
+class SolidityImportPlaceHolder(SolidityVariable):
+    """
+    Placeholder for import on top level objects
+    See the example at https://blog.soliditylang.org/2020/09/02/solidity-0.7.1-release-announcement/
+    In the long term we should remove this and better integrate import aliases
+    """
+
+    def __init__(self, import_directive: "Import"):
+        assert import_directive.alias is not None
+        super().__init__(import_directive.alias)
+        self._import_directive = import_directive
+
+    def _check_name(self, name: str):
+        return True
+
+    @property
+    def type(self) -> ElementaryType:
+        return ElementaryType("string")
+
+    def __eq__(self, other):
+        return (
+            self.__class__ == other.__class__
+            and self.name == other.name
+            and self._import_directive.filename == self._import_directive.filename
+        )
+
+    @property
+    def import_directive(self) -> "Import":
+        return self._import_directive

+ 1 - 10
slither/core/declarations/structure.py

@@ -1,13 +1,12 @@
 from typing import List, TYPE_CHECKING, Dict
 
-from slither.core.children.child_contract import ChildContract
 from slither.core.source_mapping.source_mapping import SourceMapping
 
 if TYPE_CHECKING:
     from slither.core.variables.structure_variable import StructureVariable
 
 
-class Structure(ChildContract, SourceMapping):
+class Structure(SourceMapping):
     def __init__(self):
         super().__init__()
         self._name = None
@@ -39,14 +38,6 @@ class Structure(ChildContract, SourceMapping):
     def add_elem_in_order(self, s: str):
         self._elems_ordered.append(s)
 
-    def is_declared_by(self, contract):
-        """
-        Check if the element is declared by the contract
-        :param contract:
-        :return:
-        """
-        return self.contract == contract
-
     @property
     def elems_ordered(self) -> List["StructureVariable"]:
         ret = []

+ 12 - 0
slither/core/declarations/structure_contract.py

@@ -0,0 +1,12 @@
+from slither.core.children.child_contract import ChildContract
+from slither.core.declarations import Structure
+
+
+class StructureContract(Structure, ChildContract):
+    def is_declared_by(self, contract):
+        """
+        Check if the element is declared by the contract
+        :param contract:
+        :return:
+        """
+        return self.contract == contract

+ 6 - 0
slither/core/declarations/structure_top_level.py

@@ -0,0 +1,6 @@
+from slither.core.declarations import Structure
+from slither.core.declarations.top_level import TopLevel
+
+
+class StructureTopLevel(Structure, TopLevel):
+    pass

+ 5 - 0
slither/core/declarations/top_level.py

@@ -0,0 +1,5 @@
+from slither.core.source_mapping.source_mapping import SourceMapping
+
+
+class TopLevel(SourceMapping):
+    pass

+ 28 - 13
slither/core/slither_core.py

@@ -1,11 +1,11 @@
 """
     Main module
 """
-import os
-import logging
 import json
-import re
+import logging
 import math
+import os
+import re
 from collections import defaultdict
 from typing import Optional, Dict, List, Set, Union, Tuple
 
@@ -18,10 +18,12 @@ from slither.core.declarations import (
     Import,
     Function,
     Modifier,
-    Structure,
-    Enum,
 )
+from slither.core.declarations.enum_top_level import EnumTopLevel
+from slither.core.declarations.function_top_level import FunctionTopLevel
+from slither.core.declarations.structure_top_level import StructureTopLevel
 from slither.core.variables.state_variable import StateVariable
+from slither.core.variables.top_level_variable import TopLevelVariable
 from slither.slithir.operations import InternalCall
 from slither.slithir.variables import Constant
 from slither.utils.colors import red
@@ -44,12 +46,19 @@ class SlitherCore(Context):  # pylint: disable=too-many-instance-attributes,too-
 
     def __init__(self):
         super().__init__()
+
+        # Top level object
         self._contracts: Dict[str, Contract] = {}
+        self._structures_top_level: List[StructureTopLevel] = []
+        self._enums_top_level: List[EnumTopLevel] = []
+        self._variables_top_level: List[TopLevelVariable] = []
+        self._functions_top_level: List[FunctionTopLevel] = []
+        self._pragma_directives: List[Pragma] = []
+        self._import_directives: List[Import] = []
+
         self._filename: Optional[str] = None
         self._source_units: Dict[int, str] = {}
         self._solc_version: Optional[str] = None  # '0.3' or '0.4':!
-        self._pragma_directives: List[Pragma] = []
-        self._import_directives: List[Import] = []
         self._raw_source_code: Dict[str, str] = {}
         self._all_functions: Set[Function] = set()
         self._all_modifiers: Set[Modifier] = set()
@@ -234,14 +243,20 @@ class SlitherCore(Context):  # pylint: disable=too-many-instance-attributes,too-
     ###################################################################################
 
     @property
-    def top_level_structures(self) -> List[Structure]:
-        top_level_structures = [c.structures for c in self.contracts if c.is_top_level]
-        return [st for sublist in top_level_structures for st in sublist]
+    def structures_top_level(self) -> List[StructureTopLevel]:
+        return self._structures_top_level
+
+    @property
+    def enums_top_level(self) -> List[EnumTopLevel]:
+        return self._enums_top_level
+
+    @property
+    def variables_top_level(self) -> List[TopLevelVariable]:
+        return self._variables_top_level
 
     @property
-    def top_level_enums(self) -> List[Enum]:
-        top_level_enums = [c.enums for c in self.contracts if c.is_top_level]
-        return [st for sublist in top_level_enums for st in sublist]
+    def functions_top_level(self) -> List[FunctionTopLevel]:
+        return self._functions_top_level
 
     # endregion
     ###################################################################################

+ 36 - 0
slither/core/variables/top_level_variable.py

@@ -0,0 +1,36 @@
+from typing import Optional, TYPE_CHECKING
+
+from slither.core.declarations.top_level import TopLevel
+from slither.core.variables.variable import Variable
+
+if TYPE_CHECKING:
+    from slither.core.cfg.node import Node
+
+
+class TopLevelVariable(TopLevel, Variable):
+    def __init__(self):
+        super().__init__()
+        self._node_initialization: Optional["Node"] = None
+
+    # endregion
+    ###################################################################################
+    ###################################################################################
+    # region IRs (initialization)
+    ###################################################################################
+    ###################################################################################
+
+    @property
+    def node_initialization(self) -> Optional["Node"]:
+        """
+        Node for the state variable initalization
+        :return:
+        """
+        return self._node_initialization
+
+    @node_initialization.setter
+    def node_initialization(self, node_initialization):
+        self._node_initialization = node_initialization
+
+    # endregion
+    ###################################################################################
+    ###################################################################################

+ 24 - 19
slither/printers/summary/slithir.py

@@ -1,10 +1,25 @@
 """
     Module printing summary of the contract
 """
-
+from slither.core.declarations import Function
 from slither.printers.abstract_printer import AbstractPrinter
 
 
+def _print_function(function: Function) -> str:
+    txt = ""
+    for node in function.nodes:
+        if node.expression:
+            txt += "\t\tExpression: {}\n".format(node.expression)
+            txt += "\t\tIRs:\n"
+            for ir in node.irs:
+                txt += "\t\t\t{}\n".format(ir)
+        elif node.irs:
+            txt += "\t\tIRs:\n"
+            for ir in node.irs:
+                txt += "\t\t\t{}\n".format(ir)
+    return txt
+
+
 class PrinterSlithIR(AbstractPrinter):
     ARGUMENT = "slithir"
     HELP = "Print the slithIR representation of the functions"
@@ -24,26 +39,16 @@ class PrinterSlithIR(AbstractPrinter):
                 continue
             txt += "Contract {}\n".format(contract.name)
             for function in contract.functions:
-                txt += f'\tFunction {function.canonical_name} {"" if function.is_shadowed else "(*)"}\n'
-                for node in function.nodes:
-                    if node.expression:
-                        txt += "\t\tExpression: {}\n".format(node.expression)
-                        txt += "\t\tIRs:\n"
-                        for ir in node.irs:
-                            txt += "\t\t\t{}\n".format(ir)
-                    elif node.irs:
-                        txt += "\t\tIRs:\n"
-                        for ir in node.irs:
-                            txt += "\t\t\t{}\n".format(ir)
+                txt = f'\tFunction {function.canonical_name} {"" if function.is_shadowed else "(*)"}\n'
+                txt += _print_function(function)
             for modifier in contract.modifiers:
                 txt += "\tModifier {}\n".format(modifier.canonical_name)
-                for node in modifier.nodes:
-                    txt += str(node)
-                    if node.expression:
-                        txt += "\t\tExpression: {}\n".format(node.expression)
-                        txt += "\t\tIRs:\n"
-                        for ir in node.irs:
-                            txt += "\t\t\t{}\n".format(ir)
+                txt += _print_function(modifier)
+        if self.slither.functions_top_level:
+            txt += "Top level functions"
+        for function in self.slither.functions_top_level:
+            txt += f"\tFunction {function.canonical_name}\n"
+            txt += _print_function(function)
         self.info(txt)
         res = self.generate_output(txt)
         return res

+ 3 - 3
slither/slither.py

@@ -78,7 +78,7 @@ class Slither(SlitherCore):  # pylint: disable=too-many-instance-attributes
                 # pylint: disable=raise-missing-from
                 raise SlitherError(f"Invalid compilation: \n{str(e)}")
             for path, ast in crytic_compile.asts.items():
-                self._parser.parse_contracts_from_loaded_json(ast, path)
+                self._parser.parse_top_level_from_loaded_json(ast, path)
                 self.add_source_code(path)
 
         if kwargs.get("generate_patches", False):
@@ -120,7 +120,7 @@ class Slither(SlitherCore):  # pylint: disable=too-many-instance-attributes
         self._parser = SlitherSolc(filename, self)
 
         for c in contracts_json:
-            self._parser.parse_contracts_from_json(c)
+            self._parser.parse_top_level_from_json(c)
 
     def _init_from_list(self, contract):
         self._parser = SlitherSolc("", self)
@@ -129,7 +129,7 @@ class Slither(SlitherCore):  # pylint: disable=too-many-instance-attributes
                 path = c["absolutePath"]
             else:
                 path = c["attributes"]["absolutePath"]
-            self._parser.parse_contracts_from_loaded_json(c, path)
+            self._parser.parse_top_level_from_loaded_json(c, path)
 
     @property
     def detectors(self):

+ 28 - 20
slither/slithir/convert.py

@@ -1,5 +1,5 @@
 import logging
-from typing import List
+from typing import List, TYPE_CHECKING
 
 # pylint: disable= too-many-lines,import-outside-toplevel,too-many-branches,too-many-statements,too-many-nested-blocks
 from slither.core.declarations import (
@@ -12,6 +12,7 @@ from slither.core.declarations import (
     SolidityVariableComposed,
     Structure,
 )
+from slither.core.declarations.function_contract import FunctionContract
 from slither.core.expressions import Identifier, Literal
 from slither.core.solidity_types import (
     ArrayType,
@@ -24,10 +25,9 @@ from slither.core.solidity_types import (
 from slither.core.solidity_types.elementary_type import Int as ElementaryTypeInt
 from slither.core.solidity_types.type import Type
 from slither.core.variables.function_type_variable import FunctionTypeVariable
-from slither.core.variables.variable import Variable
 from slither.core.variables.state_variable import StateVariable
-from slither.slithir.operations.codesize import CodeSize
-from slither.slithir.variables import TupleVariable
+from slither.core.variables.variable import Variable
+from slither.slithir.exceptions import SlithIRError
 from slither.slithir.operations import (
     Assignment,
     Balance,
@@ -61,6 +61,7 @@ from slither.slithir.operations import (
     Unpack,
     Nop,
 )
+from slither.slithir.operations.codesize import CodeSize
 from slither.slithir.tmp_operations.argument import Argument, ArgumentType
 from slither.slithir.tmp_operations.tmp_call import TmpCall
 from slither.slithir.tmp_operations.tmp_new_array import TmpNewArray
@@ -68,10 +69,13 @@ from slither.slithir.tmp_operations.tmp_new_contract import TmpNewContract
 from slither.slithir.tmp_operations.tmp_new_elementary_type import TmpNewElementaryType
 from slither.slithir.tmp_operations.tmp_new_structure import TmpNewStructure
 from slither.slithir.variables import Constant, ReferenceVariable, TemporaryVariable
-from slither.visitors.slithir.expression_to_slithir import ExpressionToSlithIR
+from slither.slithir.variables import TupleVariable
 from slither.utils.function import get_function_id
 from slither.utils.type import export_nested_types_from_variable
-from slither.slithir.exceptions import SlithIRError
+from slither.visitors.slithir.expression_to_slithir import ExpressionToSlithIR
+
+if TYPE_CHECKING:
+    from slither.core.cfg.node import Node
 
 logger = logging.getLogger("ConvertToIR")
 
@@ -428,15 +432,15 @@ def _convert_type_contract(ir, slither):
         assignment.lvalue.set_type(ElementaryType("string"))
         return assignment
 
-    if isinstance(contract, ElementaryType):
-        print(contract.type)
-
     raise SlithIRError(f"type({contract.name}).{ir.variable_right} is unknown")
 
 
-def propagate_types(ir, node):  # pylint: disable=too-many-locals
+def propagate_types(ir, node: "Node"):  # pylint: disable=too-many-locals
     # propagate the type
-    using_for = node.function.contract.using_for
+    node_function = node.function
+    using_for = (
+        node_function.contract.using_for if isinstance(node_function, FunctionContract) else dict()
+    )
     if isinstance(ir, OperationWithLValue):
         # Force assignment in case of missing previous correct type
         if not ir.lvalue.type:
@@ -478,9 +482,11 @@ def propagate_types(ir, node):  # pylint: disable=too-many-locals
 
                 # Convert HighLevelCall to LowLevelCall
                 if isinstance(t, ElementaryType) and t.name == "address":
+                    # Cannot be a top level function with this.
+                    assert isinstance(node_function, FunctionContract)
                     if ir.destination.name == "this":
                         return convert_type_of_high_and_internal_level_call(
-                            ir, node.function.contract
+                            ir, node_function.contract
                         )
                     if can_be_low_level(ir):
                         return convert_to_low_level(ir)
@@ -578,22 +584,20 @@ def propagate_types(ir, node):  # pylint: disable=too-many-locals
                     return _convert_type_contract(ir, node.function.slither)
                 left = ir.variable_left
                 t = None
+                ir_func = ir.function
                 # Handling of this.function_name usage
                 if (
                     left == SolidityVariable("this")
                     and isinstance(ir.variable_right, Constant)
-                    and str(ir.variable_right) in [x.name for x in ir.function.contract.functions]
+                    and isinstance(ir_func, FunctionContract)
+                    and str(ir.variable_right) in [x.name for x in ir_func.contract.functions]
                 ):
                     # Assumption that this.function_name can only compile if
                     # And the contract does not have two functions starting with function_name
                     # Otherwise solc raises:
                     # Error: Member "f" not unique after argument-dependent lookup in contract
                     targeted_function = next(
-                        (
-                            x
-                            for x in ir.function.contract.functions
-                            if x.name == str(ir.variable_right)
-                        )
+                        (x for x in ir_func.contract.functions if x.name == str(ir.variable_right))
                     )
                     t = _make_function_type(targeted_function)
                     ir.lvalue.set_type(t)
@@ -689,7 +693,6 @@ def propagate_types(ir, node):  # pylint: disable=too-many-locals
 
 def extract_tmp_call(ins, contract):  # pylint: disable=too-many-locals
     assert isinstance(ins, TmpCall)
-
     if isinstance(ins.called, Variable) and isinstance(ins.called.type, FunctionType):
         # If the call is made to a variable member, where the member is this
         # We need to convert it to a HighLelelCall and not an internal dynamic call
@@ -753,7 +756,10 @@ def extract_tmp_call(ins, contract):  # pylint: disable=too-many-locals
             #         f.h(1);
             #     }
             # }
-            using_for = ins.node.function.contract.using_for
+            node_func = ins.node.function
+            using_for = (
+                node_func.contract.using_for if isinstance(node_func, FunctionContract) else dict()
+            )
 
             targeted_libraries = (
                 [] + using_for.get("*", []) + using_for.get(FunctionType([], []), [])
@@ -772,6 +778,8 @@ def extract_tmp_call(ins, contract):  # pylint: disable=too-many-locals
 
             if len(candidates) == 1:
                 lib_func = candidates[0]
+                # Library must be from a contract
+                assert isinstance(lib_func, FunctionContract)
                 lib_call = LibraryCall(
                     lib_func.contract,
                     Constant(lib_func.name),

+ 4 - 1
slither/slithir/operations/internal_call.py

@@ -1,5 +1,6 @@
 from slither.core.declarations import Modifier
 from slither.core.declarations.function import Function
+from slither.core.declarations.function_contract import FunctionContract
 from slither.slithir.operations.call import Call
 from slither.slithir.operations.lvalue import OperationWithLValue
 
@@ -7,10 +8,12 @@ from slither.slithir.operations.lvalue import OperationWithLValue
 class InternalCall(Call, OperationWithLValue):  # pylint: disable=too-many-instance-attributes
     def __init__(self, function, nbr_arguments, result, type_call):
         super().__init__()
+        self._contract_name = ""
         if isinstance(function, Function):
             self._function = function
             self._function_name = function.name
-            self._contract_name = function.contract_declarer.name
+            if isinstance(function, FunctionContract):
+                self._contract_name = function.contract_declarer.name
         else:
             self._function = None
             self._function_name, self._contract_name = function

+ 63 - 52
slither/solc_parsing/declarations/contract.py

@@ -1,15 +1,14 @@
 import logging
-from typing import List, Dict, Callable, TYPE_CHECKING, Union
+from typing import List, Dict, Callable, TYPE_CHECKING, Union, Set
 
-from slither.core.declarations import Modifier, Structure, Event
+from slither.core.declarations import Modifier, Event, EnumContract, StructureContract, Function
 from slither.core.declarations.contract import Contract
-from slither.core.declarations.enum import Enum
-from slither.core.declarations.function import Function
+from slither.core.declarations.function_contract import FunctionContract
 from slither.core.variables.state_variable import StateVariable
 from slither.solc_parsing.declarations.event import EventSolc
 from slither.solc_parsing.declarations.function import FunctionSolc
 from slither.solc_parsing.declarations.modifier import ModifierSolc
-from slither.solc_parsing.declarations.structure import StructureSolc
+from slither.solc_parsing.declarations.structure_contract import StructureContractSolc
 from slither.solc_parsing.exceptions import ParsingError, VariableNotFound
 from slither.solc_parsing.solidity_types.type_parsing import parse_type
 from slither.solc_parsing.variables.state_variable import StateVariableSolc
@@ -44,7 +43,7 @@ class ContractSolc:
 
         self._functions_parser: List[FunctionSolc] = []
         self._modifiers_parser: List[ModifierSolc] = []
-        self._structures_parser: List[StructureSolc] = []
+        self._structures_parser: List[StructureContractSolc] = []
 
         self._is_analyzed: bool = False
 
@@ -252,28 +251,13 @@ class ContractSolc:
         return
 
     def _parse_struct(self, struct: Dict):
-        if self.is_compact_ast:
-            name = struct["name"]
-            attributes = struct
-        else:
-            name = struct["attributes"][self.get_key()]
-            attributes = struct["attributes"]
-        if "canonicalName" in attributes:
-            canonicalName = attributes["canonicalName"]
-        else:
-            canonicalName = self._contract.name + "." + name
-
-        if self.get_children("members") in struct:
-            children = struct[self.get_children("members")]
-        else:
-            children = []  # empty struct
 
-        st = Structure()
+        st = StructureContract()
         st.set_contract(self._contract)
         st.set_offset(struct["src"], self._contract.slither)
 
-        st_parser = StructureSolc(st, name, canonicalName, children, self)
-        self._contract.structures_as_dict[name] = st
+        st_parser = StructureContractSolc(st, struct, self)
+        self._contract.structures_as_dict[st.name] = st
         self._structures_parser.append(st_parser)
 
     def parse_structs(self):
@@ -307,17 +291,17 @@ class ContractSolc:
             self._contract.add_variables_ordered([var])
 
     def _parse_modifier(self, modifier_data: Dict):
-        modif = Modifier()
+        modif = Modifier(self.slither)
         modif.set_offset(modifier_data["src"], self._contract.slither)
         modif.set_contract(self._contract)
         modif.set_contract_declarer(self._contract)
 
-        modif_parser = ModifierSolc(modif, modifier_data, self)
+        modif_parser = ModifierSolc(modif, modifier_data, self, self.slither_parser)
         self._contract.slither.add_modifier(modif)
         self._modifiers_no_params.append(modif_parser)
         self._modifiers_parser.append(modif_parser)
 
-        self._slither_parser.add_functions_parser(modif_parser)
+        self._slither_parser.add_function_or_modifier_parser(modif_parser)
 
     def parse_modifiers(self):
         for modifier in self._modifiersNotParsed:
@@ -325,17 +309,17 @@ class ContractSolc:
         self._modifiersNotParsed = None
 
     def _parse_function(self, function_data: Dict):
-        func = Function()
+        func = FunctionContract(self.slither)
         func.set_offset(function_data["src"], self._contract.slither)
         func.set_contract(self._contract)
         func.set_contract_declarer(self._contract)
 
-        func_parser = FunctionSolc(func, function_data, self)
+        func_parser = FunctionSolc(func, function_data, self, self._slither_parser)
         self._contract.slither.add_function(func)
         self._functions_no_params.append(func_parser)
         self._functions_parser.append(func_parser)
 
-        self._slither_parser.add_functions_parser(func_parser)
+        self._slither_parser.add_function_or_modifier_parser(func_parser)
 
     def parse_functions(self):
 
@@ -396,7 +380,7 @@ class ContractSolc:
             elements_no_params = self._functions_no_params
             getter = lambda c: c.functions_parser
             getter_available = lambda c: c.functions_declared
-            Cls = Function
+            Cls = FunctionContract
             Cls_parser = FunctionSolc
             functions = self._analyze_params_elements(
                 elements_no_params,
@@ -411,15 +395,56 @@ class ContractSolc:
             self.log_incorrect_parsing(f"Missing params {e}")
         self._functions_no_params = []
 
+    def _analyze_params_element(  # pylint: disable=too-many-arguments
+        self,
+        Cls: Callable,
+        Cls_parser: Callable,
+        element_parser: FunctionSolc,
+        explored_reference_id: Set[int],
+        parser: List[FunctionSolc],
+        all_elements: Dict[str, Function],
+    ):
+        elem = Cls(self.slither)
+        elem.set_contract(self._contract)
+        underlying_function = element_parser.underlying_function
+        # TopLevel function are not analyzed here
+        assert isinstance(underlying_function, FunctionContract)
+        elem.set_contract_declarer(underlying_function.contract_declarer)
+        elem.set_offset(
+            element_parser.function_not_parsed["src"], self._contract.slither,
+        )
+
+        elem_parser = Cls_parser(
+            elem, element_parser.function_not_parsed, self, self.slither_parser
+        )
+        if (
+            element_parser.referenced_declaration
+            and element_parser.referenced_declaration in explored_reference_id
+        ):
+            # Already added from other fathers
+            return
+        if element_parser.referenced_declaration:
+            explored_reference_id.add(element_parser.referenced_declaration)
+        elem_parser.analyze_params()
+        if isinstance(elem, Modifier):
+            self._contract.slither.add_modifier(elem)
+        else:
+            self._contract.slither.add_function(elem)
+
+        self._slither_parser.add_function_or_modifier_parser(elem_parser)
+
+        all_elements[elem.canonical_name] = elem
+        parser.append(elem_parser)
+
     def _analyze_params_elements(  # pylint: disable=too-many-arguments,too-many-locals
         self,
         elements_no_params: List[FunctionSolc],
         getter: Callable[["ContractSolc"], List[FunctionSolc]],
-        getter_available: Callable[[Contract], List[Function]],
+        getter_available: Callable[[Contract], List[FunctionContract]],
         Cls: Callable,
         Cls_parser: Callable,
         parser: List[FunctionSolc],
-    ) -> Dict[str, Union[Function, Modifier]]:
+    ) -> Dict[str, Union[FunctionContract, Modifier]]:
         """
         Analyze the parameters of the given elements (Function or Modifier).
         The function iterates over the inheritance to create an instance or inherited elements (Function or Modifier)
@@ -433,29 +458,15 @@ class ContractSolc:
         """
         all_elements = {}
 
+        explored_reference_id = set()
         try:
             for father in self._contract.inheritance:
                 father_parser = self._slither_parser.underlying_contract_to_parser[father]
                 for element_parser in getter(father_parser):
-                    elem = Cls()
-                    elem.set_contract(self._contract)
-                    elem.set_contract_declarer(element_parser.underlying_function.contract_declarer)
-                    elem.set_offset(
-                        element_parser.function_not_parsed["src"], self._contract.slither,
+                    self._analyze_params_element(
+                        Cls, Cls_parser, element_parser, explored_reference_id, parser, all_elements
                     )
 
-                    elem_parser = Cls_parser(elem, element_parser.function_not_parsed, self,)
-                    elem_parser.analyze_params()
-                    if isinstance(elem, Modifier):
-                        self._contract.slither.add_modifier(elem)
-                    else:
-                        self._contract.slither.add_function(elem)
-
-                    self._slither_parser.add_functions_parser(elem_parser)
-
-                    all_elements[elem.canonical_name] = elem
-                    parser.append(elem_parser)
-
             accessible_elements = self._contract.available_elements_from_inheritances(
                 all_elements, getter_available
             )
@@ -573,12 +584,12 @@ class ContractSolc:
             else:
                 values.append(child["attributes"][self.get_key()])
 
-        new_enum = Enum(name, canonicalName, values)
+        new_enum = EnumContract(name, canonicalName, values)
         new_enum.set_contract(self._contract)
         new_enum.set_offset(enum["src"], self._contract.slither)
         self._contract.enums_as_dict[canonicalName] = new_enum
 
-    def _analyze_struct(self, struct: StructureSolc):  # pylint: disable=no-self-use
+    def _analyze_struct(self, struct: StructureContractSolc):  # pylint: disable=no-self-use
         struct.analyze()
 
     def analyze_structs(self):

+ 15 - 6
slither/solc_parsing/declarations/function.py

@@ -8,6 +8,7 @@ from slither.core.declarations.function import (
     ModifierStatements,
     FunctionType,
 )
+from slither.core.declarations.function_contract import FunctionContract
 
 from slither.core.expressions import AssignmentOperation
 from slither.core.variables.local_variable import LocalVariable
@@ -48,9 +49,13 @@ class FunctionSolc:
     # elems = [(type, name)]
 
     def __init__(
-        self, function: Function, function_data: Dict, contract_parser: "ContractSolc",
+        self,
+        function: Function,
+        function_data: Dict,
+        contract_parser: Optional["ContractSolc"],
+        slither_parser: "SlitherSolc",
     ):
-        self._slither_parser: "SlitherSolc" = contract_parser.slither_parser
+        self._slither_parser: "SlitherSolc" = slither_parser
         self._contract_parser = contract_parser
         self._function = function
 
@@ -95,7 +100,7 @@ class FunctionSolc:
         return self._function
 
     @property
-    def contract_parser(self) -> "ContractSolc":
+    def contract_parser(self) -> Optional["ContractSolc"]:
         return self._contract_parser
 
     @property
@@ -200,8 +205,9 @@ class FunctionSolc:
         else:
             self._function.function_type = FunctionType.NORMAL
 
-        if self._function.name == self._function.contract_declarer.name:
-            self._function.function_type = FunctionType.CONSTRUCTOR
+        if isinstance(self._function, FunctionContract):
+            if self._function.name == self._function.contract_declarer.name:
+                self._function.function_type = FunctionType.CONSTRUCTOR
 
     def _analyze_attributes(self):
         if self.is_compact_ast:
@@ -332,8 +338,11 @@ class FunctionSolc:
 
     def _new_yul_block(self, src: Union[str, Dict]) -> YulBlock:
         node = self._function.new_node(NodeType.ASSEMBLY, src)
+        contract = None
+        if isinstance(self._function, FunctionContract):
+            contract = self._function.contract
         yul_object = YulBlock(
-            self._function.contract,
+            contract,
             node,
             [self._function.name, f"asm_{len(self._node_to_yulobject)}"],
             parent_func=self._function,

+ 9 - 2
slither/solc_parsing/declarations/modifier.py

@@ -11,11 +11,18 @@ from slither.solc_parsing.declarations.function import FunctionSolc
 
 if TYPE_CHECKING:
     from slither.solc_parsing.declarations.contract import ContractSolc
+    from slither.solc_parsing.slitherSolc import SlitherSolc
 
 
 class ModifierSolc(FunctionSolc):
-    def __init__(self, modifier: Modifier, function_data: Dict, contract_parser: "ContractSolc"):
-        super().__init__(modifier, function_data, contract_parser)
+    def __init__(
+        self,
+        modifier: Modifier,
+        function_data: Dict,
+        contract_parser: "ContractSolc",
+        slither_parser: "SlitherSolc",
+    ):
+        super().__init__(modifier, function_data, contract_parser, slither_parser)
         # _modifier is equal to _function, but keep it here to prevent
         # confusion for mypy in underlying_function
         self._modifier = modifier

+ 19 - 10
slither/solc_parsing/declarations/structure.py

@@ -1,17 +1,17 @@
 """
     Structure module
 """
-from typing import List, TYPE_CHECKING
+from typing import TYPE_CHECKING, Dict
 
+from slither.core.declarations.structure import Structure
 from slither.core.variables.structure_variable import StructureVariable
 from slither.solc_parsing.variables.structure_variable import StructureVariableSolc
-from slither.core.declarations.structure import Structure
 
 if TYPE_CHECKING:
     from slither.solc_parsing.declarations.contract import ContractSolc
 
 
-class StructureSolc:  # pylint: disable=too-few-public-methods
+class StructureContractSolc:  # pylint: disable=too-few-public-methods
     """
     Structure class
     """
@@ -19,19 +19,28 @@ class StructureSolc:  # pylint: disable=too-few-public-methods
     # elems = [(type, name)]
 
     def __init__(  # pylint: disable=too-many-arguments
-        self,
-        st: Structure,
-        name: str,
-        canonicalName: str,
-        elems: List[str],
-        contract_parser: "ContractSolc",
+        self, st: Structure, struct: Dict, contract_parser: "ContractSolc",
     ):
+
+        if contract_parser.is_compact_ast:
+            name = struct["name"]
+            attributes = struct
+        else:
+            name = struct["attributes"][contract_parser.get_key()]
+            attributes = struct["attributes"]
+        if "canonicalName" in attributes:
+            canonicalName = attributes["canonicalName"]
+        else:
+            canonicalName = contract_parser.underlying_contract.name + "." + name
+
+        children = struct["members"] if "members" in struct else struct.get("children", [])
+
         self._structure = st
         st.name = name
         st.canonical_name = canonicalName
         self._contract_parser = contract_parser
 
-        self._elemsNotParsed = elems
+        self._elemsNotParsed = children
 
     def analyze(self):
         for elem_to_parse in self._elemsNotParsed:

+ 56 - 0
slither/solc_parsing/declarations/structure_top_level.py

@@ -0,0 +1,56 @@
+"""
+    Structure module
+"""
+from typing import TYPE_CHECKING, Dict
+
+from slither.core.declarations.structure import Structure
+from slither.core.variables.structure_variable import StructureVariable
+from slither.solc_parsing.variables.structure_variable import StructureVariableSolc
+
+if TYPE_CHECKING:
+    from slither.solc_parsing.slitherSolc import SlitherSolc
+
+
+class StructureTopLevelSolc:  # pylint: disable=too-few-public-methods
+    """
+    Structure class
+    """
+
+    # elems = [(type, name)]
+
+    def __init__(  # pylint: disable=too-many-arguments
+        self, st: Structure, struct: Dict, slither_parser: "SlitherSolc",
+    ):
+
+        if slither_parser.is_compact_ast:
+            name = struct["name"]
+            attributes = struct
+        else:
+            name = struct["attributes"][slither_parser.get_key()]
+            attributes = struct["attributes"]
+        if "canonicalName" in attributes:
+            canonicalName = attributes["canonicalName"]
+        else:
+            canonicalName = name
+
+        children = struct["members"] if "members" in struct else struct.get("children", [])
+
+        self._structure = st
+        st.name = name
+        st.canonical_name = canonicalName
+        self._slither_parser = slither_parser
+
+        self._elemsNotParsed = children
+
+    def analyze(self):
+        for elem_to_parse in self._elemsNotParsed:
+            elem = StructureVariable()
+            elem.set_structure(self._structure)
+            elem.set_offset(elem_to_parse["src"], self._slither_parser.core)
+
+            elem_parser = StructureVariableSolc(elem, elem_to_parse)
+            elem_parser.analyze(self._slither_parser)
+
+            self._structure.elems[elem.name] = elem
+            self._structure.add_elem_in_order(elem.name)
+        self._elemsNotParsed = []

+ 206 - 78
slither/solc_parsing/expressions/expression_parsing.py

@@ -1,10 +1,11 @@
 import logging
 import re
-from typing import Dict, TYPE_CHECKING, Optional, Union
+from typing import Dict, TYPE_CHECKING, Optional, Union, List, Tuple
 
 from slither.core.declarations import Event, Enum, Structure
 from slither.core.declarations.contract import Contract
 from slither.core.declarations.function import Function
+from slither.core.declarations.function_contract import FunctionContract
 from slither.core.declarations.solidity_variables import (
     SOLIDITY_FUNCTIONS,
     SOLIDITY_VARIABLES,
@@ -12,6 +13,7 @@ from slither.core.declarations.solidity_variables import (
     SolidityFunction,
     SolidityVariable,
     SolidityVariableComposed,
+    SolidityImportPlaceHolder,
 )
 from slither.core.expressions.assignment_operation import (
     AssignmentOperation,
@@ -43,6 +45,7 @@ from slither.core.solidity_types import (
     MappingType,
 )
 from slither.core.variables.variable import Variable
+from slither.exceptions import SlitherError
 from slither.solc_parsing.exceptions import ParsingError, VariableNotFound
 from slither.solc_parsing.solidity_types.type_parsing import UnknownType, parse_type
 
@@ -50,6 +53,8 @@ if TYPE_CHECKING:
     from slither.core.expressions.expression import Expression
     from slither.solc_parsing.declarations.function import FunctionSolc
     from slither.solc_parsing.declarations.contract import ContractSolc
+    from slither.core.slither_core import SlitherCore
+    from slither.solc_parsing.slitherSolc import SlitherSolc
 
 logger = logging.getLogger("ExpressionParsing")
 
@@ -78,62 +83,85 @@ def get_pointer_name(variable: Variable):
     return None
 
 
-def find_variable(  # pylint: disable=too-many-locals,too-many-statements
+def _find_variable_from_ref_declaration(
+    referenced_declaration: Optional[int],
+    all_contracts: List["Contract"],
+    all_functions_parser: List["FunctionSolc"],
+) -> Optional[Union[Contract, Function]]:
+    if referenced_declaration is None:
+        return None
+    # id of the contracts is the referenced declaration
+    # This is not true for the functions, as we dont always have the referenced_declaration
+    # But maybe we could? (TODO)
+    for contract_candidate in all_contracts:
+        if contract_candidate.id == referenced_declaration:
+            return contract_candidate
+    for function_candidate in all_functions_parser:
+        if (
+            function_candidate.referenced_declaration == referenced_declaration
+            and not function_candidate.underlying_function.is_shadowed
+        ):
+            return function_candidate.underlying_function
+    return None
+
+
+def _find_variable_in_function_parser(
     var_name: str,
-    caller_context: CallerContext,
+    function_parser: Optional["FunctionSolc"],
     referenced_declaration: Optional[int] = None,
-    is_super=False,
-) -> Union[
-    Variable, Function, Contract, SolidityVariable, SolidityFunction, Event, Enum, Structure,
-]:
-    from slither.solc_parsing.declarations.contract import ContractSolc
-    from slither.solc_parsing.declarations.function import FunctionSolc
+) -> Optional[Variable]:
+    if function_parser is None:
+        return None
+    # We look for variable declared with the referencedDeclaration attr
+    func_variables_renamed = function_parser.variables_renamed
+    if referenced_declaration and referenced_declaration in func_variables_renamed:
+        return func_variables_renamed[referenced_declaration].underlying_variable
+    # If not found, check for name
+    func_variables = function_parser.underlying_function.variables_as_dict
+    if var_name in func_variables:
+        return func_variables[var_name]
+    # A local variable can be a pointer
+    # for example
+    # function test(function(uint) internal returns(bool) t) interna{
+    # Will have a local variable t which will match the signature
+    # t(uint256)
+    func_variables_ptr = {
+        get_pointer_name(f): f for f in function_parser.underlying_function.variables
+    }
+    if var_name and var_name in func_variables_ptr:
+        return func_variables_ptr[var_name]
 
-    # variable are looked from the contract declarer
-    # functions can be shadowed, but are looked from the contract instance, rather than the contract declarer
-    # the difference between function and variable come from the fact that an internal call, or an variable access
-    # in a function does not behave similariy, for example in:
-    # contract C{
-    #   function f(){
-    #     state_var = 1
-    #     f2()
-    #  }
-    # state_var will refer to C.state_var, no mater if C is inherited
-    # while f2() will refer to the function definition of the inherited contract (C.f2() in the context of C, or
-    # the contract inheriting from C)
-    # for events it's unclear what should be the behavior, as they can be shadowed, but there is not impact
-    # structure/enums cannot be shadowed
+    return None
 
-    if isinstance(caller_context, ContractSolc):
-        function: Optional[FunctionSolc] = None
-        contract = caller_context.underlying_contract
-        contract_declarer = caller_context.underlying_contract
-    elif isinstance(caller_context, FunctionSolc):
-        function = caller_context
-        contract = function.underlying_function.contract
-        contract_declarer = function.underlying_function.contract_declarer
-    else:
-        raise ParsingError("Incorrect caller context")
-
-    if function:
-        # We look for variable declared with the referencedDeclaration attr
-        func_variables_renamed = function.variables_renamed
-        if referenced_declaration and referenced_declaration in func_variables_renamed:
-            return func_variables_renamed[referenced_declaration].underlying_variable
-        # If not found, check for name
-        func_variables = function.underlying_function.variables_as_dict
-        if var_name in func_variables:
-            return func_variables[var_name]
-        # A local variable can be a pointer
-        # for example
-        # function test(function(uint) internal returns(bool) t) interna{
-        # Will have a local variable t which will match the signature
-        # t(uint256)
-        func_variables_ptr = {
-            get_pointer_name(f): f for f in function.underlying_function.variables
-        }
-        if var_name and var_name in func_variables_ptr:
-            return func_variables_ptr[var_name]
+
+def _find_top_level(
+    var_name: str, sl: "SlitherCore"
+) -> Optional[Union[Enum, Structure, SolidityVariable]]:
+    structures_top_level = sl.structures_top_level
+    for st in structures_top_level:
+        if st.name == var_name:
+            return st
+
+    enums_top_level = sl.enums_top_level
+    for enum in enums_top_level:
+        if enum.name == var_name:
+            return enum
+
+    for import_directive in sl.import_directives:
+        if import_directive.alias == var_name:
+            return SolidityImportPlaceHolder(import_directive)
+
+    return None
+
+
+def _find_in_contract(
+    var_name: str,
+    contract: Optional[Contract],
+    contract_declarer: Optional[Contract],
+    is_super: bool,
+) -> Optional[Union[Variable, Function, Contract, Event, Enum, Structure,]]:
+    if contract is None or contract_declarer is None:
+        return None
 
     # variable are looked from the contract declarer
     contract_variables = contract_declarer.variables_as_dict
@@ -178,11 +206,6 @@ def find_variable(  # pylint: disable=too-many-locals,too-many-statements
     if var_name in structures:
         return structures[var_name]
 
-    structures_top_level = contract.slither.top_level_structures
-    for st in structures_top_level:
-        if st.name == var_name:
-            return st
-
     events = contract.events_as_dict
     if var_name in events:
         return events[var_name]
@@ -191,42 +214,147 @@ def find_variable(  # pylint: disable=too-many-locals,too-many-statements
     if var_name in enums:
         return enums[var_name]
 
-    enums_top_level = contract.slither.top_level_enums
-    for enum in enums_top_level:
-        if enum.name == var_name:
-            return enum
-
     # If the enum is refered as its name rather than its canonicalName
     enums = {e.name: e for e in contract.enums}
     if var_name in enums:
         return enums[var_name]
 
+    return None
+
+
+def _find_variable_init(
+    caller_context: CallerContext,
+) -> Tuple[List[Contract], Union[List["FunctionSolc"]], "SlitherCore", "SlitherSolc"]:
+    from slither.solc_parsing.slitherSolc import SlitherSolc
+    from slither.solc_parsing.declarations.contract import ContractSolc
+    from slither.solc_parsing.declarations.function import FunctionSolc
+
+    direct_contracts: List[Contract]
+    direct_functions_parser: List[FunctionSolc]
+
+    if isinstance(caller_context, SlitherSolc):
+        direct_contracts = []
+        direct_functions_parser = []
+        sl = caller_context.core
+        sl_parser = caller_context
+    elif isinstance(caller_context, ContractSolc):
+        direct_contracts = [caller_context.underlying_contract]
+        direct_functions_parser = caller_context.functions_parser + caller_context.modifiers_parser
+        sl = caller_context.slither
+        sl_parser = caller_context.slither_parser
+    elif isinstance(caller_context, FunctionSolc):
+        if caller_context.contract_parser:
+            direct_contracts = [caller_context.contract_parser.underlying_contract]
+            direct_functions_parser = (
+                caller_context.contract_parser.functions_parser
+                + caller_context.contract_parser.modifiers_parser
+            )
+        else:
+            # Top level functions
+            direct_contracts = []
+            direct_functions_parser = []
+        sl = caller_context.slither
+        sl_parser = caller_context.slither_parser
+    else:
+        raise SlitherError(
+            f"{type(caller_context)} ({caller_context} is not valid for find_variable"
+        )
+
+    return direct_contracts, direct_functions_parser, sl, sl_parser
+
+
+def find_variable(
+    var_name: str,
+    caller_context: CallerContext,
+    referenced_declaration: Optional[int] = None,
+    is_super=False,
+) -> Union[
+    Variable, Function, Contract, SolidityVariable, SolidityFunction, Event, Enum, Structure,
+]:
+    from slither.solc_parsing.declarations.function import FunctionSolc
+    from slither.solc_parsing.declarations.contract import ContractSolc
+
+    # variable are looked from the contract declarer
+    # functions can be shadowed, but are looked from the contract instance, rather than the contract declarer
+    # the difference between function and variable come from the fact that an internal call, or an variable access
+    # in a function does not behave similariy, for example in:
+    # contract C{
+    #   function f(){
+    #     state_var = 1
+    #     f2()
+    #  }
+    # state_var will refer to C.state_var, no mater if C is inherited
+    # while f2() will refer to the function definition of the inherited contract (C.f2() in the context of C, or
+    # the contract inheriting from C)
+    # for events it's unclear what should be the behavior, as they can be shadowed, but there is not impact
+    # structure/enums cannot be shadowed
+
+    direct_contracts, direct_functions_parser, sl, sl_parser = _find_variable_init(caller_context)
+
+    all_contracts = sl.contracts
+    all_functions_parser = sl_parser.all_functions_and_modifiers_parser
+
+    # Look for all references delcaration
+    # First look only in the context of function/contract
+    # Then look everywhere
+    # Because functions are copied between contracts, two functions can have the same ref
+    # So we need to first look with respect to the direct context
+
+    ret = _find_variable_from_ref_declaration(
+        referenced_declaration, direct_contracts, direct_functions_parser
+    )
+    if ret:
+        return ret
+
+    ret = _find_variable_from_ref_declaration(
+        referenced_declaration, all_contracts, all_functions_parser
+    )
+    if ret:
+        return ret
+
+    function_parser: Optional[FunctionSolc] = caller_context if isinstance(
+        caller_context, FunctionSolc
+    ) else None
+    ret = _find_variable_in_function_parser(var_name, function_parser, referenced_declaration)
+    if ret:
+        return ret
+
+    contract: Optional[Contract] = None
+    contract_declarer: Optional[Contract] = None
+    if isinstance(caller_context, ContractSolc):
+        contract = caller_context.underlying_contract
+        contract_declarer = caller_context.underlying_contract
+    elif isinstance(caller_context, FunctionSolc):
+        underlying_func = caller_context.underlying_function
+        # If contract_parser is set to None, then underlying_function is a functionContract
+        assert isinstance(underlying_func, FunctionContract)
+        contract = underlying_func.contract
+        contract_declarer = underlying_func.contract_declarer
+
+    ret = _find_in_contract(var_name, contract, contract_declarer, is_super)
+    if ret:
+        return ret
+
     # Could refer to any enum
-    all_enumss = [c.enums_as_dict for c in contract.slither.contracts]
+    all_enumss = [c.enums_as_dict for c in sl.contracts]
     all_enums = {k: v for d in all_enumss for k, v in d.items()}
     if var_name in all_enums:
         return all_enums[var_name]
 
+    contracts = sl.contracts_as_dict
+    if var_name in contracts:
+        return contracts[var_name]
+
     if var_name in SOLIDITY_VARIABLES:
         return SolidityVariable(var_name)
 
     if var_name in SOLIDITY_FUNCTIONS:
         return SolidityFunction(var_name)
 
-    contracts = contract.slither.contracts_as_dict
-    if var_name in contracts:
-        return contracts[var_name]
-
-    if referenced_declaration:
-        # id of the contracts is the referenced declaration
-        # This is not true for the functions, as we dont always have the referenced_declaration
-        # But maybe we could? (TODO)
-        for contract_candidate in contract.slither.contracts:
-            if contract_candidate.id == referenced_declaration:
-                return contract_candidate
-        for function_candidate in caller_context.slither_parser.all_functions_parser:
-            if function_candidate.referenced_declaration == referenced_declaration:
-                return function_candidate.underlying_function
+    # Top level must be at the end, if nothing else was found
+    ret = _find_top_level(var_name, sl)
+    if ret:
+        return ret
 
     raise VariableNotFound("Variable not found: {} (context {})".format(var_name, caller_context))
 

+ 126 - 61
slither/solc_parsing/slitherSolc.py

@@ -5,6 +5,10 @@ import re
 from typing import List, Dict
 
 from slither.core.declarations import Contract
+from slither.core.declarations.enum_top_level import EnumTopLevel
+from slither.core.declarations.function_top_level import FunctionTopLevel
+from slither.core.declarations.structure_top_level import StructureTopLevel
+from slither.core.variables.top_level_variable import TopLevelVariable
 from slither.exceptions import SlitherException
 
 from slither.solc_parsing.declarations.contract import ContractSolc
@@ -13,6 +17,9 @@ from slither.core.slither_core import SlitherCore
 from slither.core.declarations.pragma_directive import Pragma
 from slither.core.declarations.import_directive import Import
 from slither.analyses.data_dependency.data_dependency import compute_dependency
+from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc
+from slither.solc_parsing.exceptions import VariableNotFound
+from slither.solc_parsing.variables.top_level_variable import TopLevelVariableSolc
 
 logging.basicConfig()
 logger = logging.getLogger("SlitherSolcParsing")
@@ -29,11 +36,14 @@ class SlitherSolc:
         self._analyzed = False
 
         self._underlying_contract_to_parser: Dict[Contract, ContractSolc] = dict()
+        self._structures_top_level_parser: List[StructureTopLevelSolc] = []
+        self._variables_top_level_parser: List[TopLevelVariableSolc] = []
+        self._functions_top_level_parser: List[FunctionSolc] = []
 
         self._is_compact_ast = False
         self._core: SlitherCore = core
 
-        self._all_functions_parser: List[FunctionSolc] = []
+        self._all_functions_and_modifier_parser: List[FunctionSolc] = []
 
         self._top_level_contracts_counter = 0
 
@@ -42,11 +52,11 @@ class SlitherSolc:
         return self._core
 
     @property
-    def all_functions_parser(self) -> List[FunctionSolc]:
-        return self._all_functions_parser
+    def all_functions_and_modifiers_parser(self) -> List[FunctionSolc]:
+        return self._all_functions_and_modifier_parser
 
-    def add_functions_parser(self, f: FunctionSolc):
-        self._all_functions_parser.append(f)
+    def add_function_or_modifier_parser(self, f: FunctionSolc):
+        self._all_functions_and_modifier_parser.append(f)
 
     @property
     def underlying_contract_to_parser(self) -> Dict[Contract, ContractSolc]:
@@ -79,19 +89,19 @@ class SlitherSolc:
     ###################################################################################
     ###################################################################################
 
-    def parse_contracts_from_json(self, json_data: str) -> bool:
+    def parse_top_level_from_json(self, json_data: str) -> bool:
         try:
             data_loaded = json.loads(json_data)
             # Truffle AST
             if "ast" in data_loaded:
-                self.parse_contracts_from_loaded_json(data_loaded["ast"], data_loaded["sourcePath"])
+                self.parse_top_level_from_loaded_json(data_loaded["ast"], data_loaded["sourcePath"])
                 return True
             # solc AST, where the non-json text was removed
             if "attributes" in data_loaded:
                 filename = data_loaded["attributes"]["absolutePath"]
             else:
                 filename = data_loaded["absolutePath"]
-            self.parse_contracts_from_loaded_json(data_loaded, filename)
+            self.parse_top_level_from_loaded_json(data_loaded, filename)
             return True
         except ValueError:
 
@@ -102,13 +112,40 @@ class SlitherSolc:
                 json_data = json_data[first:last]
 
                 data_loaded = json.loads(json_data)
-                self.parse_contracts_from_loaded_json(data_loaded, filename)
+                self.parse_top_level_from_loaded_json(data_loaded, filename)
                 return True
             return False
 
-    def parse_contracts_from_loaded_json(
+    def _parse_enum(self, top_level_data: Dict):
+        if self.is_compact_ast:
+            name = top_level_data["name"]
+            canonicalName = top_level_data["canonicalName"]
+        else:
+            name = top_level_data["attributes"][self.get_key()]
+            if "canonicalName" in top_level_data["attributes"]:
+                canonicalName = top_level_data["attributes"]["canonicalName"]
+            else:
+                canonicalName = name
+        values = []
+        children = (
+            top_level_data["members"]
+            if "members" in top_level_data
+            else top_level_data.get("children", [])
+        )
+        for child in children:
+            assert child[self.get_key()] == "EnumValue"
+            if self.is_compact_ast:
+                values.append(child["name"])
+            else:
+                values.append(child["attributes"][self.get_key()])
+
+        enum = EnumTopLevel(name, canonicalName, values)
+        enum.set_offset(top_level_data["src"], self._core)
+        self.core.enums_top_level.append(enum)
+
+    def parse_top_level_from_loaded_json(
         self, data_loaded: Dict, filename: str
-    ):  # pylint: disable=too-many-branches
+    ):  # pylint: disable=too-many-branches,too-many-statements
         if "nodeType" in data_loaded:
             self._is_compact_ast = True
 
@@ -128,68 +165,62 @@ class SlitherSolc:
             logger.error("solc version is not supported")
             return
 
-        for contract_data in data_loaded[self.get_children()]:
-            assert contract_data[self.get_key()] in [
-                "ContractDefinition",
-                "PragmaDirective",
-                "ImportDirective",
-                "StructDefinition",
-                "EnumDefinition",
-            ]
-            if contract_data[self.get_key()] == "ContractDefinition":
+        for top_level_data in data_loaded[self.get_children()]:
+            if top_level_data[self.get_key()] == "ContractDefinition":
                 contract = Contract()
-                contract_parser = ContractSolc(self, contract, contract_data)
-                if "src" in contract_data:
-                    contract.set_offset(contract_data["src"], self._core)
+                contract_parser = ContractSolc(self, contract, top_level_data)
+                if "src" in top_level_data:
+                    contract.set_offset(top_level_data["src"], self._core)
 
                 self._underlying_contract_to_parser[contract] = contract_parser
 
-            elif contract_data[self.get_key()] == "PragmaDirective":
+            elif top_level_data[self.get_key()] == "PragmaDirective":
                 if self._is_compact_ast:
-                    pragma = Pragma(contract_data["literals"])
+                    pragma = Pragma(top_level_data["literals"])
                 else:
-                    pragma = Pragma(contract_data["attributes"]["literals"])
-                pragma.set_offset(contract_data["src"], self._core)
+                    pragma = Pragma(top_level_data["attributes"]["literals"])
+                pragma.set_offset(top_level_data["src"], self._core)
                 self._core.pragma_directives.append(pragma)
-            elif contract_data[self.get_key()] == "ImportDirective":
+            elif top_level_data[self.get_key()] == "ImportDirective":
                 if self.is_compact_ast:
-                    import_directive = Import(contract_data["absolutePath"])
+                    import_directive = Import(top_level_data["absolutePath"])
+                    # TODO investigate unitAlias in version < 0.7 and legacy ast
+                    if "unitAlias" in top_level_data:
+                        import_directive.alias = top_level_data["unitAlias"]
                 else:
-                    import_directive = Import(contract_data["attributes"].get("absolutePath", ""))
-                import_directive.set_offset(contract_data["src"], self._core)
+                    import_directive = Import(top_level_data["attributes"].get("absolutePath", ""))
+                import_directive.set_offset(top_level_data["src"], self._core)
                 self._core.import_directives.append(import_directive)
 
-            elif contract_data[self.get_key()] in [
-                "StructDefinition",
-                "EnumDefinition",
-            ]:
-                # This can only happen for top-level structure and enum
-                # They were introduced with 0.6.5
-                assert self._is_compact_ast  # Do not support top level definition for legacy AST
-                fake_contract_data = {
-                    "name": f"SlitherInternalTopLevelContract{self._top_level_contracts_counter}",
-                    "id": -1000
-                    + self._top_level_contracts_counter,  # TODO: determine if collission possible
-                    "linearizedBaseContracts": [],
-                    "fullyImplemented": True,
-                    "contractKind": "SLitherInternal",
-                }
-                self._top_level_contracts_counter += 1
-                contract = Contract()
-                top_level_contract = ContractSolc(self, contract, fake_contract_data)
-                contract.is_top_level = True
-                contract.set_offset(contract_data["src"], self._core)
-
-                if contract_data[self.get_key()] == "StructDefinition":
-                    top_level_contract.structures_not_parsed.append(
-                        contract_data
-                    )  # Todo add proper setters
-                else:
-                    top_level_contract.enums_not_parsed.append(
-                        contract_data
-                    )  # Todo add proper setters
+            elif top_level_data[self.get_key()] == "StructDefinition":
+                st = StructureTopLevel()
+                st.set_offset(top_level_data["src"], self._core)
+                st_parser = StructureTopLevelSolc(st, top_level_data, self)
+
+                self._core.structures_top_level.append(st)
+                self._structures_top_level_parser.append(st_parser)
+
+            elif top_level_data[self.get_key()] == "EnumDefinition":
+                # Note enum don't need a complex parser, so everything is directly done
+                self._parse_enum(top_level_data)
+
+            elif top_level_data[self.get_key()] == "VariableDeclaration":
+                var = TopLevelVariable()
+                var_parser = TopLevelVariableSolc(var, top_level_data)
+                var.set_offset(top_level_data["src"], self._core)
+
+                self._core.variables_top_level.append(var)
+                self._variables_top_level_parser.append(var_parser)
+            elif top_level_data[self.get_key()] == "FunctionDefinition":
+                func = FunctionTopLevel(self.core)
+                func_parser = FunctionSolc(func, top_level_data, None, self)
 
-                self._underlying_contract_to_parser[contract] = top_level_contract
+                self._core.functions_top_level.append(func)
+                self._functions_top_level_parser.append(func_parser)
+                self.add_function_or_modifier_parser(func_parser)
+
+            else:
+                raise SlitherException(f"Top level {top_level_data[self.get_key()]} not supported")
 
     def _parse_source_unit(self, data: Dict, filename: str):
         if data[self.get_key()] != "SourceUnit":
@@ -409,6 +440,8 @@ Please rename it, this name is reserved for Slither's internals"""
         for lib in libraries:
             self._analyze_struct_events(lib)
 
+        self._analyze_top_level_structures()
+
         # Start with the contracts without inheritance
         # Analyze a contract only if all its fathers
         # Were analyzed
@@ -478,16 +511,44 @@ Please rename it, this name is reserved for Slither's internals"""
 
         contract.set_is_analyzed(True)
 
+    def _analyze_top_level_structures(self):
+        try:
+            for struct in self._structures_top_level_parser:
+                struct.analyze()
+        except (VariableNotFound, KeyError) as e:
+            raise SlitherException(f"Missing struct {e} during top level structure analyze") from e
+
+    def _analyze_top_level_variables(self):
+        try:
+            for var in self._variables_top_level_parser:
+                var.analyze(self)
+        except (VariableNotFound, KeyError) as e:
+            raise SlitherException(f"Missing struct {e} during top level structure analyze") from e
+
+    def _analyze_params_top_level_function(self):
+        for func_parser in self._functions_top_level_parser:
+            func_parser.analyze_params()
+            self.core.add_function(func_parser.underlying_function)
+
+    def _analyze_content_top_level_function(self):
+        try:
+            for func_parser in self._functions_top_level_parser:
+                func_parser.analyze_content()
+        except (VariableNotFound, KeyError) as e:
+            raise SlitherException(f"Missing {e} during top level function analyze") from e
+
     def _analyze_variables_modifiers_functions(self, contract: ContractSolc):
         # State variables, modifiers and functions can refer to anything
 
         contract.analyze_params_modifiers()
         contract.analyze_params_functions()
+        self._analyze_params_top_level_function()
 
         contract.analyze_state_variables()
 
         contract.analyze_content_modifiers()
         contract.analyze_content_functions()
+        self._analyze_content_top_level_function()
 
         contract.set_is_analyzed(True)
 
@@ -508,6 +569,10 @@ Please rename it, this name is reserved for Slither's internals"""
                     )
 
             contract.convert_expression_to_slithir_ssa()
+
+        for func in self.core.functions_top_level:
+            func.generate_slithir_and_analyze()
+            func.generate_slithir_ssa(dict())
         self._core.propagate_function_calls()
         for contract in self._core.contracts:
             contract.fix_phi()

+ 136 - 48
slither/solc_parsing/solidity_types/type_parsing.py

@@ -2,26 +2,23 @@ import logging
 import re
 from typing import List, TYPE_CHECKING, Union, Dict
 
+from slither.core.declarations.function_contract import FunctionContract
+from slither.core.expressions.literal import Literal
+from slither.core.solidity_types.array_type import ArrayType
 from slither.core.solidity_types.elementary_type import (
     ElementaryType,
     ElementaryTypeName,
 )
+from slither.core.solidity_types.function_type import FunctionType
+from slither.core.solidity_types.mapping_type import MappingType
 from slither.core.solidity_types.type import Type
 from slither.core.solidity_types.user_defined_type import UserDefinedType
-from slither.core.solidity_types.array_type import ArrayType
-from slither.core.solidity_types.mapping_type import MappingType
-from slither.core.solidity_types.function_type import FunctionType
-
 from slither.core.variables.function_type_variable import FunctionTypeVariable
-
-from slither.core.declarations.contract import Contract
-
-from slither.core.expressions.literal import Literal
-
 from slither.solc_parsing.exceptions import ParsingError
 
 if TYPE_CHECKING:
     from slither.core.declarations import Structure, Enum
+    from slither.core.declarations.contract import Contract
 
 logger = logging.getLogger("TypeParsing")
 
@@ -37,12 +34,14 @@ class UnknownType:  # pylint: disable=too-few-public-methods
         return self._name
 
 
-def _find_from_type_name(  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
+def _find_from_type_name(  # pylint: disable=too-many-locals,too-many-branches,too-many-statements,too-many-arguments
     name: str,
-    contract: Contract,
-    contracts: List[Contract],
-    structures: List["Structure"],
-    enums: List["Enum"],
+    functions_direct_access: List["Function"],
+    contracts_direct_access: List["Contract"],
+    structures_direct_access: List["Structure"],
+    all_structures: List["Structure"],
+    enums_direct_access: List["Enum"],
+    all_enums: List["Enum"],
 ) -> Type:
     name_elementary = name.split(" ")[0]
     if "[" in name_elementary:
@@ -60,19 +59,20 @@ def _find_from_type_name(  # pylint: disable=too-many-locals,too-many-branches,t
         name_contract = name_contract[len("contract ") :]
     if name_contract.startswith("library "):
         name_contract = name_contract[len("library ") :]
-    var_type = next((c for c in contracts if c.name == name_contract), None)
+    var_type = next((c for c in contracts_direct_access if c.name == name_contract), None)
 
     if not var_type:
-        var_type = next((st for st in structures if st.name == name), None)
+        var_type = next((st for st in structures_direct_access if st.name == name), None)
     if not var_type:
-        var_type = next((e for e in enums if e.name == name), None)
+        var_type = next((e for e in enums_direct_access if e.name == name), None)
     if not var_type:
         # any contract can refer to another contract's enum
         enum_name = name
         if enum_name.startswith("enum "):
             enum_name = enum_name[len("enum ") :]
-        all_enums = [c.enums for c in contracts]
-        all_enums = [item for sublist in all_enums for item in sublist]
+        # all_enums = [c.enums for c in contracts]
+        # all_enums = [item for sublist in all_enums for item in sublist]
+        # all_enums += contract.slither.enums_top_level
         var_type = next((e for e in all_enums if e.name == enum_name), None)
         if not var_type:
             var_type = next((e for e in all_enums if e.canonical_name == enum_name), None)
@@ -82,8 +82,9 @@ def _find_from_type_name(  # pylint: disable=too-many-locals,too-many-branches,t
         if name_struct.startswith("struct "):
             name_struct = name_struct[len("struct ") :]
             name_struct = name_struct.split(" ")[0]  # remove stuff like storage pointer at the end
-        all_structures = [c.structures for c in contracts]
-        all_structures = [item for sublist in all_structures for item in sublist]
+        # all_structures = [c.structures for c in contracts]
+        # all_structures = [item for sublist in all_structures for item in sublist]
+        # all_structures += contract.slither.structures_top_level
         var_type = next((st for st in all_structures if st.name == name_struct), None)
         if not var_type:
             var_type = next((st for st in all_structures if st.canonical_name == name_struct), None)
@@ -98,7 +99,7 @@ def _find_from_type_name(  # pylint: disable=too-many-locals,too-many-branches,t
                 return ArrayType(UserDefinedType(var_type), Literal(depth, "uint256"))
 
     if not var_type:
-        var_type = next((f for f in contract.functions if f.name == name), None)
+        var_type = next((f for f in functions_direct_access if f.name == name), None)
     if not var_type:
         if name.startswith("function "):
             found = re.findall(
@@ -110,10 +111,27 @@ def _find_from_type_name(  # pylint: disable=too-many-locals,too-many-branches,t
                 [v for v in found[0][1].split(",") if v != ""] if len(found[0]) > 1 else []
             )
             params = [
-                _find_from_type_name(p, contract, contracts, structures, enums) for p in params
+                _find_from_type_name(
+                    p,
+                    functions_direct_access,
+                    contracts_direct_access,
+                    structures_direct_access,
+                    all_structures,
+                    enums_direct_access,
+                    all_enums,
+                )
+                for p in params
             ]
             return_values = [
-                _find_from_type_name(r, contract, contracts, structures, enums)
+                _find_from_type_name(
+                    r,
+                    functions_direct_access,
+                    contracts_direct_access,
+                    structures_direct_access,
+                    all_structures,
+                    enums_direct_access,
+                    all_enums,
+                )
                 for r in return_values
             ]
             params_vars = []
@@ -140,8 +158,24 @@ def _find_from_type_name(  # pylint: disable=too-many-locals,too-many-branches,t
             from_ = found[0][0]
             to_ = found[0][1]
 
-            from_type = _find_from_type_name(from_, contract, contracts, structures, enums)
-            to_type = _find_from_type_name(to_, contract, contracts, structures, enums)
+            from_type = _find_from_type_name(
+                from_,
+                functions_direct_access,
+                contracts_direct_access,
+                structures_direct_access,
+                all_structures,
+                enums_direct_access,
+                all_enums,
+            )
+            to_type = _find_from_type_name(
+                to_,
+                functions_direct_access,
+                contracts_direct_access,
+                structures_direct_access,
+                all_structures,
+                enums_direct_access,
+                all_enums,
+            )
 
             return MappingType(from_type, to_type)
 
@@ -158,29 +192,71 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
     from slither.solc_parsing.variables.function_type_variable import FunctionTypeVariableSolc
     from slither.solc_parsing.declarations.contract import ContractSolc
     from slither.solc_parsing.declarations.function import FunctionSolc
-
-    if isinstance(caller_context, ContractSolc):
-        contract = caller_context.underlying_contract
-        contract_parser = caller_context
-        is_compact_ast = caller_context.is_compact_ast
-    elif isinstance(caller_context, FunctionSolc):
-        contract = caller_context.underlying_function.contract
-        contract_parser = caller_context.contract_parser
-        is_compact_ast = caller_context.is_compact_ast
+    from slither.solc_parsing.slitherSolc import SlitherSolc
+
+    # Note: for convenicence top level functions use the same parser than function in contract
+    # but contract_parser is set to None
+    if isinstance(caller_context, SlitherSolc) or (
+        isinstance(caller_context, FunctionSolc) and caller_context.contract_parser is None
+    ):
+        if isinstance(caller_context, SlitherSolc):
+            sl = caller_context.core
+            next_context = caller_context
+        else:
+            assert isinstance(caller_context, FunctionSolc)
+            sl = caller_context.underlying_function.slither
+            next_context = caller_context.slither_parser
+        structures_direct_access = sl.structures_top_level
+        all_structuress = [c.structures for c in sl.contracts]
+        all_structures = [item for sublist in all_structuress for item in sublist]
+        all_structures += structures_direct_access
+        enums_direct_access = sl.enums_top_level
+        all_enumss = [c.enums for c in sl.contracts]
+        all_enums = [item for sublist in all_enumss for item in sublist]
+        all_enums += enums_direct_access
+        contracts = sl.contracts
+        functions = []
+    elif isinstance(caller_context, (ContractSolc, FunctionSolc)):
+        if isinstance(caller_context, FunctionSolc):
+            underlying_func = caller_context.underlying_function
+            # If contract_parser is set to None, then underlying_function is a functionContract
+            # See note above
+            assert isinstance(underlying_func, FunctionContract)
+            contract = underlying_func.contract
+            next_context = caller_context.contract_parser
+        else:
+            contract = caller_context.underlying_contract
+            next_context = caller_context
+
+        structures_direct_access = contract.structures + contract.slither.structures_top_level
+        all_structuress = [c.structures for c in contract.slither.contracts]
+        all_structures = [item for sublist in all_structuress for item in sublist]
+        all_structures += contract.slither.structures_top_level
+        enums_direct_access = contract.enums + contract.slither.enums_top_level
+        all_enumss = [c.enums for c in contract.slither.contracts]
+        all_enums = [item for sublist in all_enumss for item in sublist]
+        all_enums += contract.slither.enums_top_level
+        contracts = contract.slither.contracts
+        functions = contract.functions + contract.modifiers
     else:
         raise ParsingError(f"Incorrect caller context: {type(caller_context)}")
 
+    is_compact_ast = caller_context.is_compact_ast
     if is_compact_ast:
         key = "nodeType"
     else:
         key = "name"
 
-    structures = contract.structures + contract.slither.top_level_structures
-    enums = contract.enums + contract.slither.top_level_enums
-    contracts = contract.slither.contracts
-
     if isinstance(t, UnknownType):
-        return _find_from_type_name(t.name, contract, contracts, structures, enums)
+        return _find_from_type_name(
+            t.name,
+            functions,
+            contracts,
+            structures_direct_access,
+            all_structures,
+            enums_direct_access,
+            all_enums,
+        )
 
     if t[key] == "ElementaryTypeName":
         if is_compact_ast:
@@ -190,13 +266,25 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
     if t[key] == "UserDefinedTypeName":
         if is_compact_ast:
             return _find_from_type_name(
-                t["typeDescriptions"]["typeString"], contract, contracts, structures, enums,
+                t["typeDescriptions"]["typeString"],
+                functions,
+                contracts,
+                structures_direct_access,
+                all_structures,
+                enums_direct_access,
+                all_enums,
             )
 
         # Determine if we have a type node (otherwise we use the name node, as some older solc did not have 'type').
         type_name_key = "type" if "type" in t["attributes"] else key
         return _find_from_type_name(
-            t["attributes"][type_name_key], contract, contracts, structures, enums
+            t["attributes"][type_name_key],
+            functions,
+            contracts,
+            structures_direct_access,
+            all_structures,
+            enums_direct_access,
+            all_enums,
         )
 
     if t[key] == "ArrayTypeName":
@@ -204,25 +292,25 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
         if is_compact_ast:
             if t.get("length", None):
                 length = parse_expression(t["length"], caller_context)
-            array_type = parse_type(t["baseType"], contract_parser)
+            array_type = parse_type(t["baseType"], next_context)
         else:
             if len(t["children"]) == 2:
                 length = parse_expression(t["children"][1], caller_context)
             else:
                 assert len(t["children"]) == 1
-            array_type = parse_type(t["children"][0], contract_parser)
+            array_type = parse_type(t["children"][0], next_context)
         return ArrayType(array_type, length)
 
     if t[key] == "Mapping":
 
         if is_compact_ast:
-            mappingFrom = parse_type(t["keyType"], contract_parser)
-            mappingTo = parse_type(t["valueType"], contract_parser)
+            mappingFrom = parse_type(t["keyType"], next_context)
+            mappingTo = parse_type(t["valueType"], next_context)
         else:
             assert len(t["children"]) == 2
 
-            mappingFrom = parse_type(t["children"][0], contract_parser)
-            mappingTo = parse_type(t["children"][1], contract_parser)
+            mappingFrom = parse_type(t["children"][0], next_context)
+            mappingTo = parse_type(t["children"][1], next_context)
 
         return MappingType(mappingFrom, mappingTo)
 

+ 15 - 0
slither/solc_parsing/variables/top_level_variable.py

@@ -0,0 +1,15 @@
+from typing import Dict
+
+from slither.core.variables.top_level_variable import TopLevelVariable
+from slither.solc_parsing.variables.variable_declaration import VariableDeclarationSolc
+
+
+class TopLevelVariableSolc(VariableDeclarationSolc):
+    def __init__(self, variable: TopLevelVariable, variable_data: Dict):
+        super().__init__(variable, variable_data)
+
+    @property
+    def underlying_variable(self) -> TopLevelVariable:
+        # Todo: Not sure how to overcome this with mypy
+        assert isinstance(self._variable, TopLevelVariable)
+        return self._variable

+ 8 - 4
slither/solc_parsing/yul/parse_yul.py

@@ -8,6 +8,7 @@ from slither.core.declarations import (
     SolidityFunction,
     Contract,
 )
+from slither.core.declarations.function_contract import FunctionContract
 from slither.core.expressions import (
     Literal,
     AssignmentOperation,
@@ -113,7 +114,9 @@ class YulScope(metaclass=abc.ABCMeta):
         "_parent_func",
     ]
 
-    def __init__(self, contract: Contract, yul_id: List[str], parent_func: Function = None):
+    def __init__(
+        self, contract: Optional[Contract], yul_id: List[str], parent_func: Function = None
+    ):
         self._contract = contract
         self._id: List[str] = yul_id
         self._yul_local_variables: List[YulLocalVariable] = []
@@ -125,7 +128,7 @@ class YulScope(metaclass=abc.ABCMeta):
         return self._id
 
     @property
-    def contract(self) -> Contract:
+    def contract(self) -> Optional[Contract]:
         return self._contract
 
     @property
@@ -205,6 +208,7 @@ class YulFunction(YulScope):
         func.set_visibility("private")
         func.set_offset(ast["src"], root.slither)
         func.set_contract(root.contract)
+        func.slither = root.slither
         func.set_contract_declarer(root.contract)
         func.scope = root.id
         func.is_implemented = True
@@ -267,7 +271,7 @@ class YulBlock(YulScope):
 
     __slots__ = ["_entrypoint", "_parent_func", "_nodes"]
 
-    def __init__(self, contract: Contract, entrypoint: Node, yul_id: List[str], **kwargs):
+    def __init__(self, contract: Optional[Contract], entrypoint: Node, yul_id: List[str], **kwargs):
         super().__init__(contract, yul_id, **kwargs)
 
         self._entrypoint: YulNode = YulNode(entrypoint, self)
@@ -329,7 +333,7 @@ def convert_yul_block(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
 
 
 def convert_yul_function_definition(root: YulScope, parent: YulNode, ast: Dict) -> YulNode:
-    func = Function()
+    func = FunctionContract(root.slither)
     yul_function = YulFunction(func, root, ast)
 
     root.contract.add_function(func)

+ 1 - 2
tests/ast-parsing/expected/struct-0.6.0-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.6.1-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.6.10-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.6.11-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.6.12-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.6.2-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.6.3-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.6.4-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.6.5-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.6.6-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.6.7-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.6.8-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.6.9-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.7.0-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.7.1-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 1 - 2
tests/ast-parsing/expected/struct-0.7.2-compact.json

@@ -1,4 +1,3 @@
 {
-  "C": {},
-  "SlitherInternalTopLevelContract0": {}
+  "C": {}
 }

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.0-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.1-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.10-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.11-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.12-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.12-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.13-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.13-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.14-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.14-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.15-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.15-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.16-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.16-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.17-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.17-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.18-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.18-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.19-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.19-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.2-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.20-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.20-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.21-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.21-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.22-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.22-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.23-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.23-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.24-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.24-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.25-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.25-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.26-compact.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.26-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.3-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.4-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.5-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.6-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.7-legacy.json

@@ -0,0 +1,3 @@
+{
+  "C": {}
+}

+ 3 - 0
tests/ast-parsing/expected/top-level-0.4.8-legacy.json

@@ -0,0 +1,3 @@