Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wiggle/generate/src/names.rs
1693 views
1
use escaping::{NamingConvention, escape_id, handle_2big_enum_variant};
2
use heck::{ToShoutySnakeCase, ToSnakeCase};
3
use proc_macro2::{Ident, TokenStream};
4
use quote::{format_ident, quote};
5
use witx::{BuiltinType, Id, Type, TypeRef, WasmType};
6
7
use crate::UserErrorType;
8
9
pub fn type_(id: &Id) -> Ident {
10
escape_id(id, NamingConvention::CamelCase)
11
}
12
13
pub fn builtin_type(b: BuiltinType) -> TokenStream {
14
match b {
15
BuiltinType::U8 { .. } => quote!(u8),
16
BuiltinType::U16 => quote!(u16),
17
BuiltinType::U32 { .. } => quote!(u32),
18
BuiltinType::U64 => quote!(u64),
19
BuiltinType::S8 => quote!(i8),
20
BuiltinType::S16 => quote!(i16),
21
BuiltinType::S32 => quote!(i32),
22
BuiltinType::S64 => quote!(i64),
23
BuiltinType::F32 => quote!(f32),
24
BuiltinType::F64 => quote!(f64),
25
BuiltinType::Char => quote!(char),
26
}
27
}
28
29
pub fn wasm_type(ty: WasmType) -> TokenStream {
30
match ty {
31
WasmType::I32 => quote!(i32),
32
WasmType::I64 => quote!(i64),
33
WasmType::F32 => quote!(f32),
34
WasmType::F64 => quote!(f64),
35
}
36
}
37
38
pub fn type_ref(tref: &TypeRef, lifetime: TokenStream) -> TokenStream {
39
match tref {
40
TypeRef::Name(nt) => {
41
let ident = type_(&nt.name);
42
quote!(#ident)
43
}
44
TypeRef::Value(ty) => match &**ty {
45
Type::Builtin(builtin) => builtin_type(*builtin),
46
Type::Pointer(pointee) | Type::ConstPointer(pointee) => {
47
let pointee_type = type_ref(&pointee, lifetime.clone());
48
quote!(wiggle::GuestPtr<#pointee_type>)
49
}
50
Type::List(pointee) => match &**pointee.type_() {
51
Type::Builtin(BuiltinType::Char) => {
52
quote!(wiggle::GuestPtr<str>)
53
}
54
_ => {
55
let pointee_type = type_ref(&pointee, lifetime.clone());
56
quote!(wiggle::GuestPtr<[#pointee_type]>)
57
}
58
},
59
Type::Variant(v) => match v.as_expected() {
60
Some((ok, err)) => {
61
let ok = match ok {
62
Some(ty) => type_ref(ty, lifetime.clone()),
63
None => quote!(()),
64
};
65
let err = match err {
66
Some(ty) => type_ref(ty, lifetime.clone()),
67
None => quote!(()),
68
};
69
quote!(Result<#ok, #err>)
70
}
71
None => unimplemented!("anonymous variant ref {:?}", tref),
72
},
73
Type::Record(r) if r.is_tuple() => {
74
let types = r
75
.members
76
.iter()
77
.map(|m| type_ref(&m.tref, lifetime.clone()))
78
.collect::<Vec<_>>();
79
quote!((#(#types,)*))
80
}
81
_ => unimplemented!("anonymous type ref {:?}", tref),
82
},
83
}
84
}
85
86
/// Convert an enum variant from its [`Id`][witx] name to its Rust [`Ident`][id] representation.
87
///
88
/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html
89
/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html
90
pub fn enum_variant(id: &Id) -> Ident {
91
handle_2big_enum_variant(id).unwrap_or_else(|| escape_id(id, NamingConvention::CamelCase))
92
}
93
94
pub fn flag_member(id: &Id) -> Ident {
95
format_ident!("{}", id.as_str().to_shouty_snake_case())
96
}
97
98
pub fn int_member(id: &Id) -> Ident {
99
format_ident!("{}", id.as_str().to_shouty_snake_case())
100
}
101
102
/// Convert a struct member from its [`Id`][witx] name to its Rust [`Ident`][id] representation.
103
///
104
/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html
105
/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html
106
pub fn struct_member(id: &Id) -> Ident {
107
escape_id(id, NamingConvention::SnakeCase)
108
}
109
110
/// Convert a module name from its [`Id`][witx] name to its Rust [`Ident`][id] representation.
111
///
112
/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html
113
/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html
114
pub fn module(id: &Id) -> Ident {
115
escape_id(id, NamingConvention::SnakeCase)
116
}
117
118
/// Convert a trait name from its [`Id`][witx] name to its Rust [`Ident`][id] representation.
119
///
120
/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html
121
/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html
122
pub fn trait_name(id: &Id) -> Ident {
123
escape_id(id, NamingConvention::CamelCase)
124
}
125
126
/// Convert a function name from its [`Id`][witx] name to its Rust [`Ident`][id] representation.
127
///
128
/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html
129
/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html
130
pub fn func(id: &Id) -> Ident {
131
escape_id(id, NamingConvention::SnakeCase)
132
}
133
134
/// Convert a parameter name from its [`Id`][witx] name to its Rust [`Ident`][id] representation.
135
///
136
/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html
137
/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html
138
pub fn func_param(id: &Id) -> Ident {
139
escape_id(id, NamingConvention::SnakeCase)
140
}
141
142
/// For when you need a {name}_ptr binding for passing a value by reference:
143
pub fn func_ptr_binding(id: &Id) -> Ident {
144
format_ident!("{}_ptr", id.as_str().to_snake_case())
145
}
146
147
/// For when you need a {name}_len binding for passing an array:
148
pub fn func_len_binding(id: &Id) -> Ident {
149
format_ident!("{}_len", id.as_str().to_snake_case())
150
}
151
152
fn builtin_name(b: &BuiltinType) -> &'static str {
153
match b {
154
BuiltinType::U8 { .. } => "u8",
155
BuiltinType::U16 => "u16",
156
BuiltinType::U32 { .. } => "u32",
157
BuiltinType::U64 => "u64",
158
BuiltinType::S8 => "i8",
159
BuiltinType::S16 => "i16",
160
BuiltinType::S32 => "i32",
161
BuiltinType::S64 => "i64",
162
BuiltinType::F32 => "f32",
163
BuiltinType::F64 => "f64",
164
BuiltinType::Char => "char",
165
}
166
}
167
168
fn snake_typename(tref: &TypeRef) -> String {
169
match tref {
170
TypeRef::Name(nt) => nt.name.as_str().to_snake_case(),
171
TypeRef::Value(ty) => match &**ty {
172
Type::Builtin(b) => builtin_name(&b).to_owned(),
173
_ => panic!("unexpected anonymous type: {ty:?}"),
174
},
175
}
176
}
177
178
pub fn user_error_conversion_method(user_type: &UserErrorType) -> Ident {
179
let abi_type = snake_typename(&user_type.abi_type());
180
format_ident!(
181
"{}_from_{}",
182
abi_type,
183
user_type.method_fragment().to_snake_case()
184
)
185
}
186
187
/// Identifier escaping utilities.
188
///
189
/// This module most importantly exports an `escape_id` function that can be used to properly
190
/// escape tokens that conflict with strict and reserved keywords, as of Rust's 2018 edition.
191
///
192
/// Weak keywords are not included as their semantic rules do not have the same implications as
193
/// those of strict and reserved keywords. `union` for example, is permitted as the name of a
194
/// variable. `dyn` was promoted to a strict keyword beginning in the 2018 edition.
195
mod escaping {
196
use {
197
heck::{ToSnakeCase, ToUpperCamelCase},
198
proc_macro2::Ident,
199
quote::format_ident,
200
witx::Id,
201
};
202
203
/// Identifier naming convention.
204
///
205
/// Because shouty snake case values (identifiers that look `LIKE_THIS`) cannot potentially
206
/// conflict with any Rust keywords, this enum only include snake and camel case variants.
207
pub enum NamingConvention {
208
/// Snake case. Used to denote values `LikeThis`.
209
CamelCase,
210
/// Snake case. Used to denote values `like_this`.
211
SnakeCase,
212
}
213
214
/// Given a witx [`Id`][witx] and a [`NamingConvention`][naming], return a [`Ident`] word of
215
/// Rust syntax that accounts for escaping both strict and reserved keywords. If an identifier
216
/// would have conflicted with a keyword, a trailing underscode will be appended.
217
///
218
/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html
219
/// [naming]: enum.NamingConvention.html
220
/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html
221
pub fn escape_id(id: &Id, conv: NamingConvention) -> Ident {
222
use NamingConvention::{CamelCase, SnakeCase};
223
match (conv, id.as_str()) {
224
// For camel-cased identifiers, `Self` is the only potential keyword conflict.
225
(CamelCase, "self") => format_ident!("Self_"),
226
(CamelCase, s) => format_ident!("{}", s.to_upper_camel_case()),
227
// Snake-cased identifiers are where the bulk of conflicts can occur.
228
(SnakeCase, s) => {
229
let s = s.to_snake_case();
230
if STRICT.iter().chain(RESERVED).any(|k| *k == s) {
231
// If the camel-cased string matched any strict or reserved keywords, then
232
// append a trailing underscore to the identifier we generate.
233
format_ident!("{}_", s)
234
} else {
235
format_ident!("{}", s) // Otherwise, use the string as is.
236
}
237
}
238
}
239
}
240
241
/// Strict keywords.
242
///
243
/// > Strict keywords cannot be used as the names of:
244
/// > * Items
245
/// > * Variables and function parameters
246
/// > * Fields and variants
247
/// > * Type parameters
248
/// > * Lifetime parameters or loop labels
249
/// > * Macros or attributes
250
/// > * Macro placeholders
251
/// > * Crates
252
/// >
253
/// > - <cite>[The Rust Reference][ref]</cite>
254
///
255
/// This list also includes keywords that were introduced in the 2018 edition of Rust.
256
///
257
/// [ref]: https://doc.rust-lang.org/reference/keywords.html#strict-keywords
258
const STRICT: &[&str] = &[
259
"as", "async", "await", "break", "const", "continue", "crate", "dyn", "else", "enum",
260
"extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move",
261
"mut", "pub", "ref", "return", "self", "Self", "static", "struct", "super", "trait",
262
"true", "type", "unsafe", "use", "where", "while",
263
];
264
265
/// Reserved keywords.
266
///
267
/// > These keywords aren't used yet, but they are reserved for future use. They have the same
268
/// > restrictions as strict keywords. The reasoning behind this is to make current programs
269
/// > forward compatible with future versions of Rust by forbidding them to use these keywords.
270
/// >
271
/// > - <cite>[The Rust Reference][ref]</cite>
272
///
273
/// This list also includes keywords that were introduced in the 2018 edition of Rust.
274
///
275
/// [ref]: https://doc.rust-lang.org/reference/keywords.html#reserved-keywords
276
const RESERVED: &[&str] = &[
277
"abstract", "become", "box", "do", "final", "macro", "override", "priv", "try", "typeof",
278
"unsized", "virtual", "yield",
279
];
280
281
/// Handle WASI's [`errno::2big`][err] variant.
282
///
283
/// This is an unfortunate edge case that must account for when generating `enum` variants.
284
/// This will only return `Some(_)` if the given witx identifier *is* `2big`, otherwise this
285
/// function will return `None`.
286
///
287
/// This functionality is a short-term fix that keeps WASI working. Instead of expanding these sort of special cases,
288
/// we should replace this function by having the user provide a mapping of witx identifiers to Rust identifiers in the
289
/// arguments to the macro.
290
///
291
/// [err]: https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md#-errno-enumu16
292
pub fn handle_2big_enum_variant(id: &Id) -> Option<Ident> {
293
if id.as_str() == "2big" {
294
Some(format_ident!("TooBig"))
295
} else {
296
None
297
}
298
}
299
}
300
301