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}