Exemple #1
0
def _check_ghost_functions(program: VyperProgram):
    if not isinstance(program, VyperInterface):
        node = first(program.node.stmts) or program.node
        for implemented_ghost in program.ghost_function_implementations.values(
        ):
            if program.ghost_functions.get(implemented_ghost.name) is None:
                raise InvalidProgramException(
                    implemented_ghost.node, 'missing.ghost',
                    f'This contract is implementing an unknown ghost function. '
                    f'None of the interfaces, this contract implements, declares a ghost '
                    f'function "{implemented_ghost.name}".')

        for interface in program.interfaces.values():
            for ghost_function_list in interface.ghost_functions.values():
                for ghost_function in ghost_function_list:
                    imported_ghost_functions = [
                        ghost_func
                        for ghost_func in program.ghost_functions.get(
                            ghost_function.name, [])
                        if ghost_func.file == ghost_function.file
                    ]
                    if not imported_ghost_functions:
                        prefix_length = len(
                            os.path.commonprefix(
                                [ghost_function.file, program.file]))
                        raise InvalidProgramException(
                            node, 'missing.ghost',
                            f'The interface "{interface.name}" '
                            f'needs a ghost function "{ghost_function.name}" from '
                            f'".{os.path.sep}{ghost_function.file[prefix_length:]}" but it '
                            f'was not imported for this contract.')
                    imported_ghost_functions = [
                        ghost_func for ghost_func in
                        program.ghost_functions.get(ghost_function.name)
                        if ghost_func.interface == ghost_function.interface
                    ]
                    for imported_ghost_function in imported_ghost_functions:
                        if ghost_function.file != imported_ghost_function.file:
                            prefix_length = len(
                                os.path.commonprefix([
                                    ghost_function.file,
                                    imported_ghost_function.file
                                ]))
                            ghost_function_file = ghost_function.file[
                                prefix_length:]
                            imported_ghost_function_file = imported_ghost_function.file[
                                prefix_length:]
                            raise InvalidProgramException(
                                node, 'duplicate.ghost',
                                f'There are two versions of the ghost function '
                                f'"{ghost_function.name}" defined in an interface '
                                f'"{ghost_function.interface}" one from '
                                f'[...]"{imported_ghost_function_file}" the other from '
                                f'[...]"{ghost_function_file}".')
Exemple #2
0
def _check_ghost_implements(program: VyperProgram):
    def check(cond, node, ghost_name, interface_name):
        if not cond:
            raise InvalidProgramException(
                node, 'ghost.not.implemented',
                f'The ghost function "{ghost_name}" from the interface "{interface_name}" '
                f'has not been implemented correctly.')

    ghost_function_implementations = dict(
        program.ghost_function_implementations)

    for itype in program.implements:
        interface = program.interfaces[itype.name]
        for ghost in interface.own_ghost_functions.values():
            implementation = ghost_function_implementations.pop(
                ghost.name, None)
            check(implementation is not None, program.node, ghost.name,
                  itype.name)
            check(implementation.name == ghost.name, implementation.node,
                  ghost.name, itype.name)
            check(
                len(implementation.args) == len(ghost.args),
                implementation.node, ghost.name, itype.name)
            check(implementation.type == ghost.type, implementation.node,
                  ghost.name, itype.name)

    if len(ghost_function_implementations) > 0:
        raise InvalidProgramException(
            first(ghost_function_implementations.values()).node,
            'invalid.ghost.implemented',
            f'This contract implements some ghost functions that have no declaration in '
            f'any of the implemented interfaces.\n'
            f'(Ghost functions without declaration: {list(ghost_function_implementations)})'
        )
Exemple #3
0
    def visit_AnnAssign(self, node: ast.AnnAssign):
        if node.is_ghost_code:
            raise InvalidProgramException(node, 'invalid.ghost.code')

        # No local specs are allowed before contract state variables
        self._check_no_local_spec()

        variable_name = node.target.id
        if variable_name == names.IMPLEMENTS:
            interface_name = node.annotation.id
            if interface_name not in [interfaces.ERC20, interfaces.ERC721
                                      ] or interface_name in self.interfaces:
                interface_type = InterfaceType(interface_name)
                self.implements.append(interface_type)
            elif interface_name in [interfaces.ERC20, interfaces.ERC721]:
                interface_type = InterfaceType(interface_name)
                self.additional_implements.append(interface_type)
        # We ignore the units declarations
        elif variable_name != names.UNITS:
            variable_type = self.type_builder.build(node.annotation)
            if isinstance(variable_type, EventType):
                event = VyperEvent(variable_name, variable_type)
                self.events[variable_name] = event
            else:
                self.field_types[variable_name] = variable_type
Exemple #4
0
    def funcdef(self, children, meta):
        decorators = children[0]
        name = str(children[1])
        args = children[2]

        if len(children) == 3:
            # This is a function stub without a return value
            if decorators:
                raise InvalidProgramException(decorators[0],
                                              'invalid.function.stub')
            return ast.FunctionStub(name, args, None)
        elif len(children) == 4:
            if isinstance(children[3], list):
                # This is a function definition without a return value
                body = children[3]
                return ast.FunctionDef(name, args, body, decorators, None)
            else:
                # This is a function stub with a return value
                ret = children[3].children
                ret = ret[0] if len(ret) == 1 else self._tuple(ret, meta)
                return ast.FunctionStub(name, args, ret)
        elif len(children) == 5:
            # This is a function definition with a return value
            ret = children[3].children
            ret = ret[0] if len(ret) == 1 else self._tuple(ret, meta)
            body = children[4]
            return ast.FunctionDef(name, args, body, decorators, ret)
Exemple #5
0
 def check_success_args(arg: ast.Node):
     if isinstance(arg, ast.Name):
         _assert(arg.id in names.SUCCESS_CONDITIONS, arg, 'spec.success')
     elif isinstance(arg, ast.BoolOp) and arg.op == ast.BoolOperator.OR:
         check_success_args(arg.left)
         check_success_args(arg.right)
     else:
         raise InvalidProgramException(arg, 'spec.success')
Exemple #6
0
    def visit_ContractDef(self, node: ast.ContractDef):
        if node.is_ghost_code:
            raise InvalidProgramException(node, 'invalid.ghost.code')

        vyper_type = self.type_builder.build(node)
        if isinstance(vyper_type, ContractType):
            contract = VyperContract(node.name, vyper_type, node)
            self.contracts[contract.name] = contract
Exemple #7
0
    def float(self, children, meta):
        assert 'e' not in children[0]

        numd = 10
        first, second = children[0].split('.')
        if len(second) > numd:
            node = self.number(children, meta)
            raise InvalidProgramException(node, 'invalid.decimal.literal')
        int_value = int(first) * 10**numd + int(second.ljust(numd, '0'))
        return ast.Num(Decimal[numd](scaled_value=int_value))
Exemple #8
0
    def visit_If(self, node: ast.If):
        # This is a preserves clause, since we replace all preserves clauses with if statements
        # when preprocessing
        if self.is_preserves:
            raise InvalidProgramException(node, 'preserves.in.preserves')

        self.is_preserves = True
        for stmt in node.body:
            self.visit(stmt)
        self.is_preserves = False
Exemple #9
0
 def _check_no_ghost_function(self):
     if self.ghost_functions:
         cond = "Ghost function declaration"
         node = first(self.ghost_functions.values()).node
     elif self.ghost_function_implementations:
         cond = "Ghost function definition"
         node = first(self.ghost_function_implementations.values()).node
     else:
         return
     raise InvalidProgramException(
         node, 'invalid.ghost', f'{cond} only allowed after "#@ interface"')
Exemple #10
0
    def decorator(self, children, meta):
        name = str(children[0])

        if len(children) == 1:
            args = []
        else:
            args, kwargs = children[1]

            if kwargs:
                raise InvalidProgramException(kwargs[0], 'invalid.decorator')

        return ast.Decorator(name, args)
Exemple #11
0
    def arguments(self, children, meta):
        args = []
        kwargs = []
        for c in children:
            if isinstance(c, ast.Keyword):
                kwargs.append(c)
            else:
                # kwargs cannot come before normal args
                if kwargs:
                    raise InvalidProgramException(kwargs[0], 'invalid.kwargs')
                args.append(c)

        return args, kwargs
Exemple #12
0
    def visit_Assign(self, node: ast.Assign):
        if node.is_ghost_code:
            if isinstance(node.target,
                          ast.Name) and node.target.id == names.INVARIANT:
                if self._last_loop and node in self._possible_loop_invariant_nodes:
                    self.loop_invariants.setdefault(self._last_loop,
                                                    []).append(node.value)
                else:
                    raise InvalidProgramException(
                        node, 'invalid.loop.invariant',
                        'You may only write loop invariants at beginning in loops'
                    )
                return None

        return node
Exemple #13
0
    def visit_FunctionStub(self, node: ast.FunctionStub):
        # A function stub on the top-level is a resource declaration
        self._check_no_local_spec()

        name, is_derived = Resource.get_name_and_derived_flag(node)

        if name in self.resources or name in names.SPECIAL_RESOURCES:
            raise InvalidProgramException(node, 'duplicate.resource')

        vyper_type = self.type_builder.build(node)
        if isinstance(vyper_type, ResourceType):
            if is_derived:
                resource = Resource(vyper_type, node, self.path, node.returns)
            else:
                resource = Resource(vyper_type, node, self.path)
            self.resources[name] = resource
Exemple #14
0
    def funccall(self, children, meta):
        func = children[0]
        args, kwargs = children[1]

        if isinstance(func, ast.Name):
            return ast.FunctionCall(func.id, args, kwargs)
        elif isinstance(func, ast.Subscript) and isinstance(
                func.value, ast.Name):
            return ast.FunctionCall(func.value.id, args, kwargs, func.index)
        elif isinstance(func, ast.Attribute):
            return ast.ReceiverCall(func.attr, func.value, args, kwargs)
        elif isinstance(func, ast.Subscript) and isinstance(
                func.value, ast.Attribute):
            return ast.ReceiverCall(func.value.attr, func, args, kwargs)
        else:
            raise InvalidProgramException(func, 'invalid.receiver')
Exemple #15
0
    def visit_ImportFrom(self, node: ast.ImportFrom):
        if node.is_ghost_code:
            raise InvalidProgramException(node, 'invalid.ghost.code')

        module = node.module or ''
        components = module.split('.')

        if not self.parse_further_interfaces:
            return

        if components == interfaces.VYPER_INTERFACES:
            for alias in node.names:
                name = alias.name
                if name == interfaces.ERC20:
                    self.contracts[name] = VyperContract(
                        name, interfaces.ERC20_TYPE, None)
                elif name == interfaces.ERC721:
                    self.contracts[name] = VyperContract(
                        name, interfaces.ERC721_TYPE, None)
                else:
                    assert False

            return

        if node.level == 0:
            path = os.path.join(self.root or '', *components)
        else:
            path = self.path
            for _ in range(node.level):
                path = os.path.dirname(path)

            path = os.path.join(path, *components)

        files = {}
        for alias in node.names:
            name = alias.name
            interface_path = os.path.join(path, f'{name}.vy')
            files[interface_path] = name

        for file, name in files.items():
            if name in [
                    names.SELF, names.LOG, names.LEMMA, *names.ENV_VARIABLES
            ]:
                raise UnsupportedException(node, 'Invalid file name.')
            interface = parse(file, self.root, True, name,
                              self.parse_level + 1)
            self.interfaces[name] = interface
Exemple #16
0
    def _check_no_local_spec(self):
        """
        Checks that there are no specifications for functions pending, i.e. there
        are no local specifications followed by either global specifications or eof.
        """

        if self.postconditions:
            cond = "Postcondition"
            node = self.postconditions[0]
        elif self.checks:
            cond = "Check"
            node = self.checks[0]
        elif self.performs:
            cond = "Performs"
            node = self.performs[0]
        else:
            return
        raise InvalidProgramException(node, 'local.spec',
                                      f"{cond} only allowed before function")
Exemple #17
0
 def visit_FunctionDef(self, node: ast.FunctionDef):
     args = {arg.name: self._arg(arg) for arg in node.args}
     defaults = {arg.name: arg.default for arg in node.args}
     arg_types = [arg.type for arg in args.values()]
     return_type = None if node.returns is None else self.type_builder.build(
         node.returns)
     vyper_type = FunctionType(arg_types, return_type)
     decs = node.decorators
     loop_invariant_transformer = LoopInvariantTransformer()
     loop_invariant_transformer.visit(node)
     function = VyperFunction(node.name, self.function_counter, args,
                              defaults, vyper_type, self.postconditions,
                              self.preconditions, self.checks,
                              loop_invariant_transformer.loop_invariants,
                              self.performs, decs, node)
     if node.is_lemma:
         if node.decorators:
             decorator = node.decorators[0]
             if len(node.decorators
                    ) > 1 or decorator.name != names.INTERPRETED_DECORATOR:
                 raise InvalidProgramException(
                     decorator, 'invalid.lemma',
                     f'A lemma can have only one decorator: {names.INTERPRETED_DECORATOR}'
                 )
             if not decorator.is_ghost_code:
                 raise InvalidProgramException(
                     decorator, 'invalid.lemma',
                     'The decorator of a lemma must be ghost code')
         if vyper_type.return_type is not None:
             raise InvalidProgramException(
                 node, 'invalid.lemma', 'A lemma cannot have a return type')
         if node.name in self.lemmas:
             raise InvalidProgramException(node, 'duplicate.lemma')
         if self.is_interface:
             raise InvalidProgramException(
                 node, 'invalid.lemma',
                 'Lemmas are not allowed in interfaces')
         self.lemmas[node.name] = function
     else:
         for decorator in node.decorators:
             if decorator.is_ghost_code and decorator.name != names.PURE:
                 raise InvalidProgramException(decorator,
                                               'invalid.ghost.code')
         self.functions[node.name] = function
     self.function_counter += 1
     # Reset local specs
     self.postconditions = []
     self.preconditions = []
     self.checks = []
     self.performs = []
Exemple #18
0
    def visit_Ghost(self, node: ast.Ghost):
        for func in node.body:

            def check_ghost(cond):
                if not cond:
                    raise InvalidProgramException(func, 'invalid.ghost')

            check_ghost(isinstance(func, ast.FunctionDef))
            assert isinstance(func, ast.FunctionDef)
            check_ghost(len(func.body) == 1)
            func_body = func.body[0]
            check_ghost(isinstance(func_body, ast.ExprStmt))
            assert isinstance(func_body, ast.ExprStmt)
            check_ghost(func.returns)

            decorators = [dec.name for dec in func.decorators]
            check_ghost(len(decorators) == len(func.decorators))

            name = func.name
            args = {arg.name: self._arg(arg) for arg in func.args}
            arg_types = [arg.type for arg in args.values()]
            return_type = None if func.returns is None else self.type_builder.build(
                func.returns)
            vyper_type = FunctionType(arg_types, return_type)

            if names.IMPLEMENTS in decorators:
                check_ghost(not self.is_interface)
                check_ghost(len(decorators) == 1)

                ghost_functions = self.ghost_function_implementations
            else:
                check_ghost(self.is_interface)
                check_ghost(not decorators)
                check_ghost(isinstance(func_body.value, ast.Ellipsis))

                ghost_functions = self.ghost_functions

            if name in ghost_functions:
                raise InvalidProgramException(func, 'duplicate.ghost')

            ghost_functions[name] = GhostFunction(name, args, vyper_type, func,
                                                  self.path)
Exemple #19
0
 def _visit_FunctionCall(self, node: ast.FunctionCall) -> VyperType:
     # We allow
     #   - public, indexed: not important for verification
     #   - map: map type
     #   - event: event type
     # Not allowed is
     #   - constant: should already be replaced
     # Anything else is treated as a unit
     if node.name == names.PUBLICFIELD or node.name == names.INDEXED:
         return self.visit(node.args[0])
     elif node.name == names.MAP:
         key_type = self.visit(node.args[0])
         value_type = self.visit(node.args[1])
         return MapType(key_type, value_type)
     elif node.name == names.EVENT:
         dict_literal = node.args[0]
         arg_types = [self.visit(arg) for arg in dict_literal.values]
         return EventType(arg_types)
     else:
         type = self.type_map.get(node.name) or TYPES.get(node.name)
         if type is None:
             raise InvalidProgramException(node, 'invalid.type')
         return type
Exemple #20
0
    def visit_Import(self, node: ast.Import):
        if node.is_ghost_code:
            raise InvalidProgramException(node, 'invalid.ghost.code')

        if not self.parse_further_interfaces:
            return

        files = {}
        for alias in node.names:
            components = alias.name.split('.')
            components[-1] = f'{components[-1]}.vy'
            path = os.path.join(self.root or '', *components)
            files[path] = alias.asname.value if hasattr(
                alias.asname, 'value') else alias.asname

        for file, name in files.items():
            if name in [
                    names.SELF, names.LOG, names.LEMMA, *names.ENV_VARIABLES
            ]:
                raise UnsupportedException(node, 'Invalid file name.')
            interface = parse(file, self.root, True, name,
                              self.parse_level + 1)
            self.interfaces[name] = interface
Exemple #21
0
 def check_ghost(cond):
     if not cond:
         raise InvalidProgramException(func, 'invalid.ghost')
Exemple #22
0
def _assert(cond: bool, node: ast.Node, error_code: str, msg: Optional[str] = None):
    if not cond:
        raise InvalidProgramException(node, error_code, msg)
Exemple #23
0
    def check(self, program: VyperProgram):
        if program.resources and not program.config.has_option(names.CONFIG_ALLOCATION):
            msg = "Resources require allocation config option."
            raise InvalidProgramException(first(program.node.stmts) or program.node, 'alloc.not.alloc', msg)

        seen_functions = set()
        for implements in program.real_implements:
            interface = program.interfaces.get(implements.name)
            if interface is not None:
                function_names = set(function.name for function in interface.functions.values())
            else:
                contract = program.contracts[implements.name]
                function_names = set(contract.type.function_types)
            _assert(not seen_functions & function_names, program.node, 'invalid.implemented.interfaces',
                    f'Implemented interfaces should not have a function that shares the name with another function of '
                    f'another implemented interface.\n'
                    f'(Conflicting functions: {seen_functions & function_names})')
            seen_functions.update(function_names)

        for function in program.functions.values():
            self.visit(function.node, _Context.CODE, program, function)
            if function.is_pure():
                self._function_pure_checker.check_function(function, program)

            for postcondition in function.postconditions:
                self.visit(postcondition, _Context.POSTCONDITION, program, function)

            if function.preconditions:
                _assert(not function.is_public(), function.preconditions[0],
                        'invalid.preconditions', 'Public functions are not allowed to have preconditions.')
            for precondition in function.preconditions:
                self.visit(precondition, _Context.PRECONDITION, program, function)

            if function.checks:
                _assert(not program.is_interface(), function.checks[0],
                        'invalid.checks', 'No checks are allowed in interfaces.')
            for check in function.checks:
                self.visit(check, _Context.CHECK, program, function)

            if function.performs:
                _assert(function.name != names.INIT, function.performs[0],
                        'invalid.performs', '__init__ does not require and must not have performs clauses.')
                _assert(function.is_public(), function.performs[0],
                        'invalid.performs', 'Private functions are not allowed to have performs clauses.')
            for performs in function.performs:
                self._visit_performs(performs, program, function)

        for lemma in program.lemmas.values():
            for default_val in lemma.defaults.values():
                _assert(default_val is None, lemma.node, 'invalid.lemma')
            _assert(not lemma.postconditions, first(lemma.postconditions), 'invalid.lemma',
                    'No postconditions are allowed for lemmas.')
            _assert(not lemma.checks, first(lemma.checks), 'invalid.lemma',
                    'No checks are allowed for lemmas.')
            _assert(not lemma.performs, first(lemma.performs), 'invalid.lemma')

            self.visit(lemma.node, _Context.LEMMA, program, lemma)
            for stmt in lemma.node.body:
                _assert(isinstance(stmt, ast.ExprStmt), stmt, 'invalid.lemma',
                        'All steps of the lemma should be expressions')

            for precondition in lemma.preconditions:
                self.visit(precondition, _Context.LEMMA, program, lemma)

        for invariant in program.invariants:
            self.visit(invariant, _Context.INVARIANT, program, None)

        if program.general_checks:
            _assert(not program.is_interface(), program.general_checks[0],
                    'invalid.checks', 'No checks are allowed in interfaces.')
        for check in program.general_checks:
            self.visit(check, _Context.CHECK, program, None)

        for postcondition in program.general_postconditions:
            self.visit(postcondition, _Context.POSTCONDITION, program, None)

        if program.transitive_postconditions:
            _assert(not program.is_interface(), program.transitive_postconditions[0],
                    'invalid.transitive.postconditions', 'No transitive postconditions are allowed in interfaces')
        for postcondition in program.transitive_postconditions:
            self.visit(postcondition, _Context.TRANSITIVE_POSTCONDITION, program, None)

        for ghost_function in program.ghost_function_implementations.values():
            self.visit(ghost_function.node, _Context.GHOST_FUNCTION, program, None)

        if isinstance(program, VyperInterface):
            for caller_private in program.caller_private:
                self._visited_caller_spec = False
                self._num_visited_conditional = 0
                self.visit(caller_private, _Context.CALLER_PRIVATE, program, None)
                _assert(self._visited_caller_spec, caller_private, 'invalid.caller.private',
                        'A caller private expression must contain "caller()"')
                _assert(self._num_visited_conditional <= 1, caller_private, 'invalid.caller.private',
                        'A caller private expression can only contain at most one "conditional(...)"')
Exemple #24
0
 def _visit_Name(self, node: ast.Name) -> VyperType:
     type = self.type_map.get(node.id) or TYPES.get(node.id)
     if type is None:
         raise InvalidProgramException(node, 'invalid.type')
     return type
Exemple #25
0
 def generic_visit(self, node):
     raise InvalidProgramException(node, 'invalid.type')
Exemple #26
0
 def _check_no_lemmas(self):
     if self.lemmas:
         raise InvalidProgramException(
             first(self.lemmas.values()).node, 'invalid.lemma',
             'Lemmas are not allowed in interfaces')
Exemple #27
0
def _check_resources(program: VyperProgram):
    if not isinstance(program, VyperInterface):
        node = first(program.node.stmts) or program.node
        for interface in program.interfaces.values():
            for resource_name, resources_list in interface.resources.items():
                for resource in resources_list:
                    if resource.file is None:
                        if program.resources.get(resource_name) is None:
                            if not program.config.has_option(
                                    names.CONFIG_ALLOCATION):
                                raise InvalidProgramException(
                                    node, 'alloc.not.alloc',
                                    f'The interface "{interface.name}" uses the '
                                    f'allocation config option. Therefore, this contract '
                                    f'also has to enable this config option.')
                            raise InvalidProgramException(
                                node, 'missing.resource',
                                f'The interface "{interface.name}" '
                                f'needs a default resource "{resource_name}" '
                                f'that is not present in this contract.')
                        continue
                    imported_resources = [
                        r for r in program.resources.get(resource_name, [])
                        if r.file == resource.file
                    ]
                    if not imported_resources:
                        prefix_length = len(
                            os.path.commonprefix([resource.file,
                                                  program.file]))
                        raise InvalidProgramException(
                            node, 'missing.resource',
                            f'The interface "{interface.name}" '
                            f'needs a resource "{resource_name}" from '
                            f'".{os.path.sep}{resource.file[prefix_length:]}" but it '
                            f'was not imported for this contract.')
                    imported_resources = [
                        r for r in program.resources.get(resource_name)
                        if r.interface == resource.interface
                    ]
                    for imported_resource in imported_resources:
                        if resource.file != imported_resource.file:
                            prefix_length = len(
                                os.path.commonprefix(
                                    [resource.file, imported_resource.file]))
                            resource_file = resource.file[prefix_length:]
                            imported_resource_file = imported_resource.file[
                                prefix_length:]
                            raise InvalidProgramException(
                                node, 'duplicate.resource',
                                f'There are two versions of the resource '
                                f'"{resource_name}" defined in an interface '
                                f'"{imported_resource.interface}" one from '
                                f'[...]"{imported_resource_file}" the other from '
                                f'[...]"{resource_file}".')

        for interface_type in program.implements:
            interface = program.interfaces[interface_type.name]
            for resource_name, resource in program.declared_resources.items():
                if resource_name in interface.own_resources:
                    raise InvalidProgramException(
                        resource.node, 'duplicate.resource',
                        f'A contract cannot redeclare a resource it already imports. '
                        f'The resource "{resource_name}" got already declared in the '
                        f'interface {interface.name}.')
Exemple #28
0
 def check(cond, node, ghost_name, interface_name):
     if not cond:
         raise InvalidProgramException(
             node, 'ghost.not.implemented',
             f'The ghost function "{ghost_name}" from the interface "{interface_name}" '
             f'has not been implemented correctly.')
Exemple #29
0
 def generic_visit(self, node: ast.Node, *args):
     raise InvalidProgramException(node, 'invalid.spec')
Exemple #30
0
    def visit_Assign(self, node: ast.Assign):
        # This is for invariants and postconditions which get translated to
        # assignments during preprocessing.

        name = node.target.id

        if name != names.GENERAL_POSTCONDITION and self.is_preserves:
            msg = f"Only general postconditions are allowed in preserves. ({name} is not allowed)"
            raise InvalidProgramException(node, 'invalid.preserves', msg)

        with switch(name) as case:
            if case(names.CONFIG):
                if isinstance(node.value, ast.Name):
                    options = [node.value.id]
                elif isinstance(node.value, ast.Tuple):
                    options = [n.id for n in node.value.elements]

                for option in options:
                    if option not in names.CONFIG_OPTIONS:
                        msg = f"Option {option} is invalid."
                        raise InvalidProgramException(node,
                                                      'invalid.config.option',
                                                      msg)

                if self.config is not None:
                    raise InvalidProgramException(
                        node, 'invalid.config',
                        'The "config" is specified multiple times.')
                self.config = Config(options)
            elif case(names.INTERFACE):
                self._check_no_local_spec()
                self._check_no_ghost_function()
                self._check_no_lemmas()
                self.is_interface = True
            elif case(names.INVARIANT):
                # No local specifications allowed before invariants
                self._check_no_local_spec()

                self.local_state_invariants.append(node.value)
            elif case(names.INTER_CONTRACT_INVARIANTS):
                # No local specifications allowed before inter contract invariants
                self._check_no_local_spec()

                self.inter_contract_invariants.append(node.value)
            elif case(names.GENERAL_POSTCONDITION):
                # No local specifications allowed before general postconditions
                self._check_no_local_spec()

                if self.is_preserves:
                    self.transitive_postconditions.append(node.value)
                else:
                    self.general_postconditions.append(node.value)
            elif case(names.GENERAL_CHECK):
                # No local specifications allowed before general check
                self._check_no_local_spec()

                self.general_checks.append(node.value)
            elif case(names.POSTCONDITION):
                self.postconditions.append(node.value)
            elif case(names.PRECONDITION):
                self.preconditions.append(node.value)
            elif case(names.CHECK):
                self.checks.append(node.value)
            elif case(names.CALLER_PRIVATE):
                # No local specifications allowed before caller private
                self._check_no_local_spec()

                self.caller_private.append(node.value)
            elif case(names.PERFORMS):
                self.performs.append(node.value)
            else:
                assert False