Thanks to visit codestin.com
Credit goes to docs.rs

Skip to main content

mago_codex/context/
mod.rs

1use mago_word::Word;
2
3use crate::identifier::function_like::FunctionLikeIdentifier;
4use crate::metadata::class_like::ClassLikeMetadata;
5use crate::metadata::function_like::FunctionLikeMetadata;
6use crate::metadata::property_hook::PropertyHookMetadata;
7use crate::reference::ReferenceSource;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
10#[allow(clippy::field_scoped_visibility_modifiers)]
11pub struct ScopeContext<'ctx> {
12    pub(crate) function_like: Option<&'ctx FunctionLikeMetadata>,
13    pub(crate) class_like: Option<&'ctx ClassLikeMetadata>,
14    pub(crate) property_hook: Option<(Word, &'ctx PropertyHookMetadata)>,
15    pub(crate) is_static: bool,
16}
17
18impl Default for ScopeContext<'_> {
19    fn default() -> Self {
20        Self::new()
21    }
22}
23
24impl<'ctx> ScopeContext<'ctx> {
25    /// Creates a new `ScopeContext` representing a default global, static scope.
26    #[inline]
27    #[must_use]
28    pub fn new() -> Self {
29        Self { function_like: None, class_like: None, property_hook: None, is_static: true }
30    }
31
32    /// Returns whether the current scope is a global scope.
33    #[inline]
34    #[must_use]
35    pub const fn is_global(&self) -> bool {
36        self.function_like.is_none() && self.class_like.is_none()
37    }
38
39    /// Returns whether the current scope is pure.
40    #[inline]
41    #[must_use]
42    pub const fn is_pure(&self) -> bool {
43        if let Some(function_like) = self.function_like
44            && function_like.flags.is_pure()
45        {
46            return true;
47        }
48
49        false
50    }
51
52    /// Returns the calling class-like context, if available.
53    #[inline]
54    #[must_use]
55    pub fn get_class_like(&self) -> Option<&'ctx ClassLikeMetadata> {
56        self.class_like
57    }
58
59    /// Returns the calling class FQCN, if inside a class scope.
60    #[inline]
61    #[must_use]
62    pub fn get_class_like_name(&self) -> Option<Word> {
63        self.class_like.map(|class| class.name)
64    }
65
66    /// Returns the calling function-like context, if available.
67    #[inline]
68    #[must_use]
69    pub fn get_function_like(&self) -> Option<&'ctx FunctionLikeMetadata> {
70        self.function_like
71    }
72
73    /// Returns the identifier of the calling function/method, if available.
74    #[inline]
75    #[must_use]
76    pub fn get_function_like_identifier(&self) -> Option<FunctionLikeIdentifier> {
77        let function_like = self.function_like?;
78        let function_name = function_like.name;
79
80        if function_like.get_kind().is_method() {
81            if let Some(class_like) = self.class_like {
82                return Some(FunctionLikeIdentifier::Method(class_like.name, function_name));
83            }
84
85            return Some(FunctionLikeIdentifier::Function(function_name));
86        }
87
88        let kind = function_like.get_kind();
89        if kind.is_closure() || kind.is_arrow_function() {
90            return Some(FunctionLikeIdentifier::Closure(function_name));
91        }
92
93        Some(FunctionLikeIdentifier::Function(function_name))
94    }
95
96    /// Checks if the calling class scope is marked as `final`.
97    #[inline]
98    #[must_use]
99    pub const fn is_class_like_final(&self) -> bool {
100        match self.class_like {
101            Some(class) => class.flags.is_final(),
102            None => false,
103        }
104    }
105
106    /// Checks if the calling scope is static.
107    #[inline]
108    #[must_use]
109    pub const fn is_static(&self) -> bool {
110        self.is_static
111    }
112
113    /// Sets the function-like metadata for the current scope.
114    #[inline]
115    pub fn set_function_like(&mut self, function_like: Option<&'ctx FunctionLikeMetadata>) {
116        self.function_like = function_like;
117    }
118
119    /// Sets the class-like metadata for the current scope.
120    #[inline]
121    pub fn set_class_like(&mut self, class_like: Option<&'ctx ClassLikeMetadata>) {
122        self.class_like = class_like;
123    }
124
125    /// Sets the static flag for the current scope.
126    #[inline]
127    pub fn set_static(&mut self, is_static: bool) {
128        self.is_static = is_static;
129    }
130
131    /// Returns the property hook context, if available.
132    ///
133    /// Returns a tuple of (`property_name`, `hook_metadata`) when analyzing a property hook body.
134    #[inline]
135    #[must_use]
136    pub fn get_property_hook(&self) -> Option<(Word, &'ctx PropertyHookMetadata)> {
137        self.property_hook
138    }
139
140    /// Sets the property hook context for the current scope.
141    ///
142    /// Used when analyzing property hook bodies to enable proper return type validation.
143    #[inline]
144    pub fn set_property_hook(&mut self, property_hook: Option<(Word, &'ctx PropertyHookMetadata)>) {
145        self.property_hook = property_hook;
146    }
147
148    /// Determines the `ReferenceSource` (symbol or member) based on the current function context.
149    /// Used to identify the origin of a code reference for dependency tracking.
150    #[inline]
151    #[must_use]
152    pub fn get_reference_source(&self) -> Option<ReferenceSource> {
153        if let Some(calling_functionlike_id) = self.get_function_like_identifier() {
154            match calling_functionlike_id {
155                FunctionLikeIdentifier::Function(name) => Some(ReferenceSource::Symbol(false, name)),
156                FunctionLikeIdentifier::Method(class_name, method_name) => {
157                    Some(ReferenceSource::ClassLikeMember(false, class_name, method_name))
158                }
159                _ => None,
160            }
161        } else {
162            None
163        }
164    }
165}