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("symbol conflict: found duplicate definitions of the same name")]
71    #[diagnostic()]
72    SymbolConflict {
73        #[label("conflict occurs here")]
74        span: SourceSpan,
75        #[label("previously defined here")]
76        prev_span: SourceSpan,
77    },
78    #[error("symbol undefined: no such name found in scope")]
79    #[diagnostic(help("are you missing an import?"))]
80    SymbolUndefined {
81        #[label]
82        span: SourceSpan,
83    },
84    #[error("unused import")]
85    #[diagnostic(severity(Warning), help("this import is never used and can be safely removed"))]
86    UnusedImport {
87        #[label]
88        span: SourceSpan,
89    },
90    #[error("missing import: the referenced module has not been imported")]
91    #[diagnostic()]
92    MissingImport {
93        #[label("this reference is invalid without a corresponding import")]
94        span: SourceSpan,
95    },
96    #[error("symbol conflict: import would shadow a previous import of the same name")]
97    #[diagnostic(help(
98        "imports must have unique names within a module, \
99        try aliasing one of the imports if both are needed"
100    ))]
101    ImportConflict {
102        #[label("caused by this import")]
103        span: SourceSpan,
104        #[label("previously imported here")]
105        prev_span: SourceSpan,
106    },
107    #[error(
108        "invalid re-exported procedure: kernel modules may not re-export procedures from other modules"
109    )]
110    #[diagnostic()]
111    ReexportFromKernel {
112        #[label]
113        span: SourceSpan,
114    },
115    #[error("invalid syscall: cannot make a syscall from within the kernel")]
116    #[diagnostic(help("syscalls are only valid outside the kernel, you should use exec instead"))]
117    SyscallInKernel {
118        #[label]
119        span: SourceSpan,
120    },
121    #[error("invalid call: kernel modules cannot make calls to external procedures")]
122    #[diagnostic(help(
123        "this call is being made from a kernel module, and may only refer to local procedures"
124    ))]
125    CallInKernel {
126        #[label]
127        span: SourceSpan,
128    },
129    #[error("invalid instruction usage: 'caller' is only valid in kernel modules")]
130    #[diagnostic()]
131    CallerInKernel {
132        #[label]
133        span: SourceSpan,
134    },
135    #[error("invalid syscall: callee must be resolvable to kernel module")]
136    #[diagnostic()]
137    InvalidSyscallTarget {
138        #[label]
139        span: SourceSpan,
140    },
141    #[error("invalid recursive procedure call")]
142    #[diagnostic(help(
143        "this call induces a cycle that returns back to the caller, you must break that cycle"
144    ))]
145    InvalidRecursiveCall {
146        #[label("caused by this call")]
147        span: SourceSpan,
148    },
149    #[error("invalid recursive procedure call")]
150    #[diagnostic(help("this call is self-recursive, which is not allowed"))]
151    SelfRecursive {
152        #[label]
153        span: SourceSpan,
154    },
155    #[error("invalid immediate: value is larger than expected range")]
156    #[diagnostic()]
157    ImmediateOverflow {
158        #[label]
159        span: SourceSpan,
160    },
161    #[error("invalid module: {}", kind)]
162    #[diagnostic(help("try breaking this module up into submodules"))]
163    LimitExceeded {
164        #[label]
165        span: SourceSpan,
166        kind: LimitKind,
167    },
168    #[error("unused docstring")]
169    #[diagnostic(
170        severity(Warning),
171        help(
172            "this docstring is immediately followed by at least one empty line, then another docstring,\
173            if you intended these to be a single docstring, you should remove the empty lines"
174        )
175    )]
176    UnusedDocstring {
177        #[label]
178        span: SourceSpan,
179    },
180    #[error("unused docstring")]
181    #[diagnostic(
182        severity(Warning),
183        help(
184            "module imports cannot have docstrings, you should use line comment syntax here instead"
185        )
186    )]
187    ImportDocstring {
188        #[label]
189        span: SourceSpan,
190    },
191    #[error("invalid constant")]
192    #[diagnostic(help("this constant does not resolve to a value of the right type"))]
193    InvalidConstant {
194        #[label]
195        span: SourceSpan,
196    },
197    #[error("advmap key already defined")]
198    AdvMapKeyAlreadyDefined {
199        #[label]
200        span: SourceSpan,
201    },
202}
203
204/// Represents a system limit that was exceeded
205#[derive(Debug, Copy, Clone, PartialEq, Eq)]
206pub enum LimitKind {
207    /// The total number of procedures in a module
208    Procedures,
209    /// The total number of procedure locals
210    Locals,
211    /// The total number of imports in a module
212    Imports,
213    /// The total number of calls to imports
214    ///
215    /// TODO(pauls): Is this even a limit anymore?
216    CalledImports,
217    /// The total number of instructions in a procedure.
218    Instructions,
219}
220
221impl fmt::Display for LimitKind {
222    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
223        match self {
224            Self::Procedures => f.write_str("too many procedures in module"),
225            Self::Locals => f.write_str("too many procedure locals"),
226            Self::Imports => f.write_str("too many imported procedures"),
227            Self::CalledImports => f.write_str("too many calls to imported procedures"),
228            Self::Instructions => f.write_str("too many instructions in block"),
229        }
230    }
231}