Skip to main content

ryo_analysis/query/
std_impls.rs

1//! Standard library trait implementation cache.
2//!
3//! Provides statically known trait implementations for primitive types
4//! and common std types. This avoids needing to analyze std sources.
5
6use std::collections::HashSet;
7
8/// Cache of known trait implementations for standard library types.
9///
10/// This is a static cache that knows which primitives and std types
11/// implement common traits like Clone, Default, Debug, etc.
12#[derive(Debug, Clone)]
13pub struct StdImplCache {
14    /// Set of (type_name, trait_name) pairs that are implemented.
15    impls: HashSet<(&'static str, &'static str)>,
16}
17
18impl StdImplCache {
19    /// Create a new cache with default implementations.
20    pub fn new() -> Self {
21        Self::default()
22    }
23
24    /// Check if a type implements a trait.
25    #[inline]
26    pub fn has_impl(&self, type_name: &str, trait_name: &str) -> bool {
27        // Direct lookup
28        if self.impls.contains(&(type_name, trait_name)) {
29            return true;
30        }
31
32        // Try normalized names (strip module path)
33        let short_type = type_name.rsplit("::").next().unwrap_or(type_name);
34        let short_trait = trait_name.rsplit("::").next().unwrap_or(trait_name);
35
36        self.impls
37            .iter()
38            .any(|(t, tr)| *t == short_type && *tr == short_trait)
39    }
40
41    /// Check whether `trait_name` is a `std`-derivable trait whose impl
42    /// relationships this cache can authoritatively answer.
43    ///
44    /// Only the nine traits that `#[derive(...)]` can expand to from `std`
45    /// (`Clone`, `Copy`, `Debug`, `Default`, `PartialEq`, `Eq`, `PartialOrd`,
46    /// `Ord`, `Hash`) are checkable. Proc-macro derives such as
47    /// `serde::Serialize` / `serde::Deserialize` / `thiserror::Error` /
48    /// `clap::Subcommand` generate impls for std types that cannot be
49    /// enumerated statically, so asking `has_impl("String", "Serialize")`
50    /// would return a false "missing". Such derives must be **skipped**, not
51    /// reported as a precheck failure.
52    pub fn is_checkable_derive(&self, trait_name: &str) -> bool {
53        let short = trait_name.rsplit("::").next().unwrap_or(trait_name);
54        matches!(
55            short,
56            "Clone"
57                | "Copy"
58                | "Debug"
59                | "Default"
60                | "PartialEq"
61                | "Eq"
62                | "PartialOrd"
63                | "Ord"
64                | "Hash"
65        )
66    }
67
68    /// Check if a type is a known primitive.
69    pub fn is_primitive(&self, type_name: &str) -> bool {
70        matches!(
71            type_name,
72            "i8" | "i16"
73                | "i32"
74                | "i64"
75                | "i128"
76                | "isize"
77                | "u8"
78                | "u16"
79                | "u32"
80                | "u64"
81                | "u128"
82                | "usize"
83                | "f32"
84                | "f64"
85                | "bool"
86                | "char"
87                | "()"
88                | "str"
89        )
90    }
91
92    /// Check if a type is a known std container.
93    pub fn is_std_container(&self, type_name: &str) -> bool {
94        let short = type_name.rsplit("::").next().unwrap_or(type_name);
95        matches!(
96            short,
97            "Vec"
98                | "String"
99                | "Option"
100                | "Result"
101                | "Box"
102                | "Rc"
103                | "Arc"
104                | "Cell"
105                | "RefCell"
106                | "HashMap"
107                | "HashSet"
108                | "BTreeMap"
109                | "BTreeSet"
110                | "VecDeque"
111                | "LinkedList"
112                | "BinaryHeap"
113                | "PathBuf"
114                | "OsString"
115                | "Cow"
116        )
117    }
118
119    /// Get all traits a type is known to implement.
120    pub fn traits_for(&self, type_name: &str) -> Vec<&'static str> {
121        let short = type_name.rsplit("::").next().unwrap_or(type_name);
122        self.impls
123            .iter()
124            .filter(|(t, _)| *t == short)
125            .map(|(_, tr)| *tr)
126            .collect()
127    }
128}
129
130impl Default for StdImplCache {
131    fn default() -> Self {
132        let mut impls = HashSet::new();
133
134        // === Primitive types ===
135        // Integer types (signed)
136        for prim in ["i8", "i16", "i32", "i64", "i128", "isize"] {
137            impls.insert((prim, "Clone"));
138            impls.insert((prim, "Copy"));
139            impls.insert((prim, "Debug"));
140            impls.insert((prim, "Default"));
141            impls.insert((prim, "PartialEq"));
142            impls.insert((prim, "Eq"));
143            impls.insert((prim, "PartialOrd"));
144            impls.insert((prim, "Ord"));
145            impls.insert((prim, "Hash"));
146            impls.insert((prim, "Send"));
147            impls.insert((prim, "Sync"));
148        }
149
150        // Integer types (unsigned)
151        for prim in ["u8", "u16", "u32", "u64", "u128", "usize"] {
152            impls.insert((prim, "Clone"));
153            impls.insert((prim, "Copy"));
154            impls.insert((prim, "Debug"));
155            impls.insert((prim, "Default"));
156            impls.insert((prim, "PartialEq"));
157            impls.insert((prim, "Eq"));
158            impls.insert((prim, "PartialOrd"));
159            impls.insert((prim, "Ord"));
160            impls.insert((prim, "Hash"));
161            impls.insert((prim, "Send"));
162            impls.insert((prim, "Sync"));
163        }
164
165        // Float types (no Eq, Ord, Hash)
166        for prim in ["f32", "f64"] {
167            impls.insert((prim, "Clone"));
168            impls.insert((prim, "Copy"));
169            impls.insert((prim, "Debug"));
170            impls.insert((prim, "Default"));
171            impls.insert((prim, "PartialEq"));
172            impls.insert((prim, "PartialOrd"));
173            impls.insert((prim, "Send"));
174            impls.insert((prim, "Sync"));
175        }
176
177        // bool
178        impls.insert(("bool", "Clone"));
179        impls.insert(("bool", "Copy"));
180        impls.insert(("bool", "Debug"));
181        impls.insert(("bool", "Default"));
182        impls.insert(("bool", "PartialEq"));
183        impls.insert(("bool", "Eq"));
184        impls.insert(("bool", "PartialOrd"));
185        impls.insert(("bool", "Ord"));
186        impls.insert(("bool", "Hash"));
187        impls.insert(("bool", "Send"));
188        impls.insert(("bool", "Sync"));
189
190        // char
191        impls.insert(("char", "Clone"));
192        impls.insert(("char", "Copy"));
193        impls.insert(("char", "Debug"));
194        impls.insert(("char", "PartialEq"));
195        impls.insert(("char", "Eq"));
196        impls.insert(("char", "PartialOrd"));
197        impls.insert(("char", "Ord"));
198        impls.insert(("char", "Hash"));
199        impls.insert(("char", "Send"));
200        impls.insert(("char", "Sync"));
201
202        // Unit type
203        impls.insert(("()", "Clone"));
204        impls.insert(("()", "Copy"));
205        impls.insert(("()", "Debug"));
206        impls.insert(("()", "Default"));
207        impls.insert(("()", "PartialEq"));
208        impls.insert(("()", "Eq"));
209        impls.insert(("()", "PartialOrd"));
210        impls.insert(("()", "Ord"));
211        impls.insert(("()", "Hash"));
212        impls.insert(("()", "Send"));
213        impls.insert(("()", "Sync"));
214
215        // === Common std types ===
216
217        // str — a bare `str` is unsized and can never be a struct field; when
218        // it appears as a field type it is always a reference (`&str` /
219        // `&'a str`), which derives every std trait. The Ref-walk in
220        // `derive_index` strips the `&`, so we catalogue the std derives here
221        // to avoid false "missing impl on str" precheck failures.
222        for tr in [
223            "Clone",
224            "Copy",
225            "Debug",
226            "Default",
227            "PartialEq",
228            "Eq",
229            "PartialOrd",
230            "Ord",
231            "Hash",
232            "Send",
233            "Sync",
234        ] {
235            impls.insert(("str", tr));
236        }
237
238        // String
239        impls.insert(("String", "Clone"));
240        impls.insert(("String", "Debug"));
241        impls.insert(("String", "Default"));
242        impls.insert(("String", "PartialEq"));
243        impls.insert(("String", "Eq"));
244        impls.insert(("String", "PartialOrd"));
245        impls.insert(("String", "Ord"));
246        impls.insert(("String", "Hash"));
247        impls.insert(("String", "Send"));
248        impls.insert(("String", "Sync"));
249
250        // Vec<T> - conditional impls, assume T satisfies
251        impls.insert(("Vec", "Clone"));
252        impls.insert(("Vec", "Debug"));
253        impls.insert(("Vec", "Default"));
254        impls.insert(("Vec", "PartialEq"));
255        impls.insert(("Vec", "Eq"));
256        impls.insert(("Vec", "Hash"));
257        impls.insert(("Vec", "Send"));
258        impls.insert(("Vec", "Sync"));
259
260        // Option<T>
261        impls.insert(("Option", "Clone"));
262        impls.insert(("Option", "Copy"));
263        impls.insert(("Option", "Debug"));
264        impls.insert(("Option", "Default"));
265        impls.insert(("Option", "PartialEq"));
266        impls.insert(("Option", "Eq"));
267        impls.insert(("Option", "PartialOrd"));
268        impls.insert(("Option", "Ord"));
269        impls.insert(("Option", "Hash"));
270        impls.insert(("Option", "Send"));
271        impls.insert(("Option", "Sync"));
272
273        // Result<T, E>
274        impls.insert(("Result", "Clone"));
275        impls.insert(("Result", "Copy"));
276        impls.insert(("Result", "Debug"));
277        impls.insert(("Result", "PartialEq"));
278        impls.insert(("Result", "Eq"));
279        impls.insert(("Result", "PartialOrd"));
280        impls.insert(("Result", "Ord"));
281        impls.insert(("Result", "Hash"));
282        impls.insert(("Result", "Send"));
283        impls.insert(("Result", "Sync"));
284
285        // Box<T>
286        impls.insert(("Box", "Clone"));
287        impls.insert(("Box", "Debug"));
288        impls.insert(("Box", "Default"));
289        impls.insert(("Box", "PartialEq"));
290        impls.insert(("Box", "Eq"));
291        impls.insert(("Box", "PartialOrd"));
292        impls.insert(("Box", "Ord"));
293        impls.insert(("Box", "Hash"));
294        impls.insert(("Box", "Send"));
295        impls.insert(("Box", "Sync"));
296
297        // Rc<T> (not Send/Sync)
298        impls.insert(("Rc", "Clone"));
299        impls.insert(("Rc", "Debug"));
300        impls.insert(("Rc", "Default"));
301        impls.insert(("Rc", "PartialEq"));
302        impls.insert(("Rc", "Eq"));
303        impls.insert(("Rc", "PartialOrd"));
304        impls.insert(("Rc", "Ord"));
305        impls.insert(("Rc", "Hash"));
306
307        // Arc<T>
308        impls.insert(("Arc", "Clone"));
309        impls.insert(("Arc", "Debug"));
310        impls.insert(("Arc", "Default"));
311        impls.insert(("Arc", "PartialEq"));
312        impls.insert(("Arc", "Eq"));
313        impls.insert(("Arc", "PartialOrd"));
314        impls.insert(("Arc", "Ord"));
315        impls.insert(("Arc", "Hash"));
316        impls.insert(("Arc", "Send"));
317        impls.insert(("Arc", "Sync"));
318
319        // HashMap<K, V>
320        impls.insert(("HashMap", "Clone"));
321        impls.insert(("HashMap", "Debug"));
322        impls.insert(("HashMap", "Default"));
323        impls.insert(("HashMap", "PartialEq"));
324        impls.insert(("HashMap", "Eq"));
325        impls.insert(("HashMap", "Send"));
326        impls.insert(("HashMap", "Sync"));
327
328        // HashSet<T>
329        impls.insert(("HashSet", "Clone"));
330        impls.insert(("HashSet", "Debug"));
331        impls.insert(("HashSet", "Default"));
332        impls.insert(("HashSet", "PartialEq"));
333        impls.insert(("HashSet", "Eq"));
334        impls.insert(("HashSet", "Send"));
335        impls.insert(("HashSet", "Sync"));
336
337        // BTreeMap<K, V>
338        impls.insert(("BTreeMap", "Clone"));
339        impls.insert(("BTreeMap", "Debug"));
340        impls.insert(("BTreeMap", "Default"));
341        impls.insert(("BTreeMap", "PartialEq"));
342        impls.insert(("BTreeMap", "Eq"));
343        impls.insert(("BTreeMap", "PartialOrd"));
344        impls.insert(("BTreeMap", "Ord"));
345        impls.insert(("BTreeMap", "Send"));
346        impls.insert(("BTreeMap", "Sync"));
347
348        // BTreeSet<T>
349        impls.insert(("BTreeSet", "Clone"));
350        impls.insert(("BTreeSet", "Debug"));
351        impls.insert(("BTreeSet", "Default"));
352        impls.insert(("BTreeSet", "PartialEq"));
353        impls.insert(("BTreeSet", "Eq"));
354        impls.insert(("BTreeSet", "PartialOrd"));
355        impls.insert(("BTreeSet", "Ord"));
356        impls.insert(("BTreeSet", "Hash"));
357        impls.insert(("BTreeSet", "Send"));
358        impls.insert(("BTreeSet", "Sync"));
359
360        // PathBuf
361        impls.insert(("PathBuf", "Clone"));
362        impls.insert(("PathBuf", "Debug"));
363        impls.insert(("PathBuf", "Default"));
364        impls.insert(("PathBuf", "PartialEq"));
365        impls.insert(("PathBuf", "Eq"));
366        impls.insert(("PathBuf", "PartialOrd"));
367        impls.insert(("PathBuf", "Ord"));
368        impls.insert(("PathBuf", "Hash"));
369        impls.insert(("PathBuf", "Send"));
370        impls.insert(("PathBuf", "Sync"));
371
372        // OsString
373        impls.insert(("OsString", "Clone"));
374        impls.insert(("OsString", "Debug"));
375        impls.insert(("OsString", "Default"));
376        impls.insert(("OsString", "PartialEq"));
377        impls.insert(("OsString", "Eq"));
378        impls.insert(("OsString", "PartialOrd"));
379        impls.insert(("OsString", "Ord"));
380        impls.insert(("OsString", "Hash"));
381        impls.insert(("OsString", "Send"));
382        impls.insert(("OsString", "Sync"));
383
384        // Cell<T> (not Sync)
385        impls.insert(("Cell", "Clone"));
386        impls.insert(("Cell", "Copy"));
387        impls.insert(("Cell", "Debug"));
388        impls.insert(("Cell", "Default"));
389        impls.insert(("Cell", "PartialEq"));
390        impls.insert(("Cell", "Eq"));
391        impls.insert(("Cell", "PartialOrd"));
392        impls.insert(("Cell", "Ord"));
393        impls.insert(("Cell", "Send"));
394
395        // RefCell<T> (not Sync)
396        impls.insert(("RefCell", "Clone"));
397        impls.insert(("RefCell", "Debug"));
398        impls.insert(("RefCell", "Default"));
399        impls.insert(("RefCell", "PartialEq"));
400        impls.insert(("RefCell", "Eq"));
401        impls.insert(("RefCell", "PartialOrd"));
402        impls.insert(("RefCell", "Ord"));
403        impls.insert(("RefCell", "Send"));
404
405        // Cow<T>
406        impls.insert(("Cow", "Clone"));
407        impls.insert(("Cow", "Debug"));
408        impls.insert(("Cow", "Default"));
409        impls.insert(("Cow", "PartialEq"));
410        impls.insert(("Cow", "Eq"));
411        impls.insert(("Cow", "PartialOrd"));
412        impls.insert(("Cow", "Ord"));
413        impls.insert(("Cow", "Hash"));
414        impls.insert(("Cow", "Send"));
415        impls.insert(("Cow", "Sync"));
416
417        // VecDeque<T>
418        impls.insert(("VecDeque", "Clone"));
419        impls.insert(("VecDeque", "Debug"));
420        impls.insert(("VecDeque", "Default"));
421        impls.insert(("VecDeque", "PartialEq"));
422        impls.insert(("VecDeque", "Eq"));
423        impls.insert(("VecDeque", "PartialOrd"));
424        impls.insert(("VecDeque", "Ord"));
425        impls.insert(("VecDeque", "Hash"));
426        impls.insert(("VecDeque", "Send"));
427        impls.insert(("VecDeque", "Sync"));
428
429        // PhantomData<T>
430        impls.insert(("PhantomData", "Clone"));
431        impls.insert(("PhantomData", "Copy"));
432        impls.insert(("PhantomData", "Debug"));
433        impls.insert(("PhantomData", "Default"));
434        impls.insert(("PhantomData", "PartialEq"));
435        impls.insert(("PhantomData", "Eq"));
436        impls.insert(("PhantomData", "PartialOrd"));
437        impls.insert(("PhantomData", "Ord"));
438        impls.insert(("PhantomData", "Hash"));
439        impls.insert(("PhantomData", "Send"));
440        impls.insert(("PhantomData", "Sync"));
441
442        Self { impls }
443    }
444}
445
446#[cfg(test)]
447mod tests {
448    use super::*;
449
450    #[test]
451    fn test_primitive_impls() {
452        let cache = StdImplCache::new();
453
454        // i32 has all common traits
455        assert!(cache.has_impl("i32", "Clone"));
456        assert!(cache.has_impl("i32", "Copy"));
457        assert!(cache.has_impl("i32", "Debug"));
458        assert!(cache.has_impl("i32", "Default"));
459        assert!(cache.has_impl("i32", "Eq"));
460        assert!(cache.has_impl("i32", "Hash"));
461
462        // f64 has no Eq or Hash
463        assert!(cache.has_impl("f64", "Clone"));
464        assert!(cache.has_impl("f64", "PartialEq"));
465        assert!(!cache.has_impl("f64", "Eq"));
466        assert!(!cache.has_impl("f64", "Hash"));
467    }
468
469    #[test]
470    fn test_std_type_impls() {
471        let cache = StdImplCache::new();
472
473        assert!(cache.has_impl("String", "Clone"));
474        assert!(cache.has_impl("String", "Default"));
475        assert!(cache.has_impl("Vec", "Clone"));
476        assert!(cache.has_impl("HashMap", "Default"));
477
478        // Rc is not Send
479        assert!(cache.has_impl("Rc", "Clone"));
480        assert!(!cache.has_impl("Rc", "Send"));
481    }
482
483    #[test]
484    fn test_normalized_lookup() {
485        let cache = StdImplCache::new();
486
487        // Should work with full paths
488        assert!(cache.has_impl("std::string::String", "Clone"));
489        assert!(cache.has_impl("std::vec::Vec", "Default"));
490    }
491
492    #[test]
493    fn test_is_primitive() {
494        let cache = StdImplCache::new();
495
496        assert!(cache.is_primitive("i32"));
497        assert!(cache.is_primitive("f64"));
498        assert!(cache.is_primitive("bool"));
499        assert!(!cache.is_primitive("String"));
500        assert!(!cache.is_primitive("Vec"));
501    }
502
503    #[test]
504    fn test_traits_for() {
505        let cache = StdImplCache::new();
506
507        let bool_traits = cache.traits_for("bool");
508        assert!(bool_traits.contains(&"Clone"));
509        assert!(bool_traits.contains(&"Copy"));
510        assert!(bool_traits.contains(&"Eq"));
511    }
512}