loalang 0.1.15

Loa is a general-purpose, purely immutable, object-oriented programming language.
Documentation
use crate::*;
use std::f64::INFINITY;
use std::fmt;

#[derive(Clone, IntoStaticStr)]
pub enum Diagnostic {
    SyntaxError(Span, String),
    UndefinedTypeReference(Span, String),
    UndefinedReference(Span, String),
    UndefinedBehaviour(Span, semantics::Type, String),
    UndefinedImport(Span, String),
    UnexportedImport(Span, String),
    UnassignableType {
        span: Span,
        assignability: semantics::TypeAssignability,
    },
    DuplicatedDeclaration(Span, String, usize),
    InvalidInherit {
        span: Span,
        super_type: semantics::Type,
        sub_type: semantics::Type,
        violations: Vec<InheritanceViolation>,
    },
    InvalidLiteralType(Span, semantics::Type),
    OutOfBounds(Span, semantics::Type, String),
    TooPreciseFloat(Span, semantics::Type, BigFraction),
    WrongNumberOfTypeArguments(Span, String, usize, usize),
    InvalidAccessToPrivateMethod(Span, String, String),
    InvalidTypeParameterReferenceVarianceUsage(Span, String, &'static str, &'static str),
    IncompleteInitializer(Span, String, Vec<String>),
    UndefinedInitializedVariable(Span, String, String),
}

#[derive(Clone)]
pub enum InheritanceViolation {
    BehaviourNotImplemented(semantics::Behaviour),
    OverrideNotSound(semantics::Behaviour, semantics::TypeAssignability),
}

impl Diagnostic {
    pub fn span(&self) -> &Span {
        use Diagnostic::*;

        match self {
            SyntaxError(ref s, _)
            | UndefinedTypeReference(ref s, _)
            | UndefinedReference(ref s, _)
            | UndefinedBehaviour(ref s, _, _)
            | UndefinedImport(ref s, _)
            | UnexportedImport(ref s, _)
            | UnassignableType { span: ref s, .. }
            | DuplicatedDeclaration(ref s, _, _)
            | InvalidInherit { span: ref s, .. }
            | InvalidLiteralType(ref s, _)
            | OutOfBounds(ref s, _, _)
            | TooPreciseFloat(ref s, _, _)
            | WrongNumberOfTypeArguments(ref s, _, _, _)
            | InvalidAccessToPrivateMethod(ref s, _, _)
            | InvalidTypeParameterReferenceVarianceUsage(ref s, _, _, _)
            | IncompleteInitializer(ref s, _, _)
            | UndefinedInitializedVariable(ref s, _, _) => s,
        }
    }

    pub fn level(&self) -> DiagnosticLevel {
        use Diagnostic::*;

        match self {
            SyntaxError(_, _)
            | UndefinedTypeReference(_, _)
            | UndefinedReference(_, _)
            | UndefinedBehaviour(_, _, _)
            | UndefinedImport(_, _)
            | UnexportedImport(_, _)
            | UnassignableType { .. }
            | DuplicatedDeclaration(_, _, _)
            | InvalidInherit { .. }
            | InvalidLiteralType(_, _)
            | OutOfBounds(_, _, _)
            | WrongNumberOfTypeArguments(_, _, _, _)
            | InvalidAccessToPrivateMethod(_, _, _)
            | InvalidTypeParameterReferenceVarianceUsage(_, _, _, _)
            | IncompleteInitializer(_, _, _)
            | UndefinedInitializedVariable(_, _, _) => DiagnosticLevel::Error,

            TooPreciseFloat(_, _, _) => DiagnosticLevel::Warning,
        }
    }

    pub fn code(&self) -> usize {
        use Diagnostic::*;

        match self {
            SyntaxError(_, _) => 1,
            UndefinedTypeReference(_, _) => 2,
            UndefinedReference(_, _) => 3,
            UndefinedBehaviour(_, _, _) => 4,
            UndefinedImport(_, _) => 5,
            UnexportedImport(_, _) => 6,
            UnassignableType { .. } => 7,
            DuplicatedDeclaration(_, _, _) => 8,
            InvalidInherit { .. } => 9,
            InvalidLiteralType(_, _) => 10,
            OutOfBounds(_, _, _) => 11,
            TooPreciseFloat(_, _, _) => 12,
            WrongNumberOfTypeArguments(_, _, _, _) => 13,
            InvalidAccessToPrivateMethod(_, _, _) => 14,
            InvalidTypeParameterReferenceVarianceUsage(_, _, _, _) => 15,
            IncompleteInitializer(_, _, _) => 16,
            UndefinedInitializedVariable(_, _, _) => 17,
        }
    }

    pub fn failed(diagnostics: &Vec<Diagnostic>) -> bool {
        let mut failed = false;
        for diagnostic in diagnostics.iter() {
            if let DiagnosticLevel::Error = diagnostic.level() {
                failed = true;
            }
        }
        failed
    }
}

impl fmt::Debug for Diagnostic {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let name: &'static str = self.into();
        write!(
            f,
            "{:?} ({} @ {}:{})",
            self.to_string(),
            name,
            self.span().start.uri,
            self.span().start.line,
        )
    }
}

impl fmt::Display for Diagnostic {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use Diagnostic::*;

        match self {
            SyntaxError(_, s) => write!(f, "{}", s),
            UndefinedTypeReference(_, s) => write!(f, "`{}` is undefined.", s),
            UndefinedReference(_, s) => write!(f, "`{}` is undefined.", s),
            UndefinedBehaviour(_, t, s) => write!(f, "`{}` doesn't respond to `{}`.", t, s),
            UndefinedImport(_, s) => write!(f, "`{}` is undefined.", s),
            UnexportedImport(_, s) => write!(f, "`{}` is not exported.", s),
            UnassignableType { assignability, .. } => write!(f, "{}", assignability),
            DuplicatedDeclaration(_, s, n) => {
                write!(f, "`{}` is defined {} times in this scope.", s, n)
            }
            InvalidInherit {
                sub_type,
                super_type,
                violations,
                ..
            } => {
                write!(f, "`{}` doesn't act as `{}` because:", sub_type, super_type)?;
                for violation in violations.iter() {
                    match violation {
                        InheritanceViolation::BehaviourNotImplemented(ref b) => {
                            write!(f, "\n  - it doesn't respond to `{}`", b)?
                        }
                        InheritanceViolation::OverrideNotSound(ref b, ref t) => {
                            write!(
                                f,
                                "\n• it doesn't respond to `{}` like `{}` would",
                                b.selector(),
                                super_type,
                            )?;
                            if let semantics::TypeAssignability::Invalid {
                                assignee,
                                assigned,
                                because,
                                invariant,
                            } = t
                            {
                                semantics::format_invalid_type_assignability(
                                    f, 2, assignee, assigned, &because, *invariant,
                                )?;
                            }
                        }
                    }
                }
                Ok(())
            }
            InvalidLiteralType(_, type_) => {
                write!(f, "`{}` is not a valid type for this literal.", type_)
            }
            OutOfBounds(_, type_, message) => write!(f, "`{}` must not be {}.", type_, message),
            TooPreciseFloat(_, type_, fraction) => write!(
                f,
                "`{:.2$}` is too precise to be coerced to {} without losing precision.",
                fraction, type_, INFINITY as usize
            ),
            WrongNumberOfTypeArguments(_, name, params, args) => write!(
                f,
                "`{}` takes {} type arguments, but was provided {}.",
                name,
                if *params == 0 {
                    "no".into()
                } else {
                    params.to_string()
                },
                if *args == 0 {
                    "none".into()
                } else {
                    args.to_string()
                },
            ),
            InvalidAccessToPrivateMethod(_, class_name, method_selector) => {
                write!(f, "`{}#{}` is private.", class_name, method_selector)
            }
            InvalidTypeParameterReferenceVarianceUsage(_, name, usage, mark) => write!(
                f,
                "`{}` cannot be used in {} position, because it's marked as `{}`.",
                name, usage, mark
            ),
            IncompleteInitializer(_, selector, uninitialized_names) => {
                write!(f, "Initializer `{}` must initialize ", selector)?;

                match uninitialized_names.len() {
                    1 => write!(f, "`{}`.", &uninitialized_names[0]),
                    2 => write!(
                        f,
                        "`{}` and `{}`.",
                        &uninitialized_names[0], &uninitialized_names[1]
                    ),
                    n => {
                        for (i, name) in uninitialized_names.iter().enumerate() {
                            if i < n - 1 {
                                write!(f, "`{}`, ", name)?;
                            } else {
                                write!(f, "and `{}`", name)?;
                            }
                        }
                        write!(f, ".")
                    }
                }
            }
            UndefinedInitializedVariable(_, var_name, class_name) => {
                write!(f, "`{}` is not a variable of `{}`.", var_name, class_name)
            }
        }
    }
}

pub enum DiagnosticLevel {
    Error,
    Warning,
    Info,
}