/src/wasm-tools/crates/wit-parser/src/lib.rs
Line | Count | Source |
1 | | #![no_std] |
2 | | |
3 | | extern crate alloc; |
4 | | |
5 | | #[cfg(feature = "std")] |
6 | | extern crate std; |
7 | | |
8 | | use crate::abi::AbiVariant; |
9 | | use alloc::format; |
10 | | use alloc::string::{String, ToString}; |
11 | | use alloc::vec::Vec; |
12 | | #[cfg(feature = "std")] |
13 | | use anyhow::Context; |
14 | | use anyhow::{Result, bail}; |
15 | | use id_arena::{Arena, Id}; |
16 | | use semver::Version; |
17 | | |
18 | | #[cfg(feature = "std")] |
19 | | pub type IndexMap<K, V> = indexmap::IndexMap<K, V, std::hash::RandomState>; |
20 | | #[cfg(feature = "std")] |
21 | | pub type IndexSet<T> = indexmap::IndexSet<T, std::hash::RandomState>; |
22 | | #[cfg(not(feature = "std"))] |
23 | | pub type IndexMap<K, V> = indexmap::IndexMap<K, V, hashbrown::DefaultHashBuilder>; |
24 | | #[cfg(not(feature = "std"))] |
25 | | pub type IndexSet<T> = indexmap::IndexSet<T, hashbrown::DefaultHashBuilder>; |
26 | | |
27 | | #[cfg(feature = "std")] |
28 | | pub(crate) use std::collections::{HashMap, HashSet}; |
29 | | |
30 | | #[cfg(not(feature = "std"))] |
31 | | pub(crate) use hashbrown::{HashMap, HashSet}; |
32 | | |
33 | | use alloc::borrow::Cow; |
34 | | use core::fmt; |
35 | | use core::hash::{Hash, Hasher}; |
36 | | #[cfg(feature = "std")] |
37 | | use std::path::Path; |
38 | | |
39 | | #[cfg(feature = "decoding")] |
40 | | pub mod decoding; |
41 | | #[cfg(feature = "decoding")] |
42 | | mod metadata; |
43 | | #[cfg(feature = "decoding")] |
44 | | pub use metadata::PackageMetadata; |
45 | | |
46 | | pub mod abi; |
47 | | mod ast; |
48 | | pub use ast::SourceMap; |
49 | | pub use ast::error::*; |
50 | | pub use ast::lex::Span; |
51 | | pub use ast::{ParsedUsePath, parse_use_path}; |
52 | | mod sizealign; |
53 | | pub use sizealign::*; |
54 | | mod resolve; |
55 | | pub use resolve::*; |
56 | | mod live; |
57 | | pub use live::{LiveTypes, TypeIdVisitor}; |
58 | | |
59 | | #[cfg(feature = "serde")] |
60 | | use serde_derive::Serialize; |
61 | | #[cfg(feature = "serde")] |
62 | | mod serde_; |
63 | | #[cfg(feature = "serde")] |
64 | | use serde_::*; |
65 | | |
66 | | /// Checks if the given string is a legal identifier in wit. |
67 | 0 | pub fn validate_id(s: &str) -> Result<()> { |
68 | 0 | ast::validate_id(0, s)?; |
69 | 0 | Ok(()) |
70 | 0 | } |
71 | | |
72 | | pub type WorldId = Id<World>; |
73 | | pub type InterfaceId = Id<Interface>; |
74 | | pub type TypeId = Id<TypeDef>; |
75 | | |
76 | | /// Representation of a parsed WIT package which has not resolved external |
77 | | /// dependencies yet. |
78 | | /// |
79 | | /// This representation has performed internal resolution of the WIT package |
80 | | /// itself, ensuring that all references internally are valid and the WIT was |
81 | | /// syntactically valid and such. |
82 | | /// |
83 | | /// The fields of this structure represent a flat list of arrays unioned from |
84 | | /// all documents within the WIT package. This means, for example, that all |
85 | | /// types from all documents are located in `self.types`. The fields of each |
86 | | /// item can help splitting back out into packages/interfaces/etc as necessary. |
87 | | /// |
88 | | /// Note that an `UnresolvedPackage` cannot be queried in general about |
89 | | /// information such as size or alignment as that would require resolution of |
90 | | /// foreign dependencies. Translations such as to-binary additionally are not |
91 | | /// supported on an `UnresolvedPackage` due to the lack of knowledge about the |
92 | | /// foreign types. This is intended to be an intermediate state which can be |
93 | | /// inspected by embedders, if necessary, before quickly transforming to a |
94 | | /// [`Resolve`] to fully work with a WIT package. |
95 | | /// |
96 | | /// After an [`UnresolvedPackage`] is parsed it can be fully resolved with |
97 | | /// [`Resolve::push`]. During this operation a dependency map is specified which |
98 | | /// will connect the `foreign_deps` field of this structure to packages |
99 | | /// previously inserted within the [`Resolve`]. Embedders are responsible for |
100 | | /// performing this resolution themselves. |
101 | | #[derive(Clone, PartialEq, Eq)] |
102 | | pub struct UnresolvedPackage { |
103 | | /// The namespace, name, and version information for this package. |
104 | | pub name: PackageName, |
105 | | |
106 | | /// All worlds from all documents within this package. |
107 | | /// |
108 | | /// Each world lists the document that it is from. |
109 | | pub worlds: Arena<World>, |
110 | | |
111 | | /// All interfaces from all documents within this package. |
112 | | /// |
113 | | /// Each interface lists the document that it is from. Interfaces are listed |
114 | | /// in topological order as well so iteration through this arena will only |
115 | | /// reference prior elements already visited when working with recursive |
116 | | /// references. |
117 | | pub interfaces: Arena<Interface>, |
118 | | |
119 | | /// All types from all documents within this package. |
120 | | /// |
121 | | /// Each type lists the interface or world that defined it, or nothing if |
122 | | /// it's an anonymous type. Types are listed in this arena in topological |
123 | | /// order to ensure that iteration through this arena will only reference |
124 | | /// other types transitively that are already iterated over. |
125 | | pub types: Arena<TypeDef>, |
126 | | |
127 | | /// All foreign dependencies that this package depends on. |
128 | | /// |
129 | | /// These foreign dependencies must be resolved to convert this unresolved |
130 | | /// package into a `Resolve`. The map here is keyed by the name of the |
131 | | /// foreign package that this depends on, and the sub-map is keyed by an |
132 | | /// interface name followed by the identifier within `self.interfaces`. The |
133 | | /// fields of `self.interfaces` describes the required types that are from |
134 | | /// each foreign interface. |
135 | | pub foreign_deps: IndexMap<PackageName, IndexMap<String, (AstItem, Vec<Stability>)>>, |
136 | | |
137 | | /// Doc comments for this package. |
138 | | pub docs: Docs, |
139 | | |
140 | | #[cfg_attr(not(feature = "std"), allow(dead_code))] |
141 | | package_name_span: Span, |
142 | | unknown_type_spans: Vec<Span>, |
143 | | foreign_dep_spans: Vec<Span>, |
144 | | required_resource_types: Vec<(TypeId, Span)>, |
145 | | } |
146 | | |
147 | | impl UnresolvedPackage { |
148 | | /// Adjusts all spans in this package by adding the given byte offset. |
149 | | /// |
150 | | /// This is used when merging source maps to update spans to point to the |
151 | | /// correct location in the combined source map. |
152 | 13.0k | pub(crate) fn adjust_spans(&mut self, offset: u32) { |
153 | | // Adjust parallel vec spans |
154 | 13.0k | self.package_name_span.adjust(offset); |
155 | 13.0k | for span in &mut self.unknown_type_spans { |
156 | 2.16k | span.adjust(offset); |
157 | 2.16k | } |
158 | 13.0k | for span in &mut self.foreign_dep_spans { |
159 | 2.08k | span.adjust(offset); |
160 | 2.08k | } |
161 | 13.0k | for (_, span) in &mut self.required_resource_types { |
162 | 17 | span.adjust(offset); |
163 | 17 | } |
164 | | |
165 | | // Adjust spans on arena items |
166 | 16.8k | for (_, world) in self.worlds.iter_mut() { |
167 | 16.8k | world.adjust_spans(offset); |
168 | 16.8k | } |
169 | 56.5k | for (_, iface) in self.interfaces.iter_mut() { |
170 | 56.5k | iface.adjust_spans(offset); |
171 | 56.5k | } |
172 | 142k | for (_, ty) in self.types.iter_mut() { |
173 | 142k | ty.adjust_spans(offset); |
174 | 142k | } |
175 | 13.0k | } |
176 | | } |
177 | | |
178 | | /// Tracks a set of packages, all pulled from the same group of WIT source files. |
179 | | #[derive(Clone, PartialEq, Eq)] |
180 | | pub struct UnresolvedPackageGroup { |
181 | | /// The "main" package in this package group which was found at the root of |
182 | | /// the WIT files. |
183 | | /// |
184 | | /// Note that this is required to be present in all WIT files. |
185 | | pub main: UnresolvedPackage, |
186 | | |
187 | | /// Nested packages found while parsing `main`, if any. |
188 | | pub nested: Vec<UnresolvedPackage>, |
189 | | |
190 | | /// A set of processed source files from which these packages have been parsed. |
191 | | pub source_map: SourceMap, |
192 | | } |
193 | | |
194 | | #[derive(Debug, Copy, Clone, PartialEq, Eq)] |
195 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
196 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] |
197 | | pub enum AstItem { |
198 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
199 | | Interface(InterfaceId), |
200 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
201 | | World(WorldId), |
202 | | } |
203 | | |
204 | | /// A structure used to keep track of the name of a package, containing optional |
205 | | /// information such as a namespace and version information. |
206 | | /// |
207 | | /// This is directly encoded as an "ID" in the binary component representation |
208 | | /// with an interfaced tacked on as well. |
209 | | #[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] |
210 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
211 | | #[cfg_attr(feature = "serde", serde(into = "String"))] |
212 | | pub struct PackageName { |
213 | | /// A namespace such as `wasi` in `wasi:foo/bar` |
214 | | pub namespace: String, |
215 | | /// The kebab-name of this package, which is always specified. |
216 | | pub name: String, |
217 | | /// Optional major/minor version information. |
218 | | pub version: Option<Version>, |
219 | | } |
220 | | |
221 | | impl From<PackageName> for String { |
222 | 0 | fn from(name: PackageName) -> String { |
223 | 0 | name.to_string() |
224 | 0 | } |
225 | | } |
226 | | |
227 | | impl PackageName { |
228 | | /// Returns the ID that this package name would assign the `interface` name |
229 | | /// specified. |
230 | 17.2k | pub fn interface_id(&self, interface: &str) -> String { |
231 | 17.2k | let mut s = String::new(); |
232 | 17.2k | s.push_str(&format!("{}:{}/{interface}", self.namespace, self.name)); |
233 | 17.2k | if let Some(version) = &self.version { |
234 | 10.6k | s.push_str(&format!("@{version}")); |
235 | 10.6k | } |
236 | 17.2k | s |
237 | 17.2k | } |
238 | | |
239 | | /// Determines the "semver compatible track" for the given version. |
240 | | /// |
241 | | /// This method implements the logic from the component model where semver |
242 | | /// versions can be compatible with one another. For example versions 1.2.0 |
243 | | /// and 1.2.1 would be considered both compatible with one another because |
244 | | /// they're on the same semver compatible track. |
245 | | /// |
246 | | /// This predicate is used during |
247 | | /// [`Resolve::merge_world_imports_based_on_semver`] for example to |
248 | | /// determine whether two imports can be merged together. This is |
249 | | /// additionally used when creating components to match up imports in |
250 | | /// core wasm to imports in worlds. |
251 | 413 | pub fn version_compat_track(version: &Version) -> Version { |
252 | 413 | let mut version = version.clone(); |
253 | 413 | version.build = semver::BuildMetadata::EMPTY; |
254 | 413 | if !version.pre.is_empty() { |
255 | 372 | return version; |
256 | 41 | } |
257 | 41 | if version.major != 0 { |
258 | 35 | version.minor = 0; |
259 | 35 | version.patch = 0; |
260 | 35 | return version; |
261 | 6 | } |
262 | 6 | if version.minor != 0 { |
263 | 5 | version.patch = 0; |
264 | 5 | return version; |
265 | 1 | } |
266 | 1 | version |
267 | 413 | } |
268 | | |
269 | | /// Returns the string corresponding to |
270 | | /// [`PackageName::version_compat_track`]. This is done to match the |
271 | | /// component model's expected naming scheme of imports and exports. |
272 | 92 | pub fn version_compat_track_string(version: &Version) -> String { |
273 | 92 | let version = Self::version_compat_track(version); |
274 | 92 | if !version.pre.is_empty() { |
275 | 90 | return version.to_string(); |
276 | 2 | } |
277 | 2 | if version.major != 0 { |
278 | 2 | return format!("{}", version.major); |
279 | 0 | } |
280 | 0 | if version.minor != 0 { |
281 | 0 | return format!("{}.{}", version.major, version.minor); |
282 | 0 | } |
283 | 0 | version.to_string() |
284 | 92 | } |
285 | | } |
286 | | |
287 | | impl fmt::Display for PackageName { |
288 | 6.81k | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
289 | 6.81k | write!(f, "{}:{}", self.namespace, self.name)?; |
290 | 6.81k | if let Some(version) = &self.version { |
291 | 5.12k | write!(f, "@{version}")?; |
292 | 1.69k | } |
293 | 6.81k | Ok(()) |
294 | 6.81k | } |
295 | | } |
296 | | |
297 | | #[derive(Debug)] |
298 | | struct Error { |
299 | | span: Span, |
300 | | msg: String, |
301 | | highlighted: Option<String>, |
302 | | } |
303 | | |
304 | | impl Error { |
305 | 0 | fn new(span: Span, msg: impl Into<String>) -> Error { |
306 | 0 | Error { |
307 | 0 | span, |
308 | 0 | msg: msg.into(), |
309 | 0 | highlighted: None, |
310 | 0 | } |
311 | 0 | } Unexecuted instantiation: <wit_parser::Error>::new::<alloc::string::String> Unexecuted instantiation: <wit_parser::Error>::new::<&str> |
312 | | |
313 | | /// Highlights this error using the given source map, if the span is known. |
314 | 0 | fn highlight(&mut self, source_map: &ast::SourceMap) { |
315 | 0 | if self.highlighted.is_none() { |
316 | 0 | self.highlighted = source_map.highlight_span(self.span, &self.msg); |
317 | 0 | } |
318 | 0 | } |
319 | | } |
320 | | |
321 | | impl fmt::Display for Error { |
322 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
323 | 0 | self.highlighted.as_ref().unwrap_or(&self.msg).fmt(f) |
324 | 0 | } |
325 | | } |
326 | | |
327 | | impl core::error::Error for Error {} |
328 | | |
329 | | #[derive(Debug)] |
330 | | struct PackageNotFoundError { |
331 | | span: Span, |
332 | | requested: PackageName, |
333 | | known: Vec<PackageName>, |
334 | | highlighted: Option<String>, |
335 | | } |
336 | | |
337 | | impl PackageNotFoundError { |
338 | 0 | pub fn new(span: Span, requested: PackageName, known: Vec<PackageName>) -> Self { |
339 | 0 | Self { |
340 | 0 | span, |
341 | 0 | requested, |
342 | 0 | known, |
343 | 0 | highlighted: None, |
344 | 0 | } |
345 | 0 | } |
346 | | |
347 | | /// Highlights this error using the given source map, if the span is known. |
348 | 0 | fn highlight(&mut self, source_map: &ast::SourceMap) { |
349 | 0 | if self.highlighted.is_none() { |
350 | 0 | self.highlighted = source_map.highlight_span(self.span, &format!("{self}")); |
351 | 0 | } |
352 | 0 | } |
353 | | } |
354 | | |
355 | | impl fmt::Display for PackageNotFoundError { |
356 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
357 | 0 | if let Some(highlighted) = &self.highlighted { |
358 | 0 | return highlighted.fmt(f); |
359 | 0 | } |
360 | 0 | if self.known.is_empty() { |
361 | 0 | write!( |
362 | 0 | f, |
363 | | "package '{}' not found. no known packages.", |
364 | | self.requested |
365 | 0 | )?; |
366 | | } else { |
367 | 0 | write!( |
368 | 0 | f, |
369 | | "package '{}' not found. known packages:\n", |
370 | | self.requested |
371 | 0 | )?; |
372 | 0 | for known in self.known.iter() { |
373 | 0 | write!(f, " {known}\n")?; |
374 | | } |
375 | | } |
376 | 0 | Ok(()) |
377 | 0 | } |
378 | | } |
379 | | |
380 | | impl core::error::Error for PackageNotFoundError {} |
381 | | |
382 | | impl UnresolvedPackageGroup { |
383 | | /// Parses the given string as a wit document. |
384 | | /// |
385 | | /// The `path` argument is used for error reporting. The `contents` provided |
386 | | /// are considered to be the contents of `path`. This function does not read |
387 | | /// the filesystem. |
388 | | #[cfg(feature = "std")] |
389 | 0 | pub fn parse(path: impl AsRef<Path>, contents: &str) -> Result<UnresolvedPackageGroup> { |
390 | 0 | let path = path |
391 | 0 | .as_ref() |
392 | 0 | .to_str() |
393 | 0 | .ok_or_else(|| anyhow::anyhow!("path is not valid utf-8: {:?}", path.as_ref()))?; |
394 | 0 | let mut map = SourceMap::default(); |
395 | 0 | map.push_str(path, contents); |
396 | 0 | map.parse() |
397 | 0 | .map_err(|(map, e)| anyhow::anyhow!("{}", e.highlight(&map))) |
398 | 0 | } |
399 | | |
400 | | /// Parses a WIT package from the directory provided. |
401 | | /// |
402 | | /// This method will look at all files under the `path` specified. All |
403 | | /// `*.wit` files are parsed and assumed to be part of the same package |
404 | | /// grouping. This is useful when a WIT package is split across multiple |
405 | | /// files. |
406 | | #[cfg(feature = "std")] |
407 | 0 | pub fn parse_dir(path: impl AsRef<Path>) -> Result<UnresolvedPackageGroup> { |
408 | 0 | let path = path.as_ref(); |
409 | 0 | let mut map = SourceMap::default(); |
410 | 0 | let cx = || format!("failed to read directory {path:?}");Unexecuted instantiation: <wit_parser::UnresolvedPackageGroup>::parse_dir::<&std::path::Path>::{closure#0}Unexecuted instantiation: <wit_parser::UnresolvedPackageGroup>::parse_dir::<&std::path::PathBuf>::{closure#0} |
411 | 0 | for entry in path.read_dir().with_context(&cx)? { |
412 | 0 | let entry = entry.with_context(&cx)?; |
413 | 0 | let path = entry.path(); |
414 | 0 | let ty = entry.file_type().with_context(&cx)?; |
415 | 0 | if ty.is_dir() { |
416 | 0 | continue; |
417 | 0 | } |
418 | 0 | if ty.is_symlink() { |
419 | 0 | if path.is_dir() { |
420 | 0 | continue; |
421 | 0 | } |
422 | 0 | } |
423 | 0 | let filename = match path.file_name().and_then(|s| s.to_str()) {Unexecuted instantiation: <wit_parser::UnresolvedPackageGroup>::parse_dir::<&std::path::Path>::{closure#1}Unexecuted instantiation: <wit_parser::UnresolvedPackageGroup>::parse_dir::<&std::path::PathBuf>::{closure#1} |
424 | 0 | Some(name) => name, |
425 | 0 | None => continue, |
426 | | }; |
427 | 0 | if !filename.ends_with(".wit") { |
428 | 0 | continue; |
429 | 0 | } |
430 | 0 | map.push_file(&path)?; |
431 | | } |
432 | 0 | map.parse() |
433 | 0 | .map_err(|(map, e)| anyhow::anyhow!("{}", e.highlight(&map)))Unexecuted instantiation: <wit_parser::UnresolvedPackageGroup>::parse_dir::<&std::path::Path>::{closure#2}Unexecuted instantiation: <wit_parser::UnresolvedPackageGroup>::parse_dir::<&std::path::PathBuf>::{closure#2} |
434 | 0 | } Unexecuted instantiation: <wit_parser::UnresolvedPackageGroup>::parse_dir::<&std::path::Path> Unexecuted instantiation: <wit_parser::UnresolvedPackageGroup>::parse_dir::<&std::path::PathBuf> |
435 | | } |
436 | | |
437 | | #[derive(Debug, Clone, PartialEq, Eq)] |
438 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
439 | | pub struct World { |
440 | | /// The WIT identifier name of this world. |
441 | | pub name: String, |
442 | | |
443 | | /// All imported items into this interface, both worlds and functions. |
444 | | pub imports: IndexMap<WorldKey, WorldItem>, |
445 | | |
446 | | /// All exported items from this interface, both worlds and functions. |
447 | | pub exports: IndexMap<WorldKey, WorldItem>, |
448 | | |
449 | | /// The package that owns this world. |
450 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_optional_id"))] |
451 | | pub package: Option<PackageId>, |
452 | | |
453 | | /// Documentation associated with this world declaration. |
454 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
455 | | pub docs: Docs, |
456 | | |
457 | | /// Stability annotation for this world itself. |
458 | | #[cfg_attr( |
459 | | feature = "serde", |
460 | | serde(skip_serializing_if = "Stability::is_unknown") |
461 | | )] |
462 | | pub stability: Stability, |
463 | | |
464 | | /// All the included worlds from this world. Empty if this is fully resolved. |
465 | | #[cfg_attr(feature = "serde", serde(skip))] |
466 | | pub includes: Vec<WorldInclude>, |
467 | | |
468 | | /// Source span for this world. |
469 | | #[cfg_attr(feature = "serde", serde(skip))] |
470 | | pub span: Span, |
471 | | } |
472 | | |
473 | | impl World { |
474 | | /// Adjusts all spans in this world by adding the given byte offset. |
475 | 32.3k | pub(crate) fn adjust_spans(&mut self, offset: u32) { |
476 | 32.3k | self.span.adjust(offset); |
477 | 51.8k | for item in self.imports.values_mut().chain(self.exports.values_mut()) { |
478 | 51.8k | item.adjust_spans(offset); |
479 | 51.8k | } |
480 | 32.3k | for include in &mut self.includes { |
481 | 0 | include.span.adjust(offset); |
482 | 0 | } |
483 | 32.3k | } |
484 | | } |
485 | | |
486 | | #[derive(Debug, Clone, PartialEq, Eq)] |
487 | | pub struct IncludeName { |
488 | | /// The name of the item |
489 | | pub name: String, |
490 | | |
491 | | /// The name to be replaced with |
492 | | pub as_: String, |
493 | | } |
494 | | |
495 | | /// An entry in the `includes` list of a world, representing an `include` |
496 | | /// statement in WIT. |
497 | | #[derive(Debug, Clone, PartialEq, Eq)] |
498 | | pub struct WorldInclude { |
499 | | /// The stability annotation on this include. |
500 | | pub stability: Stability, |
501 | | |
502 | | /// The world being included. |
503 | | pub id: WorldId, |
504 | | |
505 | | /// Names being renamed as part of this include. |
506 | | pub names: Vec<IncludeName>, |
507 | | |
508 | | /// Source span for this include statement. |
509 | | pub span: Span, |
510 | | } |
511 | | |
512 | | /// The key to the import/export maps of a world. Either a kebab-name or a |
513 | | /// unique interface. |
514 | | #[derive(Debug, Clone, Eq)] |
515 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
516 | | #[cfg_attr(feature = "serde", serde(into = "String"))] |
517 | | pub enum WorldKey { |
518 | | /// A kebab-name. |
519 | | Name(String), |
520 | | /// An interface which is assigned no kebab-name. |
521 | | Interface(InterfaceId), |
522 | | } |
523 | | |
524 | | impl Hash for WorldKey { |
525 | 208k | fn hash<H: Hasher>(&self, hasher: &mut H) { |
526 | 208k | match self { |
527 | 188k | WorldKey::Name(s) => { |
528 | 188k | 0u8.hash(hasher); |
529 | 188k | s.as_str().hash(hasher); |
530 | 188k | } |
531 | 19.8k | WorldKey::Interface(i) => { |
532 | 19.8k | 1u8.hash(hasher); |
533 | 19.8k | i.hash(hasher); |
534 | 19.8k | } |
535 | | } |
536 | 208k | } |
537 | | } |
538 | | |
539 | | impl PartialEq for WorldKey { |
540 | 34.8k | fn eq(&self, other: &WorldKey) -> bool { |
541 | 34.8k | match (self, other) { |
542 | 23.3k | (WorldKey::Name(a), WorldKey::Name(b)) => a.as_str() == b.as_str(), |
543 | 574 | (WorldKey::Name(_), _) => false, |
544 | 10.2k | (WorldKey::Interface(a), WorldKey::Interface(b)) => a == b, |
545 | 562 | (WorldKey::Interface(_), _) => false, |
546 | | } |
547 | 34.8k | } |
548 | | } |
549 | | |
550 | | impl From<WorldKey> for String { |
551 | 0 | fn from(key: WorldKey) -> String { |
552 | 0 | match key { |
553 | 0 | WorldKey::Name(name) => name, |
554 | 0 | WorldKey::Interface(id) => format!("interface-{}", id.index()), |
555 | | } |
556 | 0 | } |
557 | | } |
558 | | |
559 | | impl WorldKey { |
560 | | /// Asserts that this is `WorldKey::Name` and returns the name. |
561 | | #[track_caller] |
562 | 9.54k | pub fn unwrap_name(self) -> String { |
563 | 9.54k | match self { |
564 | 9.54k | WorldKey::Name(name) => name, |
565 | 0 | WorldKey::Interface(_) => panic!("expected a name, found interface"), |
566 | | } |
567 | 9.54k | } |
568 | | } |
569 | | |
570 | | #[derive(Debug, Clone, PartialEq, Eq)] |
571 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
572 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] |
573 | | pub enum WorldItem { |
574 | | /// An interface is being imported or exported from a world, indicating that |
575 | | /// it's a namespace of functions. |
576 | | Interface { |
577 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
578 | | id: InterfaceId, |
579 | | #[cfg_attr( |
580 | | feature = "serde", |
581 | | serde(skip_serializing_if = "Stability::is_unknown") |
582 | | )] |
583 | | stability: Stability, |
584 | | #[cfg_attr(feature = "serde", serde(skip))] |
585 | | span: Span, |
586 | | }, |
587 | | |
588 | | /// A function is being directly imported or exported from this world. |
589 | | Function(Function), |
590 | | |
591 | | /// A type is being exported from this world. |
592 | | /// |
593 | | /// Note that types are never imported into worlds at this time. |
594 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id_ignore_span"))] |
595 | | Type { id: TypeId, span: Span }, |
596 | | } |
597 | | |
598 | | impl WorldItem { |
599 | 32.0k | pub fn stability<'a>(&'a self, resolve: &'a Resolve) -> &'a Stability { |
600 | 32.0k | match self { |
601 | 6.33k | WorldItem::Interface { stability, .. } => stability, |
602 | 6.96k | WorldItem::Function(f) => &f.stability, |
603 | 18.7k | WorldItem::Type { id, .. } => &resolve.types[*id].stability, |
604 | | } |
605 | 32.0k | } |
606 | | |
607 | 27.9k | pub fn span(&self) -> Span { |
608 | 27.9k | match self { |
609 | 4.12k | WorldItem::Interface { span, .. } => *span, |
610 | 5.09k | WorldItem::Function(f) => f.span, |
611 | 18.7k | WorldItem::Type { span, .. } => *span, |
612 | | } |
613 | 27.9k | } |
614 | | |
615 | 51.8k | pub(crate) fn adjust_spans(&mut self, offset: u32) { |
616 | 51.8k | match self { |
617 | 10.1k | WorldItem::Function(f) => f.adjust_spans(offset), |
618 | 8.61k | WorldItem::Interface { span, .. } => span.adjust(offset), |
619 | 33.0k | WorldItem::Type { span, .. } => span.adjust(offset), |
620 | | } |
621 | 51.8k | } |
622 | | } |
623 | | |
624 | | #[derive(Debug, Clone, PartialEq, Eq)] |
625 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
626 | | pub struct Interface { |
627 | | /// Optionally listed name of this interface. |
628 | | /// |
629 | | /// This is `None` for inline interfaces in worlds. |
630 | | pub name: Option<String>, |
631 | | |
632 | | /// Exported types from this interface. |
633 | | /// |
634 | | /// Export names are listed within the types themselves. Note that the |
635 | | /// export name here matches the name listed in the `TypeDef`. |
636 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id_map"))] |
637 | | pub types: IndexMap<String, TypeId>, |
638 | | |
639 | | /// Exported functions from this interface. |
640 | | pub functions: IndexMap<String, Function>, |
641 | | |
642 | | /// Documentation associated with this interface. |
643 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
644 | | pub docs: Docs, |
645 | | |
646 | | /// Stability attribute for this interface. |
647 | | #[cfg_attr( |
648 | | feature = "serde", |
649 | | serde(skip_serializing_if = "Stability::is_unknown") |
650 | | )] |
651 | | pub stability: Stability, |
652 | | |
653 | | /// The package that owns this interface. |
654 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_optional_id"))] |
655 | | pub package: Option<PackageId>, |
656 | | |
657 | | /// Source span for this interface. |
658 | | #[cfg_attr(feature = "serde", serde(skip))] |
659 | | pub span: Span, |
660 | | |
661 | | /// The interface that this one was cloned from, if any. |
662 | | /// |
663 | | /// Applicable for [`Resolve::generate_nominal_type_ids`]. |
664 | | #[cfg_attr( |
665 | | feature = "serde", |
666 | | serde( |
667 | | skip_serializing_if = "Option::is_none", |
668 | | serialize_with = "serialize_optional_id", |
669 | | ) |
670 | | )] |
671 | | pub clone_of: Option<InterfaceId>, |
672 | | } |
673 | | |
674 | | impl Interface { |
675 | | /// Adjusts all spans in this interface by adding the given byte offset. |
676 | 60.5k | pub(crate) fn adjust_spans(&mut self, offset: u32) { |
677 | 60.5k | self.span.adjust(offset); |
678 | 60.5k | for func in self.functions.values_mut() { |
679 | 24.5k | func.adjust_spans(offset); |
680 | 24.5k | } |
681 | 60.5k | } |
682 | | } |
683 | | |
684 | | #[derive(Debug, Clone, PartialEq, Eq)] |
685 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
686 | | pub struct TypeDef { |
687 | | pub name: Option<String>, |
688 | | pub kind: TypeDefKind, |
689 | | pub owner: TypeOwner, |
690 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
691 | | pub docs: Docs, |
692 | | /// Stability attribute for this type. |
693 | | #[cfg_attr( |
694 | | feature = "serde", |
695 | | serde(skip_serializing_if = "Stability::is_unknown") |
696 | | )] |
697 | | pub stability: Stability, |
698 | | /// Source span for this type. |
699 | | #[cfg_attr(feature = "serde", serde(skip))] |
700 | | pub span: Span, |
701 | | } |
702 | | |
703 | | impl TypeDef { |
704 | | /// Adjusts all spans in this type definition by adding the given byte offset. |
705 | | /// |
706 | | /// This is used when merging source maps to update spans to point to the |
707 | | /// correct location in the combined source map. |
708 | 197k | pub(crate) fn adjust_spans(&mut self, offset: u32) { |
709 | 197k | self.span.adjust(offset); |
710 | 197k | match &mut self.kind { |
711 | 5.54k | TypeDefKind::Record(r) => { |
712 | 14.9k | for field in &mut r.fields { |
713 | 14.9k | field.span.adjust(offset); |
714 | 14.9k | } |
715 | | } |
716 | 9.00k | TypeDefKind::Variant(v) => { |
717 | 31.3k | for case in &mut v.cases { |
718 | 31.3k | case.span.adjust(offset); |
719 | 31.3k | } |
720 | | } |
721 | 32.0k | TypeDefKind::Enum(e) => { |
722 | 151k | for case in &mut e.cases { |
723 | 151k | case.span.adjust(offset); |
724 | 151k | } |
725 | | } |
726 | 1.75k | TypeDefKind::Flags(f) => { |
727 | 5.08k | for flag in &mut f.flags { |
728 | 5.08k | flag.span.adjust(offset); |
729 | 5.08k | } |
730 | | } |
731 | 148k | _ => {} |
732 | | } |
733 | 197k | } |
734 | | } |
735 | | |
736 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
737 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
738 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] |
739 | | pub enum TypeDefKind { |
740 | | Record(Record), |
741 | | Resource, |
742 | | Handle(Handle), |
743 | | Flags(Flags), |
744 | | Tuple(Tuple), |
745 | | Variant(Variant), |
746 | | Enum(Enum), |
747 | | Option(Type), |
748 | | Result(Result_), |
749 | | List(Type), |
750 | | Map(Type, Type), |
751 | | FixedLengthList(Type, u32), |
752 | | Future(Option<Type>), |
753 | | Stream(Option<Type>), |
754 | | Type(Type), |
755 | | |
756 | | /// This represents a type of unknown structure imported from a foreign |
757 | | /// interface. |
758 | | /// |
759 | | /// This variant is only used during the creation of `UnresolvedPackage` but |
760 | | /// by the time a `Resolve` is created then this will not exist. |
761 | | Unknown, |
762 | | } |
763 | | |
764 | | impl TypeDefKind { |
765 | 0 | pub fn as_str(&self) -> &'static str { |
766 | 0 | match self { |
767 | 0 | TypeDefKind::Record(_) => "record", |
768 | 0 | TypeDefKind::Resource => "resource", |
769 | 0 | TypeDefKind::Handle(handle) => match handle { |
770 | 0 | Handle::Own(_) => "own", |
771 | 0 | Handle::Borrow(_) => "borrow", |
772 | | }, |
773 | 0 | TypeDefKind::Flags(_) => "flags", |
774 | 0 | TypeDefKind::Tuple(_) => "tuple", |
775 | 0 | TypeDefKind::Variant(_) => "variant", |
776 | 0 | TypeDefKind::Enum(_) => "enum", |
777 | 0 | TypeDefKind::Option(_) => "option", |
778 | 0 | TypeDefKind::Result(_) => "result", |
779 | 0 | TypeDefKind::List(_) => "list", |
780 | 0 | TypeDefKind::Map(_, _) => "map", |
781 | 0 | TypeDefKind::FixedLengthList(..) => "fixed-length list", |
782 | 0 | TypeDefKind::Future(_) => "future", |
783 | 0 | TypeDefKind::Stream(_) => "stream", |
784 | 0 | TypeDefKind::Type(_) => "type", |
785 | 0 | TypeDefKind::Unknown => "unknown", |
786 | | } |
787 | 0 | } |
788 | | } |
789 | | |
790 | | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] |
791 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
792 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] |
793 | | pub enum TypeOwner { |
794 | | /// This type was defined within a `world` block. |
795 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
796 | | World(WorldId), |
797 | | /// This type was defined within an `interface` block. |
798 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
799 | | Interface(InterfaceId), |
800 | | /// This type wasn't inherently defined anywhere, such as a `list<T>`, which |
801 | | /// doesn't need an owner. |
802 | | #[cfg_attr(feature = "serde", serde(untagged, serialize_with = "serialize_none"))] |
803 | | None, |
804 | | } |
805 | | |
806 | | #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] |
807 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
808 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] |
809 | | pub enum Handle { |
810 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
811 | | Own(TypeId), |
812 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
813 | | Borrow(TypeId), |
814 | | } |
815 | | |
816 | | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)] |
817 | | pub enum Type { |
818 | | Bool, |
819 | | U8, |
820 | | U16, |
821 | | U32, |
822 | | U64, |
823 | | S8, |
824 | | S16, |
825 | | S32, |
826 | | S64, |
827 | | F32, |
828 | | F64, |
829 | | Char, |
830 | | String, |
831 | | ErrorContext, |
832 | | Id(TypeId), |
833 | | } |
834 | | |
835 | | #[derive(Debug, Copy, Clone, Eq, PartialEq)] |
836 | | pub enum Int { |
837 | | U8, |
838 | | U16, |
839 | | U32, |
840 | | U64, |
841 | | } |
842 | | |
843 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
844 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
845 | | pub struct Record { |
846 | | pub fields: Vec<Field>, |
847 | | } |
848 | | |
849 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
850 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
851 | | pub struct Field { |
852 | | pub name: String, |
853 | | #[cfg_attr(feature = "serde", serde(rename = "type"))] |
854 | | pub ty: Type, |
855 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
856 | | pub docs: Docs, |
857 | | /// Source span for this field. |
858 | | #[cfg_attr(feature = "serde", serde(skip))] |
859 | | pub span: Span, |
860 | | } |
861 | | |
862 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
863 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
864 | | pub struct Flags { |
865 | | pub flags: Vec<Flag>, |
866 | | } |
867 | | |
868 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
869 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
870 | | pub struct Flag { |
871 | | pub name: String, |
872 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
873 | | pub docs: Docs, |
874 | | /// Source span for this flag. |
875 | | #[cfg_attr(feature = "serde", serde(skip))] |
876 | | pub span: Span, |
877 | | } |
878 | | |
879 | | #[derive(Debug, Clone, PartialEq)] |
880 | | pub enum FlagsRepr { |
881 | | U8, |
882 | | U16, |
883 | | U32(usize), |
884 | | } |
885 | | |
886 | | impl Flags { |
887 | 307 | pub fn repr(&self) -> FlagsRepr { |
888 | 307 | match self.flags.len() { |
889 | 0 | 0 => FlagsRepr::U32(0), |
890 | 307 | n if n <= 8 => FlagsRepr::U8, |
891 | 21 | n if n <= 16 => FlagsRepr::U16, |
892 | 0 | n => FlagsRepr::U32(sizealign::align_to(n, 32) / 32), |
893 | | } |
894 | 307 | } |
895 | | } |
896 | | |
897 | | impl FlagsRepr { |
898 | 18 | pub fn count(&self) -> usize { |
899 | 18 | match self { |
900 | 18 | FlagsRepr::U8 => 1, |
901 | 0 | FlagsRepr::U16 => 1, |
902 | 0 | FlagsRepr::U32(n) => *n, |
903 | | } |
904 | 18 | } |
905 | | } |
906 | | |
907 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
908 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
909 | | pub struct Tuple { |
910 | | pub types: Vec<Type>, |
911 | | } |
912 | | |
913 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
914 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
915 | | pub struct Variant { |
916 | | pub cases: Vec<Case>, |
917 | | } |
918 | | |
919 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
920 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
921 | | pub struct Case { |
922 | | pub name: String, |
923 | | #[cfg_attr(feature = "serde", serde(rename = "type"))] |
924 | | pub ty: Option<Type>, |
925 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
926 | | pub docs: Docs, |
927 | | /// Source span for this variant case. |
928 | | #[cfg_attr(feature = "serde", serde(skip))] |
929 | | pub span: Span, |
930 | | } |
931 | | |
932 | | impl Variant { |
933 | 1.84k | pub fn tag(&self) -> Int { |
934 | 1.84k | discriminant_type(self.cases.len()) |
935 | 1.84k | } |
936 | | } |
937 | | |
938 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
939 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
940 | | pub struct Enum { |
941 | | pub cases: Vec<EnumCase>, |
942 | | } |
943 | | |
944 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
945 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
946 | | pub struct EnumCase { |
947 | | pub name: String, |
948 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
949 | | pub docs: Docs, |
950 | | /// Source span for this enum case. |
951 | | #[cfg_attr(feature = "serde", serde(skip))] |
952 | | pub span: Span, |
953 | | } |
954 | | |
955 | | impl Enum { |
956 | 1.49k | pub fn tag(&self) -> Int { |
957 | 1.49k | discriminant_type(self.cases.len()) |
958 | 1.49k | } |
959 | | } |
960 | | |
961 | | /// This corresponds to the `discriminant_type` function in the Canonical ABI. |
962 | 3.34k | fn discriminant_type(num_cases: usize) -> Int { |
963 | 3.34k | match num_cases.checked_sub(1) { |
964 | 0 | None => Int::U8, |
965 | 3.34k | Some(n) if n <= u8::max_value() as usize => Int::U8, |
966 | 0 | Some(n) if n <= u16::max_value() as usize => Int::U16, |
967 | 0 | Some(n) if n <= u32::max_value() as usize => Int::U32, |
968 | 0 | _ => panic!("too many cases to fit in a repr"), |
969 | | } |
970 | 3.34k | } |
971 | | |
972 | | #[derive(Debug, Clone, PartialEq, Hash, Eq)] |
973 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
974 | | pub struct Result_ { |
975 | | pub ok: Option<Type>, |
976 | | pub err: Option<Type>, |
977 | | } |
978 | | |
979 | | #[derive(Clone, Default, Debug, PartialEq, Eq, Hash)] |
980 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
981 | | pub struct Docs { |
982 | | pub contents: Option<String>, |
983 | | } |
984 | | |
985 | | impl Docs { |
986 | 0 | pub fn is_empty(&self) -> bool { |
987 | 0 | self.contents.is_none() |
988 | 0 | } |
989 | | } |
990 | | |
991 | | #[derive(Debug, Clone, PartialEq, Eq)] |
992 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
993 | | pub struct Param { |
994 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "String::is_empty"))] |
995 | | pub name: String, |
996 | | #[cfg_attr(feature = "serde", serde(rename = "type"))] |
997 | | pub ty: Type, |
998 | | #[cfg_attr(feature = "serde", serde(skip))] |
999 | | pub span: Span, |
1000 | | } |
1001 | | |
1002 | | #[derive(Debug, Clone, PartialEq, Eq)] |
1003 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
1004 | | pub struct Function { |
1005 | | pub name: String, |
1006 | | pub kind: FunctionKind, |
1007 | | pub params: Vec<Param>, |
1008 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] |
1009 | | pub result: Option<Type>, |
1010 | | #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Docs::is_empty"))] |
1011 | | pub docs: Docs, |
1012 | | /// Stability attribute for this function. |
1013 | | #[cfg_attr( |
1014 | | feature = "serde", |
1015 | | serde(skip_serializing_if = "Stability::is_unknown") |
1016 | | )] |
1017 | | pub stability: Stability, |
1018 | | |
1019 | | /// Source span for this function. |
1020 | | #[cfg_attr(feature = "serde", serde(skip))] |
1021 | | pub span: Span, |
1022 | | } |
1023 | | |
1024 | | #[derive(Debug, Clone, PartialEq, Eq)] |
1025 | | #[cfg_attr(feature = "serde", derive(Serialize))] |
1026 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] |
1027 | | pub enum FunctionKind { |
1028 | | /// A freestanding function. |
1029 | | /// |
1030 | | /// ```wit |
1031 | | /// interface foo { |
1032 | | /// the-func: func(); |
1033 | | /// } |
1034 | | /// ``` |
1035 | | Freestanding, |
1036 | | |
1037 | | /// An async freestanding function. |
1038 | | /// |
1039 | | /// ```wit |
1040 | | /// interface foo { |
1041 | | /// the-func: async func(); |
1042 | | /// } |
1043 | | /// ``` |
1044 | | AsyncFreestanding, |
1045 | | |
1046 | | /// A resource method where the first parameter is implicitly |
1047 | | /// `borrow<T>`. |
1048 | | /// |
1049 | | /// ```wit |
1050 | | /// interface foo { |
1051 | | /// resource r { |
1052 | | /// the-func: func(); |
1053 | | /// } |
1054 | | /// } |
1055 | | /// ``` |
1056 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
1057 | | Method(TypeId), |
1058 | | |
1059 | | /// An async resource method where the first parameter is implicitly |
1060 | | /// `borrow<T>`. |
1061 | | /// |
1062 | | /// ```wit |
1063 | | /// interface foo { |
1064 | | /// resource r { |
1065 | | /// the-func: async func(); |
1066 | | /// } |
1067 | | /// } |
1068 | | /// ``` |
1069 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
1070 | | AsyncMethod(TypeId), |
1071 | | |
1072 | | /// A static resource method. |
1073 | | /// |
1074 | | /// ```wit |
1075 | | /// interface foo { |
1076 | | /// resource r { |
1077 | | /// the-func: static func(); |
1078 | | /// } |
1079 | | /// } |
1080 | | /// ``` |
1081 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
1082 | | Static(TypeId), |
1083 | | |
1084 | | /// An async static resource method. |
1085 | | /// |
1086 | | /// ```wit |
1087 | | /// interface foo { |
1088 | | /// resource r { |
1089 | | /// the-func: static async func(); |
1090 | | /// } |
1091 | | /// } |
1092 | | /// ``` |
1093 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
1094 | | AsyncStatic(TypeId), |
1095 | | |
1096 | | /// A resource constructor where the return value is implicitly `own<T>`. |
1097 | | /// |
1098 | | /// ```wit |
1099 | | /// interface foo { |
1100 | | /// resource r { |
1101 | | /// constructor(); |
1102 | | /// } |
1103 | | /// } |
1104 | | /// ``` |
1105 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_id"))] |
1106 | | Constructor(TypeId), |
1107 | | } |
1108 | | |
1109 | | impl FunctionKind { |
1110 | | /// Returns whether this is an async function or not. |
1111 | 60.1k | pub fn is_async(&self) -> bool { |
1112 | 60.1k | match self { |
1113 | | FunctionKind::Freestanding |
1114 | | | FunctionKind::Method(_) |
1115 | | | FunctionKind::Static(_) |
1116 | 26.5k | | FunctionKind::Constructor(_) => false, |
1117 | | FunctionKind::AsyncFreestanding |
1118 | | | FunctionKind::AsyncMethod(_) |
1119 | 33.5k | | FunctionKind::AsyncStatic(_) => true, |
1120 | | } |
1121 | 60.1k | } |
1122 | | |
1123 | | /// Returns the resource, if present, that this function kind refers to. |
1124 | 57.1k | pub fn resource(&self) -> Option<TypeId> { |
1125 | 57.1k | match self { |
1126 | 41.4k | FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => None, |
1127 | 1.94k | FunctionKind::Method(id) |
1128 | 1.75k | | FunctionKind::Static(id) |
1129 | 1.73k | | FunctionKind::Constructor(id) |
1130 | 6.79k | | FunctionKind::AsyncMethod(id) |
1131 | 15.6k | | FunctionKind::AsyncStatic(id) => Some(*id), |
1132 | | } |
1133 | 57.1k | } |
1134 | | |
1135 | | /// Returns the resource, if present, that this function kind refers to. |
1136 | 41.6k | pub fn resource_mut(&mut self) -> Option<&mut TypeId> { |
1137 | 41.6k | match self { |
1138 | 32.9k | FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => None, |
1139 | 1.18k | FunctionKind::Method(id) |
1140 | 971 | | FunctionKind::Static(id) |
1141 | 1.01k | | FunctionKind::Constructor(id) |
1142 | 3.59k | | FunctionKind::AsyncMethod(id) |
1143 | 8.71k | | FunctionKind::AsyncStatic(id) => Some(id), |
1144 | | } |
1145 | 41.6k | } |
1146 | | } |
1147 | | |
1148 | | /// Possible forms of name mangling that are supported by this crate. |
1149 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
1150 | | pub enum Mangling { |
1151 | | /// The "standard" component model mangling format for 32-bit linear |
1152 | | /// memories. This is specified in WebAssembly/component-model#378 |
1153 | | Standard32, |
1154 | | |
1155 | | /// The "legacy" name mangling supported in versions 218-and-prior for this |
1156 | | /// crate. This is the original support for how components were created from |
1157 | | /// core wasm modules and this does not correspond to any standard. This is |
1158 | | /// preserved for now while tools transition to the new scheme. |
1159 | | Legacy, |
1160 | | } |
1161 | | |
1162 | | impl core::str::FromStr for Mangling { |
1163 | | type Err = anyhow::Error; |
1164 | | |
1165 | 0 | fn from_str(s: &str) -> Result<Mangling> { |
1166 | 0 | match s { |
1167 | 0 | "legacy" => Ok(Mangling::Legacy), |
1168 | 0 | "standard32" => Ok(Mangling::Standard32), |
1169 | | _ => { |
1170 | 0 | bail!( |
1171 | | "unknown name mangling `{s}`, \ |
1172 | | supported values are `legacy` or `standard32`" |
1173 | | ) |
1174 | | } |
1175 | | } |
1176 | 0 | } |
1177 | | } |
1178 | | |
1179 | | /// Possible lift/lower ABI choices supported when mangling names. |
1180 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
1181 | | pub enum LiftLowerAbi { |
1182 | | /// Both imports and exports will use the synchronous ABI. |
1183 | | Sync, |
1184 | | |
1185 | | /// Both imports and exports will use the async ABI (with a callback for |
1186 | | /// each export). |
1187 | | AsyncCallback, |
1188 | | |
1189 | | /// Both imports and exports will use the async ABI (with no callbacks for |
1190 | | /// exports). |
1191 | | AsyncStackful, |
1192 | | } |
1193 | | |
1194 | | impl LiftLowerAbi { |
1195 | 2.20k | fn import_prefix(self) -> &'static str { |
1196 | 2.20k | match self { |
1197 | 1.92k | Self::Sync => "", |
1198 | 281 | Self::AsyncCallback | Self::AsyncStackful => "[async-lower]", |
1199 | | } |
1200 | 2.20k | } |
1201 | | |
1202 | | /// Get the import [`AbiVariant`] corresponding to this [`LiftLowerAbi`] |
1203 | 1.74k | pub fn import_variant(self) -> AbiVariant { |
1204 | 1.74k | match self { |
1205 | 1.46k | Self::Sync => AbiVariant::GuestImport, |
1206 | 281 | Self::AsyncCallback | Self::AsyncStackful => AbiVariant::GuestImportAsync, |
1207 | | } |
1208 | 1.74k | } |
1209 | | |
1210 | 2.06k | fn export_prefix(self) -> &'static str { |
1211 | 2.06k | match self { |
1212 | 1.64k | Self::Sync => "", |
1213 | 343 | Self::AsyncCallback => "[async-lift]", |
1214 | 72 | Self::AsyncStackful => "[async-lift-stackful]", |
1215 | | } |
1216 | 2.06k | } |
1217 | | |
1218 | | /// Get the export [`AbiVariant`] corresponding to this [`LiftLowerAbi`] |
1219 | 1.02k | pub fn export_variant(self) -> AbiVariant { |
1220 | 1.02k | match self { |
1221 | 795 | Self::Sync => AbiVariant::GuestExport, |
1222 | 166 | Self::AsyncCallback => AbiVariant::GuestExportAsync, |
1223 | 65 | Self::AsyncStackful => AbiVariant::GuestExportAsyncStackful, |
1224 | | } |
1225 | 1.02k | } |
1226 | | } |
1227 | | |
1228 | | /// Combination of [`Mangling`] and [`LiftLowerAbi`]. |
1229 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
1230 | | pub enum ManglingAndAbi { |
1231 | | /// See [`Mangling::Standard32`]. |
1232 | | /// |
1233 | | /// As of this writing, the standard name mangling only supports the |
1234 | | /// synchronous ABI. |
1235 | | Standard32, |
1236 | | |
1237 | | /// See [`Mangling::Legacy`] and [`LiftLowerAbi`]. |
1238 | | Legacy(LiftLowerAbi), |
1239 | | } |
1240 | | |
1241 | | impl ManglingAndAbi { |
1242 | | /// Get the import [`AbiVariant`] corresponding to this [`ManglingAndAbi`] |
1243 | 1.85k | pub fn import_variant(self) -> AbiVariant { |
1244 | 1.85k | match self { |
1245 | 103 | Self::Standard32 => AbiVariant::GuestImport, |
1246 | 1.74k | Self::Legacy(abi) => abi.import_variant(), |
1247 | | } |
1248 | 1.85k | } |
1249 | | |
1250 | | /// Get the export [`AbiVariant`] corresponding to this [`ManglingAndAbi`] |
1251 | 1.13k | pub fn export_variant(self) -> AbiVariant { |
1252 | 1.13k | match self { |
1253 | 107 | Self::Standard32 => AbiVariant::GuestExport, |
1254 | 1.02k | Self::Legacy(abi) => abi.export_variant(), |
1255 | | } |
1256 | 1.13k | } |
1257 | | |
1258 | | /// Switch the ABI to be sync if it's async. |
1259 | 513 | pub fn sync(self) -> Self { |
1260 | 456 | match self { |
1261 | 421 | Self::Standard32 | Self::Legacy(LiftLowerAbi::Sync) => self, |
1262 | | Self::Legacy(LiftLowerAbi::AsyncCallback) |
1263 | 92 | | Self::Legacy(LiftLowerAbi::AsyncStackful) => Self::Legacy(LiftLowerAbi::Sync), |
1264 | | } |
1265 | 513 | } |
1266 | | |
1267 | | /// Returns whether this is an async ABI |
1268 | 7.06k | pub fn is_async(&self) -> bool { |
1269 | 6.63k | match self { |
1270 | 5.95k | Self::Standard32 | Self::Legacy(LiftLowerAbi::Sync) => false, |
1271 | | Self::Legacy(LiftLowerAbi::AsyncCallback) |
1272 | 1.11k | | Self::Legacy(LiftLowerAbi::AsyncStackful) => true, |
1273 | | } |
1274 | 7.06k | } |
1275 | | |
1276 | 231 | pub fn mangling(&self) -> Mangling { |
1277 | 231 | match self { |
1278 | 0 | Self::Standard32 => Mangling::Standard32, |
1279 | 231 | Self::Legacy(_) => Mangling::Legacy, |
1280 | | } |
1281 | 231 | } |
1282 | | } |
1283 | | |
1284 | | impl Function { |
1285 | | /// Adjusts all spans in this function by adding the given byte offset. |
1286 | 34.6k | pub(crate) fn adjust_spans(&mut self, offset: u32) { |
1287 | 34.6k | self.span.adjust(offset); |
1288 | 79.6k | for param in &mut self.params { |
1289 | 79.6k | param.span.adjust(offset); |
1290 | 79.6k | } |
1291 | 34.6k | } |
1292 | | |
1293 | 10.4k | pub fn item_name(&self) -> &str { |
1294 | 10.4k | match &self.kind { |
1295 | 8.73k | FunctionKind::Freestanding | FunctionKind::AsyncFreestanding => &self.name, |
1296 | | FunctionKind::Method(_) |
1297 | | | FunctionKind::Static(_) |
1298 | | | FunctionKind::AsyncMethod(_) |
1299 | 1.72k | | FunctionKind::AsyncStatic(_) => &self.name[self.name.find('.').unwrap() + 1..], |
1300 | 0 | FunctionKind::Constructor(_) => "constructor", |
1301 | | } |
1302 | 10.4k | } |
1303 | | |
1304 | | /// Returns an iterator over the types used in parameters and results. |
1305 | | /// |
1306 | | /// Note that this iterator is not transitive, it only iterates over the |
1307 | | /// direct references to types that this function has. |
1308 | 2.49k | pub fn parameter_and_result_types(&self) -> impl Iterator<Item = Type> + '_ { |
1309 | 2.49k | self.params.iter().map(|p| p.ty).chain(self.result) |
1310 | 2.49k | } |
1311 | | |
1312 | | /// Gets the core export name for this function. |
1313 | 0 | pub fn standard32_core_export_name<'a>(&'a self, interface: Option<&str>) -> Cow<'a, str> { |
1314 | 0 | self.core_export_name(interface, Mangling::Standard32) |
1315 | 0 | } |
1316 | | |
1317 | 13.7k | pub fn legacy_core_export_name<'a>(&'a self, interface: Option<&str>) -> Cow<'a, str> { |
1318 | 13.7k | self.core_export_name(interface, Mangling::Legacy) |
1319 | 13.7k | } |
1320 | | /// Gets the core export name for this function. |
1321 | 13.7k | pub fn core_export_name<'a>( |
1322 | 13.7k | &'a self, |
1323 | 13.7k | interface: Option<&str>, |
1324 | 13.7k | mangling: Mangling, |
1325 | 13.7k | ) -> Cow<'a, str> { |
1326 | 13.7k | match interface { |
1327 | 12.4k | Some(interface) => match mangling { |
1328 | 0 | Mangling::Standard32 => Cow::Owned(format!("cm32p2|{interface}|{}", self.name)), |
1329 | 12.4k | Mangling::Legacy => Cow::Owned(format!("{interface}#{}", self.name)), |
1330 | | }, |
1331 | 1.31k | None => match mangling { |
1332 | 0 | Mangling::Standard32 => Cow::Owned(format!("cm32p2||{}", self.name)), |
1333 | 1.31k | Mangling::Legacy => Cow::Borrowed(&self.name), |
1334 | | }, |
1335 | | } |
1336 | 13.7k | } |
1337 | | /// Collect any future and stream types appearing in the signature of this |
1338 | | /// function by doing a depth-first search over the parameter types and then |
1339 | | /// the result types. |
1340 | | /// |
1341 | | /// For example, given the WIT function `foo: func(x: future<future<u32>>, |
1342 | | /// y: u32) -> stream<u8>`, we would return `[future<u32>, |
1343 | | /// future<future<u32>>, stream<u8>]`. |
1344 | | /// |
1345 | | /// This may be used by binding generators to refer to specific `future` and |
1346 | | /// `stream` types when importing canonical built-ins such as `stream.new`, |
1347 | | /// `future.read`, etc. Using the example above, the import |
1348 | | /// `[future-new-0]foo` would indicate a call to `future.new` for the type |
1349 | | /// `future<u32>`. Likewise, `[future-new-1]foo` would indicate a call to |
1350 | | /// `future.new` for `future<future<u32>>`, and `[stream-new-2]foo` would |
1351 | | /// indicate a call to `stream.new` for `stream<u8>`. |
1352 | 3.31k | pub fn find_futures_and_streams(&self, resolve: &Resolve) -> Vec<TypeId> { |
1353 | 3.31k | let mut results = Vec::new(); |
1354 | 9.08k | for param in self.params.iter() { |
1355 | 9.08k | find_futures_and_streams(resolve, param.ty, &mut results); |
1356 | 9.08k | } |
1357 | 3.31k | if let Some(ty) = self.result { |
1358 | 3.08k | find_futures_and_streams(resolve, ty, &mut results); |
1359 | 3.08k | } |
1360 | 3.31k | results |
1361 | 3.31k | } |
1362 | | |
1363 | | /// Check if this function is a resource constructor in shorthand form. |
1364 | | /// I.e. without an explicit return type annotation. |
1365 | 11.5k | pub fn is_constructor_shorthand(&self, resolve: &Resolve) -> bool { |
1366 | 11.5k | let FunctionKind::Constructor(containing_resource_id) = self.kind else { |
1367 | 11.2k | return false; |
1368 | | }; |
1369 | | |
1370 | 242 | let Some(Type::Id(id)) = &self.result else { |
1371 | 0 | return false; |
1372 | | }; |
1373 | | |
1374 | 242 | let TypeDefKind::Handle(Handle::Own(returned_resource_id)) = resolve.types[*id].kind else { |
1375 | 0 | return false; |
1376 | | }; |
1377 | | |
1378 | 242 | return containing_resource_id == returned_resource_id; |
1379 | 11.5k | } |
1380 | | |
1381 | | /// Returns the `module`, `name`, and signature to use when importing this |
1382 | | /// function's `task.return` intrinsic using the `mangling` specified. |
1383 | 231 | pub fn task_return_import( |
1384 | 231 | &self, |
1385 | 231 | resolve: &Resolve, |
1386 | 231 | interface: Option<&WorldKey>, |
1387 | 231 | mangling: Mangling, |
1388 | 231 | ) -> (String, String, abi::WasmSignature) { |
1389 | 231 | match mangling { |
1390 | 0 | Mangling::Standard32 => todo!(), |
1391 | 231 | Mangling::Legacy => {} |
1392 | | } |
1393 | | // For exported async functions, generate a `task.return` intrinsic. |
1394 | 231 | let module = match interface { |
1395 | 175 | Some(key) => format!("[export]{}", resolve.name_world_key(key)), |
1396 | 56 | None => "[export]$root".to_string(), |
1397 | | }; |
1398 | 231 | let name = format!("[task-return]{}", self.name); |
1399 | | |
1400 | 231 | let mut func_tmp = self.clone(); |
1401 | 231 | func_tmp.params = Vec::new(); |
1402 | 231 | func_tmp.result = None; |
1403 | 231 | if let Some(ty) = self.result { |
1404 | 185 | func_tmp.params.push(Param { |
1405 | 185 | name: "x".to_string(), |
1406 | 185 | ty, |
1407 | 185 | span: Default::default(), |
1408 | 185 | }); |
1409 | 185 | } |
1410 | 231 | let sig = resolve.wasm_signature(AbiVariant::GuestImport, &func_tmp); |
1411 | 231 | (module, name, sig) |
1412 | 231 | } |
1413 | | |
1414 | | // push_imported_future_and_stream_intrinsics(wat, resolve, "[export]", interface, func); |
1415 | | } |
1416 | | |
1417 | 51.4k | fn find_futures_and_streams(resolve: &Resolve, ty: Type, results: &mut Vec<TypeId>) { |
1418 | 51.4k | let Type::Id(id) = ty else { |
1419 | 15.3k | return; |
1420 | | }; |
1421 | | |
1422 | 36.0k | match &resolve.types[id].kind { |
1423 | | TypeDefKind::Resource |
1424 | | | TypeDefKind::Handle(_) |
1425 | | | TypeDefKind::Flags(_) |
1426 | 243 | | TypeDefKind::Enum(_) => {} |
1427 | 41 | TypeDefKind::Record(r) => { |
1428 | 41 | for Field { ty, .. } in &r.fields { |
1429 | 41 | find_futures_and_streams(resolve, *ty, results); |
1430 | 41 | } |
1431 | | } |
1432 | 2.31k | TypeDefKind::Tuple(t) => { |
1433 | 5.72k | for ty in &t.types { |
1434 | 5.72k | find_futures_and_streams(resolve, *ty, results); |
1435 | 5.72k | } |
1436 | | } |
1437 | 2 | TypeDefKind::Variant(v) => { |
1438 | 10 | for Case { ty, .. } in &v.cases { |
1439 | 10 | if let Some(ty) = ty { |
1440 | 10 | find_futures_and_streams(resolve, *ty, results); |
1441 | 10 | } |
1442 | | } |
1443 | | } |
1444 | 535 | TypeDefKind::Option(ty) |
1445 | 339 | | TypeDefKind::List(ty) |
1446 | 266 | | TypeDefKind::FixedLengthList(ty, ..) |
1447 | 1.14k | | TypeDefKind::Type(ty) => { |
1448 | 1.14k | find_futures_and_streams(resolve, *ty, results); |
1449 | 1.14k | } |
1450 | 0 | TypeDefKind::Map(k, v) => { |
1451 | 0 | find_futures_and_streams(resolve, *k, results); |
1452 | 0 | find_futures_and_streams(resolve, *v, results); |
1453 | 0 | } |
1454 | 1.03k | TypeDefKind::Result(r) => { |
1455 | 1.03k | if let Some(ty) = r.ok { |
1456 | 942 | find_futures_and_streams(resolve, ty, results); |
1457 | 942 | } |
1458 | 1.03k | if let Some(ty) = r.err { |
1459 | 473 | find_futures_and_streams(resolve, ty, results); |
1460 | 561 | } |
1461 | | } |
1462 | 2.02k | TypeDefKind::Future(ty) => { |
1463 | 2.02k | if let Some(ty) = ty { |
1464 | 1.69k | find_futures_and_streams(resolve, *ty, results); |
1465 | 1.69k | } |
1466 | 2.02k | results.push(id); |
1467 | | } |
1468 | 29.2k | TypeDefKind::Stream(ty) => { |
1469 | 29.2k | if let Some(ty) = ty { |
1470 | 29.2k | find_futures_and_streams(resolve, *ty, results); |
1471 | 29.2k | } |
1472 | 29.2k | results.push(id); |
1473 | | } |
1474 | 0 | TypeDefKind::Unknown => unreachable!(), |
1475 | | } |
1476 | 51.4k | } |
1477 | | |
1478 | | /// Representation of the stability attributes associated with a world, |
1479 | | /// interface, function, or type. |
1480 | | /// |
1481 | | /// This is added for WebAssembly/component-model#332 where @since and @unstable |
1482 | | /// annotations were added to WIT. |
1483 | | /// |
1484 | | /// The order of the of enum values is significant since it is used with Ord and PartialOrd |
1485 | | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] |
1486 | | #[cfg_attr(feature = "serde", derive(serde_derive::Deserialize, Serialize))] |
1487 | | #[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))] |
1488 | | pub enum Stability { |
1489 | | /// This item does not have either `@since` or `@unstable`. |
1490 | | Unknown, |
1491 | | |
1492 | | /// `@unstable(feature = foo)` |
1493 | | /// |
1494 | | /// This item is explicitly tagged `@unstable`. A feature name is listed and |
1495 | | /// this item is excluded by default in `Resolve` unless explicitly enabled. |
1496 | | Unstable { |
1497 | | feature: String, |
1498 | | #[cfg_attr( |
1499 | | feature = "serde", |
1500 | | serde( |
1501 | | skip_serializing_if = "Option::is_none", |
1502 | | default, |
1503 | | serialize_with = "serialize_optional_version", |
1504 | | deserialize_with = "deserialize_optional_version" |
1505 | | ) |
1506 | | )] |
1507 | | deprecated: Option<Version>, |
1508 | | }, |
1509 | | |
1510 | | /// `@since(version = 1.2.3)` |
1511 | | /// |
1512 | | /// This item is explicitly tagged with `@since` as stable since the |
1513 | | /// specified version. This may optionally have a feature listed as well. |
1514 | | Stable { |
1515 | | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_version"))] |
1516 | | #[cfg_attr(feature = "serde", serde(deserialize_with = "deserialize_version"))] |
1517 | | since: Version, |
1518 | | #[cfg_attr( |
1519 | | feature = "serde", |
1520 | | serde( |
1521 | | skip_serializing_if = "Option::is_none", |
1522 | | default, |
1523 | | serialize_with = "serialize_optional_version", |
1524 | | deserialize_with = "deserialize_optional_version" |
1525 | | ) |
1526 | | )] |
1527 | | deprecated: Option<Version>, |
1528 | | }, |
1529 | | } |
1530 | | |
1531 | | impl Stability { |
1532 | | /// Returns whether this is `Stability::Unknown`. |
1533 | 151k | pub fn is_unknown(&self) -> bool { |
1534 | 151k | matches!(self, Stability::Unknown) |
1535 | 151k | } |
1536 | | |
1537 | 0 | pub fn is_stable(&self) -> bool { |
1538 | 0 | matches!(self, Stability::Stable { .. }) |
1539 | 0 | } |
1540 | | } |
1541 | | |
1542 | | impl Default for Stability { |
1543 | 288k | fn default() -> Stability { |
1544 | 288k | Stability::Unknown |
1545 | 288k | } |
1546 | | } |
1547 | | |
1548 | | #[cfg(test)] |
1549 | | mod test { |
1550 | | use super::*; |
1551 | | use alloc::vec; |
1552 | | |
1553 | | #[test] |
1554 | | fn test_discriminant_type() { |
1555 | | assert_eq!(discriminant_type(1), Int::U8); |
1556 | | assert_eq!(discriminant_type(0x100), Int::U8); |
1557 | | assert_eq!(discriminant_type(0x101), Int::U16); |
1558 | | assert_eq!(discriminant_type(0x10000), Int::U16); |
1559 | | assert_eq!(discriminant_type(0x10001), Int::U32); |
1560 | | if let Ok(num_cases) = usize::try_from(0x100000000_u64) { |
1561 | | assert_eq!(discriminant_type(num_cases), Int::U32); |
1562 | | } |
1563 | | } |
1564 | | |
1565 | | #[test] |
1566 | | fn test_find_futures_and_streams() { |
1567 | | let mut resolve = Resolve::default(); |
1568 | | let t0 = resolve.types.alloc(TypeDef { |
1569 | | name: None, |
1570 | | kind: TypeDefKind::Future(Some(Type::U32)), |
1571 | | owner: TypeOwner::None, |
1572 | | docs: Docs::default(), |
1573 | | stability: Stability::Unknown, |
1574 | | span: Default::default(), |
1575 | | }); |
1576 | | let t1 = resolve.types.alloc(TypeDef { |
1577 | | name: None, |
1578 | | kind: TypeDefKind::Future(Some(Type::Id(t0))), |
1579 | | owner: TypeOwner::None, |
1580 | | docs: Docs::default(), |
1581 | | stability: Stability::Unknown, |
1582 | | span: Default::default(), |
1583 | | }); |
1584 | | let t2 = resolve.types.alloc(TypeDef { |
1585 | | name: None, |
1586 | | kind: TypeDefKind::Stream(Some(Type::U32)), |
1587 | | owner: TypeOwner::None, |
1588 | | docs: Docs::default(), |
1589 | | stability: Stability::Unknown, |
1590 | | span: Default::default(), |
1591 | | }); |
1592 | | let found = Function { |
1593 | | name: "foo".into(), |
1594 | | kind: FunctionKind::Freestanding, |
1595 | | params: vec![ |
1596 | | Param { |
1597 | | name: "p1".into(), |
1598 | | ty: Type::Id(t1), |
1599 | | span: Default::default(), |
1600 | | }, |
1601 | | Param { |
1602 | | name: "p2".into(), |
1603 | | ty: Type::U32, |
1604 | | span: Default::default(), |
1605 | | }, |
1606 | | ], |
1607 | | result: Some(Type::Id(t2)), |
1608 | | docs: Docs::default(), |
1609 | | stability: Stability::Unknown, |
1610 | | span: Default::default(), |
1611 | | } |
1612 | | .find_futures_and_streams(&resolve); |
1613 | | assert_eq!(3, found.len()); |
1614 | | assert_eq!(t0, found[0]); |
1615 | | assert_eq!(t1, found[1]); |
1616 | | assert_eq!(t2, found[2]); |
1617 | | } |
1618 | | } |