mago_reflection/lib.rs
1use std::collections::hash_map::Entry;
2
3use ahash::HashMap;
4use ahash::HashMapExt;
5use ahash::HashSet;
6use serde::Deserialize;
7use serde::Serialize;
8
9use mago_interner::StringIdentifier;
10use mago_interner::ThreadedInterner;
11use mago_reporting::IssueCollection;
12use mago_source::HasSource;
13use mago_source::SourceCategory;
14use mago_span::HasPosition;
15use mago_span::HasSpan;
16
17use crate::class_like::ClassLikeReflection;
18use crate::constant::ConstantReflection;
19use crate::function_like::FunctionLikeReflection;
20use crate::identifier::ClassLikeName;
21use crate::identifier::FunctionLikeName;
22use crate::identifier::Name;
23
24pub mod assertion;
25pub mod attribute;
26pub mod class_like;
27pub mod constant;
28pub mod function_like;
29pub mod identifier;
30pub mod r#type;
31
32/// The `Reflection` trait is implemented by all reflection types in the system.
33///
34/// It provides a consistent interface for querying metadata about PHP constructs
35/// such as classes, functions, and other entities. This trait allows the system
36/// to introspect and categorize these constructs based on their origin, source,
37/// and other attributes.
38pub trait Reflection: HasSpan + HasSource {
39 /// Retrieves the `SourceCategory` for the entity.
40 ///
41 /// The `SourceCategory` indicates whether the entity belongs to one of the following:
42 ///
43 /// - `BuiltIn`: A PHP construct that is part of the PHP core or standard library.
44 /// - `External`: A construct defined in third-party or vendor-provided libraries.
45 /// - `UserDefined`: A construct written by the user or part of the current project.
46 ///
47 /// # Returns
48 /// - A `SourceCategory` enum variant corresponding to the entity's origin.
49 fn get_category(&self) -> SourceCategory;
50
51 /// Indicates whether the entity is user-defined or part of the current project.
52 ///
53 /// # Returns
54 ///
55 /// - `true` if the entity's `SourceCategory` is `UserDefined`.
56 /// - `false` otherwise.
57 fn is_user_defined(&self) -> bool {
58 self.get_category().is_user_defined()
59 }
60
61 /// Indicates whether the entity originates from an external source (e.g., vendor libraries).
62 ///
63 /// # Returns
64 ///
65 /// - `true` if the entity's `SourceCategory` is `Vendor` or similar external categories.
66 /// - `false` otherwise.
67 fn is_external(&self) -> bool {
68 self.get_category().is_external()
69 }
70
71 /// Indicates whether the entity is a built-in PHP construct.
72 ///
73 /// Built-in constructs include classes, functions, and constants that are
74 /// part of the PHP core or extensions.
75 ///
76 /// # Returns
77 ///
78 /// - `true` if the entity's `SourceCategory` is `BuiltIn`.
79 /// - `false` otherwise.
80 #[inline]
81 fn is_built_in(&self) -> bool {
82 self.get_category().is_built_in()
83 }
84
85 /// Indicates whether the entity has been fully populated with metadata.
86 ///
87 /// This can be useful to determine whether lazy-loaded or partially
88 /// processed entities have had their information fully resolved.
89 ///
90 /// # Returns
91 ///
92 /// - `true` if the entity's metadata is fully populated.
93 /// - `false` if additional processing is needed to populate the metadata.
94 fn is_populated(&self) -> bool;
95
96 /// Take any issues found during the population of the reflection.
97 ///
98 /// The returned `IssueCollection` contains errors, warnings, or notices
99 /// related to the metadata of the entity.
100 ///
101 /// This method is particularly useful for static analysis tools or compilers
102 /// to report potential problems in the code being analyzed.
103 ///
104 /// # Returns
105 ///
106 /// - A reference to an `IssueCollection` containing all detected issues.
107 fn take_issues(&mut self) -> IssueCollection;
108}
109
110#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Default)]
111pub struct CodebaseReflection {
112 /// Reflections for all constants in the codebase, keyed by their `Name`.
113 pub constant_reflections: HashMap<Name, ConstantReflection>,
114
115 /// Mapping of constant names to their canonical `Name` representations.
116 pub constant_names: HashMap<StringIdentifier, Name>,
117
118 /// Reflections for all function-like entities (functions, closures, etc.), keyed by their `FunctionLikeName`.
119 pub function_like_reflections: HashMap<FunctionLikeName, FunctionLikeReflection>,
120
121 /// Mapping of function-like names to their canonical `FunctionLikeName` representations.
122 pub function_names: HashMap<StringIdentifier, FunctionLikeName>,
123
124 /// Reflections for all class-like entities (classes, traits, enums, interfaces), keyed by their `ClassLikeName`.
125 pub class_like_reflections: HashMap<ClassLikeName, ClassLikeReflection>,
126
127 /// Mapping of class-like names to their canonical `ClassLikeName` representations.
128 pub class_like_names: HashMap<StringIdentifier, ClassLikeName>,
129
130 /// Direct descendants of each class-like entity, useful for hierarchy traversal.
131 pub direct_classlike_descendants: HashMap<StringIdentifier, HashSet<StringIdentifier>>,
132
133 /// All descendants of each class-like entity, useful for comprehensive hierarchy analysis.
134 pub all_classlike_descendants: HashMap<StringIdentifier, HashSet<StringIdentifier>>,
135
136 /// Indicates whether all entities in the codebase have been fully populated.
137 pub populated: bool,
138}
139
140impl CodebaseReflection {
141 /// Creates a new, empty `CodebaseReflection`.
142 ///
143 /// # Returns
144 ///
145 /// A new instance of `CodebaseReflection` with `populated` set to `false`
146 /// and all internal collections initialized to their default states.
147 pub fn new() -> Self {
148 Self { populated: false, ..Default::default() }
149 }
150
151 /// Create a new `CodebaseReflection` with a specified capacity.
152 ///
153 /// # Arguments
154 ///
155 /// - `capacity`: The initial capacity for the internal collections.
156 ///
157 /// # Returns
158 ///
159 /// A new instance of `CodebaseReflection` with the internal collections pre-allocated
160 /// to the specified capacity.
161 #[inline]
162 pub fn with_capacity(capacity: usize) -> Self {
163 Self {
164 populated: false,
165 constant_reflections: HashMap::with_capacity(capacity),
166 constant_names: HashMap::with_capacity(capacity),
167 function_like_reflections: HashMap::with_capacity(capacity),
168 function_names: HashMap::with_capacity(capacity),
169 class_like_reflections: HashMap::with_capacity(capacity),
170 class_like_names: HashMap::with_capacity(capacity),
171 direct_classlike_descendants: HashMap::with_capacity(capacity),
172 all_classlike_descendants: HashMap::with_capacity(capacity),
173 }
174 }
175
176 /// Checks if the codebase reflection is empty.
177 ///
178 /// # Returns
179 ///
180 /// - `true` if the codebase reflection is empty.
181 /// - `false` otherwise.
182 #[inline]
183 pub fn is_empty(&self) -> bool {
184 self.constant_reflections.is_empty()
185 && self.function_like_reflections.is_empty()
186 && self.class_like_reflections.is_empty()
187 }
188
189 /// Merges another `CodebaseReflection` into this one.
190 ///
191 /// This method combines the codebase reflections and issues from two `CodebaseReflection` instances
192 /// into a single `CodebaseReflection`. If duplicates are found during merging (such as functions,
193 /// classes, or constants with identical names), they are ignored.
194 ///
195 /// # Arguments
196 ///
197 /// - `interner`: A `ThreadedInterner` instance for name handling.
198 /// - `other`: The `CodebaseReflection` to merge into this one.
199 ///
200 /// # Effects
201 ///
202 /// This method modifies the current `CodebaseReflection` instance in place.
203 ///
204 /// # Notes
205 ///
206 /// This method invalidates the `populated` flag, as the codebase may have changed,
207 /// unless the other reflection is empty, in which case no changes are made.
208 pub fn merge(&mut self, interner: &ThreadedInterner, other: CodebaseReflection) {
209 if other.is_empty() {
210 return;
211 }
212
213 for (_, reflection) in other.constant_reflections.into_iter() {
214 self.register_constant(interner, reflection);
215 }
216
217 for (_, reflection) in other.function_like_reflections.into_iter() {
218 self.register_function_like(interner, reflection);
219 }
220
221 for (_, reflection) in other.class_like_reflections.into_iter() {
222 self.register_class_like(interner, reflection);
223 }
224
225 self.populated = false;
226 }
227
228 /// Registers a new constant in the codebase.
229 ///
230 /// This method ensures that the constant is uniquely registered,
231 /// accounting for case-insensitive names. If a constant with the same name
232 /// already exists, it will not be registered again.
233 ///
234 /// # Arguments
235 ///
236 /// - `interner`: A `ThreadedInterner` instance for name handling.
237 /// - `reflection`: The `ConstantReflection` to register.
238 ///
239 /// # Returns
240 ///
241 /// - `true` if the constant was successfully registered.
242 /// - `false` if the constant already exists.
243 pub fn register_constant(&mut self, interner: &ThreadedInterner, reflection: ConstantReflection) -> bool {
244 let lowercase_name = lower_constant_name(interner, &reflection.name.value);
245 if self.constant_names.contains_key(&lowercase_name) {
246 return false;
247 }
248
249 self.constant_names.insert(lowercase_name, reflection.name);
250 self.constant_reflections.insert(reflection.name, reflection);
251
252 true
253 }
254
255 /// Registers a new function-like entity (e.g., function, closure, or arrow function) in the codebase.
256 ///
257 /// This method ensures that the function-like entity is uniquely registered,
258 /// accounting for case-insensitive names. If an entity with the same name already
259 /// exists, it will not be registered again.
260 ///
261 /// # Arguments
262 ///
263 /// - `interner`: A `ThreadedInterner` instance for name handling.
264 /// - `reflection`: The `FunctionLikeReflection` to register.
265 ///
266 /// # Returns
267 ///
268 /// - `true` if the entity was successfully registered.
269 /// - `false` if the entity already exists.
270 pub fn register_function_like(&mut self, interner: &ThreadedInterner, reflection: FunctionLikeReflection) -> bool {
271 let mut exists = false;
272
273 if let FunctionLikeName::Function(name) = reflection.name {
274 let lowercase_name = interner.lowered(&name.value);
275 if let Entry::Vacant(e) = self.function_names.entry(lowercase_name) {
276 e.insert(reflection.name);
277 } else {
278 exists = true;
279 }
280 }
281
282 if !exists {
283 self.function_like_reflections.insert(reflection.name, reflection);
284 }
285
286 exists
287 }
288
289 /// Registers a new class-like entity (e.g., class, interface, trait, or enum) in the codebase.
290 ///
291 /// This method ensures that the class-like entity is uniquely registered,
292 /// accounting for case-insensitive names. If an entity with the same name
293 /// already exists, it will not be registered again.
294 ///
295 /// # Arguments
296 ///
297 /// - `interner`: A `ThreadedInterner` instance for name handling.
298 /// - `reflection`: The `ClassLikeReflection` to register.
299 ///
300 /// # Returns
301 ///
302 /// - `true` if the entity was successfully registered.
303 /// - `false` if the entity already exists.
304 pub fn register_class_like(&mut self, interner: &ThreadedInterner, reflection: ClassLikeReflection) -> bool {
305 match reflection.name {
306 ClassLikeName::Class(name) => {
307 let Entry::Vacant(e) = self.class_like_names.entry(interner.lowered(&name.value)) else {
308 return false;
309 };
310
311 e.insert(reflection.name);
312 self.class_like_reflections.insert(reflection.name, reflection);
313 }
314 ClassLikeName::Enum(name) => {
315 let Entry::Vacant(e) = self.class_like_names.entry(interner.lowered(&name.value)) else {
316 return false;
317 };
318
319 e.insert(reflection.name);
320 self.class_like_reflections.insert(reflection.name, reflection);
321 }
322 ClassLikeName::Interface(name) => {
323 let Entry::Vacant(e) = self.class_like_names.entry(interner.lowered(&name.value)) else {
324 return false;
325 };
326
327 e.insert(reflection.name);
328 self.class_like_reflections.insert(reflection.name, reflection);
329 }
330 ClassLikeName::Trait(name) => {
331 let Entry::Vacant(e) = self.class_like_names.entry(interner.lowered(&name.value)) else {
332 return false;
333 };
334
335 e.insert(reflection.name);
336 self.class_like_reflections.insert(reflection.name, reflection);
337 }
338 _ => {
339 self.class_like_reflections.insert(reflection.name, reflection);
340 }
341 }
342
343 true
344 }
345
346 /// Checks if a constant exists in the codebase.
347 ///
348 /// # Arguments
349 ///
350 /// - `interner`: A `ThreadedInterner` instance for name handling.
351 /// - `id`: A `StringIdentifier` representing the constant's name.
352 ///
353 /// # Returns
354 ///
355 /// - `true` if the constant exists.
356 /// - `false` otherwise.
357 pub fn constant_exists(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> bool {
358 let id = lower_constant_name(interner, id);
359
360 self.constant_names.contains_key(&id)
361 }
362
363 /// Checks if a function exists in the codebase.
364 ///
365 /// # Arguments
366 ///
367 /// - `interner`: A `ThreadedInterner` instance for name handling.
368 /// - `id`: A `StringIdentifier` representing the function's name.
369 ///
370 /// # Returns
371 ///
372 /// - `true` if the function exists.
373 /// - `false` otherwise.
374 pub fn function_exists(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> bool {
375 let id = interner.lowered(id);
376
377 self.function_names.contains_key(&id)
378 }
379
380 /// Checks if a class exists in the codebase.
381 ///
382 /// # Arguments
383 ///
384 /// - `interner`: A `ThreadedInterner` instance for name handling.
385 /// - `id`: A `StringIdentifier` representing the class's name.
386 ///
387 /// # Returns
388 ///
389 /// - `true` if the class exists.
390 /// - `false` otherwise.
391 pub fn class_exists(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> bool {
392 let id = interner.lowered(id);
393
394 matches!(self.class_like_names.get(&id), Some(ClassLikeName::Class(_)))
395 }
396
397 /// Checks if an enum exists in the codebase.
398 ///
399 /// # Arguments
400 ///
401 /// - `interner`: A `ThreadedInterner` instance for name handling.
402 /// - `id`: A `StringIdentifier` representing the enum's name.
403 ///
404 /// # Returns
405 ///
406 /// - `true` if the enum exists.
407 /// - `false` otherwise.
408 pub fn enum_exists(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> bool {
409 let id = interner.lowered(id);
410
411 matches!(self.class_like_names.get(&id), Some(ClassLikeName::Enum(_)))
412 }
413
414 /// Checks if an interface exists in the codebase.
415 ///
416 /// # Arguments
417 ///
418 /// - `interner`: A `ThreadedInterner` instance for name handling.
419 /// - `id`: A `StringIdentifier` representing the interface's name.
420 ///
421 /// # Returns
422 ///
423 /// - `true` if the interface exists.
424 /// - `false` otherwise.
425 pub fn interface_exists(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> bool {
426 let name = interner.lowered(id);
427
428 matches!(self.class_like_names.get(&name), Some(ClassLikeName::Interface(_)))
429 }
430
431 /// Checks if a trait exists in the codebase.
432 ///
433 /// # Arguments
434 ///
435 /// - `interner`: A `ThreadedInterner` instance for name handling.
436 /// - `id`: A `StringIdentifier` representing the trait's name.
437 ///
438 /// # Returns
439 ///
440 /// - `true` if the trait exists.
441 /// - `false` otherwise.
442 pub fn trait_exists(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> bool {
443 let id = interner.lowered(id);
444
445 matches!(self.class_like_names.get(&id), Some(ClassLikeName::Trait(_)))
446 }
447
448 /// Retrieves a constant reflection by name, if it exists.
449 ///
450 /// # Arguments
451 ///
452 /// - `interner`: A `ThreadedInterner` instance for name handling.
453 /// - `id`: A `StringIdentifier` representing the constant's name.
454 ///
455 /// # Returns
456 ///
457 /// - `Some(&ConstantReflection)` if the constant exists.
458 /// - `None` otherwise.
459 pub fn get_constant(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> Option<&ConstantReflection> {
460 let id = lower_constant_name(interner, id);
461
462 if let Some(name) = self.constant_names.get(&id) { self.constant_reflections.get(name) } else { None }
463 }
464
465 /// Retrieves a function-like reflection by its name, if it exists.
466 ///
467 /// # Arguments
468 ///
469 /// - `name`: The name of the function-like entity as a `FunctionLikeName`.
470 ///
471 /// # Returns
472 ///
473 /// - `Some(&FunctionLikeReflection)` if the function-like entity exists.
474 /// - `None` otherwise.
475 pub fn get_function_like(&self, name: FunctionLikeName) -> Option<&FunctionLikeReflection> {
476 self.function_like_reflections.get(&name)
477 }
478
479 /// Retrieves a function reflection by name, if it exists.
480 ///
481 /// # Arguments
482 ///
483 /// - `interner`: A `ThreadedInterner` instance for name handling.
484 /// - `id`: A `StringIdentifier` representing the function's name.
485 ///
486 /// # Returns
487 ///
488 /// - `Some(&FunctionLikeReflection)` if the function exists.
489 /// - `None` otherwise.
490 pub fn get_function(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> Option<&FunctionLikeReflection> {
491 let id = interner.lowered(id);
492
493 if let Some(name) = self.function_names.get(&id) { self.function_like_reflections.get(name) } else { None }
494 }
495
496 /// Retrieves a closure reflection by its position, if it exists.
497 ///
498 /// # Arguments
499 ///
500 /// - `position`: The position to search for as an implementation of `HasPosition`.
501 ///
502 /// # Returns
503 ///
504 /// - `Some(&FunctionLikeReflection)` if the closure exists at the given position.
505 /// - `None` otherwise.
506 pub fn get_closure(&self, position: &impl HasPosition) -> Option<&FunctionLikeReflection> {
507 self.function_like_reflections.iter().find_map(|(identifier, function_like)| match identifier {
508 FunctionLikeName::Closure(span) => {
509 if span.contains(position) {
510 Some(function_like)
511 } else {
512 None
513 }
514 }
515 _ => None,
516 })
517 }
518
519 /// Retrieves an arrow function reflection by its position, if it exists.
520 ///
521 /// # Arguments
522 ///
523 /// - `position`: The position to search for as an implementation of `HasPosition`.
524 ///
525 /// # Returns
526 ///
527 /// - `Some(&FunctionLikeReflection)` if the arrow function exists at the given position.
528 /// - `None` otherwise.
529 pub fn get_arrow_function(&self, position: &impl HasPosition) -> Option<&FunctionLikeReflection> {
530 self.function_like_reflections.iter().find_map(|(identifier, function_like)| match identifier {
531 FunctionLikeName::ArrowFunction(span) => {
532 if span.contains(position) {
533 Some(function_like)
534 } else {
535 None
536 }
537 }
538 _ => None,
539 })
540 }
541
542 /// Retrieves a class-like reflection by its identifier, if it exists.
543 ///
544 /// # Arguments
545 ///
546 /// - `name`: The `ClassLikeName` representing the class-like entity.
547 ///
548 /// # Returns
549 ///
550 /// - `Some(&ClassLikeReflection)` if the class-like entity exists.
551 /// - `None` otherwise.
552 pub fn get_class_like(&self, name: &ClassLikeName) -> Option<&ClassLikeReflection> {
553 self.class_like_reflections.get(name)
554 }
555
556 /// Retrieves a class-like reflection by its name, if it exists.
557 ///
558 /// # Arguments
559 ///
560 /// - `interner`: A `ThreadedInterner` instance for name handling.
561 /// - `id`: A `StringIdentifier` representing the class-like entity's name.
562 ///
563 /// # Returns
564 ///
565 /// - `Some(&ClassLikeReflection)` if the class-like entity exists.
566 /// - `None` otherwise.
567 pub fn get_named_class_like(
568 &self,
569 interner: &ThreadedInterner,
570 id: &StringIdentifier,
571 ) -> Option<&ClassLikeReflection> {
572 let id = interner.lowered(id);
573
574 if let Some(name) = self.class_like_names.get(&id) { self.class_like_reflections.get(name) } else { None }
575 }
576
577 /// Retrieves a class reflection by its name, if it exists.
578 ///
579 /// # Arguments
580 ///
581 /// - `interner`: A `ThreadedInterner` instance for name handling.
582 /// - `id`: A `StringIdentifier` representing the class's name.
583 ///
584 /// # Returns
585 ///
586 /// - `Some(&ClassLikeReflection)` if the class exists.
587 /// - `None` otherwise.
588 pub fn get_class(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> Option<&ClassLikeReflection> {
589 let id = interner.lowered(id);
590
591 if let Some(name @ ClassLikeName::Class(_)) = self.class_like_names.get(&id) {
592 self.class_like_reflections.get(name)
593 } else {
594 None
595 }
596 }
597
598 /// Retrieves an enum reflection by its name, if it exists.
599 ///
600 /// # Arguments
601 ///
602 /// - `interner`: A `ThreadedInterner` instance for name handling.
603 /// - `id`: A `StringIdentifier` representing the enum's name.
604 ///
605 /// # Returns
606 ///
607 /// - `Some(&ClassLikeReflection)` if the enum exists.
608 /// - `None` otherwise.
609 pub fn get_enum(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> Option<&ClassLikeReflection> {
610 let id = interner.lowered(id);
611
612 if let Some(name @ ClassLikeName::Enum(_)) = self.class_like_names.get(&id) {
613 self.class_like_reflections.get(name)
614 } else {
615 None
616 }
617 }
618
619 /// Retrieves an interface reflection by its name, if it exists.
620 ///
621 /// # Arguments
622 ///
623 /// - `interner`: A `ThreadedInterner` instance for name handling.
624 /// - `id`: A `StringIdentifier` representing the interface's name.
625 ///
626 /// # Returns
627 ///
628 /// - `Some(&ClassLikeReflection)` if the interface exists.
629 /// - `None` otherwise.
630 pub fn get_interface(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> Option<&ClassLikeReflection> {
631 let id = interner.lowered(id);
632
633 if let Some(name @ ClassLikeName::Interface(_)) = self.class_like_names.get(&id) {
634 self.class_like_reflections.get(name)
635 } else {
636 None
637 }
638 }
639
640 /// Retrieves a trait reflection by its name, if it exists.
641 ///
642 /// # Arguments
643 ///
644 /// - `interner`: A `ThreadedInterner` instance for name handling.
645 /// - `id`: A `StringIdentifier` representing the trait's name.
646 ///
647 /// # Returns
648 ///
649 /// - `Some(&ClassLikeReflection)` if the trait exists.
650 /// - `None` otherwise.
651 pub fn get_trait(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> Option<&ClassLikeReflection> {
652 let id = interner.lowered(id);
653
654 if let Some(name @ ClassLikeName::Trait(_)) = self.class_like_names.get(&id) {
655 self.class_like_reflections.get(name)
656 } else {
657 None
658 }
659 }
660
661 /// Retrieves an anonymous class reflection by its span, if it exists.
662 ///
663 /// # Arguments
664 ///
665 /// - `node`: The node containing the span as an implementation of `HasSpan`.
666 ///
667 /// # Returns
668 ///
669 /// - `Some(&ClassLikeReflection)` if the anonymous class exists.
670 /// - `None` otherwise.
671 pub fn get_anonymous_class(&self, node: &impl HasSpan) -> Option<&ClassLikeReflection> {
672 self.class_like_reflections.get(&ClassLikeName::AnonymousClass(node.span()))
673 }
674
675 /// Retrieves a method reflection from a class-like entity by its name, if it exists.
676 ///
677 /// This method first checks the class-like entity for the method. If the method is not found,
678 /// it checks the class-like entity's ancestors for the method.
679 ///
680 /// # Arguments
681 ///
682 /// - `class`: The class-like reflection to search for the method.
683 /// - `method`: The name of the method to retrieve.
684 ///
685 /// # Returns
686 ///
687 /// - `Some(&FunctionLikeReflection)` if the method exists.
688 /// - `None` otherwise.
689 pub fn get_method<'a>(
690 &'a self,
691 interner: &ThreadedInterner,
692 class: &'a ClassLikeReflection,
693 method: &StringIdentifier,
694 ) -> Option<&'a FunctionLikeReflection> {
695 let method = interner.lowered(method);
696
697 class.methods.members.get(&method).or_else(|| {
698 let appering_in_class = class.methods.appering_members.get(&method)?;
699
700 self.class_like_reflections.get(appering_in_class)?.methods.members.get(&method)
701 })
702 }
703
704 /// Returns the function-like reflection (function, closure, etc.) that encloses the given offset.
705 ///
706 /// This method iterates through the reflections in the codebase, filtering for function-like reflections
707 /// that contain the given offset in their definition range. It returns the reflection with the
708 /// largest starting offset, effectively finding the innermost function-like reflection containing
709 /// the offset.
710 ///
711 /// # Arguments
712 ///
713 /// * `has_position` - The position to search for.
714 ///
715 /// # Returns
716 ///
717 /// * `Option<&FunctionLikeReflection>` - The enclosing function-like reflection, if found.
718 pub fn get_enclosing_function_like(&self, has_position: &impl HasPosition) -> Option<&FunctionLikeReflection> {
719 self.function_like_reflections
720 .iter()
721 .filter(|(_, function_like)| function_like.span.has_offset(has_position.offset()))
722 .max_by_key(|(_, function_like)| function_like.span.start.offset)
723 .map(|(_, function_like)| function_like)
724 }
725
726 /// Returns the class-like reflection (class, trait, etc.) that encloses the given offset.
727 ///
728 /// This method iterates through the reflections in the codebase, filtering for class-like reflections
729 /// that contain the given offset in their definition range. It returns the reflection with the
730 /// largest starting offset, effectively finding the innermost class-like reflection containing
731 /// the offset.
732 ///
733 /// # Arguments
734 ///
735 /// * `has_position` - The position to search for.
736 ///
737 /// # Returns
738 ///
739 /// * `Option<&ClassLikeReflection>` - The enclosing class-like reflection, if found.
740 pub fn get_enclosing_class_like(&self, has_position: &impl HasPosition) -> Option<&ClassLikeReflection> {
741 self.class_like_reflections
742 .iter()
743 .filter(|(_, class_like)| class_like.span.has_offset(has_position.offset()))
744 .max_by_key(|(_, class_like)| class_like.span.start.offset)
745 .map(|(_, class_like)| class_like)
746 }
747
748 /// Takes all issues from the codebase reflection and its children.
749 ///
750 /// This method iterates over all constant, function-like, and class-like reflections
751 /// in the codebase, collecting all issues found during the population of the metadata.
752 ///
753 /// # Returns
754 ///
755 /// - An `IssueCollection` containing all issues found in the codebase and its children.
756 pub fn take_issues(&mut self) -> IssueCollection {
757 let issues = self
758 .constant_reflections
759 .iter_mut()
760 .flat_map(|(_, constant)| constant.take_issues())
761 .chain(self.function_like_reflections.iter_mut().flat_map(|(_, function_like)| function_like.take_issues()))
762 .chain(self.class_like_reflections.iter_mut().flat_map(|(_, class_like)| class_like.take_issues()));
763
764 IssueCollection::from(issues)
765 }
766}
767
768fn lower_constant_name(interner: &ThreadedInterner, name: &StringIdentifier) -> StringIdentifier {
769 let name = interner.lookup(name);
770
771 let mut parts: Vec<_> = name.split('\\').map(str::to_owned).collect();
772 let total_parts = parts.len();
773 if total_parts > 1 {
774 parts = parts
775 .into_iter()
776 .enumerate()
777 .map(|(i, part)| if i < total_parts - 1 { part.to_ascii_lowercase() } else { part })
778 .collect::<Vec<_>>();
779 }
780
781 interner.intern(parts.join("\\"))
782}