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 global_vars: DashMap<Arc<str>, Union>,
26 file_global_vars: DashMap<Arc<str>, Vec<Arc<str>>>,
29
30 pub referenced_methods: DashSet<Arc<str>>,
33 pub referenced_properties: DashSet<Arc<str>>,
35 pub referenced_functions: DashSet<Arc<str>>,
37
38 pub symbol_to_file: DashMap<Arc<str>, Arc<str>>,
41
42 pub known_symbols: DashSet<Arc<str>>,
46
47 pub file_imports: DashMap<Arc<str>, std::collections::HashMap<String, String>>,
55 pub file_namespaces: DashMap<Arc<str>, String>,
63
64 finalized: std::sync::atomic::AtomicBool,
66}
67
68impl Codebase {
69 pub fn new() -> Self {
70 Self::default()
71 }
72
73 pub fn invalidate_finalization(&self) {
79 self.finalized
80 .store(false, std::sync::atomic::Ordering::SeqCst);
81 }
82
83 pub fn remove_file_definitions(&self, file_path: &str) {
93 let symbols: Vec<Arc<str>> = self
95 .symbol_to_file
96 .iter()
97 .filter(|entry| entry.value().as_ref() == file_path)
98 .map(|entry| entry.key().clone())
99 .collect();
100
101 for sym in &symbols {
103 self.classes.remove(sym.as_ref());
104 self.interfaces.remove(sym.as_ref());
105 self.traits.remove(sym.as_ref());
106 self.enums.remove(sym.as_ref());
107 self.functions.remove(sym.as_ref());
108 self.constants.remove(sym.as_ref());
109 self.symbol_to_file.remove(sym.as_ref());
110 self.known_symbols.remove(sym.as_ref());
111 }
112
113 self.file_imports.remove(file_path);
115 self.file_namespaces.remove(file_path);
116
117 if let Some((_, var_names)) = self.file_global_vars.remove(file_path) {
119 for name in var_names {
120 self.global_vars.remove(name.as_ref());
121 }
122 }
123
124 self.invalidate_finalization();
125 }
126
127 pub fn register_global_var(&self, file: &Arc<str>, name: Arc<str>, ty: Union) {
134 self.file_global_vars
135 .entry(file.clone())
136 .or_default()
137 .push(name.clone());
138 self.global_vars.insert(name, ty);
139 }
140
141 pub fn get_property(
147 &self,
148 fqcn: &str,
149 prop_name: &str,
150 ) -> Option<crate::storage::PropertyStorage> {
151 if let Some(cls) = self.classes.get(fqcn) {
153 if let Some(p) = cls.own_properties.get(prop_name) {
154 return Some(p.clone());
155 }
156 }
157
158 let all_parents = {
160 if let Some(cls) = self.classes.get(fqcn) {
161 cls.all_parents.clone()
162 } else {
163 return None;
164 }
165 };
166
167 for ancestor_fqcn in &all_parents {
168 if let Some(ancestor_cls) = self.classes.get(ancestor_fqcn.as_ref()) {
169 if let Some(p) = ancestor_cls.own_properties.get(prop_name) {
170 return Some(p.clone());
171 }
172 }
173 }
174
175 let trait_list = {
177 if let Some(cls) = self.classes.get(fqcn) {
178 cls.traits.clone()
179 } else {
180 vec![]
181 }
182 };
183 for trait_fqcn in &trait_list {
184 if let Some(tr) = self.traits.get(trait_fqcn.as_ref()) {
185 if let Some(p) = tr.own_properties.get(prop_name) {
186 return Some(p.clone());
187 }
188 }
189 }
190
191 None
192 }
193
194 pub fn get_method(&self, fqcn: &str, method_name: &str) -> Option<MethodStorage> {
196 let method_lower = method_name.to_lowercase();
198 let method_name = method_lower.as_str();
199 if let Some(cls) = self.classes.get(fqcn) {
201 if let Some(m) = cls.get_method(method_name) {
202 return Some(m.clone());
203 }
204 }
205 if let Some(iface) = self.interfaces.get(fqcn) {
207 if let Some(m) = iface.own_methods.get(method_name).or_else(|| {
208 iface
209 .own_methods
210 .iter()
211 .find(|(k, _)| k.as_ref().eq_ignore_ascii_case(method_name))
212 .map(|(_, v)| v)
213 }) {
214 return Some(m.clone());
215 }
216 let parents = iface.all_parents.clone();
218 for parent_fqcn in &parents {
219 if let Some(parent_iface) = self.interfaces.get(parent_fqcn.as_ref()) {
220 if let Some(m) = parent_iface.own_methods.get(method_name).or_else(|| {
221 parent_iface
222 .own_methods
223 .iter()
224 .find(|(k, _)| k.as_ref().eq_ignore_ascii_case(method_name))
225 .map(|(_, v)| v)
226 }) {
227 return Some(m.clone());
228 }
229 }
230 }
231 }
232 if let Some(tr) = self.traits.get(fqcn) {
234 if let Some(m) = tr.own_methods.get(method_name).or_else(|| {
235 tr.own_methods
236 .iter()
237 .find(|(k, _)| k.as_ref().eq_ignore_ascii_case(method_name))
238 .map(|(_, v)| v)
239 }) {
240 return Some(m.clone());
241 }
242 }
243 if let Some(e) = self.enums.get(fqcn) {
245 if let Some(m) = e.own_methods.get(method_name).or_else(|| {
246 e.own_methods
247 .iter()
248 .find(|(k, _)| k.as_ref().eq_ignore_ascii_case(method_name))
249 .map(|(_, v)| v)
250 }) {
251 return Some(m.clone());
252 }
253 if matches!(method_name, "cases" | "from" | "tryfrom") {
255 return Some(crate::storage::MethodStorage {
256 fqcn: Arc::from(fqcn),
257 name: Arc::from(method_name),
258 params: vec![],
259 return_type: Some(mir_types::Union::mixed()),
260 inferred_return_type: None,
261 visibility: crate::storage::Visibility::Public,
262 is_static: true,
263 is_abstract: false,
264 is_constructor: false,
265 template_params: vec![],
266 assertions: vec![],
267 throws: vec![],
268 is_final: false,
269 is_internal: false,
270 is_pure: false,
271 is_deprecated: false,
272 location: None,
273 });
274 }
275 }
276 None
277 }
278
279 pub fn extends_or_implements(&self, child: &str, ancestor: &str) -> bool {
281 if child == ancestor {
282 return true;
283 }
284 if let Some(cls) = self.classes.get(child) {
285 return cls.implements_or_extends(ancestor);
286 }
287 if let Some(iface) = self.interfaces.get(child) {
288 return iface.all_parents.iter().any(|p| p.as_ref() == ancestor);
289 }
290 if let Some(en) = self.enums.get(child) {
293 if en.interfaces.iter().any(|i| i.as_ref() == ancestor) {
295 return true;
296 }
297 if ancestor == "UnitEnum" || ancestor == "\\UnitEnum" {
299 return true;
300 }
301 if (ancestor == "BackedEnum" || ancestor == "\\BackedEnum") && en.scalar_type.is_some()
303 {
304 return true;
305 }
306 }
307 false
308 }
309
310 pub fn type_exists(&self, fqcn: &str) -> bool {
312 self.classes.contains_key(fqcn)
313 || self.interfaces.contains_key(fqcn)
314 || self.traits.contains_key(fqcn)
315 || self.enums.contains_key(fqcn)
316 }
317
318 pub fn function_exists(&self, fqn: &str) -> bool {
319 self.functions.contains_key(fqn)
320 }
321
322 pub fn is_abstract_class(&self, fqcn: &str) -> bool {
326 self.classes.get(fqcn).is_some_and(|c| c.is_abstract)
327 }
328
329 pub fn get_class_template_params(&self, fqcn: &str) -> Vec<crate::storage::TemplateParam> {
332 if let Some(cls) = self.classes.get(fqcn) {
333 return cls.template_params.clone();
334 }
335 if let Some(iface) = self.interfaces.get(fqcn) {
336 return iface.template_params.clone();
337 }
338 if let Some(tr) = self.traits.get(fqcn) {
339 return tr.template_params.clone();
340 }
341 vec![]
342 }
343
344 pub fn has_magic_get(&self, fqcn: &str) -> bool {
347 if let Some(cls) = self.classes.get(fqcn) {
348 if cls.own_methods.contains_key("__get") || cls.all_methods.contains_key("__get") {
349 return true;
350 }
351 let traits = cls.traits.clone();
353 drop(cls);
354 for tr in &traits {
355 if let Some(t) = self.traits.get(tr.as_ref()) {
356 if t.own_methods.contains_key("__get") {
357 return true;
358 }
359 }
360 }
361 let all_parents = {
363 if let Some(c) = self.classes.get(fqcn) {
364 c.all_parents.clone()
365 } else {
366 vec![]
367 }
368 };
369 for ancestor in &all_parents {
370 if let Some(anc) = self.classes.get(ancestor.as_ref()) {
371 if anc.own_methods.contains_key("__get") {
372 return true;
373 }
374 }
375 }
376 }
377 false
378 }
379
380 pub fn has_unknown_ancestor(&self, fqcn: &str) -> bool {
388 if let Some(iface) = self.interfaces.get(fqcn) {
390 let parents = iface.all_parents.clone();
391 drop(iface);
392 for p in &parents {
393 if !self.type_exists(p.as_ref()) {
394 return true;
395 }
396 }
397 return false;
398 }
399
400 let (parent, interfaces, traits, all_parents) = {
402 let Some(cls) = self.classes.get(fqcn) else {
403 return false;
404 };
405 (
406 cls.parent.clone(),
407 cls.interfaces.clone(),
408 cls.traits.clone(),
409 cls.all_parents.clone(),
410 )
411 };
412
413 if let Some(ref p) = parent {
415 if !self.type_exists(p.as_ref()) {
416 return true;
417 }
418 }
419 for iface in &interfaces {
420 if !self.type_exists(iface.as_ref()) {
421 return true;
422 }
423 }
424 for tr in &traits {
425 if !self.type_exists(tr.as_ref()) {
426 return true;
427 }
428 }
429
430 for ancestor in &all_parents {
432 if !self.type_exists(ancestor.as_ref()) {
433 return true;
434 }
435 }
436
437 false
438 }
439
440 pub fn resolve_class_name(&self, file: &str, name: &str) -> String {
447 let name = name.trim_start_matches('\\');
448 if name.is_empty() {
449 return name.to_string();
450 }
451 if name.contains('\\') {
456 let first_segment = name.split('\\').next().unwrap_or(name);
458 if let Some(imports) = self.file_imports.get(file) {
459 if let Some(resolved_prefix) = imports.get(first_segment) {
460 let rest = &name[first_segment.len()..]; return format!("{}{}", resolved_prefix, rest);
462 }
463 }
464 if self.type_exists(name) {
466 return name.to_string();
467 }
468 if let Some(ns) = self.file_namespaces.get(file) {
470 let qualified = format!("{}\\{}", *ns, name);
471 if self.type_exists(&qualified) {
472 return qualified;
473 }
474 }
475 return name.to_string();
476 }
477 match name {
479 "self" | "parent" | "static" | "this" => return name.to_string(),
480 _ => {}
481 }
482 if let Some(imports) = self.file_imports.get(file) {
484 if let Some(resolved) = imports.get(name) {
485 return resolved.clone();
486 }
487 let name_lower = name.to_lowercase();
489 for (alias, resolved) in imports.iter() {
490 if alias.to_lowercase() == name_lower {
491 return resolved.clone();
492 }
493 }
494 }
495 if let Some(ns) = self.file_namespaces.get(file) {
497 let qualified = format!("{}\\{}", *ns, name);
498 if self.type_exists(&qualified) {
503 return qualified;
504 }
505 if self.type_exists(name) {
506 return name.to_string();
507 }
508 return qualified;
509 }
510 name.to_string()
511 }
512
513 pub fn get_symbol_location(&self, fqcn: &str) -> Option<crate::storage::Location> {
520 if let Some(cls) = self.classes.get(fqcn) {
521 return cls.location.clone();
522 }
523 if let Some(iface) = self.interfaces.get(fqcn) {
524 return iface.location.clone();
525 }
526 if let Some(tr) = self.traits.get(fqcn) {
527 return tr.location.clone();
528 }
529 if let Some(en) = self.enums.get(fqcn) {
530 return en.location.clone();
531 }
532 if let Some(func) = self.functions.get(fqcn) {
533 return func.location.clone();
534 }
535 None
536 }
537
538 pub fn get_member_location(
540 &self,
541 fqcn: &str,
542 member_name: &str,
543 ) -> Option<crate::storage::Location> {
544 if let Some(method) = self.get_method(fqcn, member_name) {
546 return method.location.clone();
547 }
548 if let Some(prop) = self.get_property(fqcn, member_name) {
550 return prop.location.clone();
551 }
552 if let Some(cls) = self.classes.get(fqcn) {
554 if let Some(c) = cls.own_constants.get(member_name) {
555 return c.location.clone();
556 }
557 }
558 if let Some(iface) = self.interfaces.get(fqcn) {
560 if let Some(c) = iface.own_constants.get(member_name) {
561 return c.location.clone();
562 }
563 }
564 if let Some(tr) = self.traits.get(fqcn) {
566 if let Some(c) = tr.own_constants.get(member_name) {
567 return c.location.clone();
568 }
569 }
570 if let Some(en) = self.enums.get(fqcn) {
572 if let Some(c) = en.own_constants.get(member_name) {
573 return c.location.clone();
574 }
575 if let Some(case) = en.cases.get(member_name) {
576 return case.location.clone();
577 }
578 }
579 None
580 }
581
582 pub fn mark_method_referenced(&self, fqcn: &str, method_name: &str) {
588 let key: Arc<str> = Arc::from(format!("{}::{}", fqcn, method_name.to_lowercase()).as_str());
589 self.referenced_methods.insert(key);
590 }
591
592 pub fn mark_property_referenced(&self, fqcn: &str, prop_name: &str) {
594 let key: Arc<str> = Arc::from(format!("{}::{}", fqcn, prop_name).as_str());
595 self.referenced_properties.insert(key);
596 }
597
598 pub fn mark_function_referenced(&self, fqn: &str) {
600 self.referenced_functions.insert(Arc::from(fqn));
601 }
602
603 pub fn is_method_referenced(&self, fqcn: &str, method_name: &str) -> bool {
604 let key = format!("{}::{}", fqcn, method_name.to_lowercase());
605 self.referenced_methods.contains(key.as_str())
606 }
607
608 pub fn is_property_referenced(&self, fqcn: &str, prop_name: &str) -> bool {
609 let key = format!("{}::{}", fqcn, prop_name);
610 self.referenced_properties.contains(key.as_str())
611 }
612
613 pub fn is_function_referenced(&self, fqn: &str) -> bool {
614 self.referenced_functions.contains(fqn)
615 }
616
617 pub fn finalize(&self) {
624 if self.finalized.load(std::sync::atomic::Ordering::SeqCst) {
625 return;
626 }
627
628 let class_keys: Vec<Arc<str>> = self.classes.iter().map(|e| e.key().clone()).collect();
630 for fqcn in &class_keys {
631 let parents = self.collect_class_ancestors(fqcn);
632 if let Some(mut cls) = self.classes.get_mut(fqcn.as_ref()) {
633 cls.all_parents = parents;
634 }
635 }
636
637 for fqcn in &class_keys {
639 let all_methods = self.build_method_table(fqcn);
640 if let Some(mut cls) = self.classes.get_mut(fqcn.as_ref()) {
641 cls.all_methods = all_methods;
642 }
643 }
644
645 let iface_keys: Vec<Arc<str>> = self.interfaces.iter().map(|e| e.key().clone()).collect();
647 for fqcn in &iface_keys {
648 let parents = self.collect_interface_ancestors(fqcn);
649 if let Some(mut iface) = self.interfaces.get_mut(fqcn.as_ref()) {
650 iface.all_parents = parents;
651 }
652 }
653
654 self.finalized
655 .store(true, std::sync::atomic::Ordering::SeqCst);
656 }
657
658 fn collect_class_ancestors(&self, fqcn: &str) -> Vec<Arc<str>> {
663 let mut result = Vec::new();
664 let mut visited = std::collections::HashSet::new();
665 self.collect_class_ancestors_inner(fqcn, &mut result, &mut visited);
666 result
667 }
668
669 fn collect_class_ancestors_inner(
670 &self,
671 fqcn: &str,
672 out: &mut Vec<Arc<str>>,
673 visited: &mut std::collections::HashSet<String>,
674 ) {
675 if !visited.insert(fqcn.to_string()) {
676 return; }
678 let (parent, interfaces, traits) = {
679 if let Some(cls) = self.classes.get(fqcn) {
680 (
681 cls.parent.clone(),
682 cls.interfaces.clone(),
683 cls.traits.clone(),
684 )
685 } else {
686 return;
687 }
688 };
689
690 if let Some(p) = parent {
691 out.push(p.clone());
692 self.collect_class_ancestors_inner(&p, out, visited);
693 }
694 for iface in interfaces {
695 out.push(iface.clone());
696 self.collect_interface_ancestors_inner(&iface, out, visited);
697 }
698 for t in traits {
699 out.push(t);
700 }
701 }
702
703 fn collect_interface_ancestors(&self, fqcn: &str) -> Vec<Arc<str>> {
704 let mut result = Vec::new();
705 let mut visited = std::collections::HashSet::new();
706 self.collect_interface_ancestors_inner(fqcn, &mut result, &mut visited);
707 result
708 }
709
710 fn collect_interface_ancestors_inner(
711 &self,
712 fqcn: &str,
713 out: &mut Vec<Arc<str>>,
714 visited: &mut std::collections::HashSet<String>,
715 ) {
716 if !visited.insert(fqcn.to_string()) {
717 return;
718 }
719 let extends = {
720 if let Some(iface) = self.interfaces.get(fqcn) {
721 iface.extends.clone()
722 } else {
723 return;
724 }
725 };
726 for e in extends {
727 out.push(e.clone());
728 self.collect_interface_ancestors_inner(&e, out, visited);
729 }
730 }
731
732 fn build_method_table(&self, fqcn: &str) -> indexmap::IndexMap<Arc<str>, MethodStorage> {
735 use indexmap::IndexMap;
736 let mut table: IndexMap<Arc<str>, MethodStorage> = IndexMap::new();
737
738 let ancestors = {
740 if let Some(cls) = self.classes.get(fqcn) {
741 cls.all_parents.clone()
742 } else {
743 return table;
744 }
745 };
746
747 for ancestor_fqcn in ancestors.iter().rev() {
750 if let Some(ancestor) = self.classes.get(ancestor_fqcn.as_ref()) {
751 let ancestor_traits = ancestor.traits.clone();
753 for trait_fqcn in ancestor_traits.iter().rev() {
754 if let Some(tr) = self.traits.get(trait_fqcn.as_ref()) {
755 for (name, method) in &tr.own_methods {
756 table.insert(name.clone(), method.clone());
757 }
758 }
759 }
760 for (name, method) in &ancestor.own_methods {
762 table.insert(name.clone(), method.clone());
763 }
764 } else if let Some(iface) = self.interfaces.get(ancestor_fqcn.as_ref()) {
765 for (name, method) in &iface.own_methods {
766 let mut m = method.clone();
770 m.is_abstract = true;
771 table.insert(name.clone(), m);
772 }
773 }
774 }
775
776 let trait_list = {
778 if let Some(cls) = self.classes.get(fqcn) {
779 cls.traits.clone()
780 } else {
781 vec![]
782 }
783 };
784 for trait_fqcn in &trait_list {
785 if let Some(tr) = self.traits.get(trait_fqcn.as_ref()) {
786 for (name, method) in &tr.own_methods {
787 table.insert(name.clone(), method.clone());
788 }
789 }
790 }
791
792 if let Some(cls) = self.classes.get(fqcn) {
794 for (name, method) in &cls.own_methods {
795 table.insert(name.clone(), method.clone());
796 }
797 }
798
799 table
800 }
801}