Coverage Report

Created: 2023-04-25 07:07

/src/wasm-tools/crates/wit-parser/src/lib.rs
Line
Count
Source (jump to first uncovered line)
1
use anyhow::{anyhow, Context, Result};
2
use id_arena::{Arena, Id};
3
use indexmap::IndexMap;
4
use std::borrow::Cow;
5
use std::fmt;
6
use std::path::Path;
7
8
pub mod abi;
9
mod ast;
10
use ast::lex::Span;
11
pub use ast::SourceMap;
12
mod sizealign;
13
pub use sizealign::*;
14
mod resolve;
15
pub use resolve::{Package, PackageId, Remap, Resolve};
16
mod live;
17
pub use live::LiveTypes;
18
19
/// Checks if the given string is a legal identifier in wit.
20
pub fn validate_id(s: &str) -> Result<()> {
21
110k
    ast::validate_id(0, s)?;
22
110k
    Ok(())
23
110k
}
24
25
pub type WorldId = Id<World>;
26
pub type InterfaceId = Id<Interface>;
27
pub type TypeId = Id<TypeDef>;
28
pub type DocumentId = Id<Document>;
29
30
/// Representation of a parsed WIT package which has not resolved external
31
/// dependencies yet.
32
///
33
/// This representation has performed internal resolution of the WIT package
34
/// itself, ensuring that all references internally are valid and the WIT was
35
/// syntactically valid and such.
36
///
37
/// The fields of this structure represent a flat list of arrays unioned from
38
/// all documents within the WIT package. This means, for example, that all
39
/// types from all documents are located in `self.types`. The fields of each
40
/// item can help splitting back out into packages/interfaces/etc as necessary.
41
///
42
/// Note that an `UnresolvedPackage` cannot be queried in general about
43
/// information such as size or alignment as that would require resolution of
44
/// foreign dependencies. Translations such as to-binary additionally are not
45
/// supported on an `UnresolvedPackage` due to the lack of knowledge about the
46
/// foreign types. This is intended to be an intermediate state which can be
47
/// inspected by embedders, if necessary, before quickly transforming to a
48
/// [`Resolve`] to fully work with a WIT package.
49
///
50
/// After an [`UnresolvedPackage`] is parsed it can be fully resolved with
51
/// [`Resolve::push`]. During this operation a dependency map is specified which
52
/// will connect the `foreign_deps` field of this structure to packages
53
/// previously inserted within the [`Resolve`]. Embedders are responsible for
54
/// performing this resolution themselves.
55
0
#[derive(Clone)]
56
pub struct UnresolvedPackage {
57
    /// Local name for this package.
58
    pub name: String,
59
60
    /// Optionally-specified URL for this package.
61
    ///
62
    /// Must be specified for non-local dependencies. Note that this is never
63
    /// automatically set from [`UnresolvedPackage::parse`] methods, and it must
64
    /// be manually configured in the return value.
65
    pub url: Option<String>,
66
67
    /// All worlds from all documents within this package.
68
    ///
69
    /// Each world lists the document that it is from.
70
    pub worlds: Arena<World>,
71
72
    /// All interfaces from all documents within this package.
73
    ///
74
    /// Each interface lists the document that it is from. Interfaces are listed
75
    /// in topological order as well so iteration through this arena will only
76
    /// reference prior elements already visited when working with recursive
77
    /// references.
78
    pub interfaces: Arena<Interface>,
79
80
    /// All types from all documents within this package.
81
    ///
82
    /// Each type lists the interface or world that defined it, or nothing if
83
    /// it's an anonymous type. Types are listed in this arena in topological
84
    /// order to ensure that iteration through this arena will only reference
85
    /// other types transitively that are already iterated over.
86
    pub types: Arena<TypeDef>,
87
88
    /// All documents found within this package.
89
    ///
90
    /// Documents are sorted topologically in this arena with respect to imports
91
    /// between them.
92
    pub documents: Arena<Document>,
93
94
    /// All foreign dependencies that this package depends on.
95
    ///
96
    /// These foreign dependencies must be resolved to convert this unresolved
97
    /// package into a `Resolve`. The map here is keyed by the name of the
98
    /// foreign package that this depends on, and the sub-map is keyed by a
99
    /// document name followed by the identifier within `self.documents`. The
100
    /// fields of `self.documents` describes the required types, interfaces,
101
    /// etc, that are required from each foreign package.
102
    pub foreign_deps: IndexMap<String, IndexMap<String, DocumentId>>,
103
104
    unknown_type_spans: Vec<Span>,
105
    world_spans: Vec<(Vec<Span>, Vec<Span>)>,
106
    document_spans: Vec<Span>,
107
    interface_spans: Vec<Span>,
108
    foreign_dep_spans: Vec<Span>,
109
    source_map: SourceMap,
110
}
111
112
0
#[derive(Debug)]
113
struct Error {
114
    span: Span,
115
    msg: String,
116
}
117
118
impl fmt::Display for Error {
119
308
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120
308
        self.msg.fmt(f)
121
308
    }
122
}
123
124
impl std::error::Error for Error {}
125
126
impl UnresolvedPackage {
127
    /// Parses the given string as a wit document.
128
    ///
129
    /// The `path` argument is used for error reporting. The `contents` provided
130
    /// will not be able to use `pkg` use paths to other documents.
131
0
    pub fn parse(path: &Path, contents: &str) -> Result<Self> {
132
0
        let mut map = SourceMap::default();
133
0
        let name = path
134
0
            .file_name()
135
0
            .and_then(|s| s.to_str())
136
0
            .ok_or_else(|| anyhow!("path doesn't end in a valid package name {path:?}"))?;
137
0
        let name = match name.find('.') {
138
0
            Some(i) => &name[..i],
139
0
            None => name,
140
        };
141
0
        map.push(path, name, contents);
142
0
        map.parse(name, None)
143
0
    }
144
145
    /// Parse a WIT package at the provided path.
146
    ///
147
    /// The path provided is inferred whether it's a file or a directory. A file
148
    /// is parsed with [`UnresolvedPackage::parse_file`] and a directory is
149
    /// parsed with [`UnresolvedPackage::parse_dir`].
150
0
    pub fn parse_path(path: &Path) -> Result<Self> {
151
0
        if path.is_dir() {
152
0
            UnresolvedPackage::parse_dir(path)
153
        } else {
154
0
            UnresolvedPackage::parse_file(path)
155
        }
156
0
    }
157
158
    /// Parses a WIT package from the file provided.
159
    ///
160
    /// The WIT package returned will be a single-document package and will not
161
    /// be able to use `pkg` paths to other documents.
162
0
    pub fn parse_file(path: &Path) -> Result<Self> {
163
0
        let contents = std::fs::read_to_string(path)
164
0
            .with_context(|| format!("failed to read file {path:?}"))?;
165
0
        Self::parse(path, &contents)
166
0
    }
167
168
    /// Parses a WIT package from the directory provided.
169
    ///
170
    /// All files with the extension `*.wit` or `*.wit.md` will be loaded from
171
    /// `path` into the returned package.
172
0
    pub fn parse_dir(path: &Path) -> Result<Self> {
173
0
        let mut map = SourceMap::default();
174
0
        let name = path
175
0
            .file_name()
176
0
            .and_then(|s| s.to_str())
177
0
            .ok_or_else(|| anyhow!("path doesn't end in a valid package name {path:?}"))?;
178
0
        let cx = || format!("failed to read directory {path:?}");
179
0
        for entry in path.read_dir().with_context(&cx)? {
180
0
            let entry = entry.with_context(&cx)?;
181
0
            let path = entry.path();
182
0
            let ty = entry.file_type().with_context(&cx)?;
183
0
            if ty.is_dir() {
184
0
                continue;
185
0
            }
186
0
            if ty.is_symlink() {
187
0
                if path.is_dir() {
188
0
                    continue;
189
0
                }
190
0
            }
191
0
            let filename = match path.file_name().and_then(|s| s.to_str()) {
192
0
                Some(name) => name,
193
0
                None => continue,
194
            };
195
0
            if !filename.ends_with(".wit") && !filename.ends_with(".wit.md") {
196
0
                continue;
197
0
            }
198
0
            map.push_file(&path)?;
199
        }
200
0
        map.parse(name, None)
201
0
    }
202
203
    /// Returns an iterator over the list of source files that were read when
204
    /// parsing this package.
205
0
    pub fn source_files(&self) -> impl Iterator<Item = &Path> {
206
0
        self.source_map.source_files()
207
0
    }
208
}
209
210
/// Represents the result of parsing a wit document.
211
0
#[derive(Debug, Clone)]
212
pub struct Document {
213
    pub name: String,
214
215
    /// The top-level interfaces contained in the document.
216
    ///
217
    /// The interfaces here are listed in topological order of the
218
    /// dependencies between them.
219
    pub interfaces: IndexMap<String, InterfaceId>,
220
221
    /// The worlds contained in the document.
222
    pub worlds: IndexMap<String, WorldId>,
223
224
    /// The default interface of this document, if any.
225
    ///
226
    /// This interface will also be listed in `self.interfaces`
227
    pub default_interface: Option<InterfaceId>,
228
229
    /// The default world of this document, if any.
230
    ///
231
    /// This will also be listed in `self.worlds`.
232
    pub default_world: Option<WorldId>,
233
234
    /// The package that this document belongs to.
235
    pub package: Option<PackageId>,
236
}
237
238
0
#[derive(Debug, Clone)]
239
pub struct World {
240
    /// The WIT identifier name of this world.
241
    pub name: String,
242
243
    /// Documentation associated with this world declaration.
244
    pub docs: Docs,
245
246
    /// All imported items into this interface, both worlds and functions.
247
    pub imports: IndexMap<String, WorldItem>,
248
249
    /// All exported items from this interface, both worlds and functions.
250
    pub exports: IndexMap<String, WorldItem>,
251
252
    /// The document that owns this world.
253
    pub document: DocumentId,
254
}
255
256
172k
#[derive(Debug, Clone)]
257
pub enum WorldItem {
258
    /// An interface is being imported or exported from a world, indicating that
259
    /// it's a namespace of functions.
260
    Interface(InterfaceId),
261
262
    /// A function is being directly imported or exported from this world.
263
    Function(Function),
264
265
    /// A type is being exported from this world.
266
    ///
267
    /// Note that types are never imported into worlds at this time.
268
    Type(TypeId),
269
}
270
271
0
#[derive(Debug, Clone)]
272
pub struct Interface {
273
    /// Optionally listed name of this interface.
274
    ///
275
    /// This is `None` for inline interfaces in worlds.
276
    pub name: Option<String>,
277
278
    /// Documentation associated with this interface.
279
    pub docs: Docs,
280
281
    /// Exported types from this interface.
282
    ///
283
    /// Export names are listed within the types themselves. Note that the
284
    /// export name here matches the name listed in the `TypeDef`.
285
    pub types: IndexMap<String, TypeId>,
286
287
    /// Exported functions from this interface.
288
    pub functions: IndexMap<String, Function>,
289
290
    /// The document that this interface belongs to.
291
    pub document: DocumentId,
292
}
293
294
0
#[derive(Debug, Clone, PartialEq)]
295
pub struct TypeDef {
296
    pub docs: Docs,
297
    pub kind: TypeDefKind,
298
    pub name: Option<String>,
299
    pub owner: TypeOwner,
300
}
301
302
0
#[derive(Debug, Clone, PartialEq)]
303
pub enum TypeDefKind {
304
    Record(Record),
305
    Flags(Flags),
306
    Tuple(Tuple),
307
    Variant(Variant),
308
    Enum(Enum),
309
    Option(Type),
310
    Result(Result_),
311
    Union(Union),
312
    List(Type),
313
    Future(Option<Type>),
314
    Stream(Stream),
315
    Type(Type),
316
317
    /// This represents a type of unknown structure imported from a foreign
318
    /// interface.
319
    ///
320
    /// This variant is only used during the creation of `UnresolvedPackage` but
321
    /// by the time a `Resolve` is created then this will not exist.
322
    Unknown,
323
}
324
325
impl TypeDefKind {
326
0
    pub fn as_str(&self) -> &'static str {
327
0
        match self {
328
0
            TypeDefKind::Record(_) => "record",
329
0
            TypeDefKind::Flags(_) => "flags",
330
0
            TypeDefKind::Tuple(_) => "tuple",
331
0
            TypeDefKind::Variant(_) => "variant",
332
0
            TypeDefKind::Enum(_) => "enum",
333
0
            TypeDefKind::Option(_) => "option",
334
0
            TypeDefKind::Result(_) => "result",
335
0
            TypeDefKind::Union(_) => "union",
336
0
            TypeDefKind::List(_) => "list",
337
0
            TypeDefKind::Future(_) => "future",
338
0
            TypeDefKind::Stream(_) => "stream",
339
0
            TypeDefKind::Type(_) => "type",
340
0
            TypeDefKind::Unknown => "unknown",
341
        }
342
0
    }
343
}
344
345
236k
#[derive(Debug, Copy, Clone, PartialEq)]
<wit_parser::TypeOwner as core::cmp::PartialEq>::eq
Line
Count
Source
345
236k
#[derive(Debug, Copy, Clone, PartialEq)]
Unexecuted instantiation: <wit_parser::TypeOwner as core::cmp::PartialEq>::eq
346
pub enum TypeOwner {
347
    /// This type was defined within a `world` block.
348
    World(WorldId),
349
    /// This type was defined within an `interface` block.
350
    Interface(InterfaceId),
351
    /// This type wasn't inherently defined anywhere, such as a `list<T>`, which
352
    /// doesn't need an owner.
353
    None,
354
}
355
356
44.3M
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
<wit_parser::Type as core::cmp::PartialEq>::eq
Line
Count
Source
356
2.21k
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
<wit_parser::Type as core::cmp::PartialEq>::eq
Line
Count
Source
356
769k
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
357
pub enum Type {
358
    Bool,
359
    U8,
360
    U16,
361
    U32,
362
    U64,
363
    S8,
364
    S16,
365
    S32,
366
    S64,
367
    Float32,
368
    Float64,
369
    Char,
370
    String,
371
    Id(TypeId),
372
}
373
374
0
#[derive(PartialEq, Debug, Copy, Clone)]
375
pub enum Int {
376
    U8,
377
    U16,
378
    U32,
379
    U64,
380
}
381
382
0
#[derive(Debug, Clone, PartialEq)]
383
pub struct Record {
384
    pub fields: Vec<Field>,
385
}
386
387
0
#[derive(Debug, Clone, PartialEq)]
388
pub struct Field {
389
    pub docs: Docs,
390
    pub name: String,
391
    pub ty: Type,
392
}
393
394
0
#[derive(Debug, Clone, PartialEq)]
395
pub struct Flags {
396
    pub flags: Vec<Flag>,
397
}
398
399
0
#[derive(Debug, Clone, PartialEq)]
400
pub struct Flag {
401
    pub docs: Docs,
402
    pub name: String,
403
}
404
405
0
#[derive(Debug)]
406
pub enum FlagsRepr {
407
    U8,
408
    U16,
409
    U32(usize),
410
}
411
412
impl Flags {
413
    pub fn repr(&self) -> FlagsRepr {
414
126k
        match self.flags.len() {
415
2.12k
            0 => FlagsRepr::U32(0),
416
124k
            n if n <= 8 => FlagsRepr::U8,
417
377
            n if n <= 16 => FlagsRepr::U16,
418
0
            n => FlagsRepr::U32(sizealign::align_to(n, 32) / 32),
419
        }
420
126k
    }
421
}
422
423
impl FlagsRepr {
424
126k
    pub fn count(&self) -> usize {
425
126k
        match self {
426
124k
            FlagsRepr::U8 => 1,
427
377
            FlagsRepr::U16 => 1,
428
2.12k
            FlagsRepr::U32(n) => *n,
429
        }
430
126k
    }
431
}
432
433
0
#[derive(Debug, Clone, PartialEq)]
434
pub struct Tuple {
435
    pub types: Vec<Type>,
436
}
437
438
0
#[derive(Debug, Clone, PartialEq)]
439
pub struct Variant {
440
    pub cases: Vec<Case>,
441
}
442
443
0
#[derive(Debug, Clone, PartialEq)]
444
pub struct Case {
445
    pub docs: Docs,
446
    pub name: String,
447
    pub ty: Option<Type>,
448
}
449
450
impl Variant {
451
55.2k
    pub fn tag(&self) -> Int {
452
55.2k
        match self.cases.len() {
453
55.2k
            n if n <= u8::max_value() as usize => Int::U8,
454
0
            n if n <= u16::max_value() as usize => Int::U16,
455
0
            n if n <= u32::max_value() as usize => Int::U32,
456
0
            _ => panic!("too many cases to fit in a repr"),
457
        }
458
55.2k
    }
459
}
460
461
0
#[derive(Debug, Clone, PartialEq)]
462
pub struct Enum {
463
    pub cases: Vec<EnumCase>,
464
}
465
466
0
#[derive(Debug, Clone, PartialEq)]
467
pub struct EnumCase {
468
    pub docs: Docs,
469
    pub name: String,
470
}
471
472
impl Enum {
473
59.2k
    pub fn tag(&self) -> Int {
474
59.2k
        match self.cases.len() {
475
59.2k
            n if n <= u8::max_value() as usize => Int::U8,
476
0
            n if n <= u16::max_value() as usize => Int::U16,
477
0
            n if n <= u32::max_value() as usize => Int::U32,
478
0
            _ => panic!("too many cases to fit in a repr"),
479
        }
480
59.2k
    }
481
}
482
483
0
#[derive(Debug, Clone, PartialEq)]
484
pub struct Result_ {
485
    pub ok: Option<Type>,
486
    pub err: Option<Type>,
487
}
488
489
0
#[derive(Debug, Clone, PartialEq)]
490
pub struct Union {
491
    pub cases: Vec<UnionCase>,
492
}
493
494
0
#[derive(Debug, Clone, PartialEq)]
495
pub struct UnionCase {
496
    pub docs: Docs,
497
    pub ty: Type,
498
}
499
500
impl Union {
501
0
    pub fn tag(&self) -> Int {
502
0
        match self.cases.len() {
503
0
            n if n <= u8::max_value() as usize => Int::U8,
504
0
            n if n <= u16::max_value() as usize => Int::U16,
505
0
            n if n <= u32::max_value() as usize => Int::U32,
506
0
            _ => panic!("too many cases to fit in a repr"),
507
        }
508
0
    }
509
}
510
511
0
#[derive(Debug, Clone, PartialEq)]
512
pub struct Stream {
513
    pub element: Option<Type>,
514
    pub end: Option<Type>,
515
}
516
517
17.1M
#[derive(Clone, Default, Debug, PartialEq)]
<wit_parser::Docs as core::default::Default>::default
Line
Count
Source
517
12.0M
#[derive(Clone, Default, Debug, PartialEq)]
<wit_parser::Docs as core::default::Default>::default
Line
Count
Source
517
5.16M
#[derive(Clone, Default, Debug, PartialEq)]
518
pub struct Docs {
519
    pub contents: Option<String>,
520
}
521
522
pub type Params = Vec<(String, Type)>;
523
524
6.78M
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
<wit_parser::Results as core::cmp::PartialEq>::eq
Line
Count
Source
524
260k
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Unexecuted instantiation: <wit_parser::Results as core::cmp::PartialEq>::eq
<wit_parser::Results as core::hash::Hash>::hash::<std::collections::hash::map::DefaultHasher>
Line
Count
Source
524
6.78M
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
Unexecuted instantiation: <wit_parser::Results as core::hash::Hash>::hash::<_>
525
pub enum Results {
526
    Named(Params),
527
    Anon(Type),
528
}
529
530
pub enum ResultsTypeIter<'a> {
531
    Named(std::slice::Iter<'a, (String, Type)>),
532
    Anon(std::iter::Once<&'a Type>),
533
}
534
535
impl<'a> Iterator for ResultsTypeIter<'a> {
536
    type Item = &'a Type;
537
538
2.23M
    fn next(&mut self) -> Option<&'a Type> {
539
2.23M
        match self {
540
2.17M
            ResultsTypeIter::Named(ps) => ps.next().map(|p| &p.1),
541
62.1k
            ResultsTypeIter::Anon(ty) => ty.next(),
542
        }
543
2.23M
    }
544
545
0
    fn size_hint(&self) -> (usize, Option<usize>) {
546
0
        match self {
547
0
            ResultsTypeIter::Named(ps) => ps.size_hint(),
548
0
            ResultsTypeIter::Anon(ty) => ty.size_hint(),
549
        }
550
0
    }
551
}
552
553
impl<'a> ExactSizeIterator for ResultsTypeIter<'a> {}
554
555
impl Results {
556
    // For the common case of an empty results list.
557
0
    pub fn empty() -> Results {
558
0
        Results::Named(Vec::new())
559
0
    }
560
561
0
    pub fn len(&self) -> usize {
562
0
        match self {
563
0
            Results::Named(params) => params.len(),
564
0
            Results::Anon(_) => 1,
565
        }
566
0
    }
567
568
0
    pub fn throws<'a>(&self, resolve: &'a Resolve) -> Option<(Option<&'a Type>, Option<&'a Type>)> {
569
0
        if self.len() != 1 {
570
0
            return None;
571
0
        }
572
0
        match self.iter_types().next().unwrap() {
573
0
            Type::Id(id) => match &resolve.types[*id].kind {
574
0
                TypeDefKind::Result(r) => Some((r.ok.as_ref(), r.err.as_ref())),
575
0
                _ => None,
576
            },
577
0
            _ => None,
578
        }
579
0
    }
580
581
619k
    pub fn iter_types(&self) -> ResultsTypeIter {
582
619k
        match self {
583
588k
            Results::Named(ps) => ResultsTypeIter::Named(ps.iter()),
584
31.2k
            Results::Anon(ty) => ResultsTypeIter::Anon(std::iter::once(ty)),
585
        }
586
619k
    }
587
}
588
589
62.5k
#[derive(Debug, Clone, PartialEq)]
590
pub struct Function {
591
    pub docs: Docs,
592
    pub name: String,
593
    pub kind: FunctionKind,
594
    pub params: Params,
595
    pub results: Results,
596
}
597
598
62.5k
#[derive(Debug, Clone, PartialEq)]
599
pub enum FunctionKind {
600
    Freestanding,
601
}
602
603
impl Function {
604
0
    pub fn item_name(&self) -> &str {
605
0
        match &self.kind {
606
0
            FunctionKind::Freestanding => &self.name,
607
0
        }
608
0
    }
609
610
    /// Gets the core export name for this function.
611
93.7k
    pub fn core_export_name<'a>(&'a self, interface: Option<&str>) -> Cow<'a, str> {
612
93.7k
        match interface {
613
87.5k
            Some(interface) => Cow::Owned(format!("{interface}#{}", self.name)),
614
6.24k
            None => Cow::Borrowed(&self.name),
615
        }
616
93.7k
    }
617
}