1use miette::{Diagnostic, SourceSpan};
4use thiserror::Error;
5
6use crate::types::Type;
7
8#[derive(Error, Debug, Diagnostic)]
10pub enum TypeError {
11 #[error("Type mismatch: expected `{expected}`, found `{found}`")]
12 #[diagnostic(
13 code(solscript::typeck::mismatch),
14 help("ensure the value type matches the expected type")
15 )]
16 TypeMismatch {
17 expected: String,
18 found: String,
19 #[label("expected `{expected}`, found `{found}`")]
20 span: SourceSpan,
21 #[source_code]
22 src: String,
23 },
24
25 #[error("Undefined variable: `{name}`")]
26 #[diagnostic(
27 code(solscript::typeck::undefined_var),
28 help("check spelling, or declare the variable before use")
29 )]
30 UndefinedVariable {
31 name: String,
32 #[label("not found in this scope")]
33 span: SourceSpan,
34 #[source_code]
35 src: String,
36 },
37
38 #[error("Undefined type: `{name}`")]
39 #[diagnostic(
40 code(solscript::typeck::undefined_type),
41 help("check spelling, or define the type (struct/enum/contract)")
42 )]
43 UndefinedType {
44 name: String,
45 #[label("unknown type")]
46 span: SourceSpan,
47 #[source_code]
48 src: String,
49 },
50
51 #[error("Undefined function: `{name}`")]
52 #[diagnostic(
53 code(solscript::typeck::undefined_fn),
54 help("check spelling, or define the function")
55 )]
56 UndefinedFunction {
57 name: String,
58 #[label("not found")]
59 span: SourceSpan,
60 #[source_code]
61 src: String,
62 },
63
64 #[error("Undefined field: `{field}` on type `{ty}`")]
65 #[diagnostic(
66 code(solscript::typeck::undefined_field),
67 help("check the struct definition for available fields")
68 )]
69 UndefinedField {
70 field: String,
71 ty: String,
72 #[label("no field `{field}` on type `{ty}`")]
73 span: SourceSpan,
74 #[source_code]
75 src: String,
76 },
77
78 #[error("Undefined method: `{method}` on type `{ty}`")]
79 #[diagnostic(
80 code(solscript::typeck::undefined_method),
81 help("check available methods for this type")
82 )]
83 UndefinedMethod {
84 method: String,
85 ty: String,
86 #[label("no method `{method}` on type `{ty}`")]
87 span: SourceSpan,
88 #[source_code]
89 src: String,
90 },
91
92 #[error("Cannot call non-function type `{ty}`")]
93 #[diagnostic(
94 code(solscript::typeck::not_callable),
95 help("only functions and interface methods can be called")
96 )]
97 NotCallable {
98 ty: String,
99 #[label("this is not a function")]
100 span: SourceSpan,
101 #[source_code]
102 src: String,
103 },
104
105 #[error("Wrong number of arguments: expected {expected}, found {found}")]
106 #[diagnostic(
107 code(solscript::typeck::wrong_arg_count),
108 help("check the function signature for required parameters")
109 )]
110 WrongArgCount {
111 expected: usize,
112 found: usize,
113 #[label("expected {expected} argument(s), found {found}")]
114 span: SourceSpan,
115 #[source_code]
116 src: String,
117 },
118
119 #[error("Cannot index type `{ty}`")]
120 #[diagnostic(
121 code(solscript::typeck::not_indexable),
122 help("only arrays, mappings, and dynamic arrays can be indexed")
123 )]
124 NotIndexable {
125 ty: String,
126 #[label("cannot use [] on this type")]
127 span: SourceSpan,
128 #[source_code]
129 src: String,
130 },
131
132 #[error("Cannot apply operator `{op}` to type `{ty}`")]
133 #[diagnostic(
134 code(solscript::typeck::invalid_unary_op),
135 help("check that the operator is valid for this type")
136 )]
137 InvalidUnaryOp {
138 op: String,
139 ty: String,
140 #[label("operator `{op}` cannot be applied to `{ty}`")]
141 span: SourceSpan,
142 #[source_code]
143 src: String,
144 },
145
146 #[error("Cannot apply operator `{op}` to types `{left}` and `{right}`")]
147 #[diagnostic(
148 code(solscript::typeck::invalid_binary_op),
149 help("ensure both operands have compatible types")
150 )]
151 InvalidBinaryOp {
152 op: String,
153 left: String,
154 right: String,
155 #[label("cannot apply `{op}` to `{left}` and `{right}`")]
156 span: SourceSpan,
157 #[source_code]
158 src: String,
159 },
160
161 #[error("Duplicate definition: `{name}`")]
162 #[diagnostic(
163 code(solscript::typeck::duplicate),
164 help("rename one of the definitions to avoid conflict")
165 )]
166 DuplicateDefinition {
167 name: String,
168 #[label("`{name}` is already defined")]
169 span: SourceSpan,
170 #[source_code]
171 src: String,
172 },
173
174 #[error("Missing return value")]
175 #[diagnostic(
176 code(solscript::typeck::missing_return),
177 help("add a return statement with the expected type")
178 )]
179 MissingReturn {
180 expected: String,
181 #[label("function expects to return `{expected}`")]
182 span: SourceSpan,
183 #[source_code]
184 src: String,
185 },
186
187 #[error("Undefined event: `{name}`")]
188 #[diagnostic(
189 code(solscript::typeck::undefined_event),
190 help("define the event before using it: event {name}(...);")
191 )]
192 UndefinedEvent {
193 name: String,
194 #[label("event `{name}` is not defined")]
195 span: SourceSpan,
196 #[source_code]
197 src: String,
198 },
199
200 #[error("Undefined modifier: `{name}`")]
201 #[diagnostic(
202 code(solscript::typeck::undefined_modifier),
203 help("define the modifier before using it")
204 )]
205 UndefinedModifier {
206 name: String,
207 #[label("modifier `{name}` is not defined")]
208 span: SourceSpan,
209 #[source_code]
210 src: String,
211 },
212
213 #[error("Undefined error: `{name}`")]
214 #[diagnostic(
215 code(solscript::typeck::undefined_error),
216 help("define the error before using it: error {name}(...);")
217 )]
218 UndefinedError {
219 name: String,
220 #[label("error `{name}` is not defined")]
221 span: SourceSpan,
222 #[source_code]
223 src: String,
224 },
225}
226
227impl TypeError {
228 pub fn type_mismatch(expected: &Type, found: &Type, span: (usize, usize), src: &str) -> Self {
229 Self::TypeMismatch {
230 expected: expected.to_string(),
231 found: found.to_string(),
232 span: SourceSpan::new(span.0.into(), span.1 - span.0),
233 src: src.to_string(),
234 }
235 }
236
237 pub fn undefined_variable(name: &str, span: (usize, usize), src: &str) -> Self {
238 Self::UndefinedVariable {
239 name: name.to_string(),
240 span: SourceSpan::new(span.0.into(), span.1 - span.0),
241 src: src.to_string(),
242 }
243 }
244
245 pub fn undefined_type(name: &str, span: (usize, usize), src: &str) -> Self {
246 Self::UndefinedType {
247 name: name.to_string(),
248 span: SourceSpan::new(span.0.into(), span.1 - span.0),
249 src: src.to_string(),
250 }
251 }
252
253 pub fn undefined_field(field: &str, ty: &Type, span: (usize, usize), src: &str) -> Self {
254 Self::UndefinedField {
255 field: field.to_string(),
256 ty: ty.to_string(),
257 span: SourceSpan::new(span.0.into(), span.1 - span.0),
258 src: src.to_string(),
259 }
260 }
261
262 pub fn undefined_method(method: &str, ty: &Type, span: (usize, usize), src: &str) -> Self {
263 Self::UndefinedMethod {
264 method: method.to_string(),
265 ty: ty.to_string(),
266 span: SourceSpan::new(span.0.into(), span.1 - span.0),
267 src: src.to_string(),
268 }
269 }
270
271 pub fn not_callable(ty: &Type, span: (usize, usize), src: &str) -> Self {
272 Self::NotCallable {
273 ty: ty.to_string(),
274 span: SourceSpan::new(span.0.into(), span.1 - span.0),
275 src: src.to_string(),
276 }
277 }
278
279 pub fn wrong_arg_count(expected: usize, found: usize, span: (usize, usize), src: &str) -> Self {
280 Self::WrongArgCount {
281 expected,
282 found,
283 span: SourceSpan::new(span.0.into(), span.1 - span.0),
284 src: src.to_string(),
285 }
286 }
287
288 pub fn invalid_binary_op(
289 op: &str,
290 left: &Type,
291 right: &Type,
292 span: (usize, usize),
293 src: &str,
294 ) -> Self {
295 Self::InvalidBinaryOp {
296 op: op.to_string(),
297 left: left.to_string(),
298 right: right.to_string(),
299 span: SourceSpan::new(span.0.into(), span.1 - span.0),
300 src: src.to_string(),
301 }
302 }
303}