Skip to main content

mago_codex/identifier/
function_like.rs

1use mago_database::file::File;
2
3use mago_span::Span;
4use mago_word::Word;
5
6use crate::identifier::method::MethodIdentifier;
7
8/// Identifies a specific function-like construct within the codebase.
9///
10/// This distinguishes between globally/namespaced defined functions, methods within
11/// class-like structures, and closures identified by their synthetic name.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub enum FunctionLikeIdentifier {
15    /// A globally or namespaced defined function.
16    /// * `Word` - The fully qualified name (FQN) of the function.
17    Function(Word),
18    /// A method within a class, interface, trait, or enum.
19    /// * `Word` - The fully qualified class name (FQCN) of the containing structure.
20    /// * `Word` - The name of the method.
21    Method(Word, Word),
22    /// A closure (anonymous function `function() {}` or arrow function `fn() => expr`).
23    ///
24    /// * `Word` - The synthetic display name produced by
25    ///   [`crate::build_synthetic_name`], for example
26    ///   `{closure:src/foo.php:12:5}`. The same word doubles as the
27    ///   `function_likes` HashMap key and as the user-facing identifier in
28    ///   issue messages, so the name is stable across machines and rebuilds.
29    Closure(Word),
30}
31
32impl FunctionLikeIdentifier {
33    #[inline]
34    #[must_use]
35    pub fn for_closure(file: &File, span: Span) -> Self {
36        Self::Closure(crate::build_synthetic_name("closure", file, span))
37    }
38
39    /// Checks if this identifier represents a `Function`.
40    #[inline]
41    #[must_use]
42    pub const fn is_function(&self) -> bool {
43        matches!(self, FunctionLikeIdentifier::Function(_))
44    }
45
46    /// Checks if this identifier represents a `Method`.
47    #[inline]
48    #[must_use]
49    pub const fn is_method(&self) -> bool {
50        matches!(self, FunctionLikeIdentifier::Method(_, _))
51    }
52
53    /// Checks if this identifier represents a `Closure`.
54    #[inline]
55    #[must_use]
56    pub const fn is_closure(&self) -> bool {
57        matches!(self, FunctionLikeIdentifier::Closure(_))
58    }
59
60    /// If this identifier represents a method, returns it as a `MethodIdentifier`.
61    /// Otherwise, returns `None`.
62    #[inline]
63    #[must_use]
64    pub const fn as_method_identifier(&self) -> Option<MethodIdentifier> {
65        match self {
66            FunctionLikeIdentifier::Method(fq_classlike_name, method_name) => {
67                Some(MethodIdentifier::new(*fq_classlike_name, *method_name))
68            }
69            _ => None,
70        }
71    }
72
73    /// Returns a string representation of the kind of function-like construct.
74    #[inline]
75    #[must_use]
76    pub const fn title_kind_str(&self) -> &'static str {
77        match self {
78            FunctionLikeIdentifier::Function(_) => "Function",
79            FunctionLikeIdentifier::Method(_, _) => "Method",
80            FunctionLikeIdentifier::Closure(_) => "Closure",
81        }
82    }
83
84    /// Returns a string representation of the kind of function-like construct.
85    #[inline]
86    #[must_use]
87    pub const fn kind_str(&self) -> &'static str {
88        match self {
89            FunctionLikeIdentifier::Function(_) => "function",
90            FunctionLikeIdentifier::Method(_, _) => "method",
91            FunctionLikeIdentifier::Closure(_) => "closure",
92        }
93    }
94
95    /// Converts the identifier to a human-readable string representation.
96    ///
97    /// Functions and methods render as `name` and `Class::method`. Closures
98    /// render as their synthetic name verbatim, e.g. `{closure:src/foo.php:12:5}`.
99    #[inline]
100    #[must_use]
101    pub fn as_string(&self) -> String {
102        match self {
103            FunctionLikeIdentifier::Function(fn_name) => fn_name.to_string(),
104            FunctionLikeIdentifier::Method(fq_classlike_name, method_name) => {
105                format!("{fq_classlike_name}::{method_name}")
106            }
107            FunctionLikeIdentifier::Closure(name) => name.to_string(),
108        }
109    }
110
111    /// Creates a stable string representation suitable for use as a key or unique ID.
112    #[inline]
113    #[must_use]
114    pub fn to_hash(&self) -> String {
115        self.as_string()
116    }
117}
118
119impl From<MethodIdentifier> for FunctionLikeIdentifier {
120    #[inline]
121    fn from(value: MethodIdentifier) -> Self {
122        FunctionLikeIdentifier::Method(value.get_class_name(), value.get_method_name())
123    }
124}