mago_analyzer/plugin/libraries/stdlib/math/
abs.rs1use 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#[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}