mago_analyzer/plugin/libraries/stdlib/reflection/
mod.rs1mod class_has_method;
11mod method_get_name;
12mod method_invoke;
13
14pub use class_has_method::ReflectionClassHasMethodAssertionProvider;
15use mago_codex::ttype::combiner::CombinerOptions;
16pub use method_get_name::ReflectionMethodGetNameProvider;
17pub use method_invoke::ReflectionMethodInvokeProvider;
18
19use mago_codex::metadata::CodebaseMetadata;
20use mago_codex::ttype::atomic::TAtomic;
21use mago_codex::ttype::atomic::object::TObject;
22use mago_codex::ttype::atomic::object::named::TNamedObject;
23use mago_codex::ttype::atomic::scalar::TScalar;
24use mago_codex::ttype::combine_union_types;
25use mago_codex::ttype::combiner;
26use mago_codex::ttype::expander::StaticClassType;
27use mago_codex::ttype::get_mixed;
28use mago_codex::ttype::union::TUnion;
29use mago_word::Word;
30
31use crate::plugin::context::InvocationInfo;
32use crate::plugin::context::ProviderContext;
33
34fn receiver_named_object<'ctx>(invocation: &InvocationInfo<'ctx, '_, '_>) -> Option<&'ctx TNamedObject> {
37 let method_context = invocation.inner().target.get_method_context()?;
38
39 match &method_context.class_type {
40 StaticClassType::Object(TObject::Named(named)) => Some(named),
41 _ => None,
42 }
43}
44
45fn reflected_class_name(
58 context: &ProviderContext<'_, '_, '_>,
59 invocation: &InvocationInfo<'_, '_, '_>,
60 expected_receiver: &[u8],
61) -> Option<Word> {
62 let receiver = receiver_named_object(invocation)?;
63 if !context.is_instance_of(receiver.get_name().as_bytes(), expected_receiver) {
64 return None;
65 }
66
67 let parameter = receiver.get_type_parameters()?.first()?;
68
69 Some(parameter.get_single_named_object()?.get_name())
70}
71
72fn method_name_union(codebase: &CodebaseMetadata, class_name: Word) -> Option<TUnion> {
76 let class_metadata = codebase.get_class_like(class_name.as_bytes())?;
77
78 let mut atomics = Vec::new();
79 for method_id in class_metadata.appearing_method_ids.values() {
80 if let Some(metadata) =
81 codebase.get_method(method_id.get_class_name().as_bytes(), method_id.get_method_name().as_bytes())
82 {
83 atomics.push(TAtomic::Scalar(TScalar::literal_string(metadata.original_name)));
84 }
85 }
86
87 if atomics.is_empty() {
88 return None;
89 }
90
91 Some(TUnion::from_vec(combiner::combine(atomics, codebase, CombinerOptions::default())))
92}
93
94fn method_return_union(codebase: &CodebaseMetadata, class_name: Word) -> Option<TUnion> {
99 let class_metadata = codebase.get_class_like(class_name.as_bytes())?;
100
101 let mut result: Option<TUnion> = None;
102 for method_id in class_metadata.appearing_method_ids.values() {
103 let Some(metadata) =
104 codebase.get_method(method_id.get_class_name().as_bytes(), method_id.get_method_name().as_bytes())
105 else {
106 continue;
107 };
108
109 let return_type =
110 metadata.return_type_metadata.as_ref().map_or_else(get_mixed, |metadata| metadata.type_union.clone());
111
112 result = Some(match result {
113 None => return_type,
114 Some(accumulated) => combine_union_types(&accumulated, &return_type, codebase, CombinerOptions::default()),
115 });
116 }
117
118 result
119}