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