mago_syntax/ast/ast/class_like/
method.rs

1use serde::Deserialize;
2use serde::Serialize;
3use strum::Display;
4
5use mago_span::HasSpan;
6use mago_span::Span;
7
8use crate::ast::ast::attribute::AttributeList;
9use crate::ast::ast::block::Block;
10use crate::ast::ast::function_like::parameter::FunctionLikeParameterList;
11use crate::ast::ast::function_like::r#return::FunctionLikeReturnTypeHint;
12use crate::ast::ast::identifier::LocalIdentifier;
13use crate::ast::ast::keyword::Keyword;
14use crate::ast::ast::modifier::Modifier;
15use crate::ast::sequence::Sequence;
16
17/// Represents a method statement in PHP.
18///
19/// Example:
20///
21/// ```php
22/// class Foo {
23///    public function bar() {
24///       return 'baz';
25///    }
26/// }
27/// ```
28#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
29#[repr(C)]
30pub struct Method {
31    pub attribute_lists: Sequence<AttributeList>,
32    pub modifiers: Sequence<Modifier>,
33    pub function: Keyword,
34    pub ampersand: Option<Span>,
35    pub name: LocalIdentifier,
36    pub parameter_list: FunctionLikeParameterList,
37    pub return_type_hint: Option<FunctionLikeReturnTypeHint>,
38    pub body: MethodBody,
39}
40
41/// Represents the body of a method statement in PHP.
42#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord, Display)]
43#[serde(tag = "type", content = "value")]
44#[repr(C, u8)]
45pub enum MethodBody {
46    Abstract(MethodAbstractBody),
47    Concrete(Block),
48}
49
50/// Represents the abstract body of a method statement in PHP.
51///
52/// Example:
53///
54/// ```php
55/// <?php
56///
57/// abstract class Foo {
58///   abstract public function bar();
59/// }
60///
61/// ```
62#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
63#[repr(C)]
64pub struct MethodAbstractBody {
65    pub semicolon: Span,
66}
67
68impl Method {
69    /// Returns `true` if the method contains any promoted properties.
70    pub fn has_promoted_properties(&self) -> bool {
71        self.parameter_list.parameters.iter().any(|parameter| parameter.is_promoted_property())
72    }
73
74    /// Returns `true` if the method is abstract.
75    #[inline]
76    pub const fn is_abstract(&self) -> bool {
77        matches!(self.body, MethodBody::Abstract(_))
78    }
79}
80
81impl HasSpan for Method {
82    fn span(&self) -> Span {
83        if let Some(attribute_list) = self.attribute_lists.first() {
84            return Span::between(attribute_list.span(), self.body.span());
85        }
86
87        if let Some(modifier) = self.modifiers.first() {
88            return Span::between(modifier.span(), self.body.span());
89        }
90
91        Span::between(self.function.span, self.body.span())
92    }
93}
94
95impl HasSpan for MethodBody {
96    fn span(&self) -> Span {
97        match self {
98            MethodBody::Abstract(body) => body.span(),
99            MethodBody::Concrete(body) => body.span(),
100        }
101    }
102}
103
104impl HasSpan for MethodAbstractBody {
105    fn span(&self) -> Span {
106        self.semicolon
107    }
108}