grip/
method_purity_registry.rs1use std::collections::{HashMap, HashSet};
6use std::path::PathBuf;
7
8use syn::visit::Visit;
9use syn::{ImplItem, ImplItemFn, Item, ItemImpl, parse_file};
10
11use crate::function_purity::FunctionPurity;
12use crate::hidden_dep_finder::HiddenDepFinder;
13use crate::struct_registry::{StructRegistry, self_ty_name};
14
15#[derive(Debug, Default)]
16pub struct MethodPurityRegistry {
17 pure_methods: HashSet<(String, String)>,
18}
19
20impl MethodPurityRegistry {
21 #[must_use]
22 pub fn build(files: &[(PathBuf, String)], struct_registry: &StructRegistry) -> Self {
23 let mut registry = Self::default();
24 let no_nested_trust = Self::default();
25 for (_, source) in files {
26 if let Ok(syntax) = parse_file(source) {
27 for item in &syntax.items {
28 registry.visit_top_level_item(item, struct_registry, &no_nested_trust);
29 }
30 }
31 }
32 registry
33 }
34
35 #[must_use]
36 pub fn is_known_pure_method(&self, type_name: &str, method: &str) -> bool {
37 self.pure_methods
38 .contains(&(type_name.to_string(), method.to_string()))
39 }
40
41 fn visit_top_level_item(
42 &mut self,
43 item: &Item,
44 struct_registry: &StructRegistry,
45 no_nested_trust: &MethodPurityRegistry,
46 ) {
47 match item {
48 Item::Impl(item_impl) => {
49 self.record_impl(item_impl, struct_registry, no_nested_trust);
50 }
51 Item::Mod(item_mod) => {
52 if let Some((_, items)) = &item_mod.content {
53 for inner in items {
54 self.visit_top_level_item(inner, struct_registry, no_nested_trust);
55 }
56 }
57 }
58 _ => {}
59 }
60 }
61
62 fn record_impl(
63 &mut self,
64 item_impl: &ItemImpl,
65 struct_registry: &StructRegistry,
66 no_nested_trust: &MethodPurityRegistry,
67 ) {
68 if item_impl.trait_.is_some() {
69 return;
70 }
71 let type_name = self_ty_name(&item_impl.self_ty);
72 let concrete_fields = struct_registry.concrete_fields_of(&type_name);
73 for item in &item_impl.items {
74 if let ImplItem::Fn(method) = item {
75 self.record_method(
76 &type_name,
77 method,
78 struct_registry,
79 no_nested_trust,
80 &concrete_fields,
81 );
82 }
83 }
84 }
85
86 fn record_method(
87 &mut self,
88 type_name: &str,
89 method: &ImplItemFn,
90 struct_registry: &StructRegistry,
91 no_nested_trust: &MethodPurityRegistry,
92 concrete_fields: &HashMap<String, String>,
93 ) {
94 if !Self::has_pure_signature(method) {
95 return;
96 }
97 let mut finder = HiddenDepFinder::new(struct_registry, no_nested_trust);
98 finder.set_concrete_fields(concrete_fields.clone());
99 finder.visit_block(&method.block);
100 if finder.count == 0 {
101 self.pure_methods
102 .insert((type_name.to_string(), method.sig.ident.to_string()));
103 }
104 }
105
106 fn has_pure_signature(method: &ImplItemFn) -> bool {
107 !FunctionPurity::has_mut_param(&method.sig)
108 && !FunctionPurity::is_unit_return(&method.sig)
109 && method.sig.unsafety.is_none()
110 && !FunctionPurity::has_unsafe_block(&method.block)
111 && !FunctionPurity::has_io_call(&method.block)
112 }
113}