mago_analyzer/plugin/libraries/stdlib/array/
compact.rs1use std::collections::BTreeMap;
4
5use mago_atom::Atom;
6use mago_codex::ttype::atomic::TAtomic;
7use mago_codex::ttype::atomic::array::TArray;
8use mago_codex::ttype::atomic::array::key::ArrayKey;
9use mago_codex::ttype::atomic::array::keyed::TKeyedArray;
10use mago_codex::ttype::get_mixed;
11use mago_codex::ttype::get_string;
12use mago_codex::ttype::union::TUnion;
13
14use crate::plugin::context::InvocationInfo;
15use crate::plugin::context::ProviderContext;
16use crate::plugin::provider::Provider;
17use crate::plugin::provider::ProviderMeta;
18use crate::plugin::provider::function::FunctionReturnTypeProvider;
19use crate::plugin::provider::function::FunctionTarget;
20
21static META: ProviderMeta =
22 ProviderMeta::new("php::array::compact", "compact", "Returns array with keys from variable names");
23
24#[derive(Default)]
28pub struct CompactProvider;
29
30impl Provider for CompactProvider {
31 fn meta() -> &'static ProviderMeta {
32 &META
33 }
34}
35
36impl FunctionReturnTypeProvider for CompactProvider {
37 fn targets() -> FunctionTarget {
38 FunctionTarget::Exact("compact")
39 }
40
41 fn get_return_type(
42 &self,
43 context: &ProviderContext<'_, '_, '_>,
44 invocation: &InvocationInfo<'_, '_, '_>,
45 ) -> Option<TUnion> {
46 let arguments = invocation.arguments();
47 let mut known_items: BTreeMap<ArrayKey, (bool, TUnion)> = BTreeMap::new();
48 let mut has_unknown = false;
49
50 for invocation_argument in arguments {
51 if invocation_argument.is_unpacked() {
52 has_unknown = true;
53 continue;
54 }
55
56 let Some(argument_expr) = invocation_argument.value() else {
57 continue;
58 };
59
60 let argument_type = context.get_expression_type(argument_expr)?;
61 let Some(variable_name) = argument_type.get_single_literal_string_value() else {
62 continue;
63 };
64
65 let variable_id = format!("${variable_name}");
66 if let Some(variable_type) = context.get_variable_type(&variable_id) {
67 let key = ArrayKey::String(Atom::from(variable_name));
68 known_items.insert(key, (false, (**variable_type).clone()));
69 } else {
70 has_unknown = true;
71 }
72 }
73
74 if known_items.is_empty() {
75 return None;
76 }
77
78 let mut keyed_array = TKeyedArray::new();
79 keyed_array.known_items = Some(known_items);
80 keyed_array.non_empty = true;
81 if has_unknown {
82 keyed_array.parameters = Some((Box::new(get_string()), Box::new(get_mixed())));
83 }
84
85 Some(TUnion::from_atomic(TAtomic::Array(TArray::Keyed(keyed_array))))
86 }
87}