loalang 0.1.15

Loa is a general-purpose, purely immutable, object-oriented programming language.
Documentation
use crate::semantics::*;
use crate::syntax::*;
use crate::BitSize;
use crate::*;
use fraction::ToPrimitive;

pub struct ImpreciseFloatLiteral;

impl ImpreciseFloatLiteral {
    fn check_literal(
        literal: Node,
        type_: Type,
        class: Id,
        analysis: &mut Analysis,
        diagnostics: &mut Vec<Diagnostic>,
    ) -> Option<()> {
        let class = analysis.navigator.find_node(class)?;
        let (name, _, _) = analysis.navigator.qualified_name_of(&class)?;

        match (literal.kind, name.as_str()) {
            (FloatExpression(_, fraction), "Loa/Float32") => {
                Self::assert_fraction_precision(
                    literal.span,
                    type_,
                    fraction,
                    BitSize::Size32,
                    diagnostics,
                );
            }
            (FloatExpression(_, fraction), "Loa/Float64") => {
                Self::assert_fraction_precision(
                    literal.span,
                    type_,
                    fraction,
                    BitSize::Size64,
                    diagnostics,
                );
            }
            _ => {}
        }

        None
    }

    fn assert_fraction_precision(
        span: Span,
        type_: Type,
        fraction: BigFraction,
        size: BitSize,
        diagnostics: &mut Vec<Diagnostic>,
    ) {
        let big_formatted = format!("{:.1$}", fraction, 310);
        let cast_formatted = match size {
            BitSize::Size32 => fraction.to_f32().unwrap().to_string(),
            BitSize::Size64 => fraction.to_f64().unwrap().to_string(),
            _ => return,
        };

        if big_formatted != cast_formatted {
            diagnostics.push(Diagnostic::TooPreciseFloat(span, type_, fraction))
        }
    }
}

impl Checker for ImpreciseFloatLiteral {
    fn check(&self, analysis: &mut Analysis, diagnostics: &mut Vec<Diagnostic>) {
        for literal in analysis.navigator.all_number_literals() {
            let type_ = analysis.types.get_type_of_expression(&literal);

            match type_ {
                Type::UnresolvedInteger(_, _) => continue,
                Type::UnresolvedFloat(_, _) => continue,
                Type::Class(_, class, _) => {
                    Self::check_literal(literal, type_, class, analysis, diagnostics);
                    continue;
                }
                _ => {}
            }

            diagnostics.push(Diagnostic::InvalidLiteralType(literal.span, type_));
        }
    }
}