Skip to main content

mago_analyzer/plugin/libraries/stdlib/array/
compact.rs

1//! `compact()` return type provider.
2
3use 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/// Provider for the `compact()` function.
25///
26/// Builds a keyed array with keys being the variable names and values being the variable values.
27#[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}