use sipha::error::{RelatedLocation, SemanticDiagnostic};
use sipha::types::Span;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum AnalysisError {
UnknownVariableOrFunction,
BreakOutOfLoop,
ContinueOutOfLoop,
VariableNameUnavailable,
IncludeOnlyInMainBlock,
FunctionOnlyInMainBlock,
GlobalOnlyInMainBlock,
DuplicateClassName,
DuplicateFunctionName,
OptionalParamsOnlyInStandardFunctionsOrMethods,
WrongArity,
TypeMismatch,
}
impl AnalysisError {
#[must_use]
pub fn code(self) -> &'static str {
match self {
Self::UnknownVariableOrFunction => "E033",
Self::BreakOutOfLoop => "E012",
Self::ContinueOutOfLoop => "E013",
Self::VariableNameUnavailable => "E021",
Self::IncludeOnlyInMainBlock => "E014",
Self::FunctionOnlyInMainBlock => "E019",
Self::GlobalOnlyInMainBlock => "E027",
Self::DuplicateClassName => "E034",
Self::DuplicateFunctionName => "E035",
Self::OptionalParamsOnlyInStandardFunctionsOrMethods => "E038",
Self::WrongArity => "E036",
Self::TypeMismatch => "E037",
}
}
#[must_use]
pub fn message(self) -> &'static str {
match self {
Self::UnknownVariableOrFunction => "unknown variable or function",
Self::BreakOutOfLoop => "break outside of loop",
Self::ContinueOutOfLoop => "continue outside of loop",
Self::VariableNameUnavailable => "variable name already used in this scope",
Self::IncludeOnlyInMainBlock => "include only allowed in main block",
Self::FunctionOnlyInMainBlock => "function declaration only allowed in main block",
Self::GlobalOnlyInMainBlock => "global declaration only allowed in main block",
Self::DuplicateClassName => "duplicate class name",
Self::DuplicateFunctionName => "duplicate function name",
Self::OptionalParamsOnlyInStandardFunctionsOrMethods => {
"optional/default parameters only allowed in standard functions or methods, not in user-defined functions"
}
Self::WrongArity => "wrong number of arguments for function call",
Self::TypeMismatch => "type mismatch",
}
}
#[must_use]
pub fn at(self, span: Span) -> SemanticDiagnostic {
SemanticDiagnostic::error(span, self.message()).with_code(self.code())
}
#[must_use]
pub fn at_with_related(
self,
span: Span,
related: Vec<(Span, &'static str)>,
) -> SemanticDiagnostic {
let related_locations = related
.into_iter()
.map(|(span, message)| RelatedLocation {
span,
message: message.to_string(),
})
.collect();
SemanticDiagnostic::error(span, self.message())
.with_code(self.code())
.with_related(related_locations)
}
}
pub fn wrong_arity_at(span: Span, expected: usize, actual: usize) -> SemanticDiagnostic {
let message = format!("wrong number of arguments (expected {expected}, got {actual})");
SemanticDiagnostic::error(span, message).with_code(AnalysisError::WrongArity.code())
}
pub fn type_mismatch_at(span: Span, expected: &str, got: &str) -> SemanticDiagnostic {
let message = format!("type mismatch: expected {expected}, got {got}");
SemanticDiagnostic::error(span, message).with_code(AnalysisError::TypeMismatch.code())
}
pub fn invalid_cast_at(span: Span, from: &str, to: &str) -> SemanticDiagnostic {
let message = format!("invalid cast from {from} to {to}");
SemanticDiagnostic::error(span, message).with_code(AnalysisError::TypeMismatch.code())
}