1use std::sync::Arc;
2
3use dashmap::{DashMap, DashSet};
4
5use crate::storage::{
6 ClassStorage, EnumStorage, FunctionStorage, InterfaceStorage, MethodStorage, TraitStorage,
7};
8use mir_types::Union;
9
10#[derive(Debug, Default)]
15pub struct Codebase {
16 pub classes: DashMap<Arc<str>, ClassStorage>,
17 pub interfaces: DashMap<Arc<str>, InterfaceStorage>,
18 pub traits: DashMap<Arc<str>, TraitStorage>,
19 pub enums: DashMap<Arc<str>, EnumStorage>,
20 pub functions: DashMap<Arc<str>, FunctionStorage>,
21 pub constants: DashMap<Arc<str>, Union>,
22
23 pub referenced_methods: DashSet<Arc<str>>,
26 pub referenced_properties: DashSet<Arc<str>>,
28 pub referenced_functions: DashSet<Arc<str>>,
30
31 pub symbol_to_file: DashMap<Arc<str>, Arc<str>>,
34
35 pub known_symbols: DashSet<Arc<str>>,
39
40 pub file_imports: DashMap<Arc<str>, std::collections::HashMap<String, String>>,
48 pub file_namespaces: DashMap<Arc<str>, String>,
56
57 finalized: std::sync::atomic::AtomicBool,
59}
60
61impl Codebase {
62 pub fn new() -> Self {
63 Self::default()
64 }
65
66 pub fn invalidate_finalization(&self) {
72 self.finalized
73 .store(false, std::sync::atomic::Ordering::SeqCst);
74 }
75
76 pub fn remove_file_definitions(&self, file_path: &str) {
86 let symbols: Vec<Arc<str>> = self
88 .symbol_to_file
89 .iter()
90 .filter(|entry| entry.value().as_ref() == file_path)
91 .map(|entry| entry.key().clone())
92 .collect();
93
94 for sym in &symbols {
96 self.classes.remove(sym.as_ref());
97 self.interfaces.remove(sym.as_ref());
98 self.traits.remove(sym.as_ref());
99 self.enums.remove(sym.as_ref());
100 self.functions.remove(sym.as_ref());
101 self.constants.remove(sym.as_ref());
102 self.symbol_to_file.remove(sym.as_ref());
103 self.known_symbols.remove(sym.as_ref());
104 }
105
106 self.file_imports.remove(file_path);
108 self.file_namespaces.remove(file_path);
109
110 self.invalidate_finalization();
111 }
112
113 pub fn get_property(
119 &self,
120 fqcn: &str,
121 prop_name: &str,
122 ) -> Option<crate::storage::PropertyStorage> {
123 if let Some(cls) = self.classes.get(fqcn) {
125 if let Some(p) = cls.own_properties.get(prop_name) {
126 return Some(p.clone());
127 }
128 }
129
130 let all_parents = {
132 if let Some(cls) = self.classes.get(fqcn) {
133 cls.all_parents.clone()
134 } else {
135 return None;
136 }
137 };
138
139 for ancestor_fqcn in &all_parents {
140 if let Some(ancestor_cls) = self.classes.get(ancestor_fqcn.as_ref()) {
141 if let Some(p) = ancestor_cls.own_properties.get(prop_name) {
142 return Some(p.clone());
143 }
144 }
145 }
146
147 let trait_list = {
149 if let Some(cls) = self.classes.get(fqcn) {
150 cls.traits.clone()
151 } else {
152 vec![]
153 }
154 };
155 for trait_fqcn in &trait_list {
156 if let Some(tr) = self.traits.get(trait_fqcn.as_ref()) {
157 if let Some(p) = tr.own_properties.get(prop_name) {
158 return Some(p.clone());
159 }
160 }
161 }
162
163 None
164 }
165
166 pub fn get_method(&self, fqcn: &str, method_name: &str) -> Option<MethodStorage> {
168 let method_lower = method_name.to_lowercase();
170 let method_name = method_lower.as_str();
171 if let Some(cls) = self.classes.get(fqcn) {
173 if let Some(m) = cls.get_method(method_name) {
174 return Some(m.clone());
175 }
176 }
177 if let Some(iface) = self.interfaces.get(fqcn) {
179 if let Some(m) = iface.own_methods.get(method_name).or_else(|| {
180 iface
181 .own_methods
182 .iter()
183 .find(|(k, _)| k.as_ref().eq_ignore_ascii_case(method_name))
184 .map(|(_, v)| v)
185 }) {
186 return Some(m.clone());
187 }
188 let parents = iface.all_parents.clone();
190 for parent_fqcn in &parents {
191 if let Some(parent_iface) = self.interfaces.get(parent_fqcn.as_ref()) {
192 if let Some(m) = parent_iface.own_methods.get(method_name).or_else(|| {
193 parent_iface
194 .own_methods
195 .iter()
196 .find(|(k, _)| k.as_ref().eq_ignore_ascii_case(method_name))
197 .map(|(_, v)| v)
198 }) {
199 return Some(m.clone());
200 }
201 }
202 }
203 }
204 if let Some(tr) = self.traits.get(fqcn) {
206 if let Some(m) = tr.own_methods.get(method_name).or_else(|| {
207 tr.own_methods
208 .iter()
209 .find(|(k, _)| k.as_ref().eq_ignore_ascii_case(method_name))
210 .map(|(_, v)| v)
211 }) {
212 return Some(m.clone());
213 }
214 }
215 if let Some(e) = self.enums.get(fqcn) {
217 if let Some(m) = e.own_methods.get(method_name).or_else(|| {
218 e.own_methods
219 .iter()
220 .find(|(k, _)| k.as_ref().eq_ignore_ascii_case(method_name))
221 .map(|(_, v)| v)
222 }) {
223 return Some(m.clone());
224 }
225 if matches!(method_name, "cases" | "from" | "tryfrom") {
227 return Some(crate::storage::MethodStorage {
228 fqcn: Arc::from(fqcn),
229 name: Arc::from(method_name),
230 params: vec![],
231 return_type: Some(mir_types::Union::mixed()),
232 inferred_return_type: None,
233 visibility: crate::storage::Visibility::Public,
234 is_static: true,
235 is_abstract: false,
236 is_constructor: false,
237 template_params: vec![],
238 assertions: vec![],
239 throws: vec![],
240 is_final: false,
241 is_internal: false,
242 is_pure: false,
243 is_deprecated: false,
244 location: None,
245 });
246 }
247 }
248 None
249 }
250
251 pub fn extends_or_implements(&self, child: &str, ancestor: &str) -> bool {
253 if child == ancestor {
254 return true;
255 }
256 if let Some(cls) = self.classes.get(child) {
257 return cls.implements_or_extends(ancestor);
258 }
259 if let Some(iface) = self.interfaces.get(child) {
260 return iface.all_parents.iter().any(|p| p.as_ref() == ancestor);
261 }
262 if let Some(en) = self.enums.get(child) {
265 if en.interfaces.iter().any(|i| i.as_ref() == ancestor) {
267 return true;
268 }
269 if ancestor == "UnitEnum" || ancestor == "\\UnitEnum" {
271 return true;
272 }
273 if (ancestor == "BackedEnum" || ancestor == "\\BackedEnum") && en.scalar_type.is_some()
275 {
276 return true;
277 }
278 }
279 false
280 }
281
282 pub fn type_exists(&self, fqcn: &str) -> bool {
284 self.classes.contains_key(fqcn)
285 || self.interfaces.contains_key(fqcn)
286 || self.traits.contains_key(fqcn)
287 || self.enums.contains_key(fqcn)
288 }
289
290 pub fn function_exists(&self, fqn: &str) -> bool {
291 self.functions.contains_key(fqn)
292 }
293
294 pub fn is_abstract_class(&self, fqcn: &str) -> bool {
298 self.classes.get(fqcn).is_some_and(|c| c.is_abstract)
299 }
300
301 pub fn get_class_template_params(&self, fqcn: &str) -> Vec<crate::storage::TemplateParam> {
304 if let Some(cls) = self.classes.get(fqcn) {
305 return cls.template_params.clone();
306 }
307 if let Some(iface) = self.interfaces.get(fqcn) {
308 return iface.template_params.clone();
309 }
310 if let Some(tr) = self.traits.get(fqcn) {
311 return tr.template_params.clone();
312 }
313 vec![]
314 }
315
316 pub fn has_magic_get(&self, fqcn: &str) -> bool {
319 if let Some(cls) = self.classes.get(fqcn) {
320 if cls.own_methods.contains_key("__get") || cls.all_methods.contains_key("__get") {
321 return true;
322 }
323 let traits = cls.traits.clone();
325 drop(cls);
326 for tr in &traits {
327 if let Some(t) = self.traits.get(tr.as_ref()) {
328 if t.own_methods.contains_key("__get") {
329 return true;
330 }
331 }
332 }
333 let all_parents = {
335 if let Some(c) = self.classes.get(fqcn) {
336 c.all_parents.clone()
337 } else {
338 vec![]
339 }
340 };
341 for ancestor in &all_parents {
342 if let Some(anc) = self.classes.get(ancestor.as_ref()) {
343 if anc.own_methods.contains_key("__get") {
344 return true;
345 }
346 }
347 }
348 }
349 false
350 }
351
352 pub fn has_unknown_ancestor(&self, fqcn: &str) -> bool {
360 if let Some(iface) = self.interfaces.get(fqcn) {
362 let parents = iface.all_parents.clone();
363 drop(iface);
364 for p in &parents {
365 if !self.type_exists(p.as_ref()) {
366 return true;
367 }
368 }
369 return false;
370 }
371
372 let (parent, interfaces, traits, all_parents) = {
374 let Some(cls) = self.classes.get(fqcn) else {
375 return false;
376 };
377 (
378 cls.parent.clone(),
379 cls.interfaces.clone(),
380 cls.traits.clone(),
381 cls.all_parents.clone(),
382 )
383 };
384
385 if let Some(ref p) = parent {
387 if !self.type_exists(p.as_ref()) {
388 return true;
389 }
390 }
391 for iface in &interfaces {
392 if !self.type_exists(iface.as_ref()) {
393 return true;
394 }
395 }
396 for tr in &traits {
397 if !self.type_exists(tr.as_ref()) {
398 return true;
399 }
400 }
401
402 for ancestor in &all_parents {
404 if !self.type_exists(ancestor.as_ref()) {
405 return true;
406 }
407 }
408
409 false
410 }
411
412 pub fn resolve_class_name(&self, file: &str, name: &str) -> String {
419 let name = name.trim_start_matches('\\');
420 if name.is_empty() {
421 return name.to_string();
422 }
423 if name.contains('\\') {
428 let first_segment = name.split('\\').next().unwrap_or(name);
430 if let Some(imports) = self.file_imports.get(file) {
431 if let Some(resolved_prefix) = imports.get(first_segment) {
432 let rest = &name[first_segment.len()..]; return format!("{}{}", resolved_prefix, rest);
434 }
435 }
436 if self.type_exists(name) {
438 return name.to_string();
439 }
440 if let Some(ns) = self.file_namespaces.get(file) {
442 let qualified = format!("{}\\{}", *ns, name);
443 if self.type_exists(&qualified) {
444 return qualified;
445 }
446 }
447 return name.to_string();
448 }
449 match name {
451 "self" | "parent" | "static" | "this" => return name.to_string(),
452 _ => {}
453 }
454 if let Some(imports) = self.file_imports.get(file) {
456 if let Some(resolved) = imports.get(name) {
457 return resolved.clone();
458 }
459 let name_lower = name.to_lowercase();
461 for (alias, resolved) in imports.iter() {
462 if alias.to_lowercase() == name_lower {
463 return resolved.clone();
464 }
465 }
466 }
467 if let Some(ns) = self.file_namespaces.get(file) {
469 let qualified = format!("{}\\{}", *ns, name);
470 if self.type_exists(&qualified) {
475 return qualified;
476 }
477 if self.type_exists(name) {
478 return name.to_string();
479 }
480 return qualified;
481 }
482 name.to_string()
483 }
484
485 pub fn get_symbol_location(&self, fqcn: &str) -> Option<crate::storage::Location> {
492 if let Some(cls) = self.classes.get(fqcn) {
493 return cls.location.clone();
494 }
495 if let Some(iface) = self.interfaces.get(fqcn) {
496 return iface.location.clone();
497 }
498 if let Some(tr) = self.traits.get(fqcn) {
499 return tr.location.clone();
500 }
501 if let Some(en) = self.enums.get(fqcn) {
502 return en.location.clone();
503 }
504 if let Some(func) = self.functions.get(fqcn) {
505 return func.location.clone();
506 }
507 None
508 }
509
510 pub fn get_member_location(
512 &self,
513 fqcn: &str,
514 member_name: &str,
515 ) -> Option<crate::storage::Location> {
516 if let Some(method) = self.get_method(fqcn, member_name) {
518 return method.location.clone();
519 }
520 if let Some(prop) = self.get_property(fqcn, member_name) {
522 return prop.location.clone();
523 }
524 if let Some(cls) = self.classes.get(fqcn) {
526 if let Some(c) = cls.own_constants.get(member_name) {
527 return c.location.clone();
528 }
529 }
530 if let Some(iface) = self.interfaces.get(fqcn) {
532 if let Some(c) = iface.own_constants.get(member_name) {
533 return c.location.clone();
534 }
535 }
536 if let Some(tr) = self.traits.get(fqcn) {
538 if let Some(c) = tr.own_constants.get(member_name) {
539 return c.location.clone();
540 }
541 }
542 if let Some(en) = self.enums.get(fqcn) {
544 if let Some(c) = en.own_constants.get(member_name) {
545 return c.location.clone();
546 }
547 if let Some(case) = en.cases.get(member_name) {
548 return case.location.clone();
549 }
550 }
551 None
552 }
553
554 pub fn mark_method_referenced(&self, fqcn: &str, method_name: &str) {
560 let key: Arc<str> = Arc::from(format!("{}::{}", fqcn, method_name.to_lowercase()).as_str());
561 self.referenced_methods.insert(key);
562 }
563
564 pub fn mark_property_referenced(&self, fqcn: &str, prop_name: &str) {
566 let key: Arc<str> = Arc::from(format!("{}::{}", fqcn, prop_name).as_str());
567 self.referenced_properties.insert(key);
568 }
569
570 pub fn mark_function_referenced(&self, fqn: &str) {
572 self.referenced_functions.insert(Arc::from(fqn));
573 }
574
575 pub fn is_method_referenced(&self, fqcn: &str, method_name: &str) -> bool {
576 let key = format!("{}::{}", fqcn, method_name.to_lowercase());
577 self.referenced_methods.contains(key.as_str())
578 }
579
580 pub fn is_property_referenced(&self, fqcn: &str, prop_name: &str) -> bool {
581 let key = format!("{}::{}", fqcn, prop_name);
582 self.referenced_properties.contains(key.as_str())
583 }
584
585 pub fn is_function_referenced(&self, fqn: &str) -> bool {
586 self.referenced_functions.contains(fqn)
587 }
588
589 pub fn finalize(&self) {
596 if self.finalized.load(std::sync::atomic::Ordering::SeqCst) {
597 return;
598 }
599
600 let class_keys: Vec<Arc<str>> = self.classes.iter().map(|e| e.key().clone()).collect();
602 for fqcn in &class_keys {
603 let parents = self.collect_class_ancestors(fqcn);
604 if let Some(mut cls) = self.classes.get_mut(fqcn.as_ref()) {
605 cls.all_parents = parents;
606 }
607 }
608
609 for fqcn in &class_keys {
611 let all_methods = self.build_method_table(fqcn);
612 if let Some(mut cls) = self.classes.get_mut(fqcn.as_ref()) {
613 cls.all_methods = all_methods;
614 }
615 }
616
617 let iface_keys: Vec<Arc<str>> = self.interfaces.iter().map(|e| e.key().clone()).collect();
619 for fqcn in &iface_keys {
620 let parents = self.collect_interface_ancestors(fqcn);
621 if let Some(mut iface) = self.interfaces.get_mut(fqcn.as_ref()) {
622 iface.all_parents = parents;
623 }
624 }
625
626 self.finalized
627 .store(true, std::sync::atomic::Ordering::SeqCst);
628 }
629
630 fn collect_class_ancestors(&self, fqcn: &str) -> Vec<Arc<str>> {
635 let mut result = Vec::new();
636 let mut visited = std::collections::HashSet::new();
637 self.collect_class_ancestors_inner(fqcn, &mut result, &mut visited);
638 result
639 }
640
641 fn collect_class_ancestors_inner(
642 &self,
643 fqcn: &str,
644 out: &mut Vec<Arc<str>>,
645 visited: &mut std::collections::HashSet<String>,
646 ) {
647 if !visited.insert(fqcn.to_string()) {
648 return; }
650 let (parent, interfaces, traits) = {
651 if let Some(cls) = self.classes.get(fqcn) {
652 (
653 cls.parent.clone(),
654 cls.interfaces.clone(),
655 cls.traits.clone(),
656 )
657 } else {
658 return;
659 }
660 };
661
662 if let Some(p) = parent {
663 out.push(p.clone());
664 self.collect_class_ancestors_inner(&p, out, visited);
665 }
666 for iface in interfaces {
667 out.push(iface.clone());
668 self.collect_interface_ancestors_inner(&iface, out, visited);
669 }
670 for t in traits {
671 out.push(t);
672 }
673 }
674
675 fn collect_interface_ancestors(&self, fqcn: &str) -> Vec<Arc<str>> {
676 let mut result = Vec::new();
677 let mut visited = std::collections::HashSet::new();
678 self.collect_interface_ancestors_inner(fqcn, &mut result, &mut visited);
679 result
680 }
681
682 fn collect_interface_ancestors_inner(
683 &self,
684 fqcn: &str,
685 out: &mut Vec<Arc<str>>,
686 visited: &mut std::collections::HashSet<String>,
687 ) {
688 if !visited.insert(fqcn.to_string()) {
689 return;
690 }
691 let extends = {
692 if let Some(iface) = self.interfaces.get(fqcn) {
693 iface.extends.clone()
694 } else {
695 return;
696 }
697 };
698 for e in extends {
699 out.push(e.clone());
700 self.collect_interface_ancestors_inner(&e, out, visited);
701 }
702 }
703
704 fn build_method_table(&self, fqcn: &str) -> indexmap::IndexMap<Arc<str>, MethodStorage> {
707 use indexmap::IndexMap;
708 let mut table: IndexMap<Arc<str>, MethodStorage> = IndexMap::new();
709
710 let ancestors = {
712 if let Some(cls) = self.classes.get(fqcn) {
713 cls.all_parents.clone()
714 } else {
715 return table;
716 }
717 };
718
719 for ancestor_fqcn in ancestors.iter().rev() {
722 if let Some(ancestor) = self.classes.get(ancestor_fqcn.as_ref()) {
723 let ancestor_traits = ancestor.traits.clone();
725 for trait_fqcn in ancestor_traits.iter().rev() {
726 if let Some(tr) = self.traits.get(trait_fqcn.as_ref()) {
727 for (name, method) in &tr.own_methods {
728 table.insert(name.clone(), method.clone());
729 }
730 }
731 }
732 for (name, method) in &ancestor.own_methods {
734 table.insert(name.clone(), method.clone());
735 }
736 } else if let Some(iface) = self.interfaces.get(ancestor_fqcn.as_ref()) {
737 for (name, method) in &iface.own_methods {
738 table.insert(name.clone(), method.clone());
739 }
740 }
741 }
742
743 let trait_list = {
745 if let Some(cls) = self.classes.get(fqcn) {
746 cls.traits.clone()
747 } else {
748 vec![]
749 }
750 };
751 for trait_fqcn in &trait_list {
752 if let Some(tr) = self.traits.get(trait_fqcn.as_ref()) {
753 for (name, method) in &tr.own_methods {
754 table.insert(name.clone(), method.clone());
755 }
756 }
757 }
758
759 if let Some(cls) = self.classes.get(fqcn) {
761 for (name, method) in &cls.own_methods {
762 table.insert(name.clone(), method.clone());
763 }
764 }
765
766 table
767 }
768}