microcad_lang/resolve/
resolve_error.rs1#![allow(unused_assignments)]
5use miette::{Diagnostic, SourceSpan};
8use thiserror::Error;
9
10use crate::resolve::grant::Scope;
11use crate::{parse::*, syntax::*};
12use microcad_lang_base::{DiagError, SrcRef, SrcReferrer};
13
14fn capitalize_first(s: &str) -> String {
15 let mut c = s.chars();
16 match c.next() {
17 None => String::new(),
18 Some(first) => first.to_uppercase().collect::<String>() + c.as_str(),
19 }
20}
21
22#[derive(Debug, Error, Diagnostic)]
24pub enum ResolveError {
25 #[error("Parse Error: {0}")]
27 #[diagnostic(transparent)]
28 ParseError(#[from] ParseErrorsWithSource),
29
30 #[error("Could not find a file with hash {0}")]
32 UnknownHash(u64),
33
34 #[error("Hash is zero")]
36 NulHash,
37
38 #[error("External symbol `{0}` not found")]
40 ExternalSymbolNotFound(QualifiedName),
41
42 #[error("External path `{0}` not found")]
44 ExternalPathNotFound(std::path::PathBuf),
45
46 #[error("Could not find a file with path {0}")]
48 FileNotFound(std::path::PathBuf),
49
50 #[error("Symbol {0} not found while resolving.")]
52 SymbolNotFound(QualifiedName),
53
54 #[error("Symbol {0} must be loaded from {1}")]
56 SymbolMustBeLoaded(QualifiedName, std::path::PathBuf),
57
58 #[error("Symbol {0} is not a value")]
60 NotAValue(QualifiedName),
61
62 #[error("Ambiguous external module files found {0:?}")]
64 AmbiguousExternals(Vec<std::path::PathBuf>),
65
66 #[error("Symbol {0} already defined")]
68 SymbolAlreadyDefined(QualifiedName),
69
70 #[error("Ambiguous symbol found: {0}")]
72 AmbiguousSymbol(QualifiedName, QualifiedNames),
73
74 #[error("Ambiguous identifier '{ambiguous}'")]
76 #[allow(missing_docs)]
77 AmbiguousId {
78 #[label(primary, "First usage of '{first}'")]
79 first: Identifier,
80 #[label("Ambiguous usage of '{ambiguous}'")]
81 ambiguous: Identifier,
82 },
83
84 #[error("{0}")]
86 ScanDirError(#[from] scan_dir::Error),
87
88 #[error("Invalid path: {0:?}")]
90 InvalidPath(std::path::PathBuf),
91
92 #[error("Diagnostic error: {0}")]
94 DiagError(#[from] DiagError),
95
96 #[error(transparent)]
98 #[diagnostic(transparent)]
99 StatementNotSupported(#[from] StatementNotSupportedError),
100
101 #[error("Resolve failed: {0}")]
103 ResolveCheckFailed(SrcRef),
104
105 #[error("Symbol {0} is private")]
107 SymbolIsPrivate(QualifiedName),
108
109 #[error("{0}")]
111 IoError(#[from] std::io::Error),
112
113 #[error(
115 "Source of module '{0}' could not be found in {1:?} (expecting a file '{0}.µcad' or '{0}/mod.µcad')"
116 )]
117 SourceFileNotFound(#[label("module not found")] Identifier, std::path::PathBuf),
118
119 #[error("Wrong lookup target")]
121 WrongTarget,
122
123 #[error("Statement not allowed within workbenches")]
125 IllegalWorkbenchStatement,
126
127 #[error("Code between initializers is not allowed")]
129 #[allow(missing_docs)]
130 CodeBetweenInitializers {
131 #[label("Between these initializers")]
132 initializers: SrcRef,
133 #[label(primary, "This statement is not allowed")]
134 statement: SrcRef,
135 #[label("Inside this {scope}")]
136 workbench: SrcRef,
137 scope: &'static str,
138 },
139
140 #[error("Statement not allowed prior initializers")]
142 #[allow(missing_docs)]
143 StatementNotAllowedPriorInitializers {
144 #[label("Before this initializer")]
145 initializer: SrcRef,
146 #[label(primary, "This statement is not allowed")]
147 statement: SrcRef,
148 #[label("Inside this {scope}")]
149 workbench: SrcRef,
150 scope: &'static str,
151 },
152}
153
154#[derive(Debug, Error, Diagnostic)]
156#[error("{} is not allowed {} {}", capitalize_first(inner), self.placement(), self.outer())]
157#[diagnostic(help("{inner} is only allowed within {}", self.allowed_parents()))]
158pub struct StatementNotSupportedError {
159 inner: &'static str,
160 #[label(primary, "This {inner} is not allowed{}", self.maybe_here())]
161 inner_span: SrcRef,
162 outer: &'static str,
163 #[label("Within this {outer}")]
164 outer_span: Option<SourceSpan>,
165 allowed_parents: Vec<&'static str>,
166}
167
168impl StatementNotSupportedError {
169 pub(super) fn new(node: &Scope, parent: &Scope) -> Self {
171 StatementNotSupportedError {
172 inner: node.to_str(),
173 inner_span: node.src_ref(),
174 outer: parent.to_str(),
175 outer_span: parent.src_ref().as_miette_span(),
176 allowed_parents: node
177 .ty()
178 .allowed_parents()
179 .iter()
180 .map(|scope| scope.to_str())
181 .collect(),
182 }
183 }
184
185 fn allowed_parents(&self) -> impl std::fmt::Display {
186 struct AllowedParents(Vec<&'static str>);
187
188 impl std::fmt::Display for AllowedParents {
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 let mut items = self.0.iter();
191 if let Some(first) = items.next() {
192 write!(f, "{first}")?;
193 }
194 let last = items.next_back();
195 for item in items {
196 write!(f, ", {item}")?;
197 }
198 if let Some(last) = last {
199 write!(f, " or {last}")?;
200 }
201 Ok(())
202 }
203 }
204
205 AllowedParents(self.allowed_parents.clone())
206 }
207
208 fn parent_is_root(&self) -> bool {
209 self.outer == "source file"
210 }
211
212 fn placement(&self) -> &'static str {
213 if self.parent_is_root() {
214 "at"
215 } else {
216 "within"
217 }
218 }
219
220 fn outer(&self) -> &'static str {
221 if self.parent_is_root() {
222 "source root"
223 } else {
224 self.outer
225 }
226 }
227
228 fn maybe_here(&self) -> &'static str {
229 if self.parent_is_root() { " here" } else { "" }
230 }
231}
232
233impl SrcReferrer for ResolveError {
234 fn src_ref(&self) -> SrcRef {
235 match self {
236 ResolveError::SourceFileNotFound(identifier, _) => identifier.src_ref(),
237 ResolveError::ParseError(parse_error) => parse_error.src_ref(),
238 ResolveError::ResolveCheckFailed(src_ref) => src_ref.clone(),
239 _ => SrcRef(None),
240 }
241 }
242}
243
244pub type ResolveResult<T> = std::result::Result<T, ResolveError>;