Skip to main content

mago_codex/
signature.rs

1use serde::Deserialize;
2use serde::Serialize;
3
4use mago_atom::Atom;
5
6/// Represents a signature node for a definition (function, class, method, constant, etc.).
7///
8/// This structure forms a hierarchical tree where top-level symbols (classes, functions)
9/// can have children (methods, properties within classes).
10///
11/// Unlike Hakana which separates `signature_hash` and `body_hash`, we use a single `hash`
12/// field that covers everything. This is more conservative but simpler, and aligns with
13/// the requirements for taint analysis where any change requires re-analysis.
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub struct DefSignatureNode {
16    /// The name of the symbol (e.g., "Foo" for class Foo, "bar" for method bar)
17    pub name: Atom,
18
19    /// Whether this node represents a function or method
20    pub is_function: bool,
21
22    /// Whether this node represents a constant
23    pub is_constant: bool,
24
25    /// Starting byte offset in the source file
26    pub start_offset: u32,
27
28    /// Ending byte offset in the source file
29    pub end_offset: u32,
30
31    /// Starting line number (1-indexed)
32    pub start_line: u32,
33
34    /// Ending line number (1-indexed)
35    pub end_line: u32,
36
37    /// Starting column (0-indexed)
38    pub start_column: u16,
39
40    /// Ending column (0-indexed)
41    pub end_column: u16,
42
43    /// Nested symbols (e.g., methods and properties within a class)
44    pub children: Vec<DefSignatureNode>,
45
46    /// Position-insensitive fingerprint hash covering the entire definition.
47    /// Any change to signature, body, modifiers, or attributes will change this hash.
48    pub hash: u64,
49}
50
51impl DefSignatureNode {
52    /// Creates a new `DefSignatureNode` with the given parameters.
53    #[inline]
54    #[allow(clippy::too_many_arguments)]
55    #[must_use]
56    pub fn new(
57        name: Atom,
58        is_function: bool,
59        is_constant: bool,
60        start_offset: u32,
61        end_offset: u32,
62        start_line: u32,
63        end_line: u32,
64        start_column: u16,
65        end_column: u16,
66        hash: u64,
67    ) -> Self {
68        Self {
69            name,
70            is_function,
71            is_constant,
72            start_offset,
73            end_offset,
74            start_line,
75            end_line,
76            start_column,
77            end_column,
78            children: Vec::new(),
79            hash,
80        }
81    }
82
83    /// Adds a child node to this definition.
84    #[inline]
85    pub fn add_child(&mut self, child: DefSignatureNode) {
86        self.children.push(child);
87    }
88
89    /// Returns a reference to the children of this node.
90    #[inline]
91    #[must_use]
92    pub fn children(&self) -> &[DefSignatureNode] {
93        &self.children
94    }
95
96    /// Returns a mutable reference to the children of this node.
97    #[inline]
98    pub fn children_mut(&mut self) -> &mut Vec<DefSignatureNode> {
99        &mut self.children
100    }
101}
102
103/// Represents the signature of an entire file.
104///
105/// This contains all top-level definitions (classes, interfaces, traits, enums,
106/// functions, constants) in the file as a flat vector. Nested definitions
107/// (methods, properties) are stored within the `children` of their parent nodes.
108#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
109pub struct FileSignature {
110    pub hash: u64,
111    pub ast_nodes: Vec<DefSignatureNode>,
112}
113
114impl FileSignature {
115    /// Creates a new empty `FileSignature`.
116    #[inline]
117    #[must_use]
118    pub fn new(hash: u64) -> Self {
119        Self { hash, ast_nodes: Vec::new() }
120    }
121
122    /// Adds a top-level definition node to this file signature.
123    #[inline]
124    pub fn add_node(&mut self, node: DefSignatureNode) {
125        self.ast_nodes.push(node);
126    }
127
128    /// Returns a reference to the top-level nodes.
129    #[inline]
130    #[must_use]
131    pub fn nodes(&self) -> &[DefSignatureNode] {
132        &self.ast_nodes
133    }
134
135    /// Returns a mutable reference to the top-level nodes.
136    #[inline]
137    pub fn nodes_mut(&mut self) -> &mut Vec<DefSignatureNode> {
138        &mut self.ast_nodes
139    }
140
141    /// Returns true if this file signature has no nodes.
142    #[inline]
143    #[must_use]
144    pub fn is_empty(&self) -> bool {
145        self.ast_nodes.is_empty()
146    }
147
148    /// Returns the number of top-level nodes.
149    #[inline]
150    #[must_use]
151    pub fn len(&self) -> usize {
152        self.ast_nodes.len()
153    }
154}