leekscript_analysis/
error.rs1use sipha::error::{RelatedLocation, SemanticDiagnostic};
23use sipha::types::Span;
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27#[non_exhaustive]
28pub enum AnalysisError {
29 UnknownVariableOrFunction,
31 BreakOutOfLoop,
33 ContinueOutOfLoop,
35 VariableNameUnavailable,
37 IncludeOnlyInMainBlock,
39 FunctionOnlyInMainBlock,
41 GlobalOnlyInMainBlock,
43 DuplicateClassName,
45 DuplicateFunctionName,
47 OptionalParamsOnlyInStandardFunctionsOrMethods,
49 WrongArity,
51 TypeMismatch,
53}
54
55impl AnalysisError {
56 #[must_use]
58 pub fn code(self) -> &'static str {
59 match self {
60 Self::UnknownVariableOrFunction => "E033",
61 Self::BreakOutOfLoop => "E012",
62 Self::ContinueOutOfLoop => "E013",
63 Self::VariableNameUnavailable => "E021",
64 Self::IncludeOnlyInMainBlock => "E014",
65 Self::FunctionOnlyInMainBlock => "E019",
66 Self::GlobalOnlyInMainBlock => "E027",
67 Self::DuplicateClassName => "E034",
68 Self::DuplicateFunctionName => "E035",
69 Self::OptionalParamsOnlyInStandardFunctionsOrMethods => "E038",
70 Self::WrongArity => "E036",
71 Self::TypeMismatch => "E037",
72 }
73 }
74
75 #[must_use]
77 pub fn message(self) -> &'static str {
78 match self {
79 Self::UnknownVariableOrFunction => "unknown variable or function",
80 Self::BreakOutOfLoop => "break outside of loop",
81 Self::ContinueOutOfLoop => "continue outside of loop",
82 Self::VariableNameUnavailable => "variable name already used in this scope",
83 Self::IncludeOnlyInMainBlock => "include only allowed in main block",
84 Self::FunctionOnlyInMainBlock => "function declaration only allowed in main block",
85 Self::GlobalOnlyInMainBlock => "global declaration only allowed in main block",
86 Self::DuplicateClassName => "duplicate class name",
87 Self::DuplicateFunctionName => "duplicate function name",
88 Self::OptionalParamsOnlyInStandardFunctionsOrMethods => {
89 "optional/default parameters only allowed in standard functions or methods, not in user-defined functions"
90 }
91 Self::WrongArity => "wrong number of arguments for function call",
92 Self::TypeMismatch => "type mismatch",
93 }
94 }
95
96 #[must_use]
98 pub fn at(self, span: Span) -> SemanticDiagnostic {
99 SemanticDiagnostic::error(span, self.message()).with_code(self.code())
100 }
101
102 #[must_use]
104 pub fn at_with_related(
105 self,
106 span: Span,
107 related: Vec<(Span, &'static str)>,
108 ) -> SemanticDiagnostic {
109 let related_locations = related
110 .into_iter()
111 .map(|(span, message)| RelatedLocation {
112 span,
113 message: message.to_string(),
114 })
115 .collect();
116 SemanticDiagnostic::error(span, self.message())
117 .with_code(self.code())
118 .with_related(related_locations)
119 }
120}
121
122pub fn wrong_arity_at(span: Span, expected: usize, actual: usize) -> SemanticDiagnostic {
124 let message = format!("wrong number of arguments (expected {expected}, got {actual})");
125 SemanticDiagnostic::error(span, message).with_code(AnalysisError::WrongArity.code())
126}
127
128pub fn type_mismatch_at(span: Span, expected: &str, got: &str) -> SemanticDiagnostic {
130 let message = format!("type mismatch: expected {expected}, got {got}");
131 SemanticDiagnostic::error(span, message).with_code(AnalysisError::TypeMismatch.code())
132}
133
134pub fn invalid_cast_at(span: Span, from: &str, to: &str) -> SemanticDiagnostic {
136 let message = format!("invalid cast from {from} to {to}");
137 SemanticDiagnostic::error(span, message).with_code(AnalysisError::TypeMismatch.code())
138}