mago_syntax/ast/ast/function_like/
parameter.rs

1use serde::Serialize;
2
3use mago_span::HasSpan;
4use mago_span::Span;
5
6use crate::ast::ast::attribute::AttributeList;
7use crate::ast::ast::class_like::property::PropertyHookList;
8use crate::ast::ast::expression::Expression;
9use crate::ast::ast::modifier::Modifier;
10use crate::ast::ast::type_hint::Hint;
11use crate::ast::ast::variable::DirectVariable;
12use crate::ast::sequence::Sequence;
13use crate::ast::sequence::TokenSeparatedSequence;
14
15/// Represents a parameter list in PHP.
16#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
17pub struct FunctionLikeParameterList<'arena> {
18    pub left_parenthesis: Span,
19    pub parameters: TokenSeparatedSequence<'arena, FunctionLikeParameter<'arena>>,
20    pub right_parenthesis: Span,
21}
22
23/// Represents a function-like parameter in PHP.
24///
25/// Example: `int $foo`, `string &$bar`, `bool ...$baz`, `mixed $qux = null`
26#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
27pub struct FunctionLikeParameter<'arena> {
28    pub attribute_lists: Sequence<'arena, AttributeList<'arena>>,
29    pub modifiers: Sequence<'arena, Modifier<'arena>>,
30    pub hint: Option<Hint<'arena>>,
31    pub ampersand: Option<Span>,
32    pub ellipsis: Option<Span>,
33    pub variable: DirectVariable<'arena>,
34    pub default_value: Option<FunctionLikeParameterDefaultValue<'arena>>,
35    pub hooks: Option<PropertyHookList<'arena>>,
36}
37
38/// Represents the default value of a function-like parameter.
39#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, PartialOrd, Ord)]
40pub struct FunctionLikeParameterDefaultValue<'arena> {
41    pub equals: Span,
42    pub value: Expression<'arena>,
43}
44
45impl FunctionLikeParameter<'_> {
46    /// Returns whether the parameter is a promoted property.
47    ///
48    /// A promoted property is a property that is declared in a constructor parameter list.
49    ///
50    /// A parameter is considered a promoted property if it has at least one modifier or a hook.
51    ///
52    /// [RFC: Constructor Property Promotion](https://wiki.php.net/rfc/constructor_promotion)
53    /// [RFC: Property Hooks](https://wiki.php.net/rfc/property-hooks)
54    pub fn is_promoted_property(&self) -> bool {
55        !self.modifiers.is_empty() || self.hooks.is_some()
56    }
57
58    #[inline]
59    pub const fn is_variadic(&self) -> bool {
60        self.ellipsis.is_some()
61    }
62
63    #[inline]
64    pub const fn is_reference(&self) -> bool {
65        self.ampersand.is_some()
66    }
67}
68
69impl HasSpan for FunctionLikeParameterList<'_> {
70    fn span(&self) -> Span {
71        Span::between(self.left_parenthesis, self.right_parenthesis)
72    }
73}
74
75impl HasSpan for FunctionLikeParameter<'_> {
76    fn span(&self) -> Span {
77        let right = self.hooks.as_ref().map(|hooks| hooks.span()).unwrap_or_else(|| {
78            self.default_value.as_ref().map_or_else(|| self.variable.span(), |default_value| default_value.span())
79        });
80
81        if let Some(attribute) = self.attribute_lists.first() {
82            return Span::between(attribute.span(), right);
83        }
84
85        if let Some(modifier) = self.modifiers.first() {
86            return Span::between(modifier.span(), right);
87        }
88
89        if let Some(type_hint) = &self.hint {
90            return Span::between(type_hint.span(), right);
91        }
92
93        if let Some(ellipsis) = self.ellipsis {
94            return Span::between(ellipsis, right);
95        }
96
97        if let Some(ampersand) = self.ampersand {
98            return Span::between(ampersand, right);
99        }
100
101        Span::between(self.variable.span(), right)
102    }
103}
104
105impl HasSpan for FunctionLikeParameterDefaultValue<'_> {
106    fn span(&self) -> Span {
107        Span::between(self.equals, self.value.span())
108    }
109}