diff --git a/slither/core/declarations/__init__.py b/slither/core/declarations/__init__.py index 89de67e6b..d3fe3dd22 100644 --- a/slither/core/declarations/__init__.py +++ b/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 diff --git a/slither/core/declarations/contract.py b/slither/core/declarations/contract.py index efacef319..f16b0e70c 100644 --- a/slither/core/declarations/contract.py +++ b/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 ) diff --git a/slither/core/declarations/enum.py b/slither/core/declarations/enum.py index ae306f266..76a43dc4d 100644 --- a/slither/core/declarations/enum.py +++ b/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 diff --git a/slither/core/declarations/enum_contract.py b/slither/core/declarations/enum_contract.py new file mode 100644 index 000000000..46168d107 --- /dev/null +++ b/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 diff --git a/slither/core/declarations/enum_top_level.py b/slither/core/declarations/enum_top_level.py new file mode 100644 index 000000000..44dccd7c9 --- /dev/null +++ b/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 diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index f71899a2b..ff44b0474 100644 --- a/slither/core/declarations/function.py +++ b/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: diff --git a/slither/core/declarations/function_contract.py b/slither/core/declarations/function_contract.py new file mode 100644 index 000000000..66f387e32 --- /dev/null +++ b/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) diff --git a/slither/core/declarations/function_top_level.py b/slither/core/declarations/function_top_level.py new file mode 100644 index 000000000..f1239b54c --- /dev/null +++ b/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) diff --git a/slither/core/declarations/import_directive.py b/slither/core/declarations/import_directive.py index acc8135ca..bf3ce67d0 100644 --- a/slither/core/declarations/import_directive.py +++ b/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 diff --git a/slither/core/declarations/modifier.py b/slither/core/declarations/modifier.py index 9dd61a353..472f6ac1e 100644 --- a/slither/core/declarations/modifier.py +++ b/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 diff --git a/slither/core/declarations/solidity_variables.py b/slither/core/declarations/solidity_variables.py index b1b0f97fa..559850ee3 100644 --- a/slither/core/declarations/solidity_variables.py +++ b/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 diff --git a/slither/core/declarations/structure.py b/slither/core/declarations/structure.py index e7eac1ac5..ee3548f57 100644 --- a/slither/core/declarations/structure.py +++ b/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 = [] diff --git a/slither/core/declarations/structure_contract.py b/slither/core/declarations/structure_contract.py new file mode 100644 index 000000000..aaf660e1e --- /dev/null +++ b/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 diff --git a/slither/core/declarations/structure_top_level.py b/slither/core/declarations/structure_top_level.py new file mode 100644 index 000000000..9385a6e9d --- /dev/null +++ b/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 diff --git a/slither/core/declarations/top_level.py b/slither/core/declarations/top_level.py new file mode 100644 index 000000000..15facf2f9 --- /dev/null +++ b/slither/core/declarations/top_level.py @@ -0,0 +1,5 @@ +from slither.core.source_mapping.source_mapping import SourceMapping + + +class TopLevel(SourceMapping): + pass diff --git a/slither/core/slither_core.py b/slither/core/slither_core.py index 51fb96aec..b0402ea63 100644 --- a/slither/core/slither_core.py +++ b/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 ################################################################################### diff --git a/slither/core/variables/top_level_variable.py b/slither/core/variables/top_level_variable.py new file mode 100644 index 000000000..705936261 --- /dev/null +++ b/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 + ################################################################################### + ################################################################################### diff --git a/slither/printers/summary/slithir.py b/slither/printers/summary/slithir.py index 391780d24..c54717560 100644 --- a/slither/printers/summary/slithir.py +++ b/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 diff --git a/slither/slither.py b/slither/slither.py index d3d20a9c2..39c7108cd 100644 --- a/slither/slither.py +++ b/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): diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index 2e174ef8f..c3e366f8a 100644 --- a/slither/slithir/convert.py +++ b/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), diff --git a/slither/slithir/operations/internal_call.py b/slither/slithir/operations/internal_call.py index bde68e46d..c9092bdc5 100644 --- a/slither/slithir/operations/internal_call.py +++ b/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 diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index 4bdfdf7ff..300d29bfe 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/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): diff --git a/slither/solc_parsing/declarations/function.py b/slither/solc_parsing/declarations/function.py index ae772dfdd..d12a6ce88 100644 --- a/slither/solc_parsing/declarations/function.py +++ b/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, diff --git a/slither/solc_parsing/declarations/modifier.py b/slither/solc_parsing/declarations/modifier.py index 158cc8263..bccbf78b5 100644 --- a/slither/solc_parsing/declarations/modifier.py +++ b/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 diff --git a/slither/solc_parsing/declarations/structure.py b/slither/solc_parsing/declarations/structure_contract.py similarity index 59% rename from slither/solc_parsing/declarations/structure.py rename to slither/solc_parsing/declarations/structure_contract.py index 0fa2a36a0..ac3e5982d 100644 --- a/slither/solc_parsing/declarations/structure.py +++ b/slither/solc_parsing/declarations/structure_contract.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: diff --git a/slither/solc_parsing/declarations/structure_top_level.py b/slither/solc_parsing/declarations/structure_top_level.py new file mode 100644 index 000000000..23108d603 --- /dev/null +++ b/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 = [] diff --git a/slither/solc_parsing/expressions/expression_parsing.py b/slither/solc_parsing/expressions/expression_parsing.py index fbec11f8c..df88090e1 100644 --- a/slither/solc_parsing/expressions/expression_parsing.py +++ b/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)) diff --git a/slither/solc_parsing/slitherSolc.py b/slither/solc_parsing/slitherSolc.py index 101204562..1e7a62485 100644 --- a/slither/solc_parsing/slitherSolc.py +++ b/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() diff --git a/slither/solc_parsing/solidity_types/type_parsing.py b/slither/solc_parsing/solidity_types/type_parsing.py index ca796b6a8..f02e8f7de 100644 --- a/slither/solc_parsing/solidity_types/type_parsing.py +++ b/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) diff --git a/slither/solc_parsing/variables/top_level_variable.py b/slither/solc_parsing/variables/top_level_variable.py new file mode 100644 index 000000000..328d2baae --- /dev/null +++ b/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 diff --git a/slither/solc_parsing/yul/parse_yul.py b/slither/solc_parsing/yul/parse_yul.py index b38ee3987..a7a427712 100644 --- a/slither/solc_parsing/yul/parse_yul.py +++ b/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) diff --git a/tests/ast-parsing/expected/struct-0.6.0-compact.json b/tests/ast-parsing/expected/struct-0.6.0-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.6.0-compact.json +++ b/tests/ast-parsing/expected/struct-0.6.0-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.6.1-compact.json b/tests/ast-parsing/expected/struct-0.6.1-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.6.1-compact.json +++ b/tests/ast-parsing/expected/struct-0.6.1-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.6.10-compact.json b/tests/ast-parsing/expected/struct-0.6.10-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.6.10-compact.json +++ b/tests/ast-parsing/expected/struct-0.6.10-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.6.11-compact.json b/tests/ast-parsing/expected/struct-0.6.11-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.6.11-compact.json +++ b/tests/ast-parsing/expected/struct-0.6.11-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.6.12-compact.json b/tests/ast-parsing/expected/struct-0.6.12-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.6.12-compact.json +++ b/tests/ast-parsing/expected/struct-0.6.12-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.6.2-compact.json b/tests/ast-parsing/expected/struct-0.6.2-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.6.2-compact.json +++ b/tests/ast-parsing/expected/struct-0.6.2-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.6.3-compact.json b/tests/ast-parsing/expected/struct-0.6.3-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.6.3-compact.json +++ b/tests/ast-parsing/expected/struct-0.6.3-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.6.4-compact.json b/tests/ast-parsing/expected/struct-0.6.4-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.6.4-compact.json +++ b/tests/ast-parsing/expected/struct-0.6.4-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.6.5-compact.json b/tests/ast-parsing/expected/struct-0.6.5-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.6.5-compact.json +++ b/tests/ast-parsing/expected/struct-0.6.5-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.6.6-compact.json b/tests/ast-parsing/expected/struct-0.6.6-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.6.6-compact.json +++ b/tests/ast-parsing/expected/struct-0.6.6-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.6.7-compact.json b/tests/ast-parsing/expected/struct-0.6.7-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.6.7-compact.json +++ b/tests/ast-parsing/expected/struct-0.6.7-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.6.8-compact.json b/tests/ast-parsing/expected/struct-0.6.8-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.6.8-compact.json +++ b/tests/ast-parsing/expected/struct-0.6.8-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.6.9-compact.json b/tests/ast-parsing/expected/struct-0.6.9-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.6.9-compact.json +++ b/tests/ast-parsing/expected/struct-0.6.9-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.7.0-compact.json b/tests/ast-parsing/expected/struct-0.7.0-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.7.0-compact.json +++ b/tests/ast-parsing/expected/struct-0.7.0-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.7.1-compact.json b/tests/ast-parsing/expected/struct-0.7.1-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.7.1-compact.json +++ b/tests/ast-parsing/expected/struct-0.7.1-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/struct-0.7.2-compact.json b/tests/ast-parsing/expected/struct-0.7.2-compact.json index c2e1d2e55..0008a4469 100644 --- a/tests/ast-parsing/expected/struct-0.7.2-compact.json +++ b/tests/ast-parsing/expected/struct-0.7.2-compact.json @@ -1,4 +1,3 @@ { - "C": {}, - "SlitherInternalTopLevelContract0": {} + "C": {} } \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.0-legacy.json b/tests/ast-parsing/expected/top-level-0.4.0-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.0-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.1-legacy.json b/tests/ast-parsing/expected/top-level-0.4.1-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.1-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.10-legacy.json b/tests/ast-parsing/expected/top-level-0.4.10-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.10-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.11-legacy.json b/tests/ast-parsing/expected/top-level-0.4.11-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.11-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.12-compact.json b/tests/ast-parsing/expected/top-level-0.4.12-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.12-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.12-legacy.json b/tests/ast-parsing/expected/top-level-0.4.12-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.12-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.13-compact.json b/tests/ast-parsing/expected/top-level-0.4.13-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.13-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.13-legacy.json b/tests/ast-parsing/expected/top-level-0.4.13-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.13-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.14-compact.json b/tests/ast-parsing/expected/top-level-0.4.14-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.14-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.14-legacy.json b/tests/ast-parsing/expected/top-level-0.4.14-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.14-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.15-compact.json b/tests/ast-parsing/expected/top-level-0.4.15-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.15-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.15-legacy.json b/tests/ast-parsing/expected/top-level-0.4.15-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.15-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.16-compact.json b/tests/ast-parsing/expected/top-level-0.4.16-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.16-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.16-legacy.json b/tests/ast-parsing/expected/top-level-0.4.16-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.16-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.17-compact.json b/tests/ast-parsing/expected/top-level-0.4.17-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.17-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.17-legacy.json b/tests/ast-parsing/expected/top-level-0.4.17-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.17-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.18-compact.json b/tests/ast-parsing/expected/top-level-0.4.18-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.18-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.18-legacy.json b/tests/ast-parsing/expected/top-level-0.4.18-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.18-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.19-compact.json b/tests/ast-parsing/expected/top-level-0.4.19-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.19-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.19-legacy.json b/tests/ast-parsing/expected/top-level-0.4.19-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.19-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.2-legacy.json b/tests/ast-parsing/expected/top-level-0.4.2-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.2-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.20-compact.json b/tests/ast-parsing/expected/top-level-0.4.20-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.20-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.20-legacy.json b/tests/ast-parsing/expected/top-level-0.4.20-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.20-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.21-compact.json b/tests/ast-parsing/expected/top-level-0.4.21-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.21-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.21-legacy.json b/tests/ast-parsing/expected/top-level-0.4.21-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.21-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.22-compact.json b/tests/ast-parsing/expected/top-level-0.4.22-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.22-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.22-legacy.json b/tests/ast-parsing/expected/top-level-0.4.22-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.22-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.23-compact.json b/tests/ast-parsing/expected/top-level-0.4.23-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.23-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.23-legacy.json b/tests/ast-parsing/expected/top-level-0.4.23-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.23-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.24-compact.json b/tests/ast-parsing/expected/top-level-0.4.24-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.24-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.24-legacy.json b/tests/ast-parsing/expected/top-level-0.4.24-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.24-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.25-compact.json b/tests/ast-parsing/expected/top-level-0.4.25-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.25-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.25-legacy.json b/tests/ast-parsing/expected/top-level-0.4.25-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.25-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.26-compact.json b/tests/ast-parsing/expected/top-level-0.4.26-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.26-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.26-legacy.json b/tests/ast-parsing/expected/top-level-0.4.26-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.26-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.3-legacy.json b/tests/ast-parsing/expected/top-level-0.4.3-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.3-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.4-legacy.json b/tests/ast-parsing/expected/top-level-0.4.4-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.4-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.5-legacy.json b/tests/ast-parsing/expected/top-level-0.4.5-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.5-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.6-legacy.json b/tests/ast-parsing/expected/top-level-0.4.6-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.6-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.7-legacy.json b/tests/ast-parsing/expected/top-level-0.4.7-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.7-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.8-legacy.json b/tests/ast-parsing/expected/top-level-0.4.8-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.8-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.4.9-legacy.json b/tests/ast-parsing/expected/top-level-0.4.9-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.4.9-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.0-compact.json b/tests/ast-parsing/expected/top-level-0.5.0-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.0-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.0-legacy.json b/tests/ast-parsing/expected/top-level-0.5.0-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.0-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.1-compact.json b/tests/ast-parsing/expected/top-level-0.5.1-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.1-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.1-legacy.json b/tests/ast-parsing/expected/top-level-0.5.1-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.1-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.10-compact.json b/tests/ast-parsing/expected/top-level-0.5.10-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.10-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.10-legacy.json b/tests/ast-parsing/expected/top-level-0.5.10-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.10-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.11-compact.json b/tests/ast-parsing/expected/top-level-0.5.11-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.11-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.11-legacy.json b/tests/ast-parsing/expected/top-level-0.5.11-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.11-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.12-compact.json b/tests/ast-parsing/expected/top-level-0.5.12-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.12-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.12-legacy.json b/tests/ast-parsing/expected/top-level-0.5.12-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.12-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.13-compact.json b/tests/ast-parsing/expected/top-level-0.5.13-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.13-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.13-legacy.json b/tests/ast-parsing/expected/top-level-0.5.13-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.13-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.14-compact.json b/tests/ast-parsing/expected/top-level-0.5.14-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.14-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.14-legacy.json b/tests/ast-parsing/expected/top-level-0.5.14-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.14-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.15-compact.json b/tests/ast-parsing/expected/top-level-0.5.15-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.15-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.15-legacy.json b/tests/ast-parsing/expected/top-level-0.5.15-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.15-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.16-compact.json b/tests/ast-parsing/expected/top-level-0.5.16-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.16-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.16-legacy.json b/tests/ast-parsing/expected/top-level-0.5.16-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.16-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.17-compact.json b/tests/ast-parsing/expected/top-level-0.5.17-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.17-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.17-legacy.json b/tests/ast-parsing/expected/top-level-0.5.17-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.17-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.2-compact.json b/tests/ast-parsing/expected/top-level-0.5.2-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.2-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.2-legacy.json b/tests/ast-parsing/expected/top-level-0.5.2-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.2-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.3-compact.json b/tests/ast-parsing/expected/top-level-0.5.3-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.3-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.3-legacy.json b/tests/ast-parsing/expected/top-level-0.5.3-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.3-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.4-compact.json b/tests/ast-parsing/expected/top-level-0.5.4-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.4-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.4-legacy.json b/tests/ast-parsing/expected/top-level-0.5.4-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.4-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.5-compact.json b/tests/ast-parsing/expected/top-level-0.5.5-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.5-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.5-legacy.json b/tests/ast-parsing/expected/top-level-0.5.5-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.5-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.6-compact.json b/tests/ast-parsing/expected/top-level-0.5.6-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.6-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.6-legacy.json b/tests/ast-parsing/expected/top-level-0.5.6-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.6-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.7-compact.json b/tests/ast-parsing/expected/top-level-0.5.7-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.7-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.7-legacy.json b/tests/ast-parsing/expected/top-level-0.5.7-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.7-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.8-compact.json b/tests/ast-parsing/expected/top-level-0.5.8-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.8-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.8-legacy.json b/tests/ast-parsing/expected/top-level-0.5.8-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.8-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.9-compact.json b/tests/ast-parsing/expected/top-level-0.5.9-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.9-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.5.9-legacy.json b/tests/ast-parsing/expected/top-level-0.5.9-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.5.9-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.0-compact.json b/tests/ast-parsing/expected/top-level-0.6.0-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.0-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.0-legacy.json b/tests/ast-parsing/expected/top-level-0.6.0-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.0-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.1-compact.json b/tests/ast-parsing/expected/top-level-0.6.1-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.1-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.1-legacy.json b/tests/ast-parsing/expected/top-level-0.6.1-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.1-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.10-compact.json b/tests/ast-parsing/expected/top-level-0.6.10-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.10-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.10-legacy.json b/tests/ast-parsing/expected/top-level-0.6.10-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.10-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.11-compact.json b/tests/ast-parsing/expected/top-level-0.6.11-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.11-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.11-legacy.json b/tests/ast-parsing/expected/top-level-0.6.11-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.11-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.12-compact.json b/tests/ast-parsing/expected/top-level-0.6.12-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.12-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.12-legacy.json b/tests/ast-parsing/expected/top-level-0.6.12-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.12-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.2-compact.json b/tests/ast-parsing/expected/top-level-0.6.2-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.2-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.2-legacy.json b/tests/ast-parsing/expected/top-level-0.6.2-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.2-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.3-compact.json b/tests/ast-parsing/expected/top-level-0.6.3-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.3-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.3-legacy.json b/tests/ast-parsing/expected/top-level-0.6.3-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.3-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.4-compact.json b/tests/ast-parsing/expected/top-level-0.6.4-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.4-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.4-legacy.json b/tests/ast-parsing/expected/top-level-0.6.4-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.4-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.5-compact.json b/tests/ast-parsing/expected/top-level-0.6.5-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.5-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.5-legacy.json b/tests/ast-parsing/expected/top-level-0.6.5-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.5-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.6-compact.json b/tests/ast-parsing/expected/top-level-0.6.6-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.6-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.6-legacy.json b/tests/ast-parsing/expected/top-level-0.6.6-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.6-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.7-compact.json b/tests/ast-parsing/expected/top-level-0.6.7-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.7-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.7-legacy.json b/tests/ast-parsing/expected/top-level-0.6.7-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.7-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.8-compact.json b/tests/ast-parsing/expected/top-level-0.6.8-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.8-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.8-legacy.json b/tests/ast-parsing/expected/top-level-0.6.8-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.8-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.9-compact.json b/tests/ast-parsing/expected/top-level-0.6.9-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.9-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.6.9-legacy.json b/tests/ast-parsing/expected/top-level-0.6.9-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.6.9-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.7.0-compact.json b/tests/ast-parsing/expected/top-level-0.7.0-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.7.0-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.7.0-legacy.json b/tests/ast-parsing/expected/top-level-0.7.0-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.7.0-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.7.1-compact.json b/tests/ast-parsing/expected/top-level-0.7.1-compact.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.7.1-compact.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.7.1-legacy.json b/tests/ast-parsing/expected/top-level-0.7.1-legacy.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.7.1-legacy.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.7.2-compact.json b/tests/ast-parsing/expected/top-level-0.7.2-compact.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.7.2-compact.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.7.2-legacy.json b/tests/ast-parsing/expected/top-level-0.7.2-legacy.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.7.2-legacy.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.7.3-compact.json b/tests/ast-parsing/expected/top-level-0.7.3-compact.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.7.3-compact.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.7.3-legacy.json b/tests/ast-parsing/expected/top-level-0.7.3-legacy.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.7.3-legacy.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.7.4-compact.json b/tests/ast-parsing/expected/top-level-0.7.4-compact.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.7.4-compact.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.7.4-legacy.json b/tests/ast-parsing/expected/top-level-0.7.4-legacy.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.7.4-legacy.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.7.5-compact.json b/tests/ast-parsing/expected/top-level-0.7.5-compact.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.7.5-compact.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-0.7.5-legacy.json b/tests/ast-parsing/expected/top-level-0.7.5-legacy.json new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/tests/ast-parsing/expected/top-level-0.7.5-legacy.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.0-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.0-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.0-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.1-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.1-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.1-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.10-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.10-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.10-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.11-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.11-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.11-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.12-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.12-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.12-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.12-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.12-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.12-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.13-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.13-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.13-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.13-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.13-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.13-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.14-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.14-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.14-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.14-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.14-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.14-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.15-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.15-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.15-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.15-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.15-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.15-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.16-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.16-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.16-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.16-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.16-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.16-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.17-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.17-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.17-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.17-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.17-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.17-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.18-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.18-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.18-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.18-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.18-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.18-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.19-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.19-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.19-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.19-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.19-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.19-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.2-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.2-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.2-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.20-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.20-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.20-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.20-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.20-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.20-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.21-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.21-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.21-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.21-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.21-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.21-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.22-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.22-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.22-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.22-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.22-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.22-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.23-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.23-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.23-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.23-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.23-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.23-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.24-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.24-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.24-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.24-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.24-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.24-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.25-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.25-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.25-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.25-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.25-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.25-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.26-compact.json b/tests/ast-parsing/expected/top-level-import-0.4.26-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.26-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.26-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.26-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.26-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.3-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.3-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.3-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.4-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.4-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.4-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.5-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.5-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.5-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.6-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.6-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.6-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.7-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.7-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.7-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.8-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.8-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.8-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.4.9-legacy.json b/tests/ast-parsing/expected/top-level-import-0.4.9-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.4.9-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.0-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.0-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.0-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.0-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.0-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.0-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.1-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.1-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.1-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.1-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.1-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.1-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.10-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.10-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.10-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.10-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.10-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.10-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.11-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.11-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.11-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.11-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.11-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.11-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.12-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.12-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.12-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.12-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.12-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.12-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.13-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.13-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.13-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.13-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.13-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.13-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.14-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.14-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.14-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.14-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.14-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.14-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.15-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.15-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.15-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.15-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.15-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.15-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.16-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.16-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.16-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.16-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.16-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.16-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.17-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.17-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.17-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.17-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.17-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.17-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.2-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.2-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.2-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.2-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.2-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.2-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.3-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.3-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.3-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.3-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.3-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.3-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.4-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.4-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.4-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.4-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.4-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.4-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.5-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.5-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.5-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.5-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.5-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.5-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.6-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.6-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.6-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.6-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.6-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.6-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.7-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.7-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.7-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.7-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.7-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.7-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.8-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.8-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.8-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.8-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.8-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.8-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.9-compact.json b/tests/ast-parsing/expected/top-level-import-0.5.9-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.9-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.5.9-legacy.json b/tests/ast-parsing/expected/top-level-import-0.5.9-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.5.9-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.0-compact.json b/tests/ast-parsing/expected/top-level-import-0.6.0-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.0-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.0-legacy.json b/tests/ast-parsing/expected/top-level-import-0.6.0-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.0-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.1-compact.json b/tests/ast-parsing/expected/top-level-import-0.6.1-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.1-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.1-legacy.json b/tests/ast-parsing/expected/top-level-import-0.6.1-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.1-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.10-compact.json b/tests/ast-parsing/expected/top-level-import-0.6.10-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.10-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.10-legacy.json b/tests/ast-parsing/expected/top-level-import-0.6.10-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.10-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.11-compact.json b/tests/ast-parsing/expected/top-level-import-0.6.11-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.11-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.11-legacy.json b/tests/ast-parsing/expected/top-level-import-0.6.11-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.11-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.12-compact.json b/tests/ast-parsing/expected/top-level-import-0.6.12-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.12-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.12-legacy.json b/tests/ast-parsing/expected/top-level-import-0.6.12-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.12-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.2-compact.json b/tests/ast-parsing/expected/top-level-import-0.6.2-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.2-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.2-legacy.json b/tests/ast-parsing/expected/top-level-import-0.6.2-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.2-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.3-compact.json b/tests/ast-parsing/expected/top-level-import-0.6.3-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.3-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.3-legacy.json b/tests/ast-parsing/expected/top-level-import-0.6.3-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.3-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.4-compact.json b/tests/ast-parsing/expected/top-level-import-0.6.4-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.4-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.4-legacy.json b/tests/ast-parsing/expected/top-level-import-0.6.4-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.4-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.5-compact.json b/tests/ast-parsing/expected/top-level-import-0.6.5-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.5-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.5-legacy.json b/tests/ast-parsing/expected/top-level-import-0.6.5-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.5-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.6-compact.json b/tests/ast-parsing/expected/top-level-import-0.6.6-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.6-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.6-legacy.json b/tests/ast-parsing/expected/top-level-import-0.6.6-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.6-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.7-compact.json b/tests/ast-parsing/expected/top-level-import-0.6.7-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.7-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.7-legacy.json b/tests/ast-parsing/expected/top-level-import-0.6.7-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.7-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.8-compact.json b/tests/ast-parsing/expected/top-level-import-0.6.8-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.8-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.8-legacy.json b/tests/ast-parsing/expected/top-level-import-0.6.8-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.8-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.9-compact.json b/tests/ast-parsing/expected/top-level-import-0.6.9-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.9-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.6.9-legacy.json b/tests/ast-parsing/expected/top-level-import-0.6.9-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.6.9-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.7.0-compact.json b/tests/ast-parsing/expected/top-level-import-0.7.0-compact.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.7.0-compact.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.7.0-legacy.json b/tests/ast-parsing/expected/top-level-import-0.7.0-legacy.json new file mode 100644 index 000000000..0008a4469 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.7.0-legacy.json @@ -0,0 +1,3 @@ +{ + "C": {} +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.7.1-compact.json b/tests/ast-parsing/expected/top-level-import-0.7.1-compact.json new file mode 100644 index 000000000..a1a35e654 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.7.1-compact.json @@ -0,0 +1,5 @@ +{ + "C": { + "f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.7.2-compact.json b/tests/ast-parsing/expected/top-level-import-0.7.2-compact.json new file mode 100644 index 000000000..a1a35e654 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.7.2-compact.json @@ -0,0 +1,5 @@ +{ + "C": { + "f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.7.3-compact.json b/tests/ast-parsing/expected/top-level-import-0.7.3-compact.json new file mode 100644 index 000000000..a1a35e654 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.7.3-compact.json @@ -0,0 +1,5 @@ +{ + "C": { + "f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.7.4-compact.json b/tests/ast-parsing/expected/top-level-import-0.7.4-compact.json new file mode 100644 index 000000000..a1a35e654 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.7.4-compact.json @@ -0,0 +1,5 @@ +{ + "C": { + "f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/ast-parsing/expected/top-level-import-0.7.5-compact.json b/tests/ast-parsing/expected/top-level-import-0.7.5-compact.json new file mode 100644 index 000000000..a1a35e654 --- /dev/null +++ b/tests/ast-parsing/expected/top-level-import-0.7.5-compact.json @@ -0,0 +1,5 @@ +{ + "C": { + "f()": "digraph{\n0[label=\"Node Type: ENTRY_POINT 0\n\"];\n0->1;\n1[label=\"Node Type: EXPRESSION 1\n\"];\n}\n" + } +} \ No newline at end of file diff --git a/tests/ast-parsing/top-level-0.4.0.sol b/tests/ast-parsing/top-level-0.4.0.sol new file mode 100644 index 000000000..3dac41d9c --- /dev/null +++ b/tests/ast-parsing/top-level-0.4.0.sol @@ -0,0 +1,2 @@ +// not available unti 0.7.1 +contract C{} diff --git a/tests/ast-parsing/top-level-0.7.1.sol b/tests/ast-parsing/top-level-0.7.1.sol new file mode 100644 index 000000000..91287c0a4 --- /dev/null +++ b/tests/ast-parsing/top-level-0.7.1.sol @@ -0,0 +1,11 @@ +/// @dev returns the smaller of the two values. +function min(uint x, uint y) pure returns (uint) { + return x < y ? x : y; +} + +/// @dev returns the sum of the elements of the storage array +function sum(uint[] storage items) view returns (uint s) { + for (uint i = 0; i < items.length; i++) + s += items[i]; +} + diff --git a/tests/ast-parsing/top-level-0.7.4.sol b/tests/ast-parsing/top-level-0.7.4.sol new file mode 100644 index 000000000..ee7fe7397 --- /dev/null +++ b/tests/ast-parsing/top-level-0.7.4.sol @@ -0,0 +1,8 @@ +uint constant EXPONENT = 10; +uint constant MULTIPLIER = 2**EXPONENT; + +struct Fixed { uint value; } + +function toFixed(uint x) pure returns (Fixed memory ret) { + ret.value = x * MULTIPLIER; +} diff --git a/tests/ast-parsing/top-level-import-0.4.0.sol b/tests/ast-parsing/top-level-import-0.4.0.sol new file mode 100644 index 000000000..3dac41d9c --- /dev/null +++ b/tests/ast-parsing/top-level-import-0.4.0.sol @@ -0,0 +1,2 @@ +// not available unti 0.7.1 +contract C{} diff --git a/tests/ast-parsing/top-level-import-0.7.1.sol b/tests/ast-parsing/top-level-import-0.7.1.sol new file mode 100644 index 000000000..ace5ff3c9 --- /dev/null +++ b/tests/ast-parsing/top-level-import-0.7.1.sol @@ -0,0 +1,10 @@ +import "./top-level-0.7.1.sol" as Helper; + +contract C{ + + + function f() public{ + Helper.min(0,1); + } +} + diff --git a/tests/test_ast_parsing.py b/tests/test_ast_parsing.py index 316f1614d..c37700324 100644 --- a/tests/test_ast_parsing.py +++ b/tests/test_ast_parsing.py @@ -396,8 +396,15 @@ XFAIL = [ "yul_0.7.3_compact", "yul_0.7.4_compact", "yul_0.7.5_compact", + "top-level-import_0.7.1_legacy", + "top-level-import_0.7.2_legacy", + "top-level-import_0.7.3_legacy", + "top-level-import_0.7.4_legacy", + "top-level-import_0.7.5_legacy", ] +TESTED_SOLC_07 = ["0.7.0", "0.7.1", "0.7.2", "0.7.3", "0.7.4", "0.7.5"] + def get_solc_versions() -> List[str]: """ @@ -409,6 +416,15 @@ def get_solc_versions() -> List[str]: # there's an extra newline so just remove all empty strings solc_versions = [version for version in solc_versions if version != ""] + + # Dont test for newer 0.7 versions until explicity updated + # Dont test for 0.8 yet + solc_versions = [ + version + for version in solc_versions + if (not version.startswith("0.7.") and not version.startswith("0.8.")) + or (version in TESTED_SOLC_07) + ] solc_versions.reverse() return solc_versions @@ -558,7 +574,7 @@ def test_parsing(test_item: Item): diff = DeepDiff(expected, actual, ignore_order=True, verbose_level=2, view="tree") if diff: - for change in diff["values_changed"]: + for change in diff.get("values_changed", []): path_list = re.findall(r"\['(.*?)'\]", change.path()) path = "_".join(path_list) with open(f"test_artifacts/{id_test(test_item)}_{path}_expected.dot", "w") as f: @@ -579,10 +595,8 @@ def _generate_test(test_item: Item, skip_existing=False): if skip_existing: if os.path.isfile(expected_file): return - if id_test(test_item) in XFAIL: return - set_solc(test_item) sl = Slither( test_file,