mago_codex/metadata/
property.rs

1use serde::Deserialize;
2use serde::Serialize;
3
4use mago_span::Span;
5
6use crate::metadata::flags::MetadataFlags;
7use crate::metadata::ttype::TypeMetadata;
8use crate::misc::VariableIdentifier;
9use crate::visibility::Visibility;
10
11/// Contains metadata associated with a declared class property in PHP.
12///
13/// This includes information about its name, location, visibility (potentially asymmetric),
14/// type hints, default values, and various modifiers (`static`, `readonly`, `abstract`, etc.).
15#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
16pub struct PropertyMetadata {
17    /// The identifier (name) of the property, including the leading '$'.
18    pub name: VariableIdentifier,
19
20    /// The specific source code location (span) of the property's name identifier itself.
21    /// `None` if the location is unknown or not relevant (e.g., for synthetic properties).
22    pub name_span: Option<Span>,
23
24    /// The source code location (span) covering the entire property declaration statement.
25    /// `None` if the location is unknown or not relevant.
26    pub span: Option<Span>,
27
28    /// The visibility level required for reading the property's value.
29    ///
30    /// In PHP, this corresponds to the primary visibility keyword specified
31    /// (e.g., the `public` in `public private(set) string $prop;`).
32    ///
33    /// If no asymmetric visibility is specified (e.g., `public string $prop`),
34    /// this level applies to both reading and writing. Defaults to `Public`.
35    pub read_visibility: Visibility,
36
37    /// The visibility level required for writing/modifying the property's value.
38    ///
39    /// In PHP, this can differ from `read_visibility` using asymmetric visibility syntax
40    /// like `private(set)` (e.g., `public private(set) string $prop;`).
41    ///
42    /// If asymmetric visibility is not used, this implicitly matches `read_visibility`.
43    /// Defaults to `Public`.
44    pub write_visibility: Visibility,
45
46    /// The explicit type declaration (type hint) associated with the property, if any.
47    ///
48    /// e.g., for `public string $name;`, this would contain the metadata for `string`.
49    pub type_declaration_metadata: Option<TypeMetadata>,
50
51    /// The type metadata for the property's type, if any.
52    ///
53    /// This is either the same as `type_declaration_metadata` or the type provided
54    /// in a docblock comment (e.g., `@var string`).
55    pub type_metadata: Option<TypeMetadata>,
56
57    /// The type inferred from the property's default value, if it has one.
58    ///
59    /// e.g., for `public $count = 0;`, this would contain the metadata for `int(0)`.
60    /// This can be used to compare against `type_signature` for consistency checks.
61    pub default_type_metadata: Option<TypeMetadata>,
62
63    /// Flags indicating various properties of the property.
64    pub flags: MetadataFlags,
65}
66
67impl PropertyMetadata {
68    /// Creates new `PropertyMetadata` with basic defaults (public, non-static, non-readonly, etc.).
69    /// Name is mandatory. Spans, types, and flags can be set using modifier methods.
70    #[inline]
71    pub fn new(name: VariableIdentifier, flags: MetadataFlags) -> Self {
72        Self {
73            name,
74            name_span: None,
75            span: None,
76            read_visibility: Visibility::Public,
77            write_visibility: Visibility::Public,
78            type_declaration_metadata: None,
79            type_metadata: None,
80            default_type_metadata: None,
81            flags,
82        }
83    }
84
85    #[inline]
86    pub fn set_default_type_metadata(&mut self, default_type_metadata: Option<TypeMetadata>) {
87        self.default_type_metadata = default_type_metadata;
88    }
89
90    #[inline]
91    pub fn set_type_declaration_metadata(&mut self, type_declaration_metadata: Option<TypeMetadata>) {
92        if self.type_metadata.is_none() {
93            self.type_metadata = type_declaration_metadata.clone();
94        }
95
96        self.type_declaration_metadata = type_declaration_metadata;
97    }
98
99    #[inline]
100    pub fn set_type_metadata(&mut self, type_metadata: Option<TypeMetadata>) {
101        self.type_metadata = type_metadata;
102    }
103
104    /// Returns a reference to the property's name identifier.
105    #[inline]
106    pub fn get_name(&self) -> &VariableIdentifier {
107        &self.name
108    }
109
110    /// Checks if the property is effectively final (private read or write access).
111    #[inline]
112    pub fn is_final(&self) -> bool {
113        self.read_visibility.is_private() || self.write_visibility.is_private()
114    }
115
116    /// Sets the span for the property name identifier.
117    #[inline]
118    pub fn set_name_span(&mut self, name_span: Option<Span>) {
119        self.name_span = name_span;
120    }
121
122    /// Sets the overall span for the property declaration.
123    #[inline]
124    pub fn set_span(&mut self, span: Option<Span>) {
125        self.span = span;
126    }
127
128    /// Sets both read and write visibility levels. Updates `is_asymmetric`. Ensures virtual properties remain symmetric.
129    #[inline]
130    pub fn set_visibility(&mut self, read: Visibility, write: Visibility) {
131        self.read_visibility = read;
132        self.write_visibility = write;
133        self.update_asymmetric();
134    }
135
136    /// Sets whether the property uses property hooks. Updates `is_asymmetric`.
137    #[inline]
138    pub fn set_is_virtual(&mut self, is_virtual: bool) {
139        if is_virtual {
140            self.flags |= MetadataFlags::VIRTUAL_PROPERTY;
141        } else {
142            self.flags &= !MetadataFlags::VIRTUAL_PROPERTY;
143        }
144
145        self.update_asymmetric();
146    }
147
148    /// Also ensures virtual properties are not asymmetric.
149    #[inline]
150    fn update_asymmetric(&mut self) {
151        if self.flags.is_virtual_property() {
152            if self.read_visibility != self.write_visibility {
153                // If virtual and somehow asymmetric, force symmetry (prefer read)
154                self.write_visibility = self.read_visibility;
155            }
156
157            self.flags &= !MetadataFlags::ASYMMETRIC_PROPERTY;
158        } else if self.read_visibility == self.write_visibility {
159            // If both visibilities are the same, ensure no asymmetric flag is set
160            self.flags &= !MetadataFlags::ASYMMETRIC_PROPERTY;
161        } else {
162            // Otherwise, set the asymmetric flag
163            self.flags |= MetadataFlags::ASYMMETRIC_PROPERTY;
164        }
165    }
166}