mago_codex/context/
mod.rs

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