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 class_own_constants(&self, fqcn: &str) -> Vec<ClassConstantNode> {
139 self.class_constant_nodes
140 .get(fqcn)
141 .map(|m| m.values().copied().collect())
142 .unwrap_or_default()
143 }
144
145 fn active_class_node_fqcns(&self) -> Vec<Arc<str>> {
146 self.class_nodes
147 .iter()
148 .filter_map(|(fqcn, node)| {
149 if node.active(self) {
150 Some(fqcn.clone())
151 } else {
152 None
153 }
154 })
155 .collect()
156 }
157
158 fn active_function_node_fqns(&self) -> Vec<Arc<str>> {
159 self.function_nodes
160 .iter()
161 .filter_map(|(fqn, node)| {
162 if node.active(self) {
163 Some(fqn.clone())
164 } else {
165 None
166 }
167 })
168 .collect()
169 }
170
171 fn file_namespace(&self, file: &str) -> Option<Arc<str>> {
172 self.file_namespaces.get(file).cloned()
173 }
174
175 fn file_imports(&self, file: &str) -> HashMap<String, String> {
176 self.file_imports.get(file).cloned().unwrap_or_default()
177 }
178
179 fn global_var_type(&self, name: &str) -> Option<Union> {
180 self.global_vars.get(name).cloned()
181 }
182
183 fn file_import_snapshots(&self) -> Vec<(Arc<str>, HashMap<String, String>)> {
184 self.file_imports
185 .iter()
186 .map(|(file, imports)| (file.clone(), imports.clone()))
187 .collect()
188 }
189
190 fn symbol_defining_file(&self, symbol: &str) -> Option<Arc<str>> {
191 self.symbol_to_file.get(symbol).cloned()
192 }
193
194 fn symbols_defined_in_file(&self, file: &str) -> Vec<Arc<str>> {
195 self.symbol_to_file
196 .iter()
197 .filter_map(|(sym, defining_file)| {
198 if defining_file.as_ref() == file {
199 Some(sym.clone())
200 } else {
201 None
202 }
203 })
204 .collect()
205 }
206
207 fn record_reference_location(&self, loc: RefLoc) {
208 let mut refs = self.reference_locations.lock();
209 let entry = refs.entry(loc.symbol_key).or_default();
210 let tuple = (loc.file, loc.line, loc.col_start, loc.col_end);
211 if !entry.iter().any(|existing| existing == &tuple) {
212 entry.push(tuple);
213 }
214 }
215
216 fn replay_reference_locations(&self, file: Arc<str>, locs: &[(String, u32, u16, u16)]) {
217 for (symbol, line, col_start, col_end) in locs {
218 self.record_reference_location(RefLoc {
219 symbol_key: Arc::from(symbol.as_str()),
220 file: file.clone(),
221 line: *line,
222 col_start: *col_start,
223 col_end: *col_end,
224 });
225 }
226 }
227
228 fn extract_file_reference_locations(&self, file: &str) -> Vec<(Arc<str>, u32, u16, u16)> {
229 let refs = self.reference_locations.lock();
230 let mut out = Vec::new();
231 for (symbol, locs) in refs.iter() {
232 for (loc_file, line, col_start, col_end) in locs {
233 if loc_file.as_ref() == file {
234 out.push((symbol.clone(), *line, *col_start, *col_end));
235 }
236 }
237 }
238 out
239 }
240
241 fn reference_locations(&self, symbol: &str) -> Vec<(Arc<str>, u32, u16, u16)> {
242 let refs = self.reference_locations.lock();
243 refs.get(symbol).cloned().unwrap_or_default()
244 }
245
246 fn has_reference(&self, symbol: &str) -> bool {
247 let refs = self.reference_locations.lock();
248 refs.get(symbol).is_some_and(|locs| !locs.is_empty())
249 }
250
251 fn clear_file_references(&self, file: &str) {
252 let mut refs = self.reference_locations.lock();
253 for locs in refs.values_mut() {
254 locs.retain(|(loc_file, _, _, _)| loc_file.as_ref() != file);
255 }
256 }
257}
258
259#[derive(Debug, Clone, Default)]
267pub struct ClassNodeFields {
268 pub fqcn: Arc<str>,
269 pub is_interface: bool,
270 pub is_trait: bool,
271 pub is_enum: bool,
272 pub is_abstract: bool,
273 pub parent: Option<Arc<str>>,
274 pub interfaces: Arc<[Arc<str>]>,
275 pub traits: Arc<[Arc<str>]>,
276 pub extends: Arc<[Arc<str>]>,
277 pub template_params: Arc<[TemplateParam]>,
278 pub require_extends: Arc<[Arc<str>]>,
279 pub require_implements: Arc<[Arc<str>]>,
280 pub is_backed_enum: bool,
281 pub mixins: Arc<[Arc<str>]>,
282 pub deprecated: Option<Arc<str>>,
283 pub enum_scalar_type: Option<Union>,
284 pub is_final: bool,
285 pub is_readonly: bool,
286 pub location: Option<Location>,
287 pub extends_type_args: Arc<[Union]>,
288 pub implements_type_args: ImplementsTypeArgs,
289}
290
291impl ClassNodeFields {
292 pub fn for_class(fqcn: Arc<str>) -> Self {
293 Self {
294 fqcn,
295 ..Self::default()
296 }
297 }
298
299 pub fn for_interface(fqcn: Arc<str>) -> Self {
300 Self {
301 fqcn,
302 is_interface: true,
303 ..Self::default()
304 }
305 }
306
307 pub fn for_trait(fqcn: Arc<str>) -> Self {
308 Self {
309 fqcn,
310 is_trait: true,
311 ..Self::default()
312 }
313 }
314
315 pub fn for_enum(fqcn: Arc<str>) -> Self {
316 Self {
317 fqcn,
318 is_enum: true,
319 ..Self::default()
320 }
321 }
322}
323
324impl MirDb {
325 pub fn remove_file_definitions(&mut self, file: &str) {
326 let symbols = self.symbols_defined_in_file(file);
327 for symbol in &symbols {
328 self.deactivate_class_node(symbol);
329 self.deactivate_function_node(symbol);
330 self.deactivate_class_methods(symbol);
331 self.deactivate_class_properties(symbol);
332 self.deactivate_class_constants(symbol);
333 self.deactivate_global_constant_node(symbol);
334 }
335 let symbol_set: HashSet<Arc<str>> = symbols.into_iter().collect();
336 Arc::make_mut(&mut self.symbol_to_file).retain(|sym, defining_file| {
337 defining_file.as_ref() != file && !symbol_set.contains(sym)
338 });
339 Arc::make_mut(&mut self.file_namespaces).retain(|path, _| path.as_ref() != file);
340 Arc::make_mut(&mut self.file_imports).retain(|path, _| path.as_ref() != file);
341 Arc::make_mut(&mut self.global_vars).retain(|name, _| !symbol_set.contains(name));
342 self.clear_file_references(file);
343 }
344
345 pub fn type_count(&self) -> usize {
346 self.class_nodes
347 .values()
348 .filter(|node| node.active(self))
349 .count()
350 }
351
352 pub fn function_count(&self) -> usize {
353 self.function_nodes
354 .values()
355 .filter(|node| node.active(self))
356 .count()
357 }
358
359 pub fn constant_count(&self) -> usize {
360 self.global_constant_nodes
361 .values()
362 .filter(|node| node.active(self))
363 .count()
364 }
365
366 pub fn ingest_stub_slice(&mut self, slice: &StubSlice) {
372 use std::collections::HashSet;
373
374 let total_methods: usize = slice
380 .classes
381 .iter()
382 .map(|c| c.own_methods.len())
383 .sum::<usize>()
384 + slice
385 .interfaces
386 .iter()
387 .map(|i| i.own_methods.len())
388 .sum::<usize>()
389 + slice
390 .traits
391 .iter()
392 .map(|t| t.own_methods.len())
393 .sum::<usize>()
394 + slice
395 .enums
396 .iter()
397 .map(|e| e.own_methods.len())
398 .sum::<usize>()
399 + slice.functions.len();
400
401 let owned_slice;
402 let slice: &StubSlice = if total_methods >= 8 {
403 let mut s = slice.clone();
404 mir_codebase::storage::deduplicate_params_in_slice(&mut s);
405 owned_slice = s;
406 &owned_slice
407 } else {
408 slice
409 };
410
411 if let Some(file) = &slice.file {
412 if let Some(namespace) = &slice.namespace {
413 Arc::make_mut(&mut self.file_namespaces).insert(file.clone(), namespace.clone());
414 }
415 if !slice.imports.is_empty() {
416 Arc::make_mut(&mut self.file_imports).insert(file.clone(), slice.imports.clone());
417 }
418 for (name, _) in &slice.global_vars {
419 let global_name = name.strip_prefix('$').unwrap_or(name.as_ref());
420 Arc::make_mut(&mut self.symbol_to_file)
421 .insert(Arc::from(global_name), file.clone());
422 }
423 }
424 for (name, ty) in &slice.global_vars {
425 let global_name = name.strip_prefix('$').unwrap_or(name.as_ref());
426 Arc::make_mut(&mut self.global_vars).insert(Arc::from(global_name), ty.clone());
427 }
428
429 let slice_file = slice.file.clone();
430 for cls in &slice.classes {
431 if let Some(file) = &slice_file {
432 Arc::make_mut(&mut self.symbol_to_file).insert(cls.fqcn.clone(), file.clone());
433 }
434 self.upsert_class_node(ClassNodeFields {
435 is_abstract: cls.is_abstract,
436 parent: cls.parent.clone(),
437 interfaces: Arc::from(cls.interfaces.as_ref()),
438 traits: Arc::from(cls.traits.as_ref()),
439 template_params: Arc::from(cls.template_params.as_ref()),
440 mixins: Arc::from(cls.mixins.as_ref()),
441 deprecated: cls.deprecated.clone(),
442 is_final: cls.is_final,
443 is_readonly: cls.is_readonly,
444 location: cls.location.clone(),
445 extends_type_args: Arc::from(cls.extends_type_args.as_ref()),
446 implements_type_args: Arc::from(
447 cls.implements_type_args
448 .iter()
449 .map(|(iface, args)| (iface.clone(), Arc::from(args.as_ref())))
450 .collect::<Vec<_>>(),
451 ),
452 ..ClassNodeFields::for_class(cls.fqcn.clone())
453 });
454 if self.method_nodes.contains_key(cls.fqcn.as_ref()) {
455 let method_keep: HashSet<&str> =
456 cls.own_methods.keys().map(|m| m.as_ref()).collect();
457 self.prune_class_methods(&cls.fqcn, &method_keep);
458 }
459 for method in cls.own_methods.values() {
460 self.upsert_method_node(method.as_ref());
465 }
466 if self.property_nodes.contains_key(cls.fqcn.as_ref()) {
467 let prop_keep: HashSet<&str> =
468 cls.own_properties.keys().map(|p| p.as_ref()).collect();
469 self.prune_class_properties(&cls.fqcn, &prop_keep);
470 }
471 for prop in cls.own_properties.values() {
472 self.upsert_property_node(&cls.fqcn, prop);
473 }
474 if self.class_constant_nodes.contains_key(cls.fqcn.as_ref()) {
475 let const_keep: HashSet<&str> =
476 cls.own_constants.keys().map(|c| c.as_ref()).collect();
477 self.prune_class_constants(&cls.fqcn, &const_keep);
478 }
479 for constant in cls.own_constants.values() {
480 self.upsert_class_constant_node(&cls.fqcn, constant);
481 }
482 }
483
484 for iface in &slice.interfaces {
485 if let Some(file) = &slice_file {
486 Arc::make_mut(&mut self.symbol_to_file).insert(iface.fqcn.clone(), file.clone());
487 }
488 self.upsert_class_node(ClassNodeFields {
489 extends: Arc::from(iface.extends.as_ref()),
490 template_params: Arc::from(iface.template_params.as_ref()),
491 location: iface.location.clone(),
492 ..ClassNodeFields::for_interface(iface.fqcn.clone())
493 });
494 if self.method_nodes.contains_key(iface.fqcn.as_ref()) {
495 let method_keep: HashSet<&str> =
496 iface.own_methods.keys().map(|m| m.as_ref()).collect();
497 self.prune_class_methods(&iface.fqcn, &method_keep);
498 }
499 for method in iface.own_methods.values() {
500 self.upsert_method_node(method.as_ref());
501 }
502 if self.class_constant_nodes.contains_key(iface.fqcn.as_ref()) {
503 let const_keep: HashSet<&str> =
504 iface.own_constants.keys().map(|c| c.as_ref()).collect();
505 self.prune_class_constants(&iface.fqcn, &const_keep);
506 }
507 for constant in iface.own_constants.values() {
508 self.upsert_class_constant_node(&iface.fqcn, constant);
509 }
510 }
511
512 for tr in &slice.traits {
513 if let Some(file) = &slice_file {
514 Arc::make_mut(&mut self.symbol_to_file).insert(tr.fqcn.clone(), file.clone());
515 }
516 self.upsert_class_node(ClassNodeFields {
517 traits: Arc::from(tr.traits.as_ref()),
518 template_params: Arc::from(tr.template_params.as_ref()),
519 require_extends: Arc::from(tr.require_extends.as_ref()),
520 require_implements: Arc::from(tr.require_implements.as_ref()),
521 location: tr.location.clone(),
522 ..ClassNodeFields::for_trait(tr.fqcn.clone())
523 });
524 if self.method_nodes.contains_key(tr.fqcn.as_ref()) {
525 let method_keep: HashSet<&str> =
526 tr.own_methods.keys().map(|m| m.as_ref()).collect();
527 self.prune_class_methods(&tr.fqcn, &method_keep);
528 }
529 for method in tr.own_methods.values() {
530 self.upsert_method_node(method.as_ref());
531 }
532 if self.property_nodes.contains_key(tr.fqcn.as_ref()) {
533 let prop_keep: HashSet<&str> =
534 tr.own_properties.keys().map(|p| p.as_ref()).collect();
535 self.prune_class_properties(&tr.fqcn, &prop_keep);
536 }
537 for prop in tr.own_properties.values() {
538 self.upsert_property_node(&tr.fqcn, prop);
539 }
540 if self.class_constant_nodes.contains_key(tr.fqcn.as_ref()) {
541 let const_keep: HashSet<&str> =
542 tr.own_constants.keys().map(|c| c.as_ref()).collect();
543 self.prune_class_constants(&tr.fqcn, &const_keep);
544 }
545 for constant in tr.own_constants.values() {
546 self.upsert_class_constant_node(&tr.fqcn, constant);
547 }
548 }
549
550 for en in &slice.enums {
551 if let Some(file) = &slice_file {
552 Arc::make_mut(&mut self.symbol_to_file).insert(en.fqcn.clone(), file.clone());
553 }
554 self.upsert_class_node(ClassNodeFields {
555 interfaces: Arc::from(en.interfaces.as_ref()),
556 is_backed_enum: en.scalar_type.is_some(),
557 enum_scalar_type: en.scalar_type.clone(),
558 location: en.location.clone(),
559 ..ClassNodeFields::for_enum(en.fqcn.clone())
560 });
561 if self.method_nodes.contains_key(en.fqcn.as_ref()) {
562 let mut method_keep: HashSet<&str> =
563 en.own_methods.keys().map(|m| m.as_ref()).collect();
564 method_keep.insert("cases");
565 if en.scalar_type.is_some() {
566 method_keep.insert("from");
567 method_keep.insert("tryfrom");
568 }
569 self.prune_class_methods(&en.fqcn, &method_keep);
570 }
571 for method in en.own_methods.values() {
572 self.upsert_method_node(method.as_ref());
573 }
574 let synth_method = |name: &str| mir_codebase::storage::MethodStorage {
575 fqcn: en.fqcn.clone(),
576 name: Arc::from(name),
577 params: Arc::from([].as_ref()),
578 return_type: Some(Arc::new(Union::mixed())),
579 inferred_return_type: None,
580 visibility: Visibility::Public,
581 is_static: true,
582 is_abstract: false,
583 is_constructor: false,
584 template_params: vec![],
585 assertions: vec![],
586 throws: vec![],
587 is_final: false,
588 is_internal: false,
589 is_pure: false,
590 deprecated: None,
591 location: None,
592 docstring: None,
593 };
594 let already = |name: &str| {
595 en.own_methods
596 .keys()
597 .any(|k| k.as_ref().eq_ignore_ascii_case(name))
598 };
599 if !already("cases") {
600 self.upsert_method_node(&synth_method("cases"));
601 }
602 if en.scalar_type.is_some() {
603 if !already("from") {
604 self.upsert_method_node(&synth_method("from"));
605 }
606 if !already("tryFrom") {
607 self.upsert_method_node(&synth_method("tryFrom"));
608 }
609 }
610 if self.class_constant_nodes.contains_key(en.fqcn.as_ref()) {
611 let mut const_keep: HashSet<&str> =
612 en.own_constants.keys().map(|c| c.as_ref()).collect();
613 for case in en.cases.values() {
614 const_keep.insert(case.name.as_ref());
615 }
616 self.prune_class_constants(&en.fqcn, &const_keep);
617 }
618 for constant in en.own_constants.values() {
619 self.upsert_class_constant_node(&en.fqcn, constant);
620 }
621 for case in en.cases.values() {
622 let case_const = ConstantStorage {
623 name: case.name.clone(),
624 ty: mir_types::Union::mixed(),
625 visibility: None,
626 is_final: false,
627 location: case.location.clone(),
628 };
629 self.upsert_class_constant_node(&en.fqcn, &case_const);
630 }
631 }
632
633 for func in &slice.functions {
634 if let Some(file) = &slice_file {
635 Arc::make_mut(&mut self.symbol_to_file).insert(func.fqn.clone(), file.clone());
636 }
637 self.upsert_function_node(func);
638 }
639 for (fqn, ty) in &slice.constants {
640 self.upsert_global_constant_node(fqn.clone(), ty.clone());
641 }
642 }
643
644 pub fn ingest_stub_slices<'a, I>(&mut self, slices: I)
665 where
666 I: IntoIterator<Item = &'a StubSlice>,
667 {
668 for slice in slices {
669 self.ingest_stub_slice(slice);
670 }
671 }
672
673 #[allow(clippy::too_many_arguments)]
678 pub fn upsert_class_node(&mut self, fields: ClassNodeFields) -> ClassNode {
679 use salsa::Setter as _;
680 let ClassNodeFields {
681 fqcn,
682 is_interface,
683 is_trait,
684 is_enum,
685 is_abstract,
686 parent,
687 interfaces,
688 traits,
689 extends,
690 template_params,
691 require_extends,
692 require_implements,
693 is_backed_enum,
694 mixins,
695 deprecated,
696 enum_scalar_type,
697 is_final,
698 is_readonly,
699 location,
700 extends_type_args,
701 implements_type_args,
702 } = fields;
703 if let Some(&node) = self.class_nodes.get(&fqcn) {
704 if node.active(self)
717 && node.is_interface(self) == is_interface
718 && node.is_trait(self) == is_trait
719 && node.is_enum(self) == is_enum
720 && node.is_abstract(self) == is_abstract
721 && node.is_backed_enum(self) == is_backed_enum
722 && node.parent(self) == parent
723 && *node.interfaces(self) == *interfaces
724 && *node.traits(self) == *traits
725 && *node.extends(self) == *extends
726 && *node.template_params(self) == *template_params
727 && *node.require_extends(self) == *require_extends
728 && *node.require_implements(self) == *require_implements
729 && *node.mixins(self) == *mixins
730 && node.deprecated(self) == deprecated
731 && node.enum_scalar_type(self) == enum_scalar_type
732 && node.is_final(self) == is_final
733 && node.is_readonly(self) == is_readonly
734 && node.location(self) == location
735 && *node.extends_type_args(self) == *extends_type_args
736 && *node.implements_type_args(self) == *implements_type_args
737 {
738 return node;
739 }
740 node.set_active(self).to(true);
741 node.set_is_interface(self).to(is_interface);
742 node.set_is_trait(self).to(is_trait);
743 node.set_is_enum(self).to(is_enum);
744 node.set_is_abstract(self).to(is_abstract);
745 node.set_parent(self).to(parent);
746 node.set_interfaces(self).to(interfaces);
747 node.set_traits(self).to(traits);
748 node.set_extends(self).to(extends);
749 node.set_template_params(self).to(template_params);
750 node.set_require_extends(self).to(require_extends);
751 node.set_require_implements(self).to(require_implements);
752 node.set_is_backed_enum(self).to(is_backed_enum);
753 node.set_mixins(self).to(mixins);
754 node.set_deprecated(self).to(deprecated);
755 node.set_enum_scalar_type(self).to(enum_scalar_type);
756 node.set_is_final(self).to(is_final);
757 node.set_is_readonly(self).to(is_readonly);
758 node.set_location(self).to(location);
759 node.set_extends_type_args(self).to(extends_type_args);
760 node.set_implements_type_args(self).to(implements_type_args);
761 node
762 } else {
763 let node = ClassNode::new(
764 self,
765 fqcn.clone(),
766 true,
767 is_interface,
768 is_trait,
769 is_enum,
770 is_abstract,
771 parent,
772 interfaces,
773 traits,
774 extends,
775 template_params,
776 require_extends,
777 require_implements,
778 is_backed_enum,
779 mixins,
780 deprecated,
781 enum_scalar_type,
782 is_final,
783 is_readonly,
784 location,
785 extends_type_args,
786 implements_type_args,
787 );
788 Arc::make_mut(&mut self.class_node_keys_lower)
789 .insert(fqcn.to_ascii_lowercase(), fqcn.clone());
790 Arc::make_mut(&mut self.class_nodes).insert(fqcn, node);
791 node
792 }
793 }
794
795 pub fn deactivate_class_node(&mut self, fqcn: &str) {
800 use salsa::Setter as _;
801 if let Some(&node) = self.class_nodes.get(fqcn) {
802 node.set_active(self).to(false);
803 }
804 }
805
806 pub fn upsert_function_node(&mut self, storage: &FunctionStorage) -> FunctionNode {
808 use salsa::Setter as _;
809 let fqn = &storage.fqn;
810 if let Some(&node) = self.function_nodes.get(fqn.as_ref()) {
811 if node.active(self)
817 && node.short_name(self) == storage.short_name
818 && node.is_pure(self) == storage.is_pure
819 && node.deprecated(self) == storage.deprecated
820 && node.return_type(self).as_deref() == storage.return_type.as_deref()
821 && node.location(self) == storage.location
822 && *node.params(self) == *storage.params.as_ref()
823 && *node.template_params(self) == *storage.template_params
824 && *node.assertions(self) == *storage.assertions
825 && *node.throws(self) == *storage.throws
826 {
827 return node;
828 }
829 node.set_active(self).to(true);
830 node.set_short_name(self).to(storage.short_name.clone());
831 node.set_params(self).to(storage.params.clone());
832 node.set_return_type(self).to(storage.return_type.clone());
833 node.set_template_params(self)
834 .to(Arc::from(storage.template_params.as_slice()));
835 node.set_assertions(self)
836 .to(Arc::from(storage.assertions.as_slice()));
837 node.set_throws(self)
838 .to(Arc::from(storage.throws.as_slice()));
839 node.set_deprecated(self).to(storage.deprecated.clone());
840 node.set_docstring(self).to(storage.docstring.clone());
841 node.set_is_pure(self).to(storage.is_pure);
842 node.set_location(self).to(storage.location.clone());
843 node
844 } else {
845 let node = FunctionNode::new(
846 self,
847 fqn.clone(),
848 storage.short_name.clone(),
849 true,
850 storage.params.clone(),
851 storage.return_type.clone(),
852 storage
853 .inferred_return_type
854 .as_ref()
855 .map(|t| Arc::new(t.clone())),
856 Arc::from(storage.template_params.as_slice()),
857 Arc::from(storage.assertions.as_slice()),
858 Arc::from(storage.throws.as_slice()),
859 storage.deprecated.clone(),
860 storage.docstring.clone(),
861 storage.is_pure,
862 storage.location.clone(),
863 );
864 Arc::make_mut(&mut self.function_node_keys_lower)
865 .insert(fqn.to_ascii_lowercase(), fqn.clone());
866 Arc::make_mut(&mut self.function_nodes).insert(fqn.clone(), node);
867 node
868 }
869 }
870
871 pub fn commit_inferred_return_types(
885 &mut self,
886 functions: Vec<(Arc<str>, mir_types::Union)>,
887 methods: Vec<(Arc<str>, Arc<str>, mir_types::Union)>,
888 ) {
889 use salsa::Setter as _;
890 for (fqn, inferred) in functions {
891 if let Some(&node) = self.function_nodes.get(fqn.as_ref()) {
892 if !node.active(self) {
893 continue;
894 }
895 let new = Some(Arc::new(inferred));
896 if node.inferred_return_type(self) == new {
897 continue;
898 }
899 node.set_inferred_return_type(self).to(new);
900 }
901 }
902 for (fqcn, name, inferred) in methods {
903 let name_lower: Arc<str> = if name.chars().all(|c| !c.is_uppercase()) {
904 name.clone()
905 } else {
906 Arc::from(name.to_lowercase().as_str())
907 };
908 let node = self
909 .method_nodes
910 .get(fqcn.as_ref())
911 .and_then(|m| m.get(&name_lower))
912 .copied();
913 if let Some(node) = node {
914 if !node.active(self) {
915 continue;
916 }
917 let new = Some(Arc::new(inferred));
918 if node.inferred_return_type(self) == new {
919 continue;
920 }
921 node.set_inferred_return_type(self).to(new);
922 }
923 }
924 }
925
926 pub fn deactivate_function_node(&mut self, fqn: &str) {
928 use salsa::Setter as _;
929 if let Some(&node) = self.function_nodes.get(fqn) {
930 node.set_active(self).to(false);
931 }
932 }
933
934 pub fn upsert_method_node(&mut self, storage: &MethodStorage) -> MethodNode {
936 use salsa::Setter as _;
937 let fqcn = &storage.fqcn;
938 let name_lower: Arc<str> = Arc::from(storage.name.to_lowercase().as_str());
939 let existing = self
942 .method_nodes
943 .get(fqcn.as_ref())
944 .and_then(|m| m.get(&name_lower))
945 .copied();
946 if let Some(node) = existing {
947 if node.active(self)
951 && node.visibility(self) == storage.visibility
952 && node.is_static(self) == storage.is_static
953 && node.is_abstract(self) == storage.is_abstract
954 && node.is_final(self) == storage.is_final
955 && node.is_constructor(self) == storage.is_constructor
956 && node.is_pure(self) == storage.is_pure
957 && node.is_internal(self) == storage.is_internal
958 && node.deprecated(self) == storage.deprecated
959 && node.return_type(self).as_deref() == storage.return_type.as_deref()
960 && node.location(self) == storage.location
961 && *node.params(self) == *storage.params.as_ref()
962 && *node.template_params(self) == *storage.template_params
963 && *node.assertions(self) == *storage.assertions
964 && *node.throws(self) == *storage.throws
965 {
966 return node;
967 }
968 node.set_active(self).to(true);
969 node.set_params(self).to(storage.params.clone());
970 node.set_return_type(self).to(storage.return_type.clone());
971 node.set_template_params(self)
972 .to(Arc::from(storage.template_params.as_slice()));
973 node.set_assertions(self)
974 .to(Arc::from(storage.assertions.as_slice()));
975 node.set_throws(self)
976 .to(Arc::from(storage.throws.as_slice()));
977 node.set_deprecated(self).to(storage.deprecated.clone());
978 node.set_docstring(self).to(storage.docstring.clone());
979 node.set_is_internal(self).to(storage.is_internal);
980 node.set_visibility(self).to(storage.visibility);
981 node.set_is_static(self).to(storage.is_static);
982 node.set_is_abstract(self).to(storage.is_abstract);
983 node.set_is_final(self).to(storage.is_final);
984 node.set_is_constructor(self).to(storage.is_constructor);
985 node.set_is_pure(self).to(storage.is_pure);
986 node.set_location(self).to(storage.location.clone());
987 node
988 } else {
989 let node = MethodNode::new(
991 self,
992 fqcn.clone(),
993 storage.name.clone(),
994 true,
995 storage.params.clone(),
996 storage.return_type.clone(),
997 storage
998 .inferred_return_type
999 .as_ref()
1000 .map(|t| Arc::new(t.clone())),
1001 Arc::from(storage.template_params.as_slice()),
1002 Arc::from(storage.assertions.as_slice()),
1003 Arc::from(storage.throws.as_slice()),
1004 storage.deprecated.clone(),
1005 storage.docstring.clone(),
1006 storage.is_internal,
1007 storage.visibility,
1008 storage.is_static,
1009 storage.is_abstract,
1010 storage.is_final,
1011 storage.is_constructor,
1012 storage.is_pure,
1013 storage.location.clone(),
1014 );
1015 Arc::make_mut(&mut self.method_nodes)
1016 .entry(fqcn.clone())
1017 .or_default()
1018 .insert(name_lower, node);
1019 node
1020 }
1021 }
1022
1023 pub fn deactivate_class_methods(&mut self, fqcn: &str) {
1025 use salsa::Setter as _;
1026 let nodes: Vec<MethodNode> = match self.method_nodes.get(fqcn) {
1027 Some(methods) => methods.values().copied().collect(),
1028 None => return,
1029 };
1030 for node in nodes {
1031 node.set_active(self).to(false);
1032 }
1033 }
1034
1035 pub fn prune_class_methods<T>(&mut self, fqcn: &str, keep_lower: &std::collections::HashSet<T>)
1041 where
1042 T: Eq + std::hash::Hash + std::borrow::Borrow<str>,
1043 {
1044 use salsa::Setter as _;
1045 let candidates: Vec<MethodNode> = self
1046 .method_nodes
1047 .get(fqcn)
1048 .map(|m| {
1049 m.iter()
1050 .filter(|(k, _)| !keep_lower.contains(k.as_ref()))
1051 .map(|(_, n)| *n)
1052 .collect()
1053 })
1054 .unwrap_or_default();
1055 for node in candidates {
1056 if node.active(self) {
1057 node.set_active(self).to(false);
1058 }
1059 }
1060 }
1061
1062 pub fn prune_class_properties<T>(&mut self, fqcn: &str, keep: &std::collections::HashSet<T>)
1064 where
1065 T: Eq + std::hash::Hash + std::borrow::Borrow<str>,
1066 {
1067 use salsa::Setter as _;
1068 let candidates: Vec<PropertyNode> = self
1069 .property_nodes
1070 .get(fqcn)
1071 .map(|m| {
1072 m.iter()
1073 .filter(|(k, _)| !keep.contains(k.as_ref()))
1074 .map(|(_, n)| *n)
1075 .collect()
1076 })
1077 .unwrap_or_default();
1078 for node in candidates {
1079 if node.active(self) {
1080 node.set_active(self).to(false);
1081 }
1082 }
1083 }
1084
1085 pub fn prune_class_constants<T>(&mut self, fqcn: &str, keep: &std::collections::HashSet<T>)
1087 where
1088 T: Eq + std::hash::Hash + std::borrow::Borrow<str>,
1089 {
1090 use salsa::Setter as _;
1091 let candidates: Vec<ClassConstantNode> = self
1092 .class_constant_nodes
1093 .get(fqcn)
1094 .map(|m| {
1095 m.iter()
1096 .filter(|(k, _)| !keep.contains(k.as_ref()))
1097 .map(|(_, n)| *n)
1098 .collect()
1099 })
1100 .unwrap_or_default();
1101 for node in candidates {
1102 if node.active(self) {
1103 node.set_active(self).to(false);
1104 }
1105 }
1106 }
1107
1108 pub fn upsert_property_node(&mut self, fqcn: &Arc<str>, storage: &PropertyStorage) {
1110 use salsa::Setter as _;
1111 let existing = self
1112 .property_nodes
1113 .get(fqcn.as_ref())
1114 .and_then(|m| m.get(storage.name.as_ref()))
1115 .copied();
1116 if let Some(node) = existing {
1117 if node.active(self)
1119 && node.visibility(self) == storage.visibility
1120 && node.is_static(self) == storage.is_static
1121 && node.is_readonly(self) == storage.is_readonly
1122 && node.ty(self) == storage.ty
1123 && node.location(self) == storage.location
1124 {
1125 return;
1126 }
1127 node.set_active(self).to(true);
1128 node.set_ty(self).to(storage.ty.clone());
1129 node.set_visibility(self).to(storage.visibility);
1130 node.set_is_static(self).to(storage.is_static);
1131 node.set_is_readonly(self).to(storage.is_readonly);
1132 node.set_location(self).to(storage.location.clone());
1133 } else {
1134 let node = PropertyNode::new(
1135 self,
1136 fqcn.clone(),
1137 storage.name.clone(),
1138 true,
1139 storage.ty.clone(),
1140 storage.visibility,
1141 storage.is_static,
1142 storage.is_readonly,
1143 storage.location.clone(),
1144 );
1145 Arc::make_mut(&mut self.property_nodes)
1146 .entry(fqcn.clone())
1147 .or_default()
1148 .insert(storage.name.clone(), node);
1149 }
1150 }
1151
1152 pub fn deactivate_class_properties(&mut self, fqcn: &str) {
1154 use salsa::Setter as _;
1155 let nodes: Vec<PropertyNode> = match self.property_nodes.get(fqcn) {
1156 Some(props) => props.values().copied().collect(),
1157 None => return,
1158 };
1159 for node in nodes {
1160 node.set_active(self).to(false);
1161 }
1162 }
1163
1164 pub fn upsert_class_constant_node(&mut self, fqcn: &Arc<str>, storage: &ConstantStorage) {
1166 use salsa::Setter as _;
1167 let existing = self
1168 .class_constant_nodes
1169 .get(fqcn.as_ref())
1170 .and_then(|m| m.get(storage.name.as_ref()))
1171 .copied();
1172 if let Some(node) = existing {
1173 if node.active(self)
1175 && node.visibility(self) == storage.visibility
1176 && node.is_final(self) == storage.is_final
1177 && node.ty(self) == storage.ty
1178 && node.location(self) == storage.location
1179 {
1180 return;
1181 }
1182 node.set_active(self).to(true);
1183 node.set_ty(self).to(storage.ty.clone());
1184 node.set_visibility(self).to(storage.visibility);
1185 node.set_is_final(self).to(storage.is_final);
1186 node.set_location(self).to(storage.location.clone());
1187 } else {
1188 let node = ClassConstantNode::new(
1189 self,
1190 fqcn.clone(),
1191 storage.name.clone(),
1192 true,
1193 storage.ty.clone(),
1194 storage.visibility,
1195 storage.is_final,
1196 storage.location.clone(),
1197 );
1198 Arc::make_mut(&mut self.class_constant_nodes)
1199 .entry(fqcn.clone())
1200 .or_default()
1201 .insert(storage.name.clone(), node);
1202 }
1203 }
1204
1205 pub fn upsert_global_constant_node(&mut self, fqn: Arc<str>, ty: Union) -> GlobalConstantNode {
1207 use salsa::Setter as _;
1208 if let Some(&node) = self.global_constant_nodes.get(&fqn) {
1209 if node.active(self) && node.ty(self) == ty {
1211 return node;
1212 }
1213 node.set_active(self).to(true);
1214 node.set_ty(self).to(ty);
1215 node
1216 } else {
1217 let node = GlobalConstantNode::new(self, fqn.clone(), true, ty);
1218 Arc::make_mut(&mut self.global_constant_nodes).insert(fqn, node);
1219 node
1220 }
1221 }
1222
1223 pub fn deactivate_global_constant_node(&mut self, fqn: &str) {
1225 use salsa::Setter as _;
1226 if let Some(&node) = self.global_constant_nodes.get(fqn) {
1227 node.set_active(self).to(false);
1228 }
1229 }
1230
1231 pub fn deactivate_class_constants(&mut self, fqcn: &str) {
1233 use salsa::Setter as _;
1234 let nodes: Vec<ClassConstantNode> = match self.class_constant_nodes.get(fqcn) {
1235 Some(consts) => consts.values().copied().collect(),
1236 None => return,
1237 };
1238 for node in nodes {
1239 node.set_active(self).to(false);
1240 }
1241 }
1242}