Skip to main content

mago_analyzer/plugin/libraries/stdlib/math/
abs.rs

1use mago_codex::ttype::atomic::TAtomic;
2use mago_codex::ttype::atomic::scalar::TScalar;
3use mago_codex::ttype::atomic::scalar::int::TInteger;
4use mago_codex::ttype::union::TUnion;
5
6use crate::plugin::context::InvocationInfo;
7use crate::plugin::context::ProviderContext;
8use crate::plugin::provider::Provider;
9use crate::plugin::provider::ProviderMeta;
10use crate::plugin::provider::function::FunctionReturnTypeProvider;
11use crate::plugin::provider::function::FunctionTarget;
12
13use super::get_integer_from_type;
14
15/// Provider for the `abs()` function.
16#[derive(Default)]
17pub struct AbsProvider;
18
19impl Provider for AbsProvider {
20    fn meta() -> &'static ProviderMeta {
21        static META: ProviderMeta =
22            ProviderMeta::new("php::math::abs", "abs", "Return the absolute value of a number.");
23
24        &META
25    }
26}
27
28impl FunctionReturnTypeProvider for AbsProvider {
29    fn targets() -> FunctionTarget {
30        FunctionTarget::ExactMultiple(&[b"abs", b"psl\\math\\abs"])
31    }
32
33    #[allow(clippy::similar_names)]
34    fn get_return_type(
35        &self,
36        context: &ProviderContext<'_, '_, '_>,
37        invocation: &InvocationInfo<'_, '_, '_>,
38    ) -> Option<TUnion> {
39        let arg = invocation.get_argument(0, &[b"num"])?;
40        let arg_type = context.get_expression_type(arg)?;
41        let integer = get_integer_from_type(arg_type)?;
42
43        let (lb, ub) = integer.get_bounds();
44
45        let result = match (lb, ub) {
46            (Some(low), _) if low >= 0 => integer,
47            (_, Some(high)) if high <= 0 => {
48                let new_lb = high.checked_neg().map(Some).unwrap_or(None);
49                let new_ub = lb.and_then(|v| v.checked_neg());
50
51                TInteger::from_bounds(new_lb, new_ub)
52            }
53            (Some(low), Some(high)) => {
54                let abs_low = low.checked_neg().unwrap_or(i64::MAX);
55                let abs_high = high;
56
57                TInteger::from_bounds(Some(0), Some(std::cmp::max(abs_low, abs_high)))
58            }
59            _ => TInteger::from_bounds(Some(0), None),
60        };
61
62        Some(TUnion::from_atomic(TAtomic::Scalar(TScalar::Integer(result))))
63    }
64}