use std::rc::Rc;
use crate::{
    hir_nodes::{
        Attribute, HirComparisonOperator, HirConditionalBranch, HirConstValue, HirControlFlow,
        HirControlKind, HirDeclarationMode, HirExpression, HirFormatStringMember, HirFunction,
        HirFunctionCall, HirFunctionName, HirImport, HirInfiniteLoop, HirInfix, HirInfixOperator,
        HirModule, HirObject, HirParameterDeclaration, HirPrefix, HirPrefixOperator,
        HirPropertyDeclaration, HirStatement, HirStruct, HirStructInitialization,
        HirTupleInitialization, HirTypePattern, HirVariableInitialization, HirVariablePattern,
        HirVariableUpdate,
    },
    IdentifierPath, SpannedIdentifier,
};
use debris_common::{CodeId, CodeRef, CompileContext};
use debris_error::{ParseError, SingleCompileError};
use debris_parser::{
    ast::{self, Ast, AstToken, FormatStringComponent},
    error::ExpectedItem,
    parser::parse,
    token::PrefixOperator,
};
use super::{
    hir_nodes::{HirBlock, HirItem},
    HirContext, ImportDependencies,
};
#[derive(Debug)]
pub struct HirFile {
    pub main_function: HirBlock,
    pub code_id: CodeId,
}
impl HirFile {
    pub fn from_code(
        input: CodeRef,
        compile_context: &CompileContext,
        dependencies: &mut ImportDependencies,
    ) -> Result<Self, Vec<SingleCompileError>> {
        let mut context = HirContext::new(input, compile_context, dependencies);
        let syntax_tree = Rc::new(parse(&input.get_code().source));
        if !syntax_tree.errors.is_empty() {
            return Err(syntax_tree
                .errors
                .iter()
                .map(|error| context.handle_parse_error(error).into())
                .collect());
        }
        let ast = Ast::from(syntax_tree);
        let program = ast.program;
        let mut objects = Vec::new();
        let mut statements = Vec::new();
        for item in program.statements() {
            let item = context.handle_item(&item);
            match item {
                HirItem::Statement(stmt) => statements.push(stmt),
                HirItem::Object(obj) => objects.push(obj),
            }
        }
        if !context.errors.is_empty() {
            return Err(context.errors.into_iter().map(Into::into).collect());
        }
        let span = context.item_span(&program);
        let hir_file = HirFile {
            main_function: HirBlock {
                objects,
                return_value: None,
                statements,
                span,
            },
            code_id: input.file,
        };
        Ok(hir_file)
    }
}
impl HirContext<'_, '_> {
    fn build_accessor(lhs: HirExpression, rhs: &HirExpression) -> HirExpression {
        let rhs_opt = match rhs {
            HirExpression::Variable(ident) => *ident,
            _ => unreachable!("rhs must always be an ident"),
        };
        match (lhs, rhs_opt) {
            (HirExpression::Variable(var), rhs) => {
                HirExpression::Path(IdentifierPath::new(vec![var, rhs]))
            }
            (HirExpression::Path(path), rhs) => {
                let mut prev_parts = path.into_inner();
                prev_parts.push(rhs);
                HirExpression::Path(IdentifierPath::new(prev_parts))
            }
            (lhs, rhs) => HirExpression::PropertyAccess {
                lhs: Box::new(lhs),
                rhs,
            },
        }
    }
    fn handle_assignment(&mut self, assignment: &ast::Assignment) -> HirVariableInitialization {
        let span = self.item_span(assignment);
        let pattern = self.handle_pattern(&assignment.pattern());
        let value = self.handle_expression(&assignment.value());
        let value = match &pattern {
            HirVariablePattern::Path(path) if path.single_ident().is_some() => Box::new(
                Self::maybe_add_name_to_expression(value, *path.single_ident().unwrap()),
            ),
            _ => Box::new(value),
        };
        let mode = match assignment.assign_mode() {
            ast::AssignMode::Let(_) => HirDeclarationMode::Let,
            ast::AssignMode::Comptime(_) => HirDeclarationMode::Comptime,
        };
        HirVariableInitialization {
            span,
            pattern,
            value,
            mode,
        }
    }
    fn handle_attribute_list(&mut self, list: &ast::AttributeList) -> Vec<Attribute> {
        list.attributes()
            .map(|attr| Attribute {
                expression: self.handle_expression(&attr),
            })
            .collect()
    }
    fn handle_block(&mut self, block: &ast::Block) -> HirBlock {
        let span = self.item_span(block);
        let (mut statements, mut objects) = (Vec::new(), Vec::new());
        for item in block.statements() {
            let item = self.handle_item(&item);
            match item {
                HirItem::Object(obj) => objects.push(obj),
                HirItem::Statement(stmt) => statements.push(stmt),
            }
        }
        let return_value = block
            .last_expr()
            .map(|expr| self.handle_expression(&expr))
            .map(Box::new);
        HirBlock {
            span,
            statements,
            return_value,
            objects,
        }
    }
    fn handle_branch(&mut self, branch: &ast::Branch) -> HirConditionalBranch {
        let span = self.item_span(branch);
        let is_comptime = branch.comptime_token().is_some();
        let condition = Box::new(self.handle_expression(&branch.condition()));
        let block_positive = Box::new(self.handle_block(&branch.block()));
        let block_negative = match branch.else_branch() {
            Some(ast::BranchElse::Block(block)) => Some(Box::new(self.handle_block(&block))),
            Some(ast::BranchElse::Branch(else_branch)) => {
                let span = self.item_span(&else_branch);
                let else_branch = self.handle_branch(&else_branch);
                let block = HirBlock {
                    span,
                    objects: Vec::new(),
                    statements: Vec::new(),
                    return_value: Some(Box::new(HirExpression::ConditionalBranch(else_branch))),
                };
                Some(Box::new(block))
            }
            None => None,
        };
        HirConditionalBranch {
            span,
            is_comptime,
            condition,
            block_positive,
            block_negative,
        }
    }
    fn handle_control_flow(&mut self, control_flow: &ast::ControlFlowOperation) -> HirControlFlow {
        let span = self.item_span(control_flow);
        let op = control_flow.op().to_token();
        let kind = match op.kind.control_flow_operator().unwrap() {
            debris_parser::token::ControlFlowOperator::Break => HirControlKind::Break,
            debris_parser::token::ControlFlowOperator::Continue => HirControlKind::Continue,
            debris_parser::token::ControlFlowOperator::Return => HirControlKind::Return,
        };
        let expression = control_flow
            .expr()
            .map(|expr| self.handle_expression(&expr))
            .map(Box::new);
        HirControlFlow {
            span,
            kind,
            expression,
        }
    }
    fn handle_expression(&mut self, expression: &ast::Expression) -> HirExpression {
        match expression {
            ast::Expression::InfixOp(op) => self.handle_infix_op(op),
            ast::Expression::PrefixOp(op) => self.handle_prefix_op(op),
            ast::Expression::PostfixOp(op) => self.handle_postfix_op(op),
            ast::Expression::Value(value) => self.handle_value(value),
        }
    }
    fn handle_function(&mut self, function: &ast::Function) -> HirFunction {
        let span = self.item_span(function);
        let attributes = function
            .attributes()
            .map(|attr| self.handle_attribute_list(&attr))
            .unwrap_or_default();
        let is_comptime = function.comptime_token().is_some();
        let block = self.handle_block(&function.block());
        let ident = match function.ident() {
            Some(ident) => HirFunctionName::Ident(self.span(ident.to_token()).into()),
            None => HirFunctionName::Unnamed {
                file: self.input_file.file,
                pos: span,
            },
        };
        let param_list = function.param_declaration_list();
        let parameter_span = self.item_span(¶m_list);
        let parameters = param_list
            .declarations()
            .map(|decl| {
                let span = self.item_span(&decl);
                let ident = self.item_span(&decl.lhs()).into();
                let typ = self.handle_type_pattern(&decl.rhs());
                HirParameterDeclaration { span, ident, typ }
            })
            .collect();
        let ret_decl = function.ret_declaration();
        let signature_span = ret_decl.as_ref().map_or(parameter_span, |ret_decl| {
            parameter_span.until(self.item_span(ret_decl))
        });
        let return_type = ret_decl.map(|typ| self.handle_type_pattern(&typ));
        HirFunction {
            span,
            signature_span,
            parameter_span,
            is_comptime,
            attributes,
            ident,
            block,
            parameters,
            return_type,
        }
    }
    fn handle_import(&mut self, import: &ast::Import) -> HirImport {
        let span = self.item_span(import);
        let ident = self.item_span(&import.ident()).into();
        let id = self.add_import_file(ident);
        HirImport { span, ident, id }
    }
    fn handle_infinite_loop(&mut self, inf_loop: &ast::InfLoop) -> HirInfiniteLoop {
        let span = self.item_span(inf_loop);
        let block = Box::new(self.handle_block(&inf_loop.block()));
        HirInfiniteLoop { span, block }
    }
    fn handle_infix_op(&mut self, op: &ast::InfixOp) -> HirExpression {
        let left = self.handle_expression(&op.left());
        if let Some(right) = op.right() {
            let right = self.handle_expression(&right);
            let op_span = self.item_span(op);
            let op = op
                .operator()
                .expect("Cannot be missing operator with rhs existing")
                .to_token()
                .kind
                .infix_operator()
                .unwrap();
            let op = match op {
                debris_parser::token::InfixOperator::Dot => {
                    return Self::build_accessor(left, &right)
                }
                debris_parser::token::InfixOperator::Plus => HirInfixOperator::Plus,
                debris_parser::token::InfixOperator::Minus => HirInfixOperator::Minus,
                debris_parser::token::InfixOperator::Times => HirInfixOperator::Times,
                debris_parser::token::InfixOperator::Divide => HirInfixOperator::Divide,
                debris_parser::token::InfixOperator::Modulo => HirInfixOperator::Modulo,
                debris_parser::token::InfixOperator::Equal => {
                    HirInfixOperator::Comparison(HirComparisonOperator::Eq)
                }
                debris_parser::token::InfixOperator::NotEqual => {
                    HirInfixOperator::Comparison(HirComparisonOperator::Ne)
                }
                debris_parser::token::InfixOperator::GreaterOrEqual => {
                    HirInfixOperator::Comparison(HirComparisonOperator::Ge)
                }
                debris_parser::token::InfixOperator::Greater => {
                    HirInfixOperator::Comparison(HirComparisonOperator::Gt)
                }
                debris_parser::token::InfixOperator::LessOrEqual => {
                    HirInfixOperator::Comparison(HirComparisonOperator::Le)
                }
                debris_parser::token::InfixOperator::Less => {
                    HirInfixOperator::Comparison(HirComparisonOperator::Lt)
                }
                debris_parser::token::InfixOperator::And => HirInfixOperator::And,
                debris_parser::token::InfixOperator::Or => HirInfixOperator::Or,
            };
            let operation = HirInfix {
                operator: op,
                span: op_span,
            };
            HirExpression::BinaryOperation {
                operation,
                lhs: Box::new(left),
                rhs: Box::new(right),
            }
        } else {
            left
        }
    }
    fn handle_item(&mut self, statement: &ast::Statement) -> HirItem {
        use ast::Statement::*;
        match statement {
            Assignment(assignment) => HirItem::Statement(HirStatement::VariableDecl(
                self.handle_assignment(assignment),
            )),
            Block(block) => HirItem::Statement(HirStatement::Block(self.handle_block(block))),
            Branch(branch) => {
                HirItem::Statement(HirStatement::ConditionalBranch(self.handle_branch(branch)))
            }
            Expression(expr) => {
                HirItem::Statement(HirStatement::Expression(self.handle_expression(expr)))
            }
            Function(function) => {
                HirItem::Object(HirObject::Function(self.handle_function(function)))
            }
            Import(import) => HirItem::Object(HirObject::Import(self.handle_import(import))),
            InfLoop(inf_loop) => HirItem::Statement(HirStatement::InfiniteLoop(
                self.handle_infinite_loop(inf_loop),
            )),
            Module(module) => HirItem::Object(HirObject::Module(self.handle_module(module))),
            Update(update) => {
                HirItem::Statement(HirStatement::VariableUpdate(self.handle_update(update)))
            }
            Struct(strukt) => HirItem::Object(HirObject::Struct(self.handle_struct(strukt))),
            WhileLoop(while_loop) => HirItem::Statement(HirStatement::InfiniteLoop(
                self.handle_while_loop(while_loop),
            )),
        }
    }
    fn handle_module(&mut self, module: &ast::Module) -> HirModule {
        let span = self.item_span(module);
        let ident: SpannedIdentifier = self.span(module.ident().to_token()).into();
        let block = self.handle_block(&module.block());
        HirModule {
            span,
            attributes: Vec::new(),
            ident,
            block,
        }
    }
    fn handle_path(&self, path: &ast::Path) -> IdentifierPath {
        let idents = path
            .segments()
            .map(|segment| self.span(segment.to_token()))
            .map(|span| SpannedIdentifier { span })
            .collect();
        IdentifierPath::new(idents)
    }
    fn handle_parse_error(&mut self, error: &debris_parser::error::ParseErrorKind) -> ParseError {
        match error {
            debris_parser::error::ParseErrorKind::LeftOverInput(ident) => {
                ParseError::LeftoverInput {
                    span: self.span(*ident),
                }
            }
            debris_parser::error::ParseErrorKind::UnexpectedComma(ident) => {
                ParseError::UnexpectedComma {
                    span: self.span(*ident),
                }
            }
            debris_parser::error::ParseErrorKind::UnexpectedFunctionIdent(ident) => {
                ParseError::UnexpectedFunctionIdent {
                    span: self.span(*ident),
                }
            }
            debris_parser::error::ParseErrorKind::UnexpectedPath(span) => {
                ParseError::UnexpectedPath {
                    span: self.normalize_local_span(*span),
                }
            }
            debris_parser::error::ParseErrorKind::UnexpectedToken { got, expected } => {
                ParseError::UnexpectedToken {
                    span: self.span(*got),
                    expected: expected
                        .iter()
                        .map(|kind| match kind {
                            ExpectedItem::ControlFlowOperator => {
                                "control flow operator".to_string()
                            }
                            ExpectedItem::FormatString => "format string".to_string(),
                            ExpectedItem::TokenKind(kind) => kind.to_string(),
                            ExpectedItem::Statement => "statement".to_string(),
                            ExpectedItem::Value => "value".to_string(),
                        })
                        .collect(),
                }
            }
        }
    }
    fn handle_pattern(&self, pattern: &ast::Pattern) -> HirVariablePattern {
        match pattern {
            ast::Pattern::Function(_) => unreachable!(),
            ast::Pattern::Path(path) => HirVariablePattern::Path(self.handle_path(path)),
            ast::Pattern::Tuple(tuple) => HirVariablePattern::Tuple(
                tuple
                    .sub_patterns()
                    .map(|pat| self.handle_pattern(&pat))
                    .collect(),
            ),
        }
    }
    fn handle_postfix_op(&mut self, op: &ast::PostfixOp) -> HirExpression {
        let span = self.item_span(op);
        let value = self.handle_expression(&op.value());
        match op.op() {
            ast::PostfixOperator::ParamList(params) => {
                let parameters_span = self.item_span(¶ms);
                let mut parameters: Vec<HirExpression> = params
                    .arguments()
                    .map(|arg| self.handle_expression(&arg))
                    .collect();
                if let Some(lambda_block) = params.lambda() {
                    let block_span = self.item_span(&lambda_block);
                    let function = HirFunction {
                        attributes: Vec::new(),
                        is_comptime: false,
                        block: self.handle_block(&lambda_block),
                        ident: HirFunctionName::Unnamed {
                            file: self.input_file.file,
                            pos: block_span,
                        },
                        parameter_span: block_span.at_start(),
                        parameters: Vec::new(),
                        return_type: None,
                        signature_span: block_span.at_start(),
                        span: block_span,
                    };
                    parameters.push(HirExpression::Function(function));
                }
                HirExpression::FunctionCall(HirFunctionCall {
                    span,
                    value: Box::new(value),
                    parameters,
                    parameters_span,
                })
            }
            ast::PostfixOperator::StructLiteral(struct_literal) => {
                HirExpression::StructInitialization(
                    self.handle_struct_literal(value, &struct_literal),
                )
            }
        }
    }
    fn handle_prefix_op(&mut self, op: &ast::PrefixOp) -> HirExpression {
        let value = self.handle_expression(&op.expr());
        let token = op.op().to_token();
        let prefix_op = token.kind.prefix_operator().unwrap();
        let hir_operator = match prefix_op {
            PrefixOperator::Minus => HirPrefixOperator::Minus,
            PrefixOperator::Not => HirPrefixOperator::Not,
        };
        HirExpression::UnaryOperation {
            operation: HirPrefix {
                operator: hir_operator,
                span: self.span(token),
            },
            value: Box::new(value),
        }
    }
    fn handle_struct(&mut self, strukt: &ast::Struct) -> HirStruct {
        let span = self.item_span(strukt);
        let ident = self.span(strukt.ident().unwrap().to_token()).into();
        let properties = strukt.variables().map_or_else(Vec::new, |variables| {
            variables
                .vars()
                .map(|property| {
                    let span = self.item_span(&property);
                    let ident = self.span(property.ident().to_token()).into();
                    let datatype = HirTypePattern::Path(IdentifierPath::new(
                        property
                            .typ()
                            .segments()
                            .map(|segment| self.span(segment.to_token()).into())
                            .collect(),
                    ));
                    HirPropertyDeclaration {
                        span,
                        ident,
                        datatype,
                    }
                })
                .collect()
        });
        let objects = strukt
            .items()
            .map(|item| HirObject::Function(self.handle_function(&item)))
            .collect();
        HirStruct {
            span,
            ident,
            attributes: Vec::new(),
            properties,
            objects,
        }
    }
    fn handle_struct_literal(
        &mut self,
        base: HirExpression,
        struct_lit: &ast::StructLiteral,
    ) -> HirStructInitialization {
        let span = self.item_span(struct_lit);
        let values = struct_lit
            .items()
            .map(|item| {
                (
                    self.span(item.ident().to_token()).into(),
                    self.handle_expression(&item.expr()),
                )
            })
            .collect();
        HirStructInitialization {
            span,
            base: Box::new(base),
            values,
        }
    }
    fn handle_type_pattern(&mut self, pattern: &ast::Pattern) -> HirTypePattern {
        let span = self.item_span(pattern);
        match pattern {
            ast::Pattern::Function(func) => {
                let parameters = func
                    .parameters()
                    .items()
                    .map(|pattern| self.handle_type_pattern(&pattern))
                    .collect();
                let return_type = func
                    .ret()
                    .map(|pattern| self.handle_type_pattern(&pattern))
                    .map(Box::new);
                HirTypePattern::Function {
                    span,
                    parameters,
                    return_type,
                }
            }
            ast::Pattern::Path(path) => HirTypePattern::Path(IdentifierPath::new(
                path.segments()
                    .map(|ident| self.span(ident.to_token()).into())
                    .collect(),
            )),
            ast::Pattern::Tuple(tuple) => {
                let span = self.item_span(tuple);
                let values = tuple
                    .sub_patterns()
                    .map(|pattern| self.handle_type_pattern(&pattern))
                    .collect();
                HirTypePattern::Tuple { span, values }
            }
        }
    }
    fn handle_update(&mut self, update: &ast::Update) -> HirVariableUpdate {
        let span = self.item_span(update);
        let pattern = self.handle_pattern(&update.pattern());
        let value = Box::new(self.handle_expression(&update.expr()));
        let op_token = update.op().to_token();
        let operator = match op_token.kind.assign_operator().unwrap() {
            debris_parser::token::AssignOperator::Assign => {
                return HirVariableUpdate {
                    span,
                    pattern,
                    value,
                }
            }
            debris_parser::token::AssignOperator::AssignPlus => HirInfixOperator::Plus,
            debris_parser::token::AssignOperator::AssignMinus => HirInfixOperator::Minus,
            debris_parser::token::AssignOperator::AssignTimes => HirInfixOperator::Times,
            debris_parser::token::AssignOperator::AssignDivide => HirInfixOperator::Divide,
            debris_parser::token::AssignOperator::AssignModulo => HirInfixOperator::Modulo,
        };
        let lhs = Box::new(Self::convert_pattern_to_value(&pattern));
        let operation = HirInfix {
            operator,
            span: self.span(op_token),
        };
        let full_value = Box::new(HirExpression::BinaryOperation {
            operation,
            lhs,
            rhs: value,
        });
        HirVariableUpdate {
            span,
            pattern,
            value: full_value,
        }
    }
    fn convert_pattern_to_value(pattern: &HirVariablePattern) -> HirExpression {
        match pattern {
            HirVariablePattern::Path(path) => HirExpression::Path(path.clone()),
            HirVariablePattern::Tuple(tuple) => {
                HirExpression::TupleInitialization(HirTupleInitialization {
                    span: pattern.span(),
                    values: tuple.iter().map(Self::convert_pattern_to_value).collect(),
                })
            }
        }
    }
    fn handle_value(&mut self, value: &ast::Value) -> HirExpression {
        match value {
            ast::Value::Block(block) => HirExpression::Block(self.handle_block(block)),
            ast::Value::Bool(bool) => {
                let span = self.span(bool.to_token());
                let value = match bool {
                    ast::Bool::True(_) => true,
                    ast::Bool::False(_) => false,
                };
                HirExpression::Value(HirConstValue::Bool { span, value })
            }
            ast::Value::Branch(branch) => {
                HirExpression::ConditionalBranch(self.handle_branch(branch))
            }
            ast::Value::ControlFlow(control_flow) => {
                HirExpression::ControlFlow(self.handle_control_flow(control_flow))
            }
            ast::Value::FormatString(fmt_string) => {
                let span = self.item_span(fmt_string);
                let value = fmt_string
                    .components()
                    .map(|component| match component {
                        FormatStringComponent::EscapedChar(char) => {
                            let span = self.span(char);
                            let mut chars =
                                self.compile_context.input_files.get_span_str(span).chars();
                            chars.next();
                            HirFormatStringMember::String(chars.as_str().into())
                        }
                        FormatStringComponent::StringInner(string) => {
                            let span = self.span(string);
                            let value = self.compile_context.input_files.get_span_str(span);
                            HirFormatStringMember::String(value.into())
                        }
                        FormatStringComponent::Path(path) => {
                            let expr = self.handle_path(&path);
                            HirFormatStringMember::Variable(Box::new(HirExpression::Path(expr)))
                        }
                    })
                    .collect();
                HirExpression::Value(HirConstValue::FormatString { span, value })
            }
            ast::Value::Function(function) => {
                HirExpression::Function(self.handle_function(function))
            }
            ast::Value::Ident(ident) => {
                let span = self.span(ident.to_token());
                HirExpression::Variable(SpannedIdentifier::new(span))
            }
            ast::Value::Int(int) => {
                let span = self.span(int.to_token());
                let int_str = self.compile_context.input_files.get_span_str(span);
                let value = match int_str.parse() {
                    Ok(value) => value,
                    Err(error) => {
                        self.errors
                            .push(ParseError::InvalidIntLiteral { span, error });
                        0
                    }
                };
                HirExpression::Value(HirConstValue::Integer { span, value })
            }
            ast::Value::InfLoop(inf_loop) => {
                HirExpression::InfiniteLoop(self.handle_infinite_loop(inf_loop))
            }
            ast::Value::ParenthesisValue(value) => self.handle_expression(&value.expr()),
            ast::Value::String(string) => {
                let span = self.span(string.to_token());
                let value = self.compile_context.input_files.get_span_str(span);
                let value = {
                    let mut chars = value.chars();
                    chars.next();
                    chars.next_back();
                    chars.as_str().into()
                };
                HirExpression::Value(HirConstValue::String { span, value })
            }
            ast::Value::Tuple(tuple) => {
                let span = self.item_span(tuple);
                let values = tuple
                    .items()
                    .map(|item| self.handle_expression(&item))
                    .collect();
                HirExpression::TupleInitialization(HirTupleInitialization { span, values })
            }
            ast::Value::WhileLoop(while_loop) => {
                HirExpression::InfiniteLoop(self.handle_while_loop(while_loop))
            }
        }
    }
    fn handle_while_loop(&mut self, while_loop: &ast::WhileLoop) -> HirInfiniteLoop {
        let span = self.item_span(while_loop);
        let while_loop_block = self.handle_block(&while_loop.block());
        let condition_statement = HirStatement::ConditionalBranch(HirConditionalBranch {
            span,
            is_comptime: false, condition: Box::new(self.handle_expression(&while_loop.condition())),
            block_positive: Box::new(while_loop_block),
            block_negative: Some(Box::new(HirBlock {
                span,
                objects: Vec::new(),
                statements: vec![HirStatement::ControlFlow(HirControlFlow {
                    expression: None,
                    kind: HirControlKind::Break,
                    span: self.item_span(&while_loop.condition()),
                })],
                return_value: None,
            })),
        });
        let block = Box::new(HirBlock {
            objects: Vec::new(),
            statements: vec![condition_statement],
            span,
            return_value: None,
        });
        HirInfiniteLoop { span, block }
    }
    fn maybe_add_name_to_expression(
        mut expr: HirExpression,
        ident: SpannedIdentifier,
    ) -> HirExpression {
        match &mut expr {
            HirExpression::Function(function) if function.ident.is_unknown() => {
                function.ident = HirFunctionName::Ident(ident);
            }
            _ => {}
        }
        expr
    }
}