mago_codex/misc.rs
1use serde::Deserialize;
2use serde::Serialize;
3
4use mago_span::Span;
5use mago_word::Word;
6use mago_word::concat_word;
7
8/// Represents a PHP variable identifier (e.g., `$foo`, `$this`).
9/// Wraps a `Word` which holds the interned name (including '$').
10#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
11pub struct VariableIdentifier(
12 /// The atom for the variable name (e.g., "$foo").
13 pub Word,
14);
15
16/// Identifies the target of an expression, distinguishing simple variables from property accesses.
17#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
18pub enum ExpressionIdentifier {
19 /// A simple variable identifier.
20 ///
21 /// * `VariableIdentifier` - The identifier for the variable (e.g., `$foo`).
22 Variable(VariableIdentifier),
23 /// An instance property access (e.g., `$this->prop`, `$user->name`).
24 ///
25 /// * `VariableIdentifier` - The identifier for the object variable (e.g., `$this`, `$user`).
26 /// * `Span` - The source code location covering the property name part (e.g., `prop` or `name`).
27 /// * `Word` - The name of the property being accessed (e.g., `prop`, `name`).
28 InstanceProperty(VariableIdentifier, Span, Word),
29}
30
31/// Identifies the scope where a generic template parameter (`@template`) is defined.
32#[derive(PartialEq, Eq, Hash, Clone, Copy, Serialize, Deserialize, PartialOrd, Ord, Debug)]
33pub enum GenericParent {
34 /// The template is defined on a class, interface, trait, or enum.
35 /// * `Word` - The fully qualified name (FQCN) of the class-like structure.
36 ClassLike(Word),
37 /// The template is defined on a function or method.
38 /// * `(Word, Word)` - A tuple representing the function/method.
39 /// - `.0`: The FQCN of the class if it's a method, or the FQN of the function if global/namespaced.
40 /// - `.1`: The method name if it's a method, or `Word::empty()` if it's a function.
41 FunctionLike((Word, Word)),
42}
43
44impl GenericParent {
45 /// Builds the identifier form of this scope using the raw interned bytes of every `Word`.
46 ///
47 /// Unlike the [`Display`](std::fmt::Display) impl, this never escapes, so a non-UTF-8
48 /// class or function name round-trips byte-for-byte into the resulting id. This matters
49 /// because type ids key the subtype cache and combiner dedup, where an escaped rendering
50 /// could collide distinct byte sequences.
51 #[inline]
52 #[must_use]
53 pub fn id_word(&self) -> Word {
54 match self {
55 GenericParent::ClassLike(id) => *id,
56 GenericParent::FunctionLike((part1, part2)) => {
57 if part1.is_empty() {
58 concat_word!(part2.as_bytes(), b"()")
59 } else {
60 concat_word!(part1.as_bytes(), b"::", part2.as_bytes(), b"()")
61 }
62 }
63 }
64 }
65}
66
67impl std::fmt::Display for GenericParent {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 match self {
70 GenericParent::ClassLike(id) => write!(f, "{id}"),
71 GenericParent::FunctionLike(id) => {
72 let part1 = id.0;
73 let part2 = id.1;
74
75 if part1.is_empty() { write!(f, "{part2}()") } else { write!(f, "{part1}::{part2}()") }
76 }
77 }
78 }
79}