miden_assembly_syntax/sema/
errors.rs

1use alloc::{sync::Arc, vec::Vec};
2use core::fmt;
3
4use miden_debug_types::{SourceFile, SourceSpan};
5use miden_utils_diagnostics::{Diagnostic, miette};
6
7/// The high-level error type for all semantic analysis errors.
8///
9/// This rolls up multiple errors into a single one, and as such, can emit many
10/// diagnostics at once. You should prefer to gather up errors and continue with
11/// analysis for as long as possible before returning an error of this type, so
12/// as to provide as much information to the user as possible, but there are cases
13/// where doing so would not be profitable - it is a judgement call.
14///
15/// The semantic analyzer does this though, and you can examine its implementation
16/// to see how we approach this during the semantic analysis phase specifically.
17#[derive(Debug, thiserror::Error, Diagnostic)]
18#[error("syntax error")]
19#[diagnostic(help("see emitted diagnostics for details"))]
20pub struct SyntaxError {
21    #[source_code]
22    pub source_file: Arc<SourceFile>,
23    #[related]
24    pub errors: Vec<SemanticAnalysisError>,
25}
26
27/// This type is used when emitting advice/warnings, when those are not being treated as errors.
28///
29/// Like [SyntaxError], this rolls up all such notices into a single batch, and emits them all
30/// at once. The difference is that we never return this as an error from any API, it simply
31/// exists to leverage the diagnostic infrastructure of `miette`.
32#[derive(Debug, thiserror::Error, Diagnostic)]
33#[error("one or more warnings were emitted")]
34#[diagnostic(help("see below for details"))]
35pub struct SyntaxWarning {
36    #[source_code]
37    pub source_file: Arc<SourceFile>,
38    #[related]
39    pub errors: Vec<SemanticAnalysisError>,
40}
41
42/// Represents an error that occurs during semantic analysis
43#[derive(Debug, thiserror::Error, Diagnostic)]
44pub enum SemanticAnalysisError {
45    #[error("invalid program: no entrypoint defined")]
46    #[diagnostic(help(
47        "ensure you define an entrypoint somewhere in the body with `begin`..`end`"
48    ))]
49    MissingEntrypoint,
50    #[error("invalid module: unexpected entrypoint definition")]
51    #[diagnostic(help("library modules cannot contain `begin`..`end` blocks"))]
52    UnexpectedEntrypoint {
53        #[label]
54        span: SourceSpan,
55    },
56    #[error("invalid module: multiple conflicting entrypoints defined")]
57    #[diagnostic(help("an executable module can only have a single `begin`..`end` block"))]
58    MultipleEntrypoints {
59        #[label]
60        span: SourceSpan,
61        #[label]
62        prev_span: SourceSpan,
63    },
64    #[error("invalid program: procedure exports are not allowed")]
65    #[diagnostic(help("perhaps you meant to use `proc` instead of `export`?"))]
66    UnexpectedExport {
67        #[label]
68        span: SourceSpan,
69    },
70    #[error("invalid enum type representation: underlying type must be an integral type")]
71    #[diagnostic()]
72    InvalidEnumRepr {
73        #[label]
74        span: SourceSpan,
75    },
76    #[error("symbol conflict: found duplicate definitions of the same name")]
77    #[diagnostic()]
78    SymbolConflict {
79        #[label("conflict occurs here")]
80        span: SourceSpan,
81        #[label("previously defined here")]
82        prev_span: SourceSpan,
83    },
84    #[error("symbol undefined: no such name found in scope")]
85    #[diagnostic(help("are you missing an import?"))]
86    SymbolUndefined {
87        #[label]
88        span: SourceSpan,
89    },
90    #[error("unused import")]
91    #[diagnostic(severity(Warning), help("this import is never used and can be safely removed"))]
92    UnusedImport {
93        #[label]
94        span: SourceSpan,
95    },
96    #[error("missing import: the referenced module has not been imported")]
97    #[diagnostic()]
98    MissingImport {
99        #[label("this reference is invalid without a corresponding import")]
100        span: SourceSpan,
101    },
102    #[error("symbol conflict: import would shadow a previous import of the same name")]
103    #[diagnostic(help(
104        "imports must have unique names within a module, \
105        try aliasing one of the imports if both are needed"
106    ))]
107    ImportConflict {
108        #[label("caused by this import")]
109        span: SourceSpan,
110        #[label("previously imported here")]
111        prev_span: SourceSpan,
112    },
113    #[error(
114        "invalid re-exported procedure: kernel modules may not re-export procedures from other modules"
115    )]
116    #[diagnostic()]
117    ReexportFromKernel {
118        #[label]
119        span: SourceSpan,
120    },
121    #[error("invalid syscall: cannot make a syscall from within the kernel")]
122    #[diagnostic(help("syscalls are only valid outside the kernel, you should use exec instead"))]
123    SyscallInKernel {
124        #[label]
125        span: SourceSpan,
126    },
127    #[error("invalid call: kernel modules cannot make calls to external procedures")]
128    #[diagnostic(help(
129        "this call is being made from a kernel module, and may only refer to local procedures"
130    ))]
131    CallInKernel {
132        #[label]
133        span: SourceSpan,
134    },
135    #[error("invalid instruction usage: 'caller' is only valid in kernel modules")]
136    #[diagnostic()]
137    CallerInKernel {
138        #[label]
139        span: SourceSpan,
140    },
141    #[error("invalid syscall: callee must be resolvable to kernel module")]
142    #[diagnostic()]
143    InvalidSyscallTarget {
144        #[label]
145        span: SourceSpan,
146    },
147    #[error("invalid recursive procedure call")]
148    #[diagnostic(help(
149        "this call induces a cycle that returns back to the caller, you must break that cycle"
150    ))]
151    InvalidRecursiveCall {
152        #[label("caused by this call")]
153        span: SourceSpan,
154    },
155    #[error("invalid recursive procedure call")]
156    #[diagnostic(help("this call is self-recursive, which is not allowed"))]
157    SelfRecursive {
158        #[label]
159        span: SourceSpan,
160    },
161    #[error("invalid immediate: value is larger than expected range")]
162    #[diagnostic()]
163    ImmediateOverflow {
164        #[label]
165        span: SourceSpan,
166    },
167    #[error("invalid module: {}", kind)]
168    #[diagnostic(help("try breaking this module up into submodules"))]
169    LimitExceeded {
170        #[label]
171        span: SourceSpan,
172        kind: LimitKind,
173    },
174    #[error("unused docstring")]
175    #[diagnostic(
176        severity(Warning),
177        help(
178            "this docstring is immediately followed by at least one empty line, then another docstring,\
179            if you intended these to be a single docstring, you should remove the empty lines"
180        )
181    )]
182    UnusedDocstring {
183        #[label]
184        span: SourceSpan,
185    },
186    #[error("unused docstring")]
187    #[diagnostic(
188        severity(Warning),
189        help(
190            "module imports cannot have docstrings, you should use line comment syntax here instead"
191        )
192    )]
193    ImportDocstring {
194        #[label]
195        span: SourceSpan,
196    },
197    #[error("invalid constant")]
198    #[diagnostic(help("this constant does not resolve to a value of the right type"))]
199    InvalidConstant {
200        #[label]
201        span: SourceSpan,
202    },
203    #[error("advmap key already defined")]
204    AdvMapKeyAlreadyDefined {
205        #[label]
206        span: SourceSpan,
207    },
208}
209
210/// Represents a system limit that was exceeded
211#[derive(Debug, Copy, Clone, PartialEq, Eq)]
212pub enum LimitKind {
213    /// The total number of procedures in a module
214    Procedures,
215    /// The total number of procedure locals
216    Locals,
217    /// The total number of imports in a module
218    Imports,
219    /// The total number of calls to imports
220    ///
221    /// TODO(pauls): Is this even a limit anymore?
222    CalledImports,
223    /// The total number of instructions in a procedure.
224    Instructions,
225}
226
227impl fmt::Display for LimitKind {
228    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
229        match self {
230            Self::Procedures => f.write_str("too many procedures in module"),
231            Self::Locals => f.write_str("too many procedure locals"),
232            Self::Imports => f.write_str("too many imported procedures"),
233            Self::CalledImports => f.write_str("too many calls to imported procedures"),
234            Self::Instructions => f.write_str("too many instructions in block"),
235        }
236    }
237}