Skip to main content

mago_codex/identifier/
function_like.rs

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