mago_analyzer/plugin/libraries/stdlib/array/
array_all.rs1use std::sync::Arc;
11
12use mago_codex::assertion::Assertion;
13use mago_codex::ttype::atomic::TAtomic;
14use mago_codex::ttype::atomic::array::TArray;
15use mago_codex::ttype::atomic::array::keyed::TKeyedArray;
16use mago_codex::ttype::get_array_parameters;
17use mago_word::word;
18
19use crate::plugin::context::InvocationInfo;
20use crate::plugin::context::ProviderContext;
21use crate::plugin::libraries::stdlib::array::array_filter::apply_assertion_to_narrow_type;
22use crate::plugin::provider::Provider;
23use crate::plugin::provider::ProviderMeta;
24use crate::plugin::provider::assertion::FunctionAssertionProvider;
25use crate::plugin::provider::assertion::InvocationAssertions;
26use crate::plugin::provider::function::FunctionTarget;
27
28static META: ProviderMeta =
29 ProviderMeta::new("php::array::array_all", "array_all", "Lifts a callback's if-true assertion onto the array");
30
31#[derive(Default)]
32pub struct ArrayAllAssertionProvider;
33
34impl Provider for ArrayAllAssertionProvider {
35 fn meta() -> &'static ProviderMeta {
36 &META
37 }
38}
39
40impl FunctionAssertionProvider for ArrayAllAssertionProvider {
41 fn targets() -> FunctionTarget {
42 FunctionTarget::Exact(b"array_all")
43 }
44
45 fn get_assertions(
46 &self,
47 context: &ProviderContext<'_, '_, '_>,
48 invocation: &InvocationInfo<'_, '_, '_>,
49 ) -> Option<InvocationAssertions> {
50 let array_argument = invocation.get_argument(0, &[b"array"])?;
51 let array_type = context.get_expression_type(array_argument)?;
52
53 let callback_argument = invocation.get_argument(1, &[b"callback"])?;
54 let callback_metadata = context.get_callable_metadata(callback_argument)?;
55 if callback_metadata.if_true_assertions.is_empty() {
56 return None;
57 }
58
59 let first_param = callback_metadata.parameters.first()?;
60 let param_name = first_param.get_name().0;
61 let callback_assertions = callback_metadata.if_true_assertions.get(¶m_name)?;
62
63 let codebase = context.codebase();
64 let mut narrowed_atomics: Vec<TAtomic> = Vec::new();
65 for variant in array_type.types.as_ref().iter() {
66 let TAtomic::Array(array) = variant else {
67 return None;
68 };
69
70 let (key_type, value_type) = get_array_parameters(array, codebase);
71 let mut narrowed_value = value_type;
72 for assertion in callback_assertions {
73 narrowed_value = apply_assertion_to_narrow_type(narrowed_value, assertion, codebase);
74 }
75
76 let mut keyed = if let TArray::Keyed(keyed) = array { keyed.clone() } else { TKeyedArray::new() };
79
80 keyed = keyed.with_parameters(Arc::new(key_type), Arc::new(narrowed_value));
81 narrowed_atomics.push(TAtomic::Array(TArray::Keyed(keyed)));
82 }
83
84 let [narrowed_atomic] = narrowed_atomics.try_into().ok()?;
88
89 let assertion = Assertion::IsType(narrowed_atomic);
90
91 let mut result = InvocationAssertions::new();
92 result.add_if_true(word("$array"), vec![assertion]);
93 Some(result)
94 }
95}