Skip to main content

ryo_analysis/query/
derive_index.rs

1//! DeriveIndex: Fast lookup for derive trait validation.
2//!
3//! This index pre-computes the relationship between types and their derive traits,
4//! enabling O(1) lookup for derive possibility checks instead of O(N) AST traversal.
5
6use super::{CodeGraphV2, TypeFlowGraphV2};
7use crate::ast::ASTRegistry;
8use crate::symbol::SymbolRegistry;
9use crate::SymbolId;
10use ryo_source::pure::{PureAttrMeta, PureItem};
11use serde::Serialize;
12use slotmap::SecondaryMap;
13use smallvec::SmallVec;
14
15/// Index for fast derive trait validation.
16///
17/// # Memory Layout
18/// ```text
19/// DeriveIndex
20/// ├── symbol_derives: SecondaryMap<SymbolId, SmallVec<[String; 4]>>
21/// │   └── struct/enum → derive trait names (e.g., ["Debug", "Clone"])
22/// └── field_type_names: SecondaryMap<SymbolId, SmallVec<[String; 8]>>
23///     └── struct/enum → field type names (for derive validation)
24/// ```
25#[derive(Clone, Default, Debug, Serialize)]
26pub struct DeriveIndex {
27    /// struct/enum SymbolId → derive trait names.
28    /// Most types derive 2-4 traits, so SmallVec<[String; 4]> is optimal.
29    symbol_derives: SecondaryMap<SymbolId, SmallVec<[String; 4]>>,
30
31    /// struct/enum SymbolId → field type names.
32    /// Used for checking if all fields implement the trait.
33    field_type_names: SecondaryMap<SymbolId, SmallVec<[String; 8]>>,
34}
35
36impl DeriveIndex {
37    /// Create a new empty index.
38    pub fn new() -> Self {
39        Self::default()
40    }
41
42    /// Build the index from ASTRegistry, CodeGraph, TypeFlow, and SymbolRegistry.
43    pub fn build(
44        ast_registry: &ASTRegistry,
45        code_graph: &CodeGraphV2,
46        typeflow: &TypeFlowGraphV2,
47        symbol_registry: &SymbolRegistry,
48    ) -> Self {
49        let mut index = Self::new();
50        index.rebuild_all(ast_registry, code_graph, typeflow, symbol_registry);
51        index
52    }
53
54    /// Rebuild the entire index.
55    pub fn rebuild_all(
56        &mut self,
57        ast_registry: &ASTRegistry,
58        code_graph: &CodeGraphV2,
59        typeflow: &TypeFlowGraphV2,
60        symbol_registry: &SymbolRegistry,
61    ) {
62        self.symbol_derives.clear();
63        self.field_type_names.clear();
64
65        for (id, item) in ast_registry.iter() {
66            self.index_item(id, item, code_graph, typeflow, symbol_registry);
67        }
68    }
69
70    /// Incrementally update for specific symbols.
71    ///
72    /// This is O(S) where S is the number of affected symbols,
73    /// instead of O(N) for full rebuild.
74    pub fn rebuild_for_symbols(
75        &mut self,
76        symbols: &[SymbolId],
77        ast_registry: &ASTRegistry,
78        code_graph: &CodeGraphV2,
79        typeflow: &TypeFlowGraphV2,
80        symbol_registry: &SymbolRegistry,
81    ) {
82        for &symbol_id in symbols {
83            // Remove old entries
84            self.symbol_derives.remove(symbol_id);
85            self.field_type_names.remove(symbol_id);
86
87            // Re-index if the symbol still exists
88            if let Some(item) = ast_registry.get(symbol_id) {
89                self.index_item(symbol_id, item, code_graph, typeflow, symbol_registry);
90            }
91        }
92    }
93
94    /// Index a single item.
95    fn index_item(
96        &mut self,
97        id: SymbolId,
98        item: &PureItem,
99        code_graph: &CodeGraphV2,
100        typeflow: &TypeFlowGraphV2,
101        symbol_registry: &SymbolRegistry,
102    ) {
103        let attrs = match item {
104            PureItem::Struct(s) => &s.attrs,
105            PureItem::Enum(e) => &e.attrs,
106            _ => return,
107        };
108
109        // Extract derive traits
110        let mut derives: SmallVec<[String; 4]> = SmallVec::new();
111        for attr in attrs {
112            if attr.path == "derive" {
113                if let PureAttrMeta::List(args) = &attr.meta {
114                    for trait_name in args.split(',').map(|s| s.trim()) {
115                        if !trait_name.is_empty() {
116                            derives.push(trait_name.to_string());
117                        }
118                    }
119                }
120            }
121        }
122
123        if !derives.is_empty() {
124            self.symbol_derives.insert(id, derives);
125        }
126
127        // Extract field type names from TypeFlow
128        let mut field_types: SmallVec<[String; 8]> = SmallVec::new();
129        for child_id in code_graph.children_of(id) {
130            // Get the type that this field uses (via TypeFlow)
131            for use_id in typeflow.types_used_by(child_id) {
132                if let Some(path) = symbol_registry.resolve(use_id) {
133                    field_types.push(path.name().to_string());
134                    break; // Only first type reference
135                }
136            }
137        }
138
139        if !field_types.is_empty() {
140            self.field_type_names.insert(id, field_types);
141        }
142    }
143
144    // ========================================================================
145    // Query Methods
146    // ========================================================================
147
148    /// Iterate over all symbols and their derives.
149    pub fn iter_derives(&self) -> impl Iterator<Item = (SymbolId, &SmallVec<[String; 4]>)> {
150        self.symbol_derives.iter()
151    }
152
153    /// Get derive traits for a symbol.
154    pub fn get_derives(&self, id: SymbolId) -> Option<&SmallVec<[String; 4]>> {
155        self.symbol_derives.get(id)
156    }
157
158    /// Get field type names for a symbol.
159    pub fn get_field_types(&self, id: SymbolId) -> Option<&SmallVec<[String; 8]>> {
160        self.field_type_names.get(id)
161    }
162
163    /// Check if a symbol has a specific derive trait.
164    pub fn has_derive(&self, id: SymbolId, trait_name: &str) -> bool {
165        self.symbol_derives
166            .get(id)
167            .map(|derives| derives.iter().any(|d| d == trait_name))
168            .unwrap_or(false)
169    }
170
171    /// Get all symbols that derive a specific trait.
172    pub fn symbols_deriving(&self, trait_name: &str) -> Vec<SymbolId> {
173        self.symbol_derives
174            .iter()
175            .filter(|(_, derives)| derives.iter().any(|d| d == trait_name))
176            .map(|(id, _)| id)
177            .collect()
178    }
179
180    /// Get statistics about the index.
181    pub fn stats(&self) -> DeriveIndexStats {
182        let total_derives: usize = self.symbol_derives.values().map(|v| v.len()).sum();
183        let total_fields: usize = self.field_type_names.values().map(|v| v.len()).sum();
184
185        DeriveIndexStats {
186            symbols_with_derives: self.symbol_derives.len(),
187            total_derives,
188            symbols_with_fields: self.field_type_names.len(),
189            total_field_types: total_fields,
190        }
191    }
192}
193
194/// Statistics about the DeriveIndex.
195#[derive(Debug, Clone)]
196pub struct DeriveIndexStats {
197    /// Number of distinct symbols that carry at least one `#[derive(...)]`.
198    pub symbols_with_derives: usize,
199    /// Sum of derive entries across all symbols (counts duplicates per
200    /// symbol).
201    pub total_derives: usize,
202    /// Number of distinct symbols that have at least one indexed field
203    /// type.
204    pub symbols_with_fields: usize,
205    /// Sum of indexed field-type entries across all symbols.
206    pub total_field_types: usize,
207}
208
209#[cfg(test)]
210mod tests {
211    use super::*;
212
213    #[test]
214    fn test_derive_index_creation() {
215        let index = DeriveIndex::new();
216        assert_eq!(index.stats().symbols_with_derives, 0);
217    }
218}