1#![allow(unused_assignments)]
3
4use alloc::{string::String, sync::Arc, vec::Vec};
5use core::{fmt, ops::Range};
6
7use miden_debug_types::{SourceId, 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("invalid constant expression: division by zero")]
174 DivisionByZero {
175 #[label]
176 span: SourceSpan,
177 },
178 #[error("doc comment is too large")]
179 #[diagnostic(help("make sure it is less than u16::MAX bytes in length"))]
180 DocsTooLarge {
181 #[label]
182 span: SourceSpan,
183 },
184 #[error("invalid literal: {}", kind)]
185 #[diagnostic()]
186 InvalidLiteral {
187 #[label]
188 span: SourceSpan,
189 kind: LiteralErrorKind,
190 },
191 #[error("invalid literal: {}", kind)]
192 #[diagnostic()]
193 InvalidHexLiteral {
194 #[label]
195 span: SourceSpan,
196 kind: HexErrorKind,
197 },
198 #[error("invalid literal: {}", kind)]
199 #[diagnostic()]
200 InvalidBinaryLiteral {
201 #[label]
202 span: SourceSpan,
203 kind: BinErrorKind,
204 },
205 #[error("invalid MAST root literal")]
206 InvalidMastRoot {
207 #[label]
208 span: SourceSpan,
209 },
210 #[error("invalid library path: {}", message)]
211 InvalidLibraryPath {
212 #[label]
213 span: SourceSpan,
214 message: String,
215 },
216 #[error("invalid immediate: value must be in the range {}..{} (exclusive)", range.start, range.end)]
217 ImmediateOutOfRange {
218 #[label]
219 span: SourceSpan,
220 range: Range<usize>,
221 },
222 #[error("too many procedures in this module")]
223 #[diagnostic()]
224 ModuleTooLarge {
225 #[label]
226 span: SourceSpan,
227 },
228 #[error("too many re-exported procedures in this module")]
229 #[diagnostic()]
230 ModuleTooManyReexports {
231 #[label]
232 span: SourceSpan,
233 },
234 #[error(
235 "too many operands for `push`: tried to push {} elements, but only 16 can be pushed at one time",
236 count
237 )]
238 #[diagnostic()]
239 PushOverflow {
240 #[label]
241 span: SourceSpan,
242 count: usize,
243 },
244 #[error("expected a fully-qualified module path, e.g. `std::u64`")]
245 UnqualifiedImport {
246 #[label]
247 span: SourceSpan,
248 },
249 #[error(
250 "source-level digest re-exports are not supported; re-export a named item with `pub use {{item}} from module`"
251 )]
252 UnnamedReexportOfMastRoot {
253 #[label]
254 span: SourceSpan,
255 },
256 #[error("conflicting attributes for procedure definition")]
257 #[diagnostic()]
258 AttributeConflict {
259 #[label(
260 "conflict occurs because an attribute with the same name has already been defined"
261 )]
262 span: SourceSpan,
263 #[label("previously defined here")]
264 prev: SourceSpan,
265 },
266 #[error("conflicting key-value attributes for procedure definition")]
267 #[diagnostic()]
268 AttributeKeyValueConflict {
269 #[label(
270 "conflict occurs because a key with the same name has already been set in a previous declaration"
271 )]
272 span: SourceSpan,
273 #[label("previously defined here")]
274 prev: SourceSpan,
275 },
276 #[error("invalid Advice Map key")]
277 #[diagnostic()]
278 InvalidAdvMapKey {
279 #[label(
280 "an Advice Map key must be a word, either in 64-character hex format or in array-like format `[f0,f1,f2,f3]`"
281 )]
282 span: SourceSpan,
283 },
284 #[error("invalid slice constant")]
285 #[diagnostic()]
286 InvalidSliceConstant {
287 #[label("slices are only supported over word-sized constants")]
288 span: SourceSpan,
289 },
290 #[error("invalid slice: expected valid range")]
291 #[diagnostic()]
292 InvalidRange {
293 #[label("range used for the word constant slice is malformed: `{range:?}`")]
294 span: SourceSpan,
295 range: Range<usize>,
296 },
297 #[error("invalid slice: expected non-empty range")]
298 #[diagnostic()]
299 EmptySlice {
300 #[label("range used for the word constant slice is empty: `{range:?}`")]
301 span: SourceSpan,
302 range: Range<usize>,
303 },
304 #[error("unrecognized calling convention")]
305 #[diagnostic(help("expected one of: 'fast', 'C', 'wasm', 'canon-lift', or 'canon-lower'"))]
306 UnrecognizedCallConv {
307 #[label]
308 span: SourceSpan,
309 },
310 #[error("invalid struct annotation")]
311 #[diagnostic(help(
312 "expected one of: '@packed', '@packed(N)', '@transparent', '@bigendian', or '@align(N)'"
313 ))]
314 InvalidStructAnnotation {
315 #[label]
316 span: SourceSpan,
317 },
318 #[error("invalid struct representation")]
319 #[diagnostic()]
320 InvalidStructRepr {
321 #[label("{message}")]
322 span: SourceSpan,
323 message: String,
324 },
325 #[error("deprecated instruction: `{instruction}` has been removed")]
326 #[diagnostic(help("use `{}` instead", replacement))]
327 DeprecatedInstruction {
328 #[label("this instruction is no longer supported")]
329 span: SourceSpan,
330 instruction: String,
331 replacement: String,
332 },
333 #[error("invalid procedure @locals attribute")]
334 #[diagnostic()]
335 InvalidLocalsAttr {
336 #[label("{message}")]
337 span: SourceSpan,
338 message: String,
339 },
340 #[error("invalid padding value for the `adv.push_mapvaln` instruction: {padding}")]
341 #[diagnostic(help("valid padding values are 0, 4, and 8"))]
342 InvalidPadValue {
343 #[label]
344 span: SourceSpan,
345 padding: u8,
346 },
347 #[error(
348 "invalid submodule declaration '{name}': could not find module sources at '{directory}/{basename}.masm' or '{directory}/{basename}/mod.masm'"
349 )]
350 UndefinedSubmodule {
351 name: crate::ast::Ident,
352 basename: alloc::boxed::Box<str>,
353 directory: miden_debug_types::Uri,
354 #[label]
355 span: SourceSpan,
356 #[source_code]
357 source_file: Option<Arc<miden_debug_types::SourceFile>>,
358 },
359 #[error(
360 "invalid submodule declaration '{name}': submodules must not have the same name as their parent"
361 )]
362 #[diagnostic(help("occurred while parsing {parent_module_uri}"))]
363 SelfReferentialSubmodule {
364 name: crate::ast::Ident,
365 parent_module_uri: miden_debug_types::Uri,
366 #[label(
367 "module source resolution rules require this declaration to resolve to the current source file"
368 )]
369 span: SourceSpan,
370 #[source_code]
371 source_file: Option<Arc<miden_debug_types::SourceFile>>,
372 },
373 #[error(
374 "conflicting submodule paths detected: '{name}' can be parsed from either '{first}' and '{second}', but not both"
375 )]
376 AmbiguousSubmoduleLocation {
377 name: crate::ast::Ident,
378 first: miden_debug_types::Uri,
379 second: miden_debug_types::Uri,
380 #[label]
381 span: SourceSpan,
382 #[source_code]
383 source_file: Option<Arc<miden_debug_types::SourceFile>>,
384 },
385 #[error(
386 "invalid submodule declaration '{name}': module source '{module_uri}' is already reachable through another submodule declaration"
387 )]
388 #[diagnostic(help("each module source file can only be owned by one module in a module tree"))]
389 DuplicateSubmoduleSource {
390 name: crate::ast::Ident,
391 module_uri: miden_debug_types::Uri,
392 #[label("this declaration resolves to an already visited module source")]
393 span: SourceSpan,
394 #[source_code]
395 source_file: Option<Arc<miden_debug_types::SourceFile>>,
396 },
397}
398
399impl ParsingError {
400 fn tag(&self) -> u8 {
401 unsafe { *<*const _>::from(self).cast::<u8>() }
408 }
409}
410
411impl Eq for ParsingError {}
412
413impl PartialEq for ParsingError {
414 fn eq(&self, other: &Self) -> bool {
415 match (self, other) {
416 (Self::Failed, Self::Failed) => true,
417 (Self::InvalidSyntax { message: l, .. }, Self::InvalidSyntax { message: r, .. }) => {
418 l == r
419 },
420 (Self::InvalidLiteral { kind: l, .. }, Self::InvalidLiteral { kind: r, .. }) => l == r,
421 (Self::InvalidHexLiteral { kind: l, .. }, Self::InvalidHexLiteral { kind: r, .. }) => {
422 l == r
423 },
424 (
425 Self::InvalidLibraryPath { message: l, .. },
426 Self::InvalidLibraryPath { message: r, .. },
427 ) => l == r,
428 (
429 Self::ImmediateOutOfRange { range: l, .. },
430 Self::ImmediateOutOfRange { range: r, .. },
431 ) => l == r,
432 (Self::PushOverflow { count: l, .. }, Self::PushOverflow { count: r, .. }) => l == r,
433 (
434 Self::UnrecognizedToken { token: ltok, expected: lexpect, .. },
435 Self::UnrecognizedToken { token: rtok, expected: rexpect, .. },
436 ) => ltok == rtok && lexpect == rexpect,
437 (Self::ExtraToken { token: ltok, .. }, Self::ExtraToken { token: rtok, .. }) => {
438 ltok == rtok
439 },
440 (
441 Self::UnrecognizedEof { expected: lexpect, .. },
442 Self::UnrecognizedEof { expected: rexpect, .. },
443 ) => lexpect == rexpect,
444 (x, y) => x.tag() == y.tag(),
445 }
446 }
447}
448
449impl ParsingError {
450 pub fn from_utf8_error(source_id: SourceId, err: core::str::Utf8Error) -> Self {
451 let start = u32::try_from(err.valid_up_to()).ok().unwrap_or(u32::MAX);
452 match err.error_len() {
453 None => Self::IncompleteUtf8 { span: SourceSpan::at(source_id, start) },
454 Some(len) => Self::InvalidUtf8 {
455 span: SourceSpan::new(source_id, start..(start + len as u32)),
456 },
457 }
458 }
459}