def visit(self, node: AttrDeclarationNode, scope: Scope, set_type=None):
        # print('attr declaration')
        if node.id == "self":
            self.errors.append(SEMANTIC_ERROR % (
                node.lineno, node.colno,
                f'"self" is used as attribute name in class "{self.current_type.name}".'
            ))
            if node.val is not None:
                self.visit(node.val, scope)
            return
        var, _ = scope.my_find_var(node.id)
        attr_type = self.context.get_type(node.type)
        if var is not None:
            self.errors.append(ATTR_ALREADY_DEFINED %
                               (node.lineno, node.colno, node.id))
        else:
            scope.define_variable(node.id, attr_type)

        if node.val is not None:
            return_type = self.visit(node.val, scope)
        else:
            return_type = attr_type

        if attr_type.name == BasicTypes.SELF.value:
            attr_type = self.current_type
        if return_type.name == BasicTypes.SELF.value:
            return_type = self.current_type
        if not return_type.conforms_to(attr_type):
            self.errors.append(
                INCOMPATIBLE_TYPES %
                (node.lineno, node.colno, return_type.name, attr_type.name))
 def visit(self, node, scope: Scope, set_type=None):
     # print('variable')
     var, scope_id = scope.my_find_var(node.lex)
     if var is None:
         self.errors.append(
             VARIABLE_NOT_DEFINED %
             (node.lineno, node.colno, node.lex, self.current_method.name))
         error_type = self.context.get_type(BasicTypes.ERROR.value)
         return error_type
     else:
         node.computed_type = var.type
         return var.type
 def visit(self, node, scope: Scope, set_type=None):
     # print('assign')
     error_type = self.context.get_type(BasicTypes.ERROR.value)
     if node.id == "self":
         self.errors.append(
             SEMANTIC_ERROR %
             (node.lineno, node.colno, f'"self" variable is read-only'))
         expr_type = self.visit(node.expr, scope)
         return expr_type
     var, scope_id = scope.my_find_var(node.id)
     if var is None:
         self.errors.append(
             VARIABLE_NOT_DEFINED %
             (node.lineno, node.colno, node.id, self.current_method.name))
         var_type = error_type
     else:
         var_type = var.type
     expr_type = self.visit(node.expr, scope)
     if not expr_type.conforms_to(var_type):
         self.errors.append(
             INCOMPATIBLE_TYPES %
             (node.lineno, node.colno, expr_type.name, var_type.name))
     node.computed_type = expr_type
     return expr_type