1use crate::error::AstError;
10use crate::model::Ast;
11use crate::node::AnyId;
12use crate::refs::RefKind;
13use alloc::string::String;
14use alloc::vec::Vec;
15use zerodds_ast_core::ScopedName;
16use zerodds_ast_core::{BreakCause, Id, IndexLookup, NodeId, PendingRef, RefState};
17
18#[derive(Debug, Clone, Default, PartialEq, Eq)]
20pub struct ResolveReport {
21 pub resolved: usize,
23 pub broken: Vec<(NodeId, BreakCause)>,
25}
26
27impl ResolveReport {
28 #[must_use]
30 pub fn is_clean(&self) -> bool {
31 self.broken.is_empty()
32 }
33}
34
35impl Ast {
36 #[must_use]
38 pub fn pending_waiters(&self, key: &PendingRef) -> &[NodeId] {
39 self.by_pending.get(key)
40 }
41
42 #[must_use]
44 pub fn pending_count(&self) -> usize {
45 self.by_pending.iter().map(|(_, nodes)| nodes.len()).sum()
46 }
47
48 pub fn resolve_pending(
58 &mut self,
59 key: &PendingRef,
60 target: Id<crate::TypeNode>,
61 ) -> Result<usize, AstError> {
62 let waiters: Vec<NodeId> = self.by_pending.get(key).to_vec();
63 if waiters.is_empty() {
64 return Ok(0);
65 }
66 let Ok(node) = self.types.get(target) else {
67 return Ok(0);
68 };
69 let target_any = AnyId::Type(target, node.node_id);
70 let mut changed = 0usize;
71 for waiter in waiters {
72 if self.set_reference_to(waiter, key, target_any) {
73 changed += 1;
74 }
75 }
76 self.by_pending.remove_key(key);
77 Ok(changed)
78 }
79
80 pub fn resolve_pending_to(
89 &mut self,
90 key: &PendingRef,
91 target: AnyId,
92 ) -> Result<usize, AstError> {
93 let waiters: Vec<NodeId> = self.by_pending.get(key).to_vec();
94 if waiters.is_empty() {
95 return Ok(0);
96 }
97 let mut changed = 0usize;
98 for waiter in waiters {
99 if self.set_reference_to(waiter, key, target) {
100 changed += 1;
101 }
102 }
103 self.by_pending.remove_key(key);
104 Ok(changed)
105 }
106
107 pub fn break_pending(
113 &mut self,
114 key: &PendingRef,
115 cause: BreakCause,
116 ) -> Result<usize, AstError> {
117 let waiters: Vec<NodeId> = self.by_pending.get(key).to_vec();
118 let mut changed = 0usize;
119 for waiter in waiters {
120 if self.break_reference(waiter, key, &cause) {
121 changed += 1;
122 }
123 }
124 self.by_pending.remove_key(key);
125 Ok(changed)
126 }
127
128 fn find_in_enclosing_scopes(&self, key: &PendingRef) -> Option<AnyId> {
147 let PendingRef::ScopedName(name) = key else {
148 return None;
149 };
150 let written = name.segments();
151 if written.is_empty() {
152 return None;
153 }
154 let mut found: Option<AnyId> = None;
155 for waiter in self.by_pending.get(key) {
156 let Some(scope) = self.enclosing_module_path(*waiter) else {
157 continue;
158 };
159 for depth in (0..=scope.len()).rev() {
161 let mut segments: Vec<String> = scope.iter().take(depth).cloned().collect();
162 segments.extend(written.iter().cloned());
163 let candidate = ScopedName::absolute(segments);
164 let Ok(node) = self.by_scoped_name.unique(&candidate) else {
165 continue;
166 };
167 let Some(any) = self.resolve_node_id(node) else {
168 continue;
169 };
170 match found {
171 None => found = Some(any),
172 Some(first) if first != any => return None,
175 Some(_) => {}
176 }
177 break;
178 }
179 }
180 found
181 }
182
183 fn enclosing_module_path(&self, node: NodeId) -> Option<Vec<String>> {
185 let any = self.resolve_node_id(node)?;
186 let module = match any {
187 AnyId::Type(id, _) => self.types.get(id).ok()?.module,
188 AnyId::Member(id, _) => match self.members.get(id).ok()?.parent {
189 AnyId::Type(type_, _) => self.types.get(type_).ok()?.module,
190 _ => return None,
191 },
192 AnyId::Case(id, _) => {
193 let parent = self.cases.get(id).ok()?.parent;
194 self.types.get(parent).ok()?.module
195 }
196 _ => return None,
197 };
198 self.scoped_name_of(AnyId::Module(
199 module,
200 self.modules.get(module).ok()?.node_id,
201 ))
202 .map(|name| name.segments().to_vec())
203 .or_else(|| Some(Vec::new()))
204 }
205
206 pub fn resolve_all_by_name(&mut self) -> Result<ResolveReport, AstError> {
209 let keys: Vec<PendingRef> = self.by_pending.keys().cloned().collect();
210 let mut report = ResolveReport::default();
211
212 for key in keys {
213 let PendingRef::ScopedName(name) = &key else {
214 continue;
215 };
216 match self.by_scoped_name.unique(name) {
217 Ok(node) => match self.resolve_node_id(node) {
218 Some(target @ AnyId::Interface(interface, _))
231 if self.any_waiter_wants(&key, RefKind::Type) =>
232 {
233 let object_ref = self.object_reference_to(interface)?;
234 let node_id = self.types.get(object_ref)?.node_id;
235 report.resolved +=
236 self.point_waiters(&key, AnyId::Type(object_ref, node_id));
237 report.resolved += self.point_waiters(&key, target);
238 self.break_or_clear(&key, &mut report)?;
239 }
240 Some(target) if self.any_waiter_accepts(&key, target) => {
241 report.resolved += self.resolve_pending_to(&key, target)?;
242 let remaining = self.by_pending.get(&key).to_vec();
246 if !remaining.is_empty() {
247 let cause = BreakCause::KindMismatch {
248 expected: "a different kind of declaration",
249 found: target.kind().as_str(),
250 };
251 for waiter in &remaining {
252 report.broken.push((*waiter, cause.clone()));
253 }
254 let _ = self.break_pending(&key, cause)?;
255 }
256 }
257 found => {
258 let found = found.map_or("nothing", |any| any.kind().as_str());
259 let cause = BreakCause::KindMismatch {
260 expected: "a declaration the reference accepts",
261 found,
262 };
263 for waiter in self.by_pending.get(&key).to_vec() {
264 report.broken.push((waiter, cause.clone()));
265 }
266 let _ = self.break_pending(&key, cause)?;
267 }
268 },
269 Err(IndexLookup::Missing) => {
270 if let Some(target) = self.find_in_enclosing_scopes(&key) {
279 report.resolved += self.point_waiters(&key, target);
280 continue;
281 }
282 let cause = BreakCause::NotFound { what: key.clone() };
283 for waiter in self.by_pending.get(&key).to_vec() {
284 report.broken.push((waiter, cause.clone()));
285 }
286 let _ = self.break_pending(&key, cause)?;
287 }
288 Err(IndexLookup::Ambiguous(candidates)) => {
289 let cause = BreakCause::Ambiguous {
290 what: key.clone(),
291 candidates,
292 };
293 for waiter in self.by_pending.get(&key).to_vec() {
294 report.broken.push((waiter, cause.clone()));
295 }
296 let _ = self.break_pending(&key, cause)?;
297 }
298 }
299 }
300
301 Ok(report)
302 }
303
304 fn point_waiters(&mut self, key: &PendingRef, target: AnyId) -> usize {
312 let waiters: Vec<NodeId> = self.by_pending.get(key).to_vec();
313 waiters
314 .into_iter()
315 .filter(|waiter| self.set_reference_to(*waiter, key, target))
316 .count()
317 }
318
319 fn break_or_clear(
321 &mut self,
322 key: &PendingRef,
323 report: &mut ResolveReport,
324 ) -> Result<(), AstError> {
325 let remaining: Vec<NodeId> = self
326 .by_pending
327 .get(key)
328 .iter()
329 .copied()
330 .filter(|waiter| {
331 self.resolve_node_id(*waiter)
332 .is_some_and(|any| self.pending_slots(any).iter().any(|slot| &slot.key == key))
333 })
334 .collect();
335 if remaining.is_empty() {
336 self.by_pending.remove_key(key);
337 return Ok(());
338 }
339 let cause = BreakCause::KindMismatch {
340 expected: "a different kind of declaration",
341 found: "interface",
342 };
343 for waiter in &remaining {
344 report.broken.push((*waiter, cause.clone()));
345 }
346 let _ = self.break_pending(key, cause)?;
347 Ok(())
348 }
349
350 fn any_waiter_wants(&self, key: &PendingRef, kind: RefKind) -> bool {
352 self.by_pending.get(key).iter().any(|waiter| {
353 self.resolve_node_id(*waiter).is_some_and(|any| {
354 self.pending_slots(any)
355 .iter()
356 .any(|slot| &slot.key == key && slot.kind == kind)
357 })
358 })
359 }
360
361 fn any_waiter_accepts(&self, key: &PendingRef, target: AnyId) -> bool {
363 self.by_pending.get(key).iter().any(|waiter| {
364 self.resolve_node_id(*waiter).is_some_and(|any| {
365 self.pending_slots(any)
366 .iter()
367 .any(|slot| &slot.key == key && slot.kind.accepts(target))
368 })
369 })
370 }
371
372 fn set_reference_to(&mut self, node: NodeId, key: &PendingRef, target: AnyId) -> bool {
379 let Some(any) = self.resolve_node_id(node) else {
380 return false;
381 };
382 let slots: Vec<usize> = self
383 .pending_slots(any)
384 .into_iter()
385 .filter(|slot| &slot.key == key && slot.kind.accepts(target))
386 .map(|slot| slot.slot)
387 .collect();
388
389 let mut changed = Vec::new();
390 for slot in slots {
391 if self.set_slot(any, slot, target) {
392 changed.push(slot);
393 }
394 }
395 for slot in &changed {
399 self.record_reference_change(node, *slot, key, RefState::Resolved(target.node_id()));
400 }
401 !changed.is_empty()
402 }
403
404 fn break_reference(&mut self, node: NodeId, key: &PendingRef, cause: &BreakCause) -> bool {
406 let Some(any) = self.resolve_node_id(node) else {
407 return false;
408 };
409 let slots: Vec<usize> = self
410 .pending_slots(any)
411 .into_iter()
412 .filter(|slot| &slot.key == key)
413 .map(|slot| slot.slot)
414 .collect();
415
416 let mut changed = Vec::new();
417 for slot in slots {
418 if self.break_slot(any, slot, cause.clone()) {
419 changed.push(slot);
420 }
421 }
422 for slot in &changed {
423 self.record_reference_change(node, *slot, key, RefState::Broken);
424 }
425 !changed.is_empty()
426 }
427
428 fn record_reference_change(
429 &mut self,
430 node: NodeId,
431 slot: usize,
432 key: &PendingRef,
433 after: RefState,
434 ) {
435 let change = zerodds_ast_core::Change::Updated {
436 node,
437 field: crate::field::REFERENCE,
438 before: zerodds_ast_core::FieldValue::Reference(zerodds_ast_core::RefState::Pending),
439 after: zerodds_ast_core::FieldValue::Reference(after),
440 };
441 let undo = zerodds_ast_core::UndoRecord {
442 node,
443 payload: zerodds_ast_core::UndoPayload::RestorePending {
444 key: key.clone(),
445 slot,
446 },
447 };
448 if let Some(log) = self.pending.as_mut() {
449 log.record(undo, change);
450 } else if let Err(error) = self.record_standalone(undo, change) {
451 self.last_commit_error = Some(error);
452 }
453 }
454
455 pub(crate) fn index_pending(&mut self, any: AnyId) {
457 let node = any.node_id();
458 for slot in self.pending_slots(any) {
459 self.by_pending.insert(slot.key, node);
460 }
461 }
462
463 pub fn reindex_pending(&mut self, any: AnyId) {
469 self.unindex_pending(any);
470 self.index_pending(any);
471 }
472
473 pub(crate) fn unindex_pending(&mut self, any: AnyId) {
479 self.by_pending.remove_node(any.node_id());
480 }
481}