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(always)]
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(always)]
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) {
463 self.constant_reflections.get(name)
464 } else {
465 None
466 }
467 }
468
469 /// Retrieves a function-like reflection by its name, if it exists.
470 ///
471 /// # Arguments
472 ///
473 /// - `name`: The name of the function-like entity as a `FunctionLikeName`.
474 ///
475 /// # Returns
476 ///
477 /// - `Some(&FunctionLikeReflection)` if the function-like entity exists.
478 /// - `None` otherwise.
479 pub fn get_function_like(&self, name: FunctionLikeName) -> Option<&FunctionLikeReflection> {
480 self.function_like_reflections.get(&name)
481 }
482
483 /// Retrieves a function reflection by name, if it exists.
484 ///
485 /// # Arguments
486 ///
487 /// - `interner`: A `ThreadedInterner` instance for name handling.
488 /// - `id`: A `StringIdentifier` representing the function's name.
489 ///
490 /// # Returns
491 ///
492 /// - `Some(&FunctionLikeReflection)` if the function exists.
493 /// - `None` otherwise.
494 pub fn get_function(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> Option<&FunctionLikeReflection> {
495 let id = interner.lowered(id);
496
497 if let Some(name) = self.function_names.get(&id) {
498 self.function_like_reflections.get(name)
499 } else {
500 None
501 }
502 }
503
504 /// Retrieves a closure reflection by its position, if it exists.
505 ///
506 /// # Arguments
507 ///
508 /// - `position`: The position to search for as an implementation of `HasPosition`.
509 ///
510 /// # Returns
511 ///
512 /// - `Some(&FunctionLikeReflection)` if the closure exists at the given position.
513 /// - `None` otherwise.
514 pub fn get_closure(&self, position: &impl HasPosition) -> Option<&FunctionLikeReflection> {
515 self.function_like_reflections.iter().find_map(|(identifier, function_like)| match identifier {
516 FunctionLikeName::Closure(span) => {
517 if span.contains(position) {
518 Some(function_like)
519 } else {
520 None
521 }
522 }
523 _ => None,
524 })
525 }
526
527 /// Retrieves an arrow function reflection by its position, if it exists.
528 ///
529 /// # Arguments
530 ///
531 /// - `position`: The position to search for as an implementation of `HasPosition`.
532 ///
533 /// # Returns
534 ///
535 /// - `Some(&FunctionLikeReflection)` if the arrow function exists at the given position.
536 /// - `None` otherwise.
537 pub fn get_arrow_function(&self, position: &impl HasPosition) -> Option<&FunctionLikeReflection> {
538 self.function_like_reflections.iter().find_map(|(identifier, function_like)| match identifier {
539 FunctionLikeName::ArrowFunction(span) => {
540 if span.contains(position) {
541 Some(function_like)
542 } else {
543 None
544 }
545 }
546 _ => None,
547 })
548 }
549
550 /// Retrieves a class-like reflection by its identifier, if it exists.
551 ///
552 /// # Arguments
553 ///
554 /// - `name`: The `ClassLikeName` representing the class-like entity.
555 ///
556 /// # Returns
557 ///
558 /// - `Some(&ClassLikeReflection)` if the class-like entity exists.
559 /// - `None` otherwise.
560 pub fn get_class_like(&self, name: ClassLikeName) -> Option<&ClassLikeReflection> {
561 self.class_like_reflections.get(&name)
562 }
563
564 /// Retrieves a class-like reflection by its name, if it exists.
565 ///
566 /// # Arguments
567 ///
568 /// - `interner`: A `ThreadedInterner` instance for name handling.
569 /// - `id`: A `StringIdentifier` representing the class-like entity's name.
570 ///
571 /// # Returns
572 ///
573 /// - `Some(&ClassLikeReflection)` if the class-like entity exists.
574 /// - `None` otherwise.
575 pub fn get_named_class_like(
576 &self,
577 interner: &ThreadedInterner,
578 id: &StringIdentifier,
579 ) -> Option<&ClassLikeReflection> {
580 let id = interner.lowered(id);
581
582 if let Some(name) = self.class_like_names.get(&id) {
583 self.class_like_reflections.get(name)
584 } else {
585 None
586 }
587 }
588
589 /// Retrieves a class reflection by its name, if it exists.
590 ///
591 /// # Arguments
592 ///
593 /// - `interner`: A `ThreadedInterner` instance for name handling.
594 /// - `id`: A `StringIdentifier` representing the class's name.
595 ///
596 /// # Returns
597 ///
598 /// - `Some(&ClassLikeReflection)` if the class exists.
599 /// - `None` otherwise.
600 pub fn get_class(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> Option<&ClassLikeReflection> {
601 let id = interner.lowered(id);
602
603 if let Some(name @ ClassLikeName::Class(_)) = self.class_like_names.get(&id) {
604 self.class_like_reflections.get(name)
605 } else {
606 None
607 }
608 }
609
610 /// Retrieves an enum reflection by its name, if it exists.
611 ///
612 /// # Arguments
613 ///
614 /// - `interner`: A `ThreadedInterner` instance for name handling.
615 /// - `id`: A `StringIdentifier` representing the enum's name.
616 ///
617 /// # Returns
618 ///
619 /// - `Some(&ClassLikeReflection)` if the enum exists.
620 /// - `None` otherwise.
621 pub fn get_enum(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> Option<&ClassLikeReflection> {
622 let id = interner.lowered(id);
623
624 if let Some(name @ ClassLikeName::Enum(_)) = self.class_like_names.get(&id) {
625 self.class_like_reflections.get(name)
626 } else {
627 None
628 }
629 }
630
631 /// Retrieves an interface reflection by its name, if it exists.
632 ///
633 /// # Arguments
634 ///
635 /// - `interner`: A `ThreadedInterner` instance for name handling.
636 /// - `id`: A `StringIdentifier` representing the interface's name.
637 ///
638 /// # Returns
639 ///
640 /// - `Some(&ClassLikeReflection)` if the interface exists.
641 /// - `None` otherwise.
642 pub fn get_interface(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> Option<&ClassLikeReflection> {
643 let id = interner.lowered(id);
644
645 if let Some(name @ ClassLikeName::Interface(_)) = self.class_like_names.get(&id) {
646 self.class_like_reflections.get(name)
647 } else {
648 None
649 }
650 }
651
652 /// Retrieves a trait reflection by its name, if it exists.
653 ///
654 /// # Arguments
655 ///
656 /// - `interner`: A `ThreadedInterner` instance for name handling.
657 /// - `id`: A `StringIdentifier` representing the trait's name.
658 ///
659 /// # Returns
660 ///
661 /// - `Some(&ClassLikeReflection)` if the trait exists.
662 /// - `None` otherwise.
663 pub fn get_trait(&self, interner: &ThreadedInterner, id: &StringIdentifier) -> Option<&ClassLikeReflection> {
664 let id = interner.lowered(id);
665
666 if let Some(name @ ClassLikeName::Trait(_)) = self.class_like_names.get(&id) {
667 self.class_like_reflections.get(name)
668 } else {
669 None
670 }
671 }
672
673 /// Retrieves an anonymous class reflection by its span, if it exists.
674 ///
675 /// # Arguments
676 ///
677 /// - `node`: The node containing the span as an implementation of `HasSpan`.
678 ///
679 /// # Returns
680 ///
681 /// - `Some(&ClassLikeReflection)` if the anonymous class exists.
682 /// - `None` otherwise.
683 pub fn get_anonymous_class(&self, node: &impl HasSpan) -> Option<&ClassLikeReflection> {
684 self.class_like_reflections.get(&ClassLikeName::AnonymousClass(node.span()))
685 }
686
687 /// Retrieves a method reflection from a class-like entity by its name, if it exists.
688 ///
689 /// This method first checks the class-like entity for the method. If the method is not found,
690 /// it checks the class-like entity's ancestors for the method.
691 ///
692 /// # Arguments
693 ///
694 /// - `class`: The class-like reflection to search for the method.
695 /// - `method`: The name of the method to retrieve.
696 ///
697 /// # Returns
698 ///
699 /// - `Some(&FunctionLikeReflection)` if the method exists.
700 /// - `None` otherwise.
701 pub fn get_method<'a>(
702 &'a self,
703 class: &'a ClassLikeReflection,
704 method: &StringIdentifier,
705 ) -> Option<&'a FunctionLikeReflection> {
706 class.methods.members.get(method).or_else(|| {
707 let appering_in_class = class.methods.appering_members.get(method)?;
708
709 self.class_like_reflections.get(appering_in_class)?.methods.members.get(method)
710 })
711 }
712
713 /// Returns the function-like reflection (function, closure, etc.) that encloses the given offset.
714 ///
715 /// This method iterates through the reflections in the codebase, filtering for function-like reflections
716 /// that contain the given offset in their definition range. It returns the reflection with the
717 /// largest starting offset, effectively finding the innermost function-like reflection containing
718 /// the offset.
719 ///
720 /// # Arguments
721 ///
722 /// * `has_position` - The position to search for.
723 ///
724 /// # Returns
725 ///
726 /// * `Option<&FunctionLikeReflection>` - The enclosing function-like reflection, if found.
727 pub fn get_enclosing_function_like(&self, has_position: &impl HasPosition) -> Option<&FunctionLikeReflection> {
728 self.function_like_reflections
729 .iter()
730 .filter(|(_, function_like)| function_like.span.has_offset(has_position.offset()))
731 .max_by_key(|(_, function_like)| function_like.span.start.offset)
732 .map(|(_, function_like)| function_like)
733 }
734
735 /// Returns the class-like reflection (class, trait, etc.) that encloses the given offset.
736 ///
737 /// This method iterates through the reflections in the codebase, filtering for class-like reflections
738 /// that contain the given offset in their definition range. It returns the reflection with the
739 /// largest starting offset, effectively finding the innermost class-like reflection containing
740 /// the offset.
741 ///
742 /// # Arguments
743 ///
744 /// * `has_position` - The position to search for.
745 ///
746 /// # Returns
747 ///
748 /// * `Option<&ClassLikeReflection>` - The enclosing class-like reflection, if found.
749 pub fn get_enclosing_class_like(&self, has_position: &impl HasPosition) -> Option<&ClassLikeReflection> {
750 self.class_like_reflections
751 .iter()
752 .filter(|(_, class_like)| class_like.span.has_offset(has_position.offset()))
753 .max_by_key(|(_, class_like)| class_like.span.start.offset)
754 .map(|(_, class_like)| class_like)
755 }
756
757 /// Takes all issues from the codebase reflection and its children.
758 ///
759 /// This method iterates over all constant, function-like, and class-like reflections
760 /// in the codebase, collecting all issues found during the population of the metadata.
761 ///
762 /// # Returns
763 ///
764 /// - An `IssueCollection` containing all issues found in the codebase and its children.
765 pub fn take_issues(&mut self) -> IssueCollection {
766 let issues = self
767 .constant_reflections
768 .iter_mut()
769 .flat_map(|(_, constant)| constant.take_issues())
770 .chain(self.function_like_reflections.iter_mut().flat_map(|(_, function_like)| function_like.take_issues()))
771 .chain(self.class_like_reflections.iter_mut().flat_map(|(_, class_like)| class_like.take_issues()));
772
773 IssueCollection::from(issues)
774 }
775}
776
777fn lower_constant_name(interner: &ThreadedInterner, name: &StringIdentifier) -> StringIdentifier {
778 let name = interner.lookup(name);
779
780 let mut parts: Vec<_> = name.split('\\').map(str::to_owned).collect();
781 let total_parts = parts.len();
782 if total_parts > 1 {
783 parts = parts
784 .into_iter()
785 .enumerate()
786 .map(|(i, part)| if i < total_parts - 1 { part.to_ascii_lowercase() } else { part })
787 .collect::<Vec<_>>();
788 }
789
790 interner.intern(parts.join("\\"))
791}