1use std::collections::{HashMap, HashSet};
2
3use parking_lot::Mutex;
4use rustc_hash::FxHashMap;
5use std::sync::Arc;
6
7use mir_codebase::storage::{
8 ConstantStorage, FunctionStorage, Location, MethodStorage, PropertyStorage, TemplateParam,
9 Visibility,
10};
11use mir_codebase::StubSlice;
12use mir_types::Union;
13
14use super::*;
15
16type MemberRegistry<V> = Arc<FxHashMap<Arc<str>, FxHashMap<Arc<str>, V>>>;
26type ReferenceLocations = Arc<Mutex<FxHashMap<Arc<str>, Vec<(Arc<str>, u32, u16, u16)>>>>;
27
28#[salsa::db]
29#[derive(Default, Clone)]
30pub struct MirDb {
31 storage: salsa::Storage<Self>,
32 class_nodes: Arc<FxHashMap<Arc<str>, ClassNode>>,
40 class_node_keys_lower: Arc<FxHashMap<String, Arc<str>>>,
44 function_nodes: Arc<FxHashMap<Arc<str>, FunctionNode>>,
47 function_node_keys_lower: Arc<FxHashMap<String, Arc<str>>>,
51 method_nodes: MemberRegistry<MethodNode>,
53 property_nodes: MemberRegistry<PropertyNode>,
55 class_constant_nodes: MemberRegistry<ClassConstantNode>,
57 global_constant_nodes: Arc<FxHashMap<Arc<str>, GlobalConstantNode>>,
59 file_namespaces: Arc<FxHashMap<Arc<str>, Arc<str>>>,
61 file_imports: Arc<FxHashMap<Arc<str>, HashMap<String, String>>>,
63 global_vars: Arc<FxHashMap<Arc<str>, Union>>,
65 symbol_to_file: Arc<FxHashMap<Arc<str>, Arc<str>>>,
67 reference_locations: ReferenceLocations,
69}
70
71#[salsa::db]
72impl salsa::Database for MirDb {}
73
74#[salsa::db]
75impl MirDatabase for MirDb {
76 fn php_version_str(&self) -> Arc<str> {
77 Arc::from("8.2")
78 }
79
80 fn lookup_class_node(&self, fqcn: &str) -> Option<ClassNode> {
81 if let Some(&node) = self.class_nodes.get(fqcn) {
82 return Some(node);
83 }
84 let lower = fqcn.to_ascii_lowercase();
85 let canonical = self.class_node_keys_lower.get(&lower)?;
86 self.class_nodes.get(canonical.as_ref()).copied()
87 }
88
89 fn lookup_function_node(&self, fqn: &str) -> Option<FunctionNode> {
90 if let Some(&node) = self.function_nodes.get(fqn) {
91 return Some(node);
92 }
93 let lower = fqn.to_ascii_lowercase();
94 let canonical = self.function_node_keys_lower.get(&lower)?;
95 self.function_nodes.get(canonical.as_ref()).copied()
96 }
97
98 fn lookup_method_node(&self, fqcn: &str, method_name_lower: &str) -> Option<MethodNode> {
99 self.method_nodes
100 .get(fqcn)
101 .and_then(|m| m.get(method_name_lower).copied())
102 }
103
104 fn lookup_property_node(&self, fqcn: &str, prop_name: &str) -> Option<PropertyNode> {
105 self.property_nodes
106 .get(fqcn)
107 .and_then(|m| m.get(prop_name).copied())
108 }
109
110 fn lookup_class_constant_node(
111 &self,
112 fqcn: &str,
113 const_name: &str,
114 ) -> Option<ClassConstantNode> {
115 self.class_constant_nodes
116 .get(fqcn)
117 .and_then(|m| m.get(const_name).copied())
118 }
119
120 fn lookup_global_constant_node(&self, fqn: &str) -> Option<GlobalConstantNode> {
121 self.global_constant_nodes.get(fqn).copied()
122 }
123
124 fn class_own_methods(&self, fqcn: &str) -> Vec<MethodNode> {
125 self.method_nodes
126 .get(fqcn)
127 .map(|m| m.values().copied().collect())
128 .unwrap_or_default()
129 }
130
131 fn class_own_properties(&self, fqcn: &str) -> Vec<PropertyNode> {
132 self.property_nodes
133 .get(fqcn)
134 .map(|m| m.values().copied().collect())
135 .unwrap_or_default()
136 }
137
138 fn active_class_node_fqcns(&self) -> Vec<Arc<str>> {
139 self.class_nodes
140 .iter()
141 .filter_map(|(fqcn, node)| {
142 if node.active(self) {
143 Some(fqcn.clone())
144 } else {
145 None
146 }
147 })
148 .collect()
149 }
150
151 fn active_function_node_fqns(&self) -> Vec<Arc<str>> {
152 self.function_nodes
153 .iter()
154 .filter_map(|(fqn, node)| {
155 if node.active(self) {
156 Some(fqn.clone())
157 } else {
158 None
159 }
160 })
161 .collect()
162 }
163
164 fn file_namespace(&self, file: &str) -> Option<Arc<str>> {
165 self.file_namespaces.get(file).cloned()
166 }
167
168 fn file_imports(&self, file: &str) -> HashMap<String, String> {
169 self.file_imports.get(file).cloned().unwrap_or_default()
170 }
171
172 fn global_var_type(&self, name: &str) -> Option<Union> {
173 self.global_vars.get(name).cloned()
174 }
175
176 fn file_import_snapshots(&self) -> Vec<(Arc<str>, HashMap<String, String>)> {
177 self.file_imports
178 .iter()
179 .map(|(file, imports)| (file.clone(), imports.clone()))
180 .collect()
181 }
182
183 fn symbol_defining_file(&self, symbol: &str) -> Option<Arc<str>> {
184 self.symbol_to_file.get(symbol).cloned()
185 }
186
187 fn symbols_defined_in_file(&self, file: &str) -> Vec<Arc<str>> {
188 self.symbol_to_file
189 .iter()
190 .filter_map(|(sym, defining_file)| {
191 if defining_file.as_ref() == file {
192 Some(sym.clone())
193 } else {
194 None
195 }
196 })
197 .collect()
198 }
199
200 fn record_reference_location(&self, loc: RefLoc) {
201 let mut refs = self.reference_locations.lock();
202 let entry = refs.entry(loc.symbol_key).or_default();
203 let tuple = (loc.file, loc.line, loc.col_start, loc.col_end);
204 if !entry.iter().any(|existing| existing == &tuple) {
205 entry.push(tuple);
206 }
207 }
208
209 fn replay_reference_locations(&self, file: Arc<str>, locs: &[(String, u32, u16, u16)]) {
210 for (symbol, line, col_start, col_end) in locs {
211 self.record_reference_location(RefLoc {
212 symbol_key: Arc::from(symbol.as_str()),
213 file: file.clone(),
214 line: *line,
215 col_start: *col_start,
216 col_end: *col_end,
217 });
218 }
219 }
220
221 fn extract_file_reference_locations(&self, file: &str) -> Vec<(Arc<str>, u32, u16, u16)> {
222 let refs = self.reference_locations.lock();
223 let mut out = Vec::new();
224 for (symbol, locs) in refs.iter() {
225 for (loc_file, line, col_start, col_end) in locs {
226 if loc_file.as_ref() == file {
227 out.push((symbol.clone(), *line, *col_start, *col_end));
228 }
229 }
230 }
231 out
232 }
233
234 fn reference_locations(&self, symbol: &str) -> Vec<(Arc<str>, u32, u16, u16)> {
235 let refs = self.reference_locations.lock();
236 refs.get(symbol).cloned().unwrap_or_default()
237 }
238
239 fn has_reference(&self, symbol: &str) -> bool {
240 let refs = self.reference_locations.lock();
241 refs.get(symbol).is_some_and(|locs| !locs.is_empty())
242 }
243
244 fn clear_file_references(&self, file: &str) {
245 let mut refs = self.reference_locations.lock();
246 for locs in refs.values_mut() {
247 locs.retain(|(loc_file, _, _, _)| loc_file.as_ref() != file);
248 }
249 }
250}
251
252#[derive(Debug, Clone, Default)]
260pub struct ClassNodeFields {
261 pub fqcn: Arc<str>,
262 pub is_interface: bool,
263 pub is_trait: bool,
264 pub is_enum: bool,
265 pub is_abstract: bool,
266 pub parent: Option<Arc<str>>,
267 pub interfaces: Arc<[Arc<str>]>,
268 pub traits: Arc<[Arc<str>]>,
269 pub extends: Arc<[Arc<str>]>,
270 pub template_params: Arc<[TemplateParam]>,
271 pub require_extends: Arc<[Arc<str>]>,
272 pub require_implements: Arc<[Arc<str>]>,
273 pub is_backed_enum: bool,
274 pub mixins: Arc<[Arc<str>]>,
275 pub deprecated: Option<Arc<str>>,
276 pub enum_scalar_type: Option<Union>,
277 pub is_final: bool,
278 pub is_readonly: bool,
279 pub location: Option<Location>,
280 pub extends_type_args: Arc<[Union]>,
281 pub implements_type_args: ImplementsTypeArgs,
282}
283
284impl ClassNodeFields {
285 pub fn for_class(fqcn: Arc<str>) -> Self {
286 Self {
287 fqcn,
288 ..Self::default()
289 }
290 }
291
292 pub fn for_interface(fqcn: Arc<str>) -> Self {
293 Self {
294 fqcn,
295 is_interface: true,
296 ..Self::default()
297 }
298 }
299
300 pub fn for_trait(fqcn: Arc<str>) -> Self {
301 Self {
302 fqcn,
303 is_trait: true,
304 ..Self::default()
305 }
306 }
307
308 pub fn for_enum(fqcn: Arc<str>) -> Self {
309 Self {
310 fqcn,
311 is_enum: true,
312 ..Self::default()
313 }
314 }
315}
316
317impl MirDb {
318 pub fn remove_file_definitions(&mut self, file: &str) {
319 let symbols = self.symbols_defined_in_file(file);
320 for symbol in &symbols {
321 self.deactivate_class_node(symbol);
322 self.deactivate_function_node(symbol);
323 self.deactivate_class_methods(symbol);
324 self.deactivate_class_properties(symbol);
325 self.deactivate_class_constants(symbol);
326 self.deactivate_global_constant_node(symbol);
327 }
328 let symbol_set: HashSet<Arc<str>> = symbols.into_iter().collect();
329 Arc::make_mut(&mut self.symbol_to_file).retain(|sym, defining_file| {
330 defining_file.as_ref() != file && !symbol_set.contains(sym)
331 });
332 Arc::make_mut(&mut self.file_namespaces).retain(|path, _| path.as_ref() != file);
333 Arc::make_mut(&mut self.file_imports).retain(|path, _| path.as_ref() != file);
334 Arc::make_mut(&mut self.global_vars).retain(|name, _| !symbol_set.contains(name));
335 self.clear_file_references(file);
336 }
337
338 pub fn type_count(&self) -> usize {
339 self.class_nodes
340 .values()
341 .filter(|node| node.active(self))
342 .count()
343 }
344
345 pub fn function_count(&self) -> usize {
346 self.function_nodes
347 .values()
348 .filter(|node| node.active(self))
349 .count()
350 }
351
352 pub fn constant_count(&self) -> usize {
353 self.global_constant_nodes
354 .values()
355 .filter(|node| node.active(self))
356 .count()
357 }
358
359 pub fn ingest_stub_slice(&mut self, slice: &StubSlice) {
365 use std::collections::HashSet;
366
367 let mut slice = slice.clone();
370 mir_codebase::storage::deduplicate_params_in_slice(&mut slice);
371
372 if let Some(file) = &slice.file {
373 let file_cloned = file.clone();
374 if let Some(namespace) = &slice.namespace {
375 Arc::make_mut(&mut self.file_namespaces)
376 .insert(file_cloned.clone(), namespace.clone());
377 }
378 if !slice.imports.is_empty() {
379 Arc::make_mut(&mut self.file_imports)
380 .insert(file_cloned.clone(), slice.imports.clone());
381 }
382 for (name, _) in &slice.global_vars {
383 let global_name = name.strip_prefix('$').unwrap_or(name.as_ref());
384 Arc::make_mut(&mut self.symbol_to_file)
385 .insert(Arc::from(global_name), file_cloned.clone());
386 }
387 }
388 for (name, ty) in &slice.global_vars {
389 let global_name = name.strip_prefix('$').unwrap_or(name.as_ref());
390 Arc::make_mut(&mut self.global_vars).insert(Arc::from(global_name), ty.clone());
391 }
392
393 let slice_file = slice.file.as_ref().map(|f| f.clone());
394 for cls in &slice.classes {
395 if let Some(file) = &slice_file {
396 Arc::make_mut(&mut self.symbol_to_file).insert(cls.fqcn.clone(), file.clone());
397 }
398 let fqcn_cloned = cls.fqcn.clone();
399 self.upsert_class_node(ClassNodeFields {
400 is_abstract: cls.is_abstract,
401 parent: cls.parent.clone(),
402 interfaces: Arc::from(cls.interfaces.as_ref()),
403 traits: Arc::from(cls.traits.as_ref()),
404 template_params: Arc::from(cls.template_params.as_ref()),
405 mixins: Arc::from(cls.mixins.as_ref()),
406 deprecated: cls.deprecated.clone(),
407 is_final: cls.is_final,
408 is_readonly: cls.is_readonly,
409 location: cls.location.clone(),
410 extends_type_args: Arc::from(cls.extends_type_args.as_ref()),
411 implements_type_args: Arc::from(
412 cls.implements_type_args
413 .iter()
414 .map(|(iface, args)| (iface.clone(), Arc::from(args.as_ref())))
415 .collect::<Vec<_>>(),
416 ),
417 ..ClassNodeFields::for_class(fqcn_cloned)
418 });
419 if self.method_nodes.contains_key(cls.fqcn.as_ref()) {
420 let method_keep: HashSet<&str> =
421 cls.own_methods.keys().map(|m| m.as_ref()).collect();
422 self.prune_class_methods(&cls.fqcn, &method_keep);
423 }
424 for method in cls.own_methods.values() {
425 self.upsert_method_node(method.as_ref());
430 }
431 if self.property_nodes.contains_key(cls.fqcn.as_ref()) {
432 let prop_keep: HashSet<&str> =
433 cls.own_properties.keys().map(|p| p.as_ref()).collect();
434 self.prune_class_properties(&cls.fqcn, &prop_keep);
435 }
436 for prop in cls.own_properties.values() {
437 self.upsert_property_node(&cls.fqcn, prop);
438 }
439 if self.class_constant_nodes.contains_key(cls.fqcn.as_ref()) {
440 let const_keep: HashSet<&str> =
441 cls.own_constants.keys().map(|c| c.as_ref()).collect();
442 self.prune_class_constants(&cls.fqcn, &const_keep);
443 }
444 for constant in cls.own_constants.values() {
445 self.upsert_class_constant_node(&cls.fqcn, constant);
446 }
447 }
448
449 for iface in &slice.interfaces {
450 if let Some(file) = &slice_file {
451 Arc::make_mut(&mut self.symbol_to_file).insert(iface.fqcn.clone(), file.clone());
452 }
453 let fqcn_cloned = iface.fqcn.clone();
454 self.upsert_class_node(ClassNodeFields {
455 extends: Arc::from(iface.extends.as_ref()),
456 template_params: Arc::from(iface.template_params.as_ref()),
457 location: iface.location.clone(),
458 ..ClassNodeFields::for_interface(fqcn_cloned)
459 });
460 if self.method_nodes.contains_key(iface.fqcn.as_ref()) {
461 let method_keep: HashSet<&str> =
462 iface.own_methods.keys().map(|m| m.as_ref()).collect();
463 self.prune_class_methods(&iface.fqcn, &method_keep);
464 }
465 for method in iface.own_methods.values() {
466 self.upsert_method_node(method.as_ref());
467 }
468 if self.class_constant_nodes.contains_key(iface.fqcn.as_ref()) {
469 let const_keep: HashSet<&str> =
470 iface.own_constants.keys().map(|c| c.as_ref()).collect();
471 self.prune_class_constants(&iface.fqcn, &const_keep);
472 }
473 for constant in iface.own_constants.values() {
474 self.upsert_class_constant_node(&iface.fqcn, constant);
475 }
476 }
477
478 for tr in &slice.traits {
479 if let Some(file) = &slice_file {
480 Arc::make_mut(&mut self.symbol_to_file).insert(tr.fqcn.clone(), file.clone());
481 }
482 let fqcn_cloned = tr.fqcn.clone();
483 self.upsert_class_node(ClassNodeFields {
484 traits: Arc::from(tr.traits.as_ref()),
485 template_params: Arc::from(tr.template_params.as_ref()),
486 require_extends: Arc::from(tr.require_extends.as_ref()),
487 require_implements: Arc::from(tr.require_implements.as_ref()),
488 location: tr.location.clone(),
489 ..ClassNodeFields::for_trait(fqcn_cloned)
490 });
491 if self.method_nodes.contains_key(tr.fqcn.as_ref()) {
492 let method_keep: HashSet<&str> =
493 tr.own_methods.keys().map(|m| m.as_ref()).collect();
494 self.prune_class_methods(&tr.fqcn, &method_keep);
495 }
496 for method in tr.own_methods.values() {
497 self.upsert_method_node(method.as_ref());
498 }
499 if self.property_nodes.contains_key(tr.fqcn.as_ref()) {
500 let prop_keep: HashSet<&str> =
501 tr.own_properties.keys().map(|p| p.as_ref()).collect();
502 self.prune_class_properties(&tr.fqcn, &prop_keep);
503 }
504 for prop in tr.own_properties.values() {
505 self.upsert_property_node(&tr.fqcn, prop);
506 }
507 if self.class_constant_nodes.contains_key(tr.fqcn.as_ref()) {
508 let const_keep: HashSet<&str> =
509 tr.own_constants.keys().map(|c| c.as_ref()).collect();
510 self.prune_class_constants(&tr.fqcn, &const_keep);
511 }
512 for constant in tr.own_constants.values() {
513 self.upsert_class_constant_node(&tr.fqcn, constant);
514 }
515 }
516
517 for en in &slice.enums {
518 if let Some(file) = &slice_file {
519 Arc::make_mut(&mut self.symbol_to_file).insert(en.fqcn.clone(), file.clone());
520 }
521 let fqcn_cloned = en.fqcn.clone();
522 self.upsert_class_node(ClassNodeFields {
523 interfaces: Arc::from(en.interfaces.as_ref()),
524 is_backed_enum: en.scalar_type.is_some(),
525 enum_scalar_type: en.scalar_type.clone(),
526 location: en.location.clone(),
527 ..ClassNodeFields::for_enum(fqcn_cloned)
528 });
529 if self.method_nodes.contains_key(en.fqcn.as_ref()) {
530 let mut method_keep: HashSet<&str> =
531 en.own_methods.keys().map(|m| m.as_ref()).collect();
532 method_keep.insert("cases");
533 if en.scalar_type.is_some() {
534 method_keep.insert("from");
535 method_keep.insert("tryfrom");
536 }
537 self.prune_class_methods(&en.fqcn, &method_keep);
538 }
539 for method in en.own_methods.values() {
540 self.upsert_method_node(method.as_ref());
541 }
542 let synth_method = |name: &str| mir_codebase::storage::MethodStorage {
543 fqcn: en.fqcn.clone(),
544 name: Arc::from(name),
545 params: Arc::from([].as_ref()),
546 return_type: Some(Arc::new(Union::mixed())),
547 inferred_return_type: None,
548 visibility: Visibility::Public,
549 is_static: true,
550 is_abstract: false,
551 is_constructor: false,
552 template_params: vec![],
553 assertions: vec![],
554 throws: vec![],
555 is_final: false,
556 is_internal: false,
557 is_pure: false,
558 deprecated: None,
559 location: None,
560 };
561 let already = |name: &str| {
562 en.own_methods
563 .keys()
564 .any(|k| k.as_ref().eq_ignore_ascii_case(name))
565 };
566 if !already("cases") {
567 self.upsert_method_node(&synth_method("cases"));
568 }
569 if en.scalar_type.is_some() {
570 if !already("from") {
571 self.upsert_method_node(&synth_method("from"));
572 }
573 if !already("tryFrom") {
574 self.upsert_method_node(&synth_method("tryFrom"));
575 }
576 }
577 if self.class_constant_nodes.contains_key(en.fqcn.as_ref()) {
578 let mut const_keep: HashSet<&str> =
579 en.own_constants.keys().map(|c| c.as_ref()).collect();
580 for case in en.cases.values() {
581 const_keep.insert(case.name.as_ref());
582 }
583 self.prune_class_constants(&en.fqcn, &const_keep);
584 }
585 for constant in en.own_constants.values() {
586 self.upsert_class_constant_node(&en.fqcn, constant);
587 }
588 for case in en.cases.values() {
589 let case_const = ConstantStorage {
590 name: case.name.clone(),
591 ty: mir_types::Union::mixed(),
592 visibility: None,
593 is_final: false,
594 location: case.location.clone(),
595 };
596 self.upsert_class_constant_node(&en.fqcn, &case_const);
597 }
598 }
599
600 for func in &slice.functions {
601 if let Some(file) = &slice_file {
602 Arc::make_mut(&mut self.symbol_to_file).insert(func.fqn.clone(), file.clone());
603 }
604 self.upsert_function_node(func);
605 }
606 for (fqn, ty) in &slice.constants {
607 let fqn_cloned = fqn.clone();
608 self.upsert_global_constant_node(fqn_cloned, ty.clone());
609 }
610 }
611
612 #[allow(clippy::too_many_arguments)]
617 pub fn upsert_class_node(&mut self, fields: ClassNodeFields) -> ClassNode {
618 use salsa::Setter as _;
619 let ClassNodeFields {
620 fqcn,
621 is_interface,
622 is_trait,
623 is_enum,
624 is_abstract,
625 parent,
626 interfaces,
627 traits,
628 extends,
629 template_params,
630 require_extends,
631 require_implements,
632 is_backed_enum,
633 mixins,
634 deprecated,
635 enum_scalar_type,
636 is_final,
637 is_readonly,
638 location,
639 extends_type_args,
640 implements_type_args,
641 } = fields;
642 if let Some(&node) = self.class_nodes.get(&fqcn) {
643 if node.active(self)
656 && node.is_interface(self) == is_interface
657 && node.is_trait(self) == is_trait
658 && node.is_enum(self) == is_enum
659 && node.is_abstract(self) == is_abstract
660 && node.is_backed_enum(self) == is_backed_enum
661 && node.parent(self) == parent
662 && *node.interfaces(self) == *interfaces
663 && *node.traits(self) == *traits
664 && *node.extends(self) == *extends
665 && *node.template_params(self) == *template_params
666 && *node.require_extends(self) == *require_extends
667 && *node.require_implements(self) == *require_implements
668 && *node.mixins(self) == *mixins
669 && node.deprecated(self) == deprecated
670 && node.enum_scalar_type(self) == enum_scalar_type
671 && node.is_final(self) == is_final
672 && node.is_readonly(self) == is_readonly
673 && node.location(self) == location
674 && *node.extends_type_args(self) == *extends_type_args
675 && *node.implements_type_args(self) == *implements_type_args
676 {
677 return node;
678 }
679 node.set_active(self).to(true);
680 node.set_is_interface(self).to(is_interface);
681 node.set_is_trait(self).to(is_trait);
682 node.set_is_enum(self).to(is_enum);
683 node.set_is_abstract(self).to(is_abstract);
684 node.set_parent(self).to(parent);
685 node.set_interfaces(self).to(interfaces);
686 node.set_traits(self).to(traits);
687 node.set_extends(self).to(extends);
688 node.set_template_params(self).to(template_params);
689 node.set_require_extends(self).to(require_extends);
690 node.set_require_implements(self).to(require_implements);
691 node.set_is_backed_enum(self).to(is_backed_enum);
692 node.set_mixins(self).to(mixins);
693 node.set_deprecated(self).to(deprecated);
694 node.set_enum_scalar_type(self).to(enum_scalar_type);
695 node.set_is_final(self).to(is_final);
696 node.set_is_readonly(self).to(is_readonly);
697 node.set_location(self).to(location);
698 node.set_extends_type_args(self).to(extends_type_args);
699 node.set_implements_type_args(self).to(implements_type_args);
700 node
701 } else {
702 let node = ClassNode::new(
703 self,
704 fqcn.clone(),
705 true,
706 is_interface,
707 is_trait,
708 is_enum,
709 is_abstract,
710 parent,
711 interfaces,
712 traits,
713 extends,
714 template_params,
715 require_extends,
716 require_implements,
717 is_backed_enum,
718 mixins,
719 deprecated,
720 enum_scalar_type,
721 is_final,
722 is_readonly,
723 location,
724 extends_type_args,
725 implements_type_args,
726 );
727 Arc::make_mut(&mut self.class_node_keys_lower)
728 .insert(fqcn.to_ascii_lowercase(), fqcn.clone());
729 Arc::make_mut(&mut self.class_nodes).insert(fqcn, node);
730 node
731 }
732 }
733
734 pub fn deactivate_class_node(&mut self, fqcn: &str) {
739 use salsa::Setter as _;
740 if let Some(&node) = self.class_nodes.get(fqcn) {
741 node.set_active(self).to(false);
742 }
743 }
744
745 pub fn upsert_function_node(&mut self, storage: &FunctionStorage) -> FunctionNode {
747 use salsa::Setter as _;
748 let fqn = &storage.fqn;
749 if let Some(&node) = self.function_nodes.get(fqn.as_ref()) {
750 if node.active(self)
756 && node.short_name(self) == storage.short_name
757 && node.is_pure(self) == storage.is_pure
758 && node.deprecated(self) == storage.deprecated
759 && node.return_type(self).as_deref() == storage.return_type.as_deref()
760 && node.location(self) == storage.location
761 && *node.params(self) == *storage.params.as_ref()
762 && *node.template_params(self) == *storage.template_params
763 && *node.assertions(self) == *storage.assertions
764 && *node.throws(self) == *storage.throws
765 {
766 return node;
767 }
768 node.set_active(self).to(true);
769 node.set_short_name(self).to(storage.short_name.clone());
770 node.set_params(self).to(storage.params.clone());
771 node.set_return_type(self).to(storage.return_type.clone());
772 node.set_template_params(self)
773 .to(Arc::from(storage.template_params.as_slice()));
774 node.set_assertions(self)
775 .to(Arc::from(storage.assertions.as_slice()));
776 node.set_throws(self)
777 .to(Arc::from(storage.throws.as_slice()));
778 node.set_deprecated(self).to(storage.deprecated.clone());
779 node.set_is_pure(self).to(storage.is_pure);
780 node.set_location(self).to(storage.location.clone());
781 node
782 } else {
783 let node = FunctionNode::new(
784 self,
785 fqn.clone(),
786 storage.short_name.clone(),
787 true,
788 storage.params.clone(),
789 storage.return_type.clone(),
790 storage
791 .inferred_return_type
792 .as_ref()
793 .map(|t| Arc::new(t.clone())),
794 Arc::from(storage.template_params.as_slice()),
795 Arc::from(storage.assertions.as_slice()),
796 Arc::from(storage.throws.as_slice()),
797 storage.deprecated.clone(),
798 storage.is_pure,
799 storage.location.clone(),
800 );
801 Arc::make_mut(&mut self.function_node_keys_lower)
802 .insert(fqn.to_ascii_lowercase(), fqn.clone());
803 Arc::make_mut(&mut self.function_nodes).insert(fqn.clone(), node);
804 node
805 }
806 }
807
808 pub fn commit_inferred_return_types(
822 &mut self,
823 functions: Vec<(Arc<str>, mir_types::Union)>,
824 methods: Vec<(Arc<str>, Arc<str>, mir_types::Union)>,
825 ) {
826 use salsa::Setter as _;
827 for (fqn, inferred) in functions {
828 if let Some(&node) = self.function_nodes.get(fqn.as_ref()) {
829 if !node.active(self) {
830 continue;
831 }
832 let new = Some(Arc::new(inferred));
833 if node.inferred_return_type(self) == new {
834 continue;
835 }
836 node.set_inferred_return_type(self).to(new);
837 }
838 }
839 for (fqcn, name, inferred) in methods {
840 let name_lower: Arc<str> = if name.chars().all(|c| !c.is_uppercase()) {
841 name.clone()
842 } else {
843 Arc::from(name.to_lowercase().as_str())
844 };
845 let node = self
846 .method_nodes
847 .get(fqcn.as_ref())
848 .and_then(|m| m.get(&name_lower))
849 .copied();
850 if let Some(node) = node {
851 if !node.active(self) {
852 continue;
853 }
854 let new = Some(Arc::new(inferred));
855 if node.inferred_return_type(self) == new {
856 continue;
857 }
858 node.set_inferred_return_type(self).to(new);
859 }
860 }
861 }
862
863 pub fn deactivate_function_node(&mut self, fqn: &str) {
865 use salsa::Setter as _;
866 if let Some(&node) = self.function_nodes.get(fqn) {
867 node.set_active(self).to(false);
868 }
869 }
870
871 pub fn upsert_method_node(&mut self, storage: &MethodStorage) -> MethodNode {
873 use salsa::Setter as _;
874 let fqcn = &storage.fqcn;
875 let name_lower: Arc<str> = Arc::from(storage.name.to_lowercase().as_str());
876 let existing = self
879 .method_nodes
880 .get(fqcn.as_ref())
881 .and_then(|m| m.get(&name_lower))
882 .copied();
883 if let Some(node) = existing {
884 if node.active(self)
888 && node.visibility(self) == storage.visibility
889 && node.is_static(self) == storage.is_static
890 && node.is_abstract(self) == storage.is_abstract
891 && node.is_final(self) == storage.is_final
892 && node.is_constructor(self) == storage.is_constructor
893 && node.is_pure(self) == storage.is_pure
894 && node.is_internal(self) == storage.is_internal
895 && node.deprecated(self) == storage.deprecated
896 && node.return_type(self).as_deref() == storage.return_type.as_deref()
897 && node.location(self) == storage.location
898 && *node.params(self) == *storage.params.as_ref()
899 && *node.template_params(self) == *storage.template_params
900 && *node.assertions(self) == *storage.assertions
901 && *node.throws(self) == *storage.throws
902 {
903 return node;
904 }
905 node.set_active(self).to(true);
906 node.set_params(self).to(storage.params.clone());
907 node.set_return_type(self).to(storage.return_type.clone());
908 node.set_template_params(self)
909 .to(Arc::from(storage.template_params.as_slice()));
910 node.set_assertions(self)
911 .to(Arc::from(storage.assertions.as_slice()));
912 node.set_throws(self)
913 .to(Arc::from(storage.throws.as_slice()));
914 node.set_deprecated(self).to(storage.deprecated.clone());
915 node.set_is_internal(self).to(storage.is_internal);
916 node.set_visibility(self).to(storage.visibility);
917 node.set_is_static(self).to(storage.is_static);
918 node.set_is_abstract(self).to(storage.is_abstract);
919 node.set_is_final(self).to(storage.is_final);
920 node.set_is_constructor(self).to(storage.is_constructor);
921 node.set_is_pure(self).to(storage.is_pure);
922 node.set_location(self).to(storage.location.clone());
923 node
924 } else {
925 let node = MethodNode::new(
927 self,
928 fqcn.clone(),
929 storage.name.clone(),
930 true,
931 storage.params.clone(),
932 storage.return_type.clone(),
933 storage
934 .inferred_return_type
935 .as_ref()
936 .map(|t| Arc::new(t.clone())),
937 Arc::from(storage.template_params.as_slice()),
938 Arc::from(storage.assertions.as_slice()),
939 Arc::from(storage.throws.as_slice()),
940 storage.deprecated.clone(),
941 storage.is_internal,
942 storage.visibility,
943 storage.is_static,
944 storage.is_abstract,
945 storage.is_final,
946 storage.is_constructor,
947 storage.is_pure,
948 storage.location.clone(),
949 );
950 Arc::make_mut(&mut self.method_nodes)
951 .entry(fqcn.clone())
952 .or_default()
953 .insert(name_lower, node);
954 node
955 }
956 }
957
958 pub fn deactivate_class_methods(&mut self, fqcn: &str) {
960 use salsa::Setter as _;
961 let nodes: Vec<MethodNode> = match self.method_nodes.get(fqcn) {
962 Some(methods) => methods.values().copied().collect(),
963 None => return,
964 };
965 for node in nodes {
966 node.set_active(self).to(false);
967 }
968 }
969
970 pub fn prune_class_methods<T>(&mut self, fqcn: &str, keep_lower: &std::collections::HashSet<T>)
976 where
977 T: Eq + std::hash::Hash + std::borrow::Borrow<str>,
978 {
979 use salsa::Setter as _;
980 let candidates: Vec<MethodNode> = self
981 .method_nodes
982 .get(fqcn)
983 .map(|m| {
984 m.iter()
985 .filter(|(k, _)| !keep_lower.contains(k.as_ref()))
986 .map(|(_, n)| *n)
987 .collect()
988 })
989 .unwrap_or_default();
990 for node in candidates {
991 if node.active(self) {
992 node.set_active(self).to(false);
993 }
994 }
995 }
996
997 pub fn prune_class_properties<T>(&mut self, fqcn: &str, keep: &std::collections::HashSet<T>)
999 where
1000 T: Eq + std::hash::Hash + std::borrow::Borrow<str>,
1001 {
1002 use salsa::Setter as _;
1003 let candidates: Vec<PropertyNode> = self
1004 .property_nodes
1005 .get(fqcn)
1006 .map(|m| {
1007 m.iter()
1008 .filter(|(k, _)| !keep.contains(k.as_ref()))
1009 .map(|(_, n)| *n)
1010 .collect()
1011 })
1012 .unwrap_or_default();
1013 for node in candidates {
1014 if node.active(self) {
1015 node.set_active(self).to(false);
1016 }
1017 }
1018 }
1019
1020 pub fn prune_class_constants<T>(&mut self, fqcn: &str, keep: &std::collections::HashSet<T>)
1022 where
1023 T: Eq + std::hash::Hash + std::borrow::Borrow<str>,
1024 {
1025 use salsa::Setter as _;
1026 let candidates: Vec<ClassConstantNode> = self
1027 .class_constant_nodes
1028 .get(fqcn)
1029 .map(|m| {
1030 m.iter()
1031 .filter(|(k, _)| !keep.contains(k.as_ref()))
1032 .map(|(_, n)| *n)
1033 .collect()
1034 })
1035 .unwrap_or_default();
1036 for node in candidates {
1037 if node.active(self) {
1038 node.set_active(self).to(false);
1039 }
1040 }
1041 }
1042
1043 pub fn upsert_property_node(&mut self, fqcn: &Arc<str>, storage: &PropertyStorage) {
1045 use salsa::Setter as _;
1046 let existing = self
1047 .property_nodes
1048 .get(fqcn.as_ref())
1049 .and_then(|m| m.get(storage.name.as_ref()))
1050 .copied();
1051 if let Some(node) = existing {
1052 if node.active(self)
1054 && node.visibility(self) == storage.visibility
1055 && node.is_static(self) == storage.is_static
1056 && node.is_readonly(self) == storage.is_readonly
1057 && node.ty(self) == storage.ty
1058 && node.location(self) == storage.location
1059 {
1060 return;
1061 }
1062 node.set_active(self).to(true);
1063 node.set_ty(self).to(storage.ty.clone());
1064 node.set_visibility(self).to(storage.visibility);
1065 node.set_is_static(self).to(storage.is_static);
1066 node.set_is_readonly(self).to(storage.is_readonly);
1067 node.set_location(self).to(storage.location.clone());
1068 } else {
1069 let node = PropertyNode::new(
1070 self,
1071 fqcn.clone(),
1072 storage.name.clone(),
1073 true,
1074 storage.ty.clone(),
1075 storage.visibility,
1076 storage.is_static,
1077 storage.is_readonly,
1078 storage.location.clone(),
1079 );
1080 Arc::make_mut(&mut self.property_nodes)
1081 .entry(fqcn.clone())
1082 .or_default()
1083 .insert(storage.name.clone(), node);
1084 }
1085 }
1086
1087 pub fn deactivate_class_properties(&mut self, fqcn: &str) {
1089 use salsa::Setter as _;
1090 let nodes: Vec<PropertyNode> = match self.property_nodes.get(fqcn) {
1091 Some(props) => props.values().copied().collect(),
1092 None => return,
1093 };
1094 for node in nodes {
1095 node.set_active(self).to(false);
1096 }
1097 }
1098
1099 pub fn upsert_class_constant_node(&mut self, fqcn: &Arc<str>, storage: &ConstantStorage) {
1101 use salsa::Setter as _;
1102 let existing = self
1103 .class_constant_nodes
1104 .get(fqcn.as_ref())
1105 .and_then(|m| m.get(storage.name.as_ref()))
1106 .copied();
1107 if let Some(node) = existing {
1108 if node.active(self)
1110 && node.visibility(self) == storage.visibility
1111 && node.is_final(self) == storage.is_final
1112 && node.ty(self) == storage.ty
1113 && node.location(self) == storage.location
1114 {
1115 return;
1116 }
1117 node.set_active(self).to(true);
1118 node.set_ty(self).to(storage.ty.clone());
1119 node.set_visibility(self).to(storage.visibility);
1120 node.set_is_final(self).to(storage.is_final);
1121 node.set_location(self).to(storage.location.clone());
1122 } else {
1123 let node = ClassConstantNode::new(
1124 self,
1125 fqcn.clone(),
1126 storage.name.clone(),
1127 true,
1128 storage.ty.clone(),
1129 storage.visibility,
1130 storage.is_final,
1131 storage.location.clone(),
1132 );
1133 Arc::make_mut(&mut self.class_constant_nodes)
1134 .entry(fqcn.clone())
1135 .or_default()
1136 .insert(storage.name.clone(), node);
1137 }
1138 }
1139
1140 pub fn upsert_global_constant_node(&mut self, fqn: Arc<str>, ty: Union) -> GlobalConstantNode {
1142 use salsa::Setter as _;
1143 if let Some(&node) = self.global_constant_nodes.get(&fqn) {
1144 if node.active(self) && node.ty(self) == ty {
1146 return node;
1147 }
1148 node.set_active(self).to(true);
1149 node.set_ty(self).to(ty);
1150 node
1151 } else {
1152 let node = GlobalConstantNode::new(self, fqn.clone(), true, ty);
1153 Arc::make_mut(&mut self.global_constant_nodes).insert(fqn, node);
1154 node
1155 }
1156 }
1157
1158 pub fn deactivate_global_constant_node(&mut self, fqn: &str) {
1160 use salsa::Setter as _;
1161 if let Some(&node) = self.global_constant_nodes.get(fqn) {
1162 node.set_active(self).to(false);
1163 }
1164 }
1165
1166 pub fn deactivate_class_constants(&mut self, fqcn: &str) {
1168 use salsa::Setter as _;
1169 let nodes: Vec<ClassConstantNode> = match self.class_constant_nodes.get(fqcn) {
1170 Some(consts) => consts.values().copied().collect(),
1171 None => return,
1172 };
1173 for node in nodes {
1174 node.set_active(self).to(false);
1175 }
1176 }
1177}