1#![allow(unused_assignments)]
3
4use alloc::{string::String, sync::Arc, vec::Vec};
5use core::{fmt, ops::Range};
6
7use miden_debug_types::SourceSpan;
8use miden_utils_diagnostics::{Diagnostic, miette};
9
10#[derive(Debug, Copy, Clone, PartialEq, Eq)]
14pub enum LiteralErrorKind {
15 Empty,
17 InvalidDigit,
19 U32Overflow,
21 FeltOverflow,
23 InvalidBitSize,
25}
26
27impl fmt::Display for LiteralErrorKind {
28 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29 match self {
30 Self::Empty => f.write_str("input was empty"),
31 Self::InvalidDigit => f.write_str("invalid digit"),
32 Self::U32Overflow => f.write_str("value overflowed the u32 range"),
33 Self::FeltOverflow => f.write_str("value overflowed the field modulus"),
34 Self::InvalidBitSize => {
35 f.write_str("expected value to be a valid bit size, e.g. 0..63")
36 },
37 }
38 }
39}
40
41#[derive(Debug, Copy, Clone, PartialEq, Eq)]
45pub enum HexErrorKind {
46 MissingDigits,
48 Invalid,
51 Overflow,
53 TooLong,
55}
56
57impl fmt::Display for HexErrorKind {
58 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59 match self {
60 Self::MissingDigits => {
61 f.write_str("expected number of hex digits to be a multiple of 2")
62 },
63 Self::Invalid => f.write_str("expected 2, 4, 8, 16, or 64 hex digits"),
64 Self::Overflow => f.write_str("value overflowed the field modulus"),
65 Self::TooLong => f.write_str(
66 "value has too many digits, long hex strings must contain exactly 64 digits",
67 ),
68 }
69 }
70}
71
72#[derive(Debug, Copy, Clone, PartialEq, Eq)]
76pub enum BinErrorKind {
77 TooLong,
79}
80
81impl fmt::Display for BinErrorKind {
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83 match self {
84 Self::TooLong => f.write_str(
85 "value has too many digits, binary string can contain no more than 32 digits",
86 ),
87 }
88 }
89}
90
91#[derive(Debug, Default, thiserror::Error, Diagnostic)]
95#[repr(u8)]
96pub enum ParsingError {
97 #[default]
98 #[error("parsing failed due to unexpected input")]
99 #[diagnostic()]
100 Failed = 0,
101 #[error("expected input to be valid utf8, but invalid byte sequences were found")]
102 #[diagnostic()]
103 InvalidUtf8 {
104 #[label("invalid byte sequence starts here")]
105 span: SourceSpan,
106 },
107 #[error(
108 "expected input to be valid utf8, but end-of-file was reached before final codepoint was read"
109 )]
110 #[diagnostic()]
111 IncompleteUtf8 {
112 #[label("the codepoint starting here is incomplete")]
113 span: SourceSpan,
114 },
115 #[error("invalid syntax")]
116 #[diagnostic()]
117 InvalidToken {
118 #[label("occurs here")]
119 span: SourceSpan,
120 },
121 #[error("invalid syntax: {message}")]
122 #[diagnostic()]
123 InvalidSyntax {
124 #[label("{message}")]
125 span: SourceSpan,
126 message: String,
127 },
128 #[error("invalid syntax")]
129 #[diagnostic(help("expected {}", expected.as_slice().join(", or ")))]
130 UnrecognizedToken {
131 #[label("found a {token} here")]
132 span: SourceSpan,
133 token: String,
134 expected: Vec<String>,
135 },
136 #[error("unexpected trailing tokens")]
137 #[diagnostic()]
138 ExtraToken {
139 #[label("{token} was found here, but was not expected")]
140 span: SourceSpan,
141 token: String,
142 },
143 #[error("unexpected end of file")]
144 #[diagnostic(help("expected {}", expected.as_slice().join(", or ")))]
145 UnrecognizedEof {
146 #[label("reached end of file here")]
147 span: SourceSpan,
148 expected: Vec<String>,
149 },
150 #[error("{error}")]
151 #[diagnostic(help(
152 "bare identifiers must be lowercase alphanumeric with '_', quoted identifiers can include any graphical character"
153 ))]
154 InvalidIdentifier {
155 #[source]
156 #[diagnostic(source)]
157 error: crate::ast::IdentError,
158 #[label]
159 span: SourceSpan,
160 },
161 #[error("unclosed quoted identifier")]
162 #[diagnostic()]
163 UnclosedQuote {
164 #[label("no match for quotation mark starting here")]
165 start: SourceSpan,
166 },
167 #[error("too many instructions in a single code block")]
168 #[diagnostic()]
169 CodeBlockTooBig {
170 #[label]
171 span: SourceSpan,
172 },
173 #[error("control-flow nesting depth exceeded")]
174 #[diagnostic(help("control-flow nesting exceeded the maximum depth of {max_depth}"))]
175 ControlFlowNestingDepthExceeded {
176 #[label("control-flow nesting exceeded the configured depth limit here")]
177 span: SourceSpan,
178 max_depth: usize,
179 },
180 #[error("invalid constant expression: division by zero")]
181 DivisionByZero {
182 #[label]
183 span: SourceSpan,
184 },
185 #[error("doc comment is too large")]
186 #[diagnostic(help("make sure it is less than u16::MAX bytes in length"))]
187 DocsTooLarge {
188 #[label]
189 span: SourceSpan,
190 },
191 #[error("invalid literal: {}", kind)]
192 #[diagnostic()]
193 InvalidLiteral {
194 #[label]
195 span: SourceSpan,
196 kind: LiteralErrorKind,
197 },
198 #[error("invalid literal: {}", kind)]
199 #[diagnostic()]
200 InvalidHexLiteral {
201 #[label]
202 span: SourceSpan,
203 kind: HexErrorKind,
204 },
205 #[error("invalid literal: {}", kind)]
206 #[diagnostic()]
207 InvalidBinaryLiteral {
208 #[label]
209 span: SourceSpan,
210 kind: BinErrorKind,
211 },
212 #[error("invalid MAST root literal")]
213 InvalidMastRoot {
214 #[label]
215 span: SourceSpan,
216 },
217 #[error("invalid library path: {}", message)]
218 InvalidLibraryPath {
219 #[label]
220 span: SourceSpan,
221 message: String,
222 },
223 #[error("invalid immediate: value must be in the range {}..{} (exclusive)", range.start, range.end)]
224 ImmediateOutOfRange {
225 #[label]
226 span: SourceSpan,
227 range: Range<usize>,
228 },
229 #[error("too many procedures in this module")]
230 #[diagnostic()]
231 ModuleTooLarge {
232 #[label]
233 span: SourceSpan,
234 },
235 #[error("too many re-exported procedures in this module")]
236 #[diagnostic()]
237 ModuleTooManyReexports {
238 #[label]
239 span: SourceSpan,
240 },
241 #[error(
242 "too many operands for `push`: tried to push {} elements, but only 16 can be pushed at one time",
243 count
244 )]
245 #[diagnostic()]
246 PushOverflow {
247 #[label]
248 span: SourceSpan,
249 count: usize,
250 },
251 #[error("expected a fully-qualified module path, e.g. `std::u64`")]
252 UnqualifiedImport {
253 #[label]
254 span: SourceSpan,
255 },
256 #[error(
257 "source-level digest re-exports are not supported; re-export a named item with `pub use {{item}} from module`"
258 )]
259 UnnamedReexportOfMastRoot {
260 #[label]
261 span: SourceSpan,
262 },
263 #[error("conflicting attributes for procedure definition")]
264 #[diagnostic()]
265 AttributeConflict {
266 #[label(
267 "conflict occurs because an attribute with the same name has already been defined"
268 )]
269 span: SourceSpan,
270 #[label("previously defined here")]
271 prev: SourceSpan,
272 },
273 #[error("conflicting key-value attributes for procedure definition")]
274 #[diagnostic()]
275 AttributeKeyValueConflict {
276 #[label(
277 "conflict occurs because a key with the same name has already been set in a previous declaration"
278 )]
279 span: SourceSpan,
280 #[label("previously defined here")]
281 prev: SourceSpan,
282 },
283 #[error("invalid Advice Map key")]
284 #[diagnostic()]
285 InvalidAdvMapKey {
286 #[label(
287 "an Advice Map key must be a word, either in 64-character hex format or in array-like format `[f0,f1,f2,f3]`"
288 )]
289 span: SourceSpan,
290 },
291 #[error("invalid slice constant")]
292 #[diagnostic()]
293 InvalidSliceConstant {
294 #[label("slices are only supported over word-sized constants")]
295 span: SourceSpan,
296 },
297 #[error("invalid slice: expected valid range")]
298 #[diagnostic()]
299 InvalidRange {
300 #[label("range used for the word constant slice is malformed: `{range:?}`")]
301 span: SourceSpan,
302 range: Range<usize>,
303 },
304 #[error("invalid slice: expected non-empty range")]
305 #[diagnostic()]
306 EmptySlice {
307 #[label("range used for the word constant slice is empty: `{range:?}`")]
308 span: SourceSpan,
309 range: Range<usize>,
310 },
311 #[error("unrecognized calling convention")]
312 #[diagnostic(help("expected one of: 'fast', 'C', 'wasm', 'canon-lift', or 'canon-lower'"))]
313 UnrecognizedCallConv {
314 #[label]
315 span: SourceSpan,
316 },
317 #[error("invalid struct annotation")]
318 #[diagnostic(help("expected one of: '@packed', '@packed(N)', '@transparent', or '@align(N)'"))]
319 InvalidStructAnnotation {
320 #[label]
321 span: SourceSpan,
322 },
323 #[error("invalid struct representation")]
324 #[diagnostic()]
325 InvalidStructRepr {
326 #[label("{message}")]
327 span: SourceSpan,
328 message: String,
329 },
330 #[error("deprecated instruction: `{instruction}` has been removed")]
331 #[diagnostic(help("use `{}` instead", replacement))]
332 DeprecatedInstruction {
333 #[label("this instruction is no longer supported")]
334 span: SourceSpan,
335 instruction: String,
336 replacement: String,
337 },
338 #[error("invalid procedure @locals attribute")]
339 #[diagnostic()]
340 InvalidLocalsAttr {
341 #[label("{message}")]
342 span: SourceSpan,
343 message: String,
344 },
345 #[error("invalid padding value for the `adv.push_mapvaln` instruction: {padding}")]
346 #[diagnostic(help("valid padding values are 0, 4, and 8"))]
347 InvalidPadValue {
348 #[label]
349 span: SourceSpan,
350 padding: u8,
351 },
352 #[error(
353 "invalid submodule declaration '{name}': could not find module sources at '{directory}/{basename}.masm' or '{directory}/{basename}/mod.masm'"
354 )]
355 UndefinedSubmodule {
356 name: crate::ast::Ident,
357 basename: alloc::boxed::Box<str>,
358 directory: miden_debug_types::Uri,
359 #[label]
360 span: SourceSpan,
361 #[source_code]
362 source_file: Option<Arc<miden_debug_types::SourceFile>>,
363 },
364 #[error(
365 "invalid submodule declaration '{name}': submodules must not have the same name as their parent"
366 )]
367 #[diagnostic(help("occurred while parsing {parent_module_uri}"))]
368 SelfReferentialSubmodule {
369 name: crate::ast::Ident,
370 parent_module_uri: miden_debug_types::Uri,
371 #[label(
372 "module source resolution rules require this declaration to resolve to the current source file"
373 )]
374 span: SourceSpan,
375 #[source_code]
376 source_file: Option<Arc<miden_debug_types::SourceFile>>,
377 },
378 #[error(
379 "conflicting submodule paths detected: '{name}' can be parsed from either '{first}' and '{second}', but not both"
380 )]
381 AmbiguousSubmoduleLocation {
382 name: crate::ast::Ident,
383 first: miden_debug_types::Uri,
384 second: miden_debug_types::Uri,
385 #[label]
386 span: SourceSpan,
387 #[source_code]
388 source_file: Option<Arc<miden_debug_types::SourceFile>>,
389 },
390 #[error(
391 "invalid submodule declaration '{name}': module source '{module_uri}' is already reachable through another submodule declaration"
392 )]
393 #[diagnostic(help("each module source file can only be owned by one module in a module tree"))]
394 DuplicateSubmoduleSource {
395 name: crate::ast::Ident,
396 module_uri: miden_debug_types::Uri,
397 #[label("this declaration resolves to an already visited module source")]
398 span: SourceSpan,
399 #[source_code]
400 source_file: Option<Arc<miden_debug_types::SourceFile>>,
401 },
402}
403
404impl ParsingError {
405 fn tag(&self) -> u8 {
406 unsafe { *<*const _>::from(self).cast::<u8>() }
413 }
414}
415
416impl Eq for ParsingError {}
417
418impl PartialEq for ParsingError {
419 fn eq(&self, other: &Self) -> bool {
420 match (self, other) {
421 (Self::Failed, Self::Failed) => true,
422 (Self::InvalidSyntax { message: l, .. }, Self::InvalidSyntax { message: r, .. }) => {
423 l == r
424 },
425 (Self::InvalidLiteral { kind: l, .. }, Self::InvalidLiteral { kind: r, .. }) => l == r,
426 (Self::InvalidHexLiteral { kind: l, .. }, Self::InvalidHexLiteral { kind: r, .. }) => {
427 l == r
428 },
429 (
430 Self::InvalidLibraryPath { message: l, .. },
431 Self::InvalidLibraryPath { message: r, .. },
432 ) => l == r,
433 (
434 Self::ImmediateOutOfRange { range: l, .. },
435 Self::ImmediateOutOfRange { range: r, .. },
436 ) => l == r,
437 (Self::PushOverflow { count: l, .. }, Self::PushOverflow { count: r, .. }) => l == r,
438 (
439 Self::UnrecognizedToken { token: ltok, expected: lexpect, .. },
440 Self::UnrecognizedToken { token: rtok, expected: rexpect, .. },
441 ) => ltok == rtok && lexpect == rexpect,
442 (Self::ExtraToken { token: ltok, .. }, Self::ExtraToken { token: rtok, .. }) => {
443 ltok == rtok
444 },
445 (
446 Self::UnrecognizedEof { expected: lexpect, .. },
447 Self::UnrecognizedEof { expected: rexpect, .. },
448 ) => lexpect == rexpect,
449 (x, y) => x.tag() == y.tag(),
450 }
451 }
452}