1use std::collections::HashSet;
8
9use crate::error::{Error, ErrorKind, Span};
10use crate::types::{ValidatedDef, ValidatedSubDecoder};
11
12use super::resolve::ResolvedBindings;
13use super::types::*;
14
15pub fn validate(resolved: &ResolvedBindings) -> Result<(), Vec<Error>> {
16 let mut errors: Vec<Error> = Vec::new();
17
18 for target in &resolved.file.targets {
19 match target.kind {
20 TargetKind::Rust => {
21 for d in &target.rust_decoders {
22 validate_decoder_ref(resolved, d, &mut errors);
23 }
24 for d in &target.rust_dispatches {
25 validate_dispatch(resolved, d, None, &mut errors);
26 }
27 }
28 TargetKind::Cpp => {
29 for d in &target.cpp_decoders {
30 validate_decoder_ref(resolved, d, &mut errors);
31 }
32 }
33 TargetKind::Ida => {
34 for p in &target.ida_processors {
35 validate_processor(resolved, p, &mut errors);
36 }
37 }
38 TargetKind::Binja => {
39 for a in &target.binja_architectures {
40 validate_architecture(resolved, a, &mut errors);
41 }
42 }
43 }
44 }
45
46 if errors.is_empty() {
47 Ok(())
48 } else {
49 Err(errors)
50 }
51}
52
53fn validate_decoder_ref(resolved: &ResolvedBindings, d: &DecoderBinding, errors: &mut Vec<Error>) {
54 if resolved.find_decoder_or_sub(&d.decoder_name).is_none() {
55 errors.push(Error::new(
56 ErrorKind::UnknownDecoderInBinding {
57 name: d.decoder_name.clone(),
58 suggestion: closest(&d.decoder_name, &resolved.all_decoder_names()),
59 },
60 d.span.clone(),
61 ));
62 }
63 for sd in &d.subdecoders {
64 if resolved.find_subdecoder(&sd.decoder_name).is_none() {
65 errors.push(Error::new(
66 ErrorKind::UnknownDecoderInBinding {
67 name: sd.decoder_name.clone(),
68 suggestion: closest(&sd.decoder_name, &resolved.all_decoder_names()),
69 },
70 sd.span.clone(),
71 ));
72 }
73 }
74}
75
76fn validate_dispatch(
77 resolved: &ResolvedBindings,
78 d: &DispatchBinding,
79 parent: Option<&DispatchBinding>,
80 errors: &mut Vec<Error>,
81) {
82 let target = resolved.find_decoder_or_sub(&d.decoder_name);
83 if target.is_none() {
84 errors.push(Error::new(
85 ErrorKind::UnknownDecoderInBinding {
86 name: d.decoder_name.clone(),
87 suggestion: closest(&d.decoder_name, &resolved.all_decoder_names()),
88 },
89 d.span.clone(),
90 ));
91 }
92
93 let inherited_invalid = parent.and_then(|p| p.invalid_handler.as_ref());
95 if d.invalid_handler.is_none() && inherited_invalid.is_none() {
96 errors.push(Error::new(
97 ErrorKind::MissingInvalidHandler(d.decoder_name.clone()),
98 d.span.clone(),
99 ));
100 }
101
102 let inherited_handlers = parent.and_then(|p| p.handlers.as_ref());
103 if d.handlers.is_none() && inherited_handlers.is_none() {
104 errors.push(Error::new(
105 ErrorKind::MissingBindingsField {
106 block: format!("dispatch {}", d.decoder_name),
107 field: "handlers".to_string(),
108 },
109 d.span.clone(),
110 ));
111 }
112
113 let inherited_context = parent.and_then(|p| p.context.as_ref());
114 if d.context.is_none() && inherited_context.is_none() {
115 errors.push(Error::new(
116 ErrorKind::MissingBindingsField {
117 block: format!("dispatch {}", d.decoder_name),
118 field: "context".to_string(),
119 },
120 d.span.clone(),
121 ));
122 }
123
124 let inherited_strategy = parent.and_then(|p| p.strategy);
125 if d.strategy.is_none() && inherited_strategy.is_none() {
126 errors.push(Error::new(
127 ErrorKind::MissingBindingsField {
128 block: format!("dispatch {}", d.decoder_name),
129 field: "strategy".to_string(),
130 },
131 d.span.clone(),
132 ));
133 }
134
135 if d.output.is_none() && parent.is_none() {
136 errors.push(Error::new(
140 ErrorKind::MissingBindingsField {
141 block: format!("dispatch {}", d.decoder_name),
142 field: "output".to_string(),
143 },
144 d.span.clone(),
145 ));
146 }
147
148 if let Some((spec, sub)) = target {
150 let instr_names: Vec<String> = match sub {
151 Some(sd) => sd.instructions.iter().map(|i| i.name.clone()).collect(),
152 None => spec
153 .def
154 .instructions
155 .iter()
156 .map(|i| i.name.clone())
157 .collect(),
158 };
159 let known: HashSet<&str> = instr_names.iter().map(|s| s.as_str()).collect();
160 for group in &d.handler_groups {
161 for (name, span) in &group.instructions {
162 if !known.contains(name.as_str()) {
163 errors.push(Error::new(
164 ErrorKind::UnknownInstructionInGroup {
165 instruction: name.clone(),
166 suggestion: closest(name, &instr_names),
167 },
168 span.clone(),
169 ));
170 }
171 }
172 }
173 }
174
175 for sd in &d.subdispatches {
177 validate_dispatch(resolved, sd, Some(d), errors);
178 }
179}
180
181fn validate_processor(
182 resolved: &ResolvedBindings,
183 p: &IdaProcessorBinding,
184 errors: &mut Vec<Error>,
185) {
186 require(
187 &p.output,
188 "output",
189 "processor",
190 &p.decoder_name,
191 &p.span,
192 errors,
193 );
194 require(
195 &p.name,
196 "name",
197 "processor",
198 &p.decoder_name,
199 &p.span,
200 errors,
201 );
202 require(
203 &p.long_name,
204 "long_name",
205 "processor",
206 &p.decoder_name,
207 &p.span,
208 errors,
209 );
210 require_n(&p.id, "id", "processor", &p.decoder_name, &p.span, errors);
211 require_n(
212 &p.address_size,
213 "address_size",
214 "processor",
215 &p.decoder_name,
216 &p.span,
217 errors,
218 );
219 require_n(
220 &p.bytes_per_unit,
221 "bytes_per_unit",
222 "processor",
223 &p.decoder_name,
224 &p.span,
225 errors,
226 );
227
228 if p.registers.is_empty() {
229 errors.push(Error::new(
230 ErrorKind::MissingBindingsField {
231 block: format!("processor {}", p.decoder_name),
232 field: "registers".to_string(),
233 },
234 p.span.clone(),
235 ));
236 }
237
238 let target = resolved.find_decoder_or_sub(&p.decoder_name);
239 if target.is_none() {
240 errors.push(Error::new(
241 ErrorKind::UnknownDecoderInBinding {
242 name: p.decoder_name.clone(),
243 suggestion: closest(&p.decoder_name, &resolved.all_decoder_names()),
244 },
245 p.span.clone(),
246 ));
247 }
248
249 let regs: HashSet<&str> = p.registers.iter().map(|s| s.as_str()).collect();
250 for (sr, span) in &p.segment_registers {
251 if !regs.contains(sr.as_str()) {
252 errors.push(Error::new(
253 ErrorKind::SegmentRegisterNotDeclared(sr.clone()),
254 span.clone(),
255 ));
256 }
257 }
258
259 if let Some((spec, sub)) = target {
260 let instr_names: Vec<String> = match sub {
261 Some(sd) => sd.instructions.iter().map(|i| i.name.clone()).collect(),
262 None => spec
263 .def
264 .instructions
265 .iter()
266 .map(|i| i.name.clone())
267 .collect(),
268 };
269 let known: HashSet<&str> = instr_names.iter().map(|s| s.as_str()).collect();
270 for group in [&p.flow.calls, &p.flow.returns, &p.flow.stops] {
271 for (name, span) in group {
272 if !known.contains(name.as_str()) {
273 errors.push(Error::new(
274 ErrorKind::UnknownInstructionInFlow {
275 instruction: name.clone(),
276 suggestion: closest(name, &instr_names),
277 },
278 span.clone(),
279 ));
280 }
281 }
282 }
283 }
284}
285
286fn validate_architecture(
287 resolved: &ResolvedBindings,
288 a: &BinjaArchitectureBinding,
289 errors: &mut Vec<Error>,
290) {
291 require(
292 &a.output,
293 "output",
294 "architecture",
295 &a.decoder_name,
296 &a.span,
297 errors,
298 );
299 require(
300 &a.name,
301 "name",
302 "architecture",
303 &a.decoder_name,
304 &a.span,
305 errors,
306 );
307 require_n(
308 &a.address_size,
309 "address_size",
310 "architecture",
311 &a.decoder_name,
312 &a.span,
313 errors,
314 );
315 require_n(
316 &a.default_int_size,
317 "default_int_size",
318 "architecture",
319 &a.decoder_name,
320 &a.span,
321 errors,
322 );
323
324 if let Some((value, span)) = &a.endianness {
325 if value != "big" && value != "little" {
326 errors.push(Error::new(
327 ErrorKind::InvalidEndianness(value.clone()),
328 span.clone(),
329 ));
330 }
331 } else {
332 errors.push(Error::new(
333 ErrorKind::MissingBindingsField {
334 block: format!("architecture {}", a.decoder_name),
335 field: "endianness".to_string(),
336 },
337 a.span.clone(),
338 ));
339 }
340
341 if a.registers.is_empty() {
342 errors.push(Error::new(
343 ErrorKind::MissingBindingsField {
344 block: format!("architecture {}", a.decoder_name),
345 field: "registers".to_string(),
346 },
347 a.span.clone(),
348 ));
349 }
350
351 if resolved.find_decoder_or_sub(&a.decoder_name).is_none() {
352 errors.push(Error::new(
353 ErrorKind::UnknownDecoderInBinding {
354 name: a.decoder_name.clone(),
355 suggestion: closest(&a.decoder_name, &resolved.all_decoder_names()),
356 },
357 a.span.clone(),
358 ));
359 }
360}
361
362fn require<T: AsRef<str>>(
363 value: &Option<T>,
364 field: &str,
365 block_kind: &str,
366 name: &str,
367 span: &Span,
368 errors: &mut Vec<Error>,
369) {
370 if value.is_none() {
371 errors.push(Error::new(
372 ErrorKind::MissingBindingsField {
373 block: format!("{} {}", block_kind, name),
374 field: field.to_string(),
375 },
376 span.clone(),
377 ));
378 }
379}
380
381fn require_n<T>(
382 value: &Option<T>,
383 field: &str,
384 block_kind: &str,
385 name: &str,
386 span: &Span,
387 errors: &mut Vec<Error>,
388) {
389 if value.is_none() {
390 errors.push(Error::new(
391 ErrorKind::MissingBindingsField {
392 block: format!("{} {}", block_kind, name),
393 field: field.to_string(),
394 },
395 span.clone(),
396 ));
397 }
398}
399
400#[allow(dead_code)]
403fn _types_used(_: &ValidatedDef, _: &ValidatedSubDecoder) {}
404
405fn closest(target: &str, pool: &[String]) -> Option<String> {
409 let cap = (target.len() / 3).max(2);
410 let mut best: Option<(usize, String)> = None;
411 for cand in pool {
412 let d = levenshtein(target, cand);
413 if d > cap {
414 continue;
415 }
416 if best.as_ref().map(|(bd, _)| d < *bd).unwrap_or(true) {
417 best = Some((d, cand.clone()));
418 }
419 }
420 best.map(|(_, s)| s)
421}
422
423fn levenshtein(a: &str, b: &str) -> usize {
424 let a: Vec<char> = a.chars().collect();
425 let b: Vec<char> = b.chars().collect();
426 let mut prev: Vec<usize> = (0..=b.len()).collect();
427 let mut cur: Vec<usize> = vec![0; b.len() + 1];
428 for i in 1..=a.len() {
429 cur[0] = i;
430 for j in 1..=b.len() {
431 let cost = if a[i - 1] == b[j - 1] { 0 } else { 1 };
432 cur[j] = (prev[j] + 1).min(cur[j - 1] + 1).min(prev[j - 1] + cost);
433 }
434 std::mem::swap(&mut prev, &mut cur);
435 }
436 prev[b.len()]
437}