weaveffi_core/validate/
warnings.rs1use weaveffi_ir::ir::{Api, TypeRef};
10
11#[derive(Debug, Clone)]
13pub enum ValidationWarning {
14 LargeEnumVariantCount {
15 enum_name: String,
16 count: usize,
17 },
18 DeepNesting {
19 location: String,
20 depth: usize,
21 },
22 EmptyModuleDoc {
23 module: String,
24 },
25 AsyncVoidFunction {
26 module: String,
27 function: String,
28 },
29 MutableOnValueType {
30 module: String,
31 function: String,
32 param: String,
33 },
34 DeprecatedFunction {
35 module: String,
36 function: String,
37 message: String,
38 },
39}
40
41impl std::fmt::Display for ValidationWarning {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 match self {
44 Self::LargeEnumVariantCount { enum_name, count } => {
45 write!(f, "enum '{enum_name}' has {count} variants (>100)")
46 }
47 Self::DeepNesting { location, depth } => {
48 write!(
49 f,
50 "deep type nesting at {location} (depth {depth}, max recommended 3)"
51 )
52 }
53 Self::EmptyModuleDoc { module } => {
54 write!(f, "module '{module}' has no doc comments on any function")
55 }
56 Self::AsyncVoidFunction { module, function } => {
57 write!(
58 f,
59 "async function {module}::{function} has no return type; async void is unusual"
60 )
61 }
62 Self::MutableOnValueType {
63 module,
64 function,
65 param,
66 } => {
67 write!(
68 f,
69 "'mutable' on value-type parameter {module}::{function}::{param} has no effect; only meaningful for pointer/reference types (struct, string, bytes)"
70 )
71 }
72 Self::DeprecatedFunction {
73 module,
74 function,
75 message,
76 } => {
77 write!(f, "function {module}::{function} is deprecated: {message}")
78 }
79 }
80 }
81}
82
83pub fn collect_warnings(api: &Api) -> Vec<ValidationWarning> {
88 let mut warnings = Vec::new();
89 for module in &api.modules {
90 for e in &module.enums {
91 if e.variants.len() > 100 {
92 warnings.push(ValidationWarning::LargeEnumVariantCount {
93 enum_name: e.name.clone(),
94 count: e.variants.len(),
95 });
96 }
97 }
98
99 for f in &module.functions {
100 for p in &f.params {
101 let depth = nesting_depth(&p.ty);
102 if depth > 3 {
103 warnings.push(ValidationWarning::DeepNesting {
104 location: format!("{}::{}::{}", module.name, f.name, p.name),
105 depth,
106 });
107 }
108 }
109 if let Some(ret) = &f.returns {
110 let depth = nesting_depth(ret);
111 if depth > 3 {
112 warnings.push(ValidationWarning::DeepNesting {
113 location: format!("{}::{}::return", module.name, f.name),
114 depth,
115 });
116 }
117 }
118 }
119 for s in &module.structs {
120 for field in &s.fields {
121 let depth = nesting_depth(&field.ty);
122 if depth > 3 {
123 warnings.push(ValidationWarning::DeepNesting {
124 location: format!("{}::{}::{}", module.name, s.name, field.name),
125 depth,
126 });
127 }
128 }
129 }
130
131 for f in &module.functions {
132 if f.r#async && f.returns.is_none() {
133 warnings.push(ValidationWarning::AsyncVoidFunction {
134 module: module.name.clone(),
135 function: f.name.clone(),
136 });
137 }
138 for p in &f.params {
139 if p.mutable && is_value_type(&p.ty) {
140 warnings.push(ValidationWarning::MutableOnValueType {
141 module: module.name.clone(),
142 function: f.name.clone(),
143 param: p.name.clone(),
144 });
145 }
146 }
147 }
148
149 for f in &module.functions {
150 if let Some(msg) = &f.deprecated {
151 warnings.push(ValidationWarning::DeprecatedFunction {
152 module: module.name.clone(),
153 function: f.name.clone(),
154 message: msg.clone(),
155 });
156 }
157 }
158
159 if !module.functions.is_empty() && module.functions.iter().all(|f| f.doc.is_none()) {
160 warnings.push(ValidationWarning::EmptyModuleDoc {
161 module: module.name.clone(),
162 });
163 }
164 }
165 warnings
166}
167
168fn is_value_type(ty: &TypeRef) -> bool {
169 matches!(
170 ty,
171 TypeRef::I32
172 | TypeRef::U32
173 | TypeRef::I64
174 | TypeRef::F64
175 | TypeRef::Bool
176 | TypeRef::Enum(_)
177 | TypeRef::Handle
178 )
179}
180
181fn nesting_depth(ty: &TypeRef) -> usize {
182 match ty {
183 TypeRef::Optional(inner) | TypeRef::List(inner) | TypeRef::Iterator(inner) => {
184 1 + nesting_depth(inner)
185 }
186 TypeRef::Map(k, v) => nesting_depth(k).max(nesting_depth(v)),
187 _ => 0,
188 }
189}