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("this attribute conflicts with another attribute")]
267 span: SourceSpan,
268 #[label("conflicting attribute here")]
269 prev: SourceSpan,
270 },
271 #[error("conflicting key-value attributes for procedure definition")]
272 #[diagnostic()]
273 AttributeKeyValueConflict {
274 #[label(
275 "conflict occurs because a key with the same name has already been set in a previous declaration"
276 )]
277 span: SourceSpan,
278 #[label("previously defined here")]
279 prev: SourceSpan,
280 },
281 #[error("invalid Advice Map key")]
282 #[diagnostic()]
283 InvalidAdvMapKey {
284 #[label(
285 "an Advice Map key must be a word, either in 64-character hex format or in array-like format `[f0,f1,f2,f3]`"
286 )]
287 span: SourceSpan,
288 },
289 #[error("invalid slice constant")]
290 #[diagnostic()]
291 InvalidSliceConstant {
292 #[label("slices are only supported over word-sized constants")]
293 span: SourceSpan,
294 },
295 #[error("invalid slice: expected valid range")]
296 #[diagnostic()]
297 InvalidRange {
298 #[label("range used for the word constant slice is malformed: `{range:?}`")]
299 span: SourceSpan,
300 range: Range<usize>,
301 },
302 #[error("invalid slice: expected non-empty range")]
303 #[diagnostic()]
304 EmptySlice {
305 #[label("range used for the word constant slice is empty: `{range:?}`")]
306 span: SourceSpan,
307 range: Range<usize>,
308 },
309 #[error("unrecognized calling convention")]
310 #[diagnostic(help("expected one of: 'fast', 'C', 'wasm', 'canon-lift', or 'canon-lower'"))]
311 UnrecognizedCallConv {
312 #[label]
313 span: SourceSpan,
314 },
315 #[error("invalid struct annotation")]
316 #[diagnostic(help("expected one of: '@packed', '@packed(N)', '@transparent', or '@align(N)'"))]
317 InvalidStructAnnotation {
318 #[label]
319 span: SourceSpan,
320 },
321 #[error("invalid struct representation")]
322 #[diagnostic()]
323 InvalidStructRepr {
324 #[label("{message}")]
325 span: SourceSpan,
326 message: String,
327 },
328 #[error("deprecated instruction: `{instruction}` has been removed")]
329 #[diagnostic(help("use `{}` instead", replacement))]
330 DeprecatedInstruction {
331 #[label("this instruction is no longer supported")]
332 span: SourceSpan,
333 instruction: String,
334 replacement: String,
335 },
336 #[error("invalid procedure @locals attribute")]
337 #[diagnostic()]
338 InvalidLocalsAttr {
339 #[label("{message}")]
340 span: SourceSpan,
341 message: String,
342 },
343 #[error("invalid padding value for the `adv.push_mapvaln` instruction: {padding}")]
344 #[diagnostic(help("valid padding values are 0, 4, and 8"))]
345 InvalidPadValue {
346 #[label]
347 span: SourceSpan,
348 padding: u8,
349 },
350 #[error(
351 "invalid submodule declaration '{name}': could not find module sources at '{directory}/{basename}.masm' or '{directory}/{basename}/mod.masm'"
352 )]
353 UndefinedSubmodule {
354 name: crate::ast::Ident,
355 basename: alloc::boxed::Box<str>,
356 directory: miden_debug_types::Uri,
357 #[label]
358 span: SourceSpan,
359 #[source_code]
360 source_file: Option<Arc<miden_debug_types::SourceFile>>,
361 },
362 #[error(
363 "invalid submodule declaration '{name}': submodules must not have the same name as their parent"
364 )]
365 #[diagnostic(help("occurred while parsing {parent_module_uri}"))]
366 SelfReferentialSubmodule {
367 name: crate::ast::Ident,
368 parent_module_uri: miden_debug_types::Uri,
369 #[label(
370 "module source resolution rules require this declaration to resolve to the current source file"
371 )]
372 span: SourceSpan,
373 #[source_code]
374 source_file: Option<Arc<miden_debug_types::SourceFile>>,
375 },
376 #[error(
377 "conflicting submodule paths detected: '{name}' can be parsed from either '{first}' and '{second}', but not both"
378 )]
379 AmbiguousSubmoduleLocation {
380 name: crate::ast::Ident,
381 first: miden_debug_types::Uri,
382 second: miden_debug_types::Uri,
383 #[label]
384 span: SourceSpan,
385 #[source_code]
386 source_file: Option<Arc<miden_debug_types::SourceFile>>,
387 },
388 #[error(
389 "invalid submodule declaration '{name}': module source '{module_uri}' is already reachable through another submodule declaration"
390 )]
391 #[diagnostic(help("each module source file can only be owned by one module in a module tree"))]
392 DuplicateSubmoduleSource {
393 name: crate::ast::Ident,
394 module_uri: miden_debug_types::Uri,
395 #[label("this declaration resolves to an already visited module source")]
396 span: SourceSpan,
397 #[source_code]
398 source_file: Option<Arc<miden_debug_types::SourceFile>>,
399 },
400}
401
402impl ParsingError {
403 fn tag(&self) -> u8 {
404 unsafe { *<*const _>::from(self).cast::<u8>() }
411 }
412}
413
414impl Eq for ParsingError {}
415
416impl PartialEq for ParsingError {
417 fn eq(&self, other: &Self) -> bool {
418 match (self, other) {
419 (Self::Failed, Self::Failed) => true,
420 (Self::InvalidSyntax { message: l, .. }, Self::InvalidSyntax { message: r, .. }) => {
421 l == r
422 },
423 (Self::InvalidLiteral { kind: l, .. }, Self::InvalidLiteral { kind: r, .. }) => l == r,
424 (Self::InvalidHexLiteral { kind: l, .. }, Self::InvalidHexLiteral { kind: r, .. }) => {
425 l == r
426 },
427 (
428 Self::InvalidLibraryPath { message: l, .. },
429 Self::InvalidLibraryPath { message: r, .. },
430 ) => l == r,
431 (
432 Self::ImmediateOutOfRange { range: l, .. },
433 Self::ImmediateOutOfRange { range: r, .. },
434 ) => l == r,
435 (Self::PushOverflow { count: l, .. }, Self::PushOverflow { count: r, .. }) => l == r,
436 (
437 Self::UnrecognizedToken { token: ltok, expected: lexpect, .. },
438 Self::UnrecognizedToken { token: rtok, expected: rexpect, .. },
439 ) => ltok == rtok && lexpect == rexpect,
440 (Self::ExtraToken { token: ltok, .. }, Self::ExtraToken { token: rtok, .. }) => {
441 ltok == rtok
442 },
443 (
444 Self::UnrecognizedEof { expected: lexpect, .. },
445 Self::UnrecognizedEof { expected: rexpect, .. },
446 ) => lexpect == rexpect,
447 (x, y) => x.tag() == y.tag(),
448 }
449 }
450}