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