mago_analyzer/plugin/libraries/stdlib/array/
array_is_list.rs1use mago_codex::ttype::atomic::TAtomic;
9use mago_codex::ttype::atomic::array::TArray;
10use mago_codex::ttype::get_bool;
11use mago_codex::ttype::union::TUnion;
12
13use crate::plugin::context::InvocationInfo;
14use crate::plugin::context::ProviderContext;
15use crate::plugin::provider::Provider;
16use crate::plugin::provider::ProviderMeta;
17use crate::plugin::provider::function::FunctionReturnTypeProvider;
18use crate::plugin::provider::function::FunctionTarget;
19
20static META: ProviderMeta = ProviderMeta::new(
21 "php::array::array_is_list",
22 "array_is_list",
23 "Keeps generic array key/list checks indeterminate until narrowed",
24);
25
26#[derive(Default)]
27pub struct ArrayIsListProvider;
28
29impl Provider for ArrayIsListProvider {
30 fn meta() -> &'static ProviderMeta {
31 &META
32 }
33}
34
35impl FunctionReturnTypeProvider for ArrayIsListProvider {
36 fn targets() -> FunctionTarget {
37 FunctionTarget::Exact(b"array_is_list")
38 }
39
40 fn get_return_type(
41 &self,
42 context: &ProviderContext<'_, '_, '_>,
43 invocation: &InvocationInfo<'_, '_, '_>,
44 ) -> Option<TUnion> {
45 let array = invocation.get_argument(0, &[b"array"])?;
46 let array_type = context.get_expression_type(array)?;
47
48 array_type.types.iter().any(generic_keyed_array_can_be_list).then_some(get_bool())
49 }
50}
51
52fn generic_keyed_array_can_be_list(atomic: &TAtomic) -> bool {
53 let TAtomic::Array(TArray::Keyed(keyed)) = atomic else {
54 return false;
55 };
56
57 let Some((key_type, _)) = keyed.parameters.as_ref() else {
58 return false;
59 };
60
61 key_type.types.iter().any(
62 |key_atomic| matches!(key_atomic, TAtomic::GenericParameter(parameter) if parameter.constraint.is_array_key()),
63 )
64}