1use crate::{
2 io::{Filesystem, WitxIo},
3 parser::{
4 CommentSyntax, DeclSyntax, Documented, EnumSyntax, ExpectedSyntax, FlagsSyntax,
5 HandleSyntax, ImportTypeSyntax, ModuleDeclSyntax, RecordSyntax, TupleSyntax, TypedefSyntax,
6 UnionSyntax, VariantSyntax,
7 },
8 Abi, BuiltinType, Case, Constant, Definition, Document, Entry, HandleDatatype, Id, IntRepr,
9 InterfaceFunc, InterfaceFuncParam, Location, Module, ModuleDefinition, ModuleEntry,
10 ModuleImport, ModuleImportVariant, NamedType, RecordDatatype, RecordKind, RecordMember, Type,
11 TypeRef, Variant,
12};
13use std::collections::{HashMap, HashSet};
14use std::path::Path;
15use std::rc::Rc;
16use thiserror::Error;
17
18#[derive(Debug, Error)]
19pub enum ValidationError {
20 #[error("Unknown name `{name}`")]
21 UnknownName { name: String, location: Location },
22 #[error("Redefinition of name `{name}`")]
23 NameAlreadyExists {
24 name: String,
25 at_location: Location,
26 previous_location: Location,
27 },
28 #[error("Wrong kind of name `{name}`: expected {expected}, got {got}")]
29 WrongKindName {
30 name: String,
31 location: Location,
32 expected: &'static str,
33 got: &'static str,
34 },
35 #[error("Recursive definition of name `{name}`")]
36 Recursive { name: String, location: Location },
37 #[error("Invalid representation `{repr:?}`")]
38 InvalidRepr {
39 repr: BuiltinType,
40 location: Location,
41 },
42 #[error("ABI error: {reason}")]
43 Abi { reason: String, location: Location },
44 #[error("Anonymous structured types (struct, union, enum, flags, handle) are not permitted")]
45 AnonymousRecord { location: Location },
46 #[error("Union expected {expected} variants, found {found}")]
47 UnionSizeMismatch {
48 expected: usize,
49 found: usize,
50 location: Location,
51 },
52 #[error("Invalid union tag: {reason}")]
53 InvalidUnionTag { reason: String, location: Location },
54 #[error("Invalid union field `{name}`: {reason}")]
55 InvalidUnionField {
56 name: String,
57 reason: String,
58 location: Location,
59 },
60}
61
62impl ValidationError {
63 pub fn report_with(&self, witxio: &dyn WitxIo) -> String {
64 use ValidationError::*;
65 match self {
66 UnknownName { location, .. }
67 | WrongKindName { location, .. }
68 | Recursive { location, .. }
69 | InvalidRepr { location, .. }
70 | Abi { location, .. }
71 | AnonymousRecord { location, .. }
72 | UnionSizeMismatch { location, .. }
73 | InvalidUnionField { location, .. }
74 | InvalidUnionTag { location, .. } => {
75 format!("{}\n{}", location.highlight_source_with(witxio), &self)
76 }
77 NameAlreadyExists {
78 at_location,
79 previous_location,
80 ..
81 } => format!(
82 "{}\n{}\nOriginally defined at:\n{}",
83 at_location.highlight_source_with(witxio),
84 &self,
85 previous_location.highlight_source_with(witxio),
86 ),
87 }
88 }
89 pub fn report(&self) -> String {
90 self.report_with(&Filesystem)
91 }
92}
93
94struct IdentValidation {
95 names: HashMap<String, Location>,
96}
97
98impl IdentValidation {
99 fn new() -> Self {
100 Self {
101 names: HashMap::new(),
102 }
103 }
104
105 fn introduce(&mut self, syntax: &str, location: Location) -> Result<Id, ValidationError> {
106 if let Some(introduced) = self.names.get(syntax) {
107 Err(ValidationError::NameAlreadyExists {
108 name: syntax.to_string(),
109 at_location: location,
110 previous_location: introduced.clone(),
111 })
112 } else {
113 self.names.insert(syntax.to_string(), location);
114 Ok(Id::new(syntax))
115 }
116 }
117
118 fn get(&self, syntax: &str, location: Location) -> Result<Id, ValidationError> {
119 if self.names.get(syntax).is_some() {
120 Ok(Id::new(syntax))
121 } else {
122 Err(ValidationError::UnknownName {
123 name: syntax.to_string(),
124 location,
125 })
126 }
127 }
128}
129
130pub struct DocValidation {
131 scope: IdentValidation,
132 entries: HashMap<Id, Entry>,
133 constant_scopes: HashMap<Id, IdentValidation>,
134 bool_ty: TypeRef,
135}
136
137pub struct DocValidationScope<'a> {
138 doc: &'a mut DocValidation,
139 text: &'a str,
140 path: &'a Path,
141}
142
143impl DocValidation {
144 pub fn new() -> Self {
145 Self {
146 scope: IdentValidation::new(),
147 entries: HashMap::new(),
148 constant_scopes: HashMap::new(),
149 bool_ty: TypeRef::Value(Rc::new(Type::Variant(Variant {
150 tag_repr: IntRepr::U32,
151 cases: vec![
152 Case {
153 name: Id::new("false"),
154 tref: None,
155 docs: String::new(),
156 },
157 Case {
158 name: Id::new("true"),
159 tref: None,
160 docs: String::new(),
161 },
162 ],
163 }))),
164 }
165 }
166
167 pub fn scope<'a>(&'a mut self, text: &'a str, path: &'a Path) -> DocValidationScope<'a> {
168 DocValidationScope {
169 doc: self,
170 text,
171 path,
172 }
173 }
174
175 pub fn into_document(self, defs: Vec<Definition>) -> Document {
176 Document::new(defs, self.entries)
177 }
178}
179
180impl DocValidationScope<'_> {
181 fn location(&self, span: wast::Span) -> Location {
182 let (line, column) = span.linecol_in(self.text);
184 Location {
185 line: line + 1,
186 column: column + 1,
187 path: self.path.to_path_buf(),
188 }
189 }
190
191 fn introduce(&mut self, name: &wast::Id<'_>) -> Result<Id, ValidationError> {
192 let loc = self.location(name.span());
193 self.doc.scope.introduce(name.name(), loc)
194 }
195
196 fn get(&self, name: &wast::Id<'_>) -> Result<Id, ValidationError> {
197 let loc = self.location(name.span());
198 self.doc.scope.get(name.name(), loc)
199 }
200
201 pub fn validate_decl(
202 &mut self,
203 decl: &DeclSyntax,
204 comments: &CommentSyntax,
205 definitions: &mut Vec<Definition>,
206 ) -> Result<(), ValidationError> {
207 match decl {
208 DeclSyntax::Typename(decl) => {
209 let name = self.introduce(&decl.ident)?;
210 let docs = comments.docs();
211 let tref = self.validate_datatype(&decl.def, true, decl.ident.span())?;
212
213 let rc_datatype = Rc::new(NamedType {
214 name: name.clone(),
215 tref,
216 docs,
217 });
218 self.doc
219 .entries
220 .insert(name.clone(), Entry::Typename(Rc::downgrade(&rc_datatype)));
221 definitions.push(Definition::Typename(rc_datatype));
222 }
223
224 DeclSyntax::Module(syntax) => {
225 let name = self.introduce(&syntax.name)?;
226 let mut module_validator = ModuleValidation::new(self);
227 let decls = syntax
228 .decls
229 .iter()
230 .map(|d| module_validator.validate_decl(&d))
231 .collect::<Result<Vec<_>, _>>()?;
232
233 let rc_module = Rc::new(Module::new(
234 name.clone(),
235 decls,
236 module_validator.entries,
237 comments.docs(),
238 ));
239 self.doc
240 .entries
241 .insert(name, Entry::Module(Rc::downgrade(&rc_module)));
242 definitions.push(Definition::Module(rc_module));
243 }
244
245 DeclSyntax::Const(syntax) => {
246 let ty = Id::new(syntax.item.ty.name());
247 let loc = self.location(syntax.item.name.span());
248 let scope = self
249 .doc
250 .constant_scopes
251 .entry(ty.clone())
252 .or_insert_with(IdentValidation::new);
253 let name = scope.introduce(syntax.item.name.name(), loc)?;
254 definitions.push(Definition::Constant(Constant {
257 ty,
258 name,
259 value: syntax.item.value,
260 docs: syntax.comments.docs(),
261 }));
262 }
263 }
264 Ok(())
265 }
266
267 fn validate_datatype(
268 &self,
269 syntax: &TypedefSyntax,
270 named: bool,
271 span: wast::Span,
272 ) -> Result<TypeRef, ValidationError> {
273 match syntax {
274 TypedefSyntax::Ident(syntax) => {
275 let i = self.get(syntax)?;
276 match self.doc.entries.get(&i) {
277 Some(Entry::Typename(weak_ref)) => Ok(TypeRef::Name(
278 weak_ref.upgrade().expect("weak backref to defined type"),
279 )),
280 Some(e) => Err(ValidationError::WrongKindName {
281 name: i.as_str().to_string(),
282 location: self.location(syntax.span()),
283 expected: "datatype",
284 got: e.kind(),
285 }),
286 None => Err(ValidationError::Recursive {
287 name: i.as_str().to_string(),
288 location: self.location(syntax.span()),
289 }),
290 }
291 }
292 TypedefSyntax::Enum { .. }
293 | TypedefSyntax::Flags { .. }
294 | TypedefSyntax::Record { .. }
295 | TypedefSyntax::Union { .. }
296 | TypedefSyntax::Handle { .. }
297 if !named =>
298 {
299 Err(ValidationError::AnonymousRecord {
300 location: self.location(span),
301 })
302 }
303 other => Ok(TypeRef::Value(Rc::new(match other {
304 TypedefSyntax::Enum(syntax) => Type::Variant(self.validate_enum(&syntax, span)?),
305 TypedefSyntax::Tuple(syntax) => Type::Record(self.validate_tuple(&syntax, span)?),
306 TypedefSyntax::Expected(syntax) => {
307 Type::Variant(self.validate_expected(&syntax, span)?)
308 }
309 TypedefSyntax::Flags(syntax) => Type::Record(self.validate_flags(&syntax, span)?),
310 TypedefSyntax::Record(syntax) => Type::Record(self.validate_record(&syntax, span)?),
311 TypedefSyntax::Union(syntax) => Type::Variant(self.validate_union(&syntax, span)?),
312 TypedefSyntax::Variant(syntax) => {
313 Type::Variant(self.validate_variant(&syntax, span)?)
314 }
315 TypedefSyntax::Handle(syntax) => Type::Handle(self.validate_handle(syntax, span)?),
316 TypedefSyntax::List(syntax) => {
317 Type::List(self.validate_datatype(syntax, false, span)?)
318 }
319 TypedefSyntax::Pointer(syntax) => {
320 Type::Pointer(self.validate_datatype(syntax, false, span)?)
321 }
322 TypedefSyntax::ConstPointer(syntax) => {
323 Type::ConstPointer(self.validate_datatype(syntax, false, span)?)
324 }
325 TypedefSyntax::Builtin(builtin) => Type::Builtin(*builtin),
326 TypedefSyntax::String => {
327 Type::List(TypeRef::Value(Rc::new(Type::Builtin(BuiltinType::Char))))
328 }
329 TypedefSyntax::Bool => return Ok(self.doc.bool_ty.clone()),
330 TypedefSyntax::Ident { .. } => unreachable!(),
331 }))),
332 }
333 }
334
335 fn validate_enum(
336 &self,
337 syntax: &EnumSyntax,
338 span: wast::Span,
339 ) -> Result<Variant, ValidationError> {
340 let mut enum_scope = IdentValidation::new();
341 let tag_repr = match &syntax.repr {
342 Some(repr) => self.validate_int_repr(repr, span)?,
343 None => IntRepr::U32,
344 };
345 let cases = syntax
346 .members
347 .iter()
348 .map(|i| {
349 let name = enum_scope.introduce(i.item.name(), self.location(i.item.span()))?;
350 let docs = i.comments.docs();
351 Ok(Case {
352 name,
353 tref: None,
354 docs,
355 })
356 })
357 .collect::<Result<Vec<_>, _>>()?;
358
359 Ok(Variant { tag_repr, cases })
360 }
361
362 fn validate_tuple(
363 &self,
364 syntax: &TupleSyntax,
365 span: wast::Span,
366 ) -> Result<RecordDatatype, ValidationError> {
367 let members = syntax
368 .types
369 .iter()
370 .enumerate()
371 .map(|(i, ty)| {
372 Ok(RecordMember {
373 name: Id::new(i.to_string()),
374 tref: self.validate_datatype(ty, false, span)?,
375 docs: String::new(),
376 })
377 })
378 .collect::<Result<Vec<_>, _>>()?;
379
380 Ok(RecordDatatype {
381 kind: RecordKind::Tuple,
382 members,
383 })
384 }
385
386 fn validate_expected(
387 &self,
388 syntax: &ExpectedSyntax,
389 span: wast::Span,
390 ) -> Result<Variant, ValidationError> {
391 let ok_ty = match &syntax.ok {
392 Some(ok) => Some(self.validate_datatype(ok, false, span)?),
393 None => None,
394 };
395 let err_ty = match &syntax.err {
396 Some(err) => Some(self.validate_datatype(err, false, span)?),
397 None => None,
398 };
399 Ok(Variant {
400 tag_repr: IntRepr::U32,
401 cases: vec![
402 Case {
403 name: Id::new("ok"),
404 tref: ok_ty,
405 docs: String::new(),
406 },
407 Case {
408 name: Id::new("err"),
409 tref: err_ty,
410 docs: String::new(),
411 },
412 ],
413 })
414 }
415
416 fn validate_flags(
417 &self,
418 syntax: &FlagsSyntax,
419 span: wast::Span,
420 ) -> Result<RecordDatatype, ValidationError> {
421 let repr = match syntax.repr {
422 Some(ty) => self.validate_int_repr(&ty, span)?,
423 None => IntRepr::U32,
424 };
425 let mut flags_scope = IdentValidation::new();
426 let mut members = Vec::new();
427 for flag in syntax.flags.iter() {
428 let name = flags_scope.introduce(flag.item.name(), self.location(flag.item.span()))?;
429 let docs = flag.comments.docs();
430 members.push(RecordMember {
431 name,
432 docs,
433 tref: self.doc.bool_ty.clone(),
434 });
435 }
436 Ok(RecordDatatype {
437 kind: RecordKind::Bitflags(repr),
438 members,
439 })
440 }
441
442 fn validate_record(
443 &self,
444 syntax: &RecordSyntax,
445 _span: wast::Span,
446 ) -> Result<RecordDatatype, ValidationError> {
447 let mut member_scope = IdentValidation::new();
448 let members = syntax
449 .fields
450 .iter()
451 .map(|f| {
452 let name = member_scope
453 .introduce(f.item.name.name(), self.location(f.item.name.span()))?;
454 let tref = self.validate_datatype(&f.item.type_, false, f.item.name.span())?;
455 let docs = f.comments.docs();
456 Ok(RecordMember { name, tref, docs })
457 })
458 .collect::<Result<Vec<RecordMember>, _>>()?;
459
460 Ok(RecordDatatype {
461 kind: RecordKind::Other,
462 members,
463 })
464 }
465
466 fn validate_union(
467 &self,
468 syntax: &UnionSyntax,
469 span: wast::Span,
470 ) -> Result<Variant, ValidationError> {
471 let (tag_repr, names) = self.union_tag_repr(&syntax.tag, span)?;
472
473 if let Some(names) = &names {
474 if names.len() != syntax.fields.len() {
475 return Err(ValidationError::UnionSizeMismatch {
476 expected: names.len(),
477 found: syntax.fields.len(),
478 location: self.location(span),
479 });
480 }
481 }
482
483 let cases = syntax
484 .fields
485 .iter()
486 .enumerate()
487 .map(|(i, case)| {
488 Ok(Case {
489 name: match &names {
490 Some(names) => names[i].clone(),
491 None => Id::new(i.to_string()),
492 },
493 tref: Some(self.validate_datatype(&case.item, false, span)?),
494 docs: case.comments.docs(),
495 })
496 })
497 .collect::<Result<Vec<_>, _>>()?;
498 Ok(Variant { tag_repr, cases })
499 }
500
501 fn validate_variant(
502 &self,
503 syntax: &VariantSyntax,
504 span: wast::Span,
505 ) -> Result<Variant, ValidationError> {
506 let (tag_repr, names) = self.union_tag_repr(&syntax.tag, span)?;
507
508 if let Some(names) = &names {
509 if names.len() != syntax.cases.len() {
510 return Err(ValidationError::UnionSizeMismatch {
511 expected: names.len(),
512 found: syntax.cases.len(),
513 location: self.location(span),
514 });
515 }
516 }
517
518 let mut name_set = names
519 .as_ref()
520 .map(|names| names.iter().collect::<HashSet<_>>());
521
522 let mut cases = syntax
523 .cases
524 .iter()
525 .map(|case| {
526 let name = Id::new(case.item.name.name());
527 if let Some(names) = &mut name_set {
528 if !names.remove(&name) {
529 return Err(ValidationError::InvalidUnionField {
530 name: name.as_str().to_string(),
531 location: self.location(case.item.name.span()),
532 reason: format!("does not correspond to variant in tag `tag`"),
533 });
534 }
535 }
536 Ok(Case {
537 name: Id::new(case.item.name.name()),
538 tref: match &case.item.ty {
539 Some(ty) => {
540 Some(self.validate_datatype(ty, false, case.item.name.span())?)
541 }
542 None => None,
543 },
544 docs: case.comments.docs(),
545 })
546 })
547 .collect::<Result<Vec<_>, _>>()?;
548
549 if let Some(names) = names {
552 let name_pos = names
553 .iter()
554 .enumerate()
555 .map(|(i, name)| (name, i))
556 .collect::<HashMap<_, _>>();
557 cases.sort_by_key(|c| name_pos[&&c.name]);
558 }
559
560 Ok(Variant { tag_repr, cases })
561 }
562
563 fn union_tag_repr(
564 &self,
565 tag: &Option<Box<TypedefSyntax<'_>>>,
566 span: wast::Span,
567 ) -> Result<(IntRepr, Option<Vec<Id>>), ValidationError> {
568 let ty = match tag {
569 Some(tag) => self.validate_datatype(tag, false, span)?,
570 None => return Ok((IntRepr::U32, None)),
571 };
572 match &**ty.type_() {
573 Type::Variant(e) => {
574 let mut names = Vec::new();
575 for c in e.cases.iter() {
576 if c.tref.is_some() {
577 return Err(ValidationError::InvalidUnionTag {
578 location: self.location(span),
579 reason: format!("all variant cases should have empty payloads"),
580 });
581 }
582 names.push(c.name.clone());
583 }
584 return Ok((e.tag_repr, Some(names)));
585 }
586 Type::Builtin(BuiltinType::U8 { .. }) => return Ok((IntRepr::U8, None)),
587 Type::Builtin(BuiltinType::U16) => return Ok((IntRepr::U16, None)),
588 Type::Builtin(BuiltinType::U32 { .. }) => return Ok((IntRepr::U32, None)),
589 Type::Builtin(BuiltinType::U64) => return Ok((IntRepr::U64, None)),
590 _ => {}
591 }
592
593 Err(ValidationError::WrongKindName {
594 name: "tag".to_string(),
595 location: self.location(span),
596 expected: "enum or builtin",
597 got: ty.type_().kind(),
598 })
599 }
600
601 fn validate_handle(
602 &self,
603 _syntax: &HandleSyntax,
604 _span: wast::Span,
605 ) -> Result<HandleDatatype, ValidationError> {
606 Ok(HandleDatatype {})
607 }
608
609 fn validate_int_repr(
610 &self,
611 type_: &BuiltinType,
612 span: wast::Span,
613 ) -> Result<IntRepr, ValidationError> {
614 match type_ {
615 BuiltinType::U8 { .. } => Ok(IntRepr::U8),
616 BuiltinType::U16 => Ok(IntRepr::U16),
617 BuiltinType::U32 { .. } => Ok(IntRepr::U32),
618 BuiltinType::U64 => Ok(IntRepr::U64),
619 _ => Err(ValidationError::InvalidRepr {
620 repr: type_.clone(),
621 location: self.location(span),
622 }),
623 }
624 }
625}
626
627struct ModuleValidation<'a> {
628 doc: &'a DocValidationScope<'a>,
629 scope: IdentValidation,
630 pub entries: HashMap<Id, ModuleEntry>,
631}
632
633impl<'a> ModuleValidation<'a> {
634 fn new(doc: &'a DocValidationScope<'a>) -> Self {
635 Self {
636 doc,
637 scope: IdentValidation::new(),
638 entries: HashMap::new(),
639 }
640 }
641
642 fn validate_decl(
643 &mut self,
644 decl: &Documented<ModuleDeclSyntax>,
645 ) -> Result<ModuleDefinition, ValidationError> {
646 match &decl.item {
647 ModuleDeclSyntax::Import(syntax) => {
648 let loc = self.doc.location(syntax.name_loc);
649 let name = self.scope.introduce(syntax.name, loc)?;
650 let variant = match syntax.type_ {
651 ImportTypeSyntax::Memory => ModuleImportVariant::Memory,
652 };
653 let rc_import = Rc::new(ModuleImport {
654 name: name.clone(),
655 variant,
656 docs: decl.comments.docs(),
657 });
658 self.entries
659 .insert(name, ModuleEntry::Import(Rc::downgrade(&rc_import)));
660 Ok(ModuleDefinition::Import(rc_import))
661 }
662 ModuleDeclSyntax::Func(syntax) => {
663 let loc = self.doc.location(syntax.export_loc);
664 let name = self.scope.introduce(syntax.export, loc)?;
665 let mut argnames = IdentValidation::new();
666 let params = syntax
667 .params
668 .iter()
669 .map(|f| {
670 Ok(InterfaceFuncParam {
671 name: argnames.introduce(
672 f.item.name.name(),
673 self.doc.location(f.item.name.span()),
674 )?,
675 tref: self.doc.validate_datatype(
676 &f.item.type_,
677 false,
678 f.item.name.span(),
679 )?,
680 docs: f.comments.docs(),
681 })
682 })
683 .collect::<Result<Vec<_>, _>>()?;
684 let results = syntax
685 .results
686 .iter()
687 .map(|f| {
688 let tref =
689 self.doc
690 .validate_datatype(&f.item.type_, false, f.item.name.span())?;
691 Ok(InterfaceFuncParam {
692 name: argnames.introduce(
693 f.item.name.name(),
694 self.doc.location(f.item.name.span()),
695 )?,
696 tref,
697 docs: f.comments.docs(),
698 })
699 })
700 .collect::<Result<Vec<_>, _>>()?;
701 let noreturn = syntax.noreturn;
702 let abi = Abi::Preview1;
703 abi.validate(¶ms, &results)
704 .map_err(|reason| ValidationError::Abi {
705 reason,
706 location: self.doc.location(syntax.export_loc),
707 })?;
708 let rc_func = Rc::new(InterfaceFunc {
709 abi,
710 name: name.clone(),
711 params,
712 results,
713 noreturn,
714 docs: decl.comments.docs(),
715 });
716 self.entries
717 .insert(name, ModuleEntry::Func(Rc::downgrade(&rc_func)));
718 Ok(ModuleDefinition::Func(rc_func))
719 }
720 }
721 }
722}