Path: blob/main/crates/wiggle/generate/src/names.rs
1693 views
use escaping::{NamingConvention, escape_id, handle_2big_enum_variant};1use heck::{ToShoutySnakeCase, ToSnakeCase};2use proc_macro2::{Ident, TokenStream};3use quote::{format_ident, quote};4use witx::{BuiltinType, Id, Type, TypeRef, WasmType};56use crate::UserErrorType;78pub fn type_(id: &Id) -> Ident {9escape_id(id, NamingConvention::CamelCase)10}1112pub fn builtin_type(b: BuiltinType) -> TokenStream {13match b {14BuiltinType::U8 { .. } => quote!(u8),15BuiltinType::U16 => quote!(u16),16BuiltinType::U32 { .. } => quote!(u32),17BuiltinType::U64 => quote!(u64),18BuiltinType::S8 => quote!(i8),19BuiltinType::S16 => quote!(i16),20BuiltinType::S32 => quote!(i32),21BuiltinType::S64 => quote!(i64),22BuiltinType::F32 => quote!(f32),23BuiltinType::F64 => quote!(f64),24BuiltinType::Char => quote!(char),25}26}2728pub fn wasm_type(ty: WasmType) -> TokenStream {29match ty {30WasmType::I32 => quote!(i32),31WasmType::I64 => quote!(i64),32WasmType::F32 => quote!(f32),33WasmType::F64 => quote!(f64),34}35}3637pub fn type_ref(tref: &TypeRef, lifetime: TokenStream) -> TokenStream {38match tref {39TypeRef::Name(nt) => {40let ident = type_(&nt.name);41quote!(#ident)42}43TypeRef::Value(ty) => match &**ty {44Type::Builtin(builtin) => builtin_type(*builtin),45Type::Pointer(pointee) | Type::ConstPointer(pointee) => {46let pointee_type = type_ref(&pointee, lifetime.clone());47quote!(wiggle::GuestPtr<#pointee_type>)48}49Type::List(pointee) => match &**pointee.type_() {50Type::Builtin(BuiltinType::Char) => {51quote!(wiggle::GuestPtr<str>)52}53_ => {54let pointee_type = type_ref(&pointee, lifetime.clone());55quote!(wiggle::GuestPtr<[#pointee_type]>)56}57},58Type::Variant(v) => match v.as_expected() {59Some((ok, err)) => {60let ok = match ok {61Some(ty) => type_ref(ty, lifetime.clone()),62None => quote!(()),63};64let err = match err {65Some(ty) => type_ref(ty, lifetime.clone()),66None => quote!(()),67};68quote!(Result<#ok, #err>)69}70None => unimplemented!("anonymous variant ref {:?}", tref),71},72Type::Record(r) if r.is_tuple() => {73let types = r74.members75.iter()76.map(|m| type_ref(&m.tref, lifetime.clone()))77.collect::<Vec<_>>();78quote!((#(#types,)*))79}80_ => unimplemented!("anonymous type ref {:?}", tref),81},82}83}8485/// Convert an enum variant from its [`Id`][witx] name to its Rust [`Ident`][id] representation.86///87/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html88/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html89pub fn enum_variant(id: &Id) -> Ident {90handle_2big_enum_variant(id).unwrap_or_else(|| escape_id(id, NamingConvention::CamelCase))91}9293pub fn flag_member(id: &Id) -> Ident {94format_ident!("{}", id.as_str().to_shouty_snake_case())95}9697pub fn int_member(id: &Id) -> Ident {98format_ident!("{}", id.as_str().to_shouty_snake_case())99}100101/// Convert a struct member from its [`Id`][witx] name to its Rust [`Ident`][id] representation.102///103/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html104/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html105pub fn struct_member(id: &Id) -> Ident {106escape_id(id, NamingConvention::SnakeCase)107}108109/// Convert a module name from its [`Id`][witx] name to its Rust [`Ident`][id] representation.110///111/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html112/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html113pub fn module(id: &Id) -> Ident {114escape_id(id, NamingConvention::SnakeCase)115}116117/// Convert a trait name from its [`Id`][witx] name to its Rust [`Ident`][id] representation.118///119/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html120/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html121pub fn trait_name(id: &Id) -> Ident {122escape_id(id, NamingConvention::CamelCase)123}124125/// Convert a function name from its [`Id`][witx] name to its Rust [`Ident`][id] representation.126///127/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html128/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html129pub fn func(id: &Id) -> Ident {130escape_id(id, NamingConvention::SnakeCase)131}132133/// Convert a parameter name from its [`Id`][witx] name to its Rust [`Ident`][id] representation.134///135/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html136/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html137pub fn func_param(id: &Id) -> Ident {138escape_id(id, NamingConvention::SnakeCase)139}140141/// For when you need a {name}_ptr binding for passing a value by reference:142pub fn func_ptr_binding(id: &Id) -> Ident {143format_ident!("{}_ptr", id.as_str().to_snake_case())144}145146/// For when you need a {name}_len binding for passing an array:147pub fn func_len_binding(id: &Id) -> Ident {148format_ident!("{}_len", id.as_str().to_snake_case())149}150151fn builtin_name(b: &BuiltinType) -> &'static str {152match b {153BuiltinType::U8 { .. } => "u8",154BuiltinType::U16 => "u16",155BuiltinType::U32 { .. } => "u32",156BuiltinType::U64 => "u64",157BuiltinType::S8 => "i8",158BuiltinType::S16 => "i16",159BuiltinType::S32 => "i32",160BuiltinType::S64 => "i64",161BuiltinType::F32 => "f32",162BuiltinType::F64 => "f64",163BuiltinType::Char => "char",164}165}166167fn snake_typename(tref: &TypeRef) -> String {168match tref {169TypeRef::Name(nt) => nt.name.as_str().to_snake_case(),170TypeRef::Value(ty) => match &**ty {171Type::Builtin(b) => builtin_name(&b).to_owned(),172_ => panic!("unexpected anonymous type: {ty:?}"),173},174}175}176177pub fn user_error_conversion_method(user_type: &UserErrorType) -> Ident {178let abi_type = snake_typename(&user_type.abi_type());179format_ident!(180"{}_from_{}",181abi_type,182user_type.method_fragment().to_snake_case()183)184}185186/// Identifier escaping utilities.187///188/// This module most importantly exports an `escape_id` function that can be used to properly189/// escape tokens that conflict with strict and reserved keywords, as of Rust's 2018 edition.190///191/// Weak keywords are not included as their semantic rules do not have the same implications as192/// those of strict and reserved keywords. `union` for example, is permitted as the name of a193/// variable. `dyn` was promoted to a strict keyword beginning in the 2018 edition.194mod escaping {195use {196heck::{ToSnakeCase, ToUpperCamelCase},197proc_macro2::Ident,198quote::format_ident,199witx::Id,200};201202/// Identifier naming convention.203///204/// Because shouty snake case values (identifiers that look `LIKE_THIS`) cannot potentially205/// conflict with any Rust keywords, this enum only include snake and camel case variants.206pub enum NamingConvention {207/// Snake case. Used to denote values `LikeThis`.208CamelCase,209/// Snake case. Used to denote values `like_this`.210SnakeCase,211}212213/// Given a witx [`Id`][witx] and a [`NamingConvention`][naming], return a [`Ident`] word of214/// Rust syntax that accounts for escaping both strict and reserved keywords. If an identifier215/// would have conflicted with a keyword, a trailing underscode will be appended.216///217/// [id]: https://docs.rs/proc-macro2/*/proc_macro2/struct.Ident.html218/// [naming]: enum.NamingConvention.html219/// [witx]: https://docs.rs/witx/*/witx/struct.Id.html220pub fn escape_id(id: &Id, conv: NamingConvention) -> Ident {221use NamingConvention::{CamelCase, SnakeCase};222match (conv, id.as_str()) {223// For camel-cased identifiers, `Self` is the only potential keyword conflict.224(CamelCase, "self") => format_ident!("Self_"),225(CamelCase, s) => format_ident!("{}", s.to_upper_camel_case()),226// Snake-cased identifiers are where the bulk of conflicts can occur.227(SnakeCase, s) => {228let s = s.to_snake_case();229if STRICT.iter().chain(RESERVED).any(|k| *k == s) {230// If the camel-cased string matched any strict or reserved keywords, then231// append a trailing underscore to the identifier we generate.232format_ident!("{}_", s)233} else {234format_ident!("{}", s) // Otherwise, use the string as is.235}236}237}238}239240/// Strict keywords.241///242/// > Strict keywords cannot be used as the names of:243/// > * Items244/// > * Variables and function parameters245/// > * Fields and variants246/// > * Type parameters247/// > * Lifetime parameters or loop labels248/// > * Macros or attributes249/// > * Macro placeholders250/// > * Crates251/// >252/// > - <cite>[The Rust Reference][ref]</cite>253///254/// This list also includes keywords that were introduced in the 2018 edition of Rust.255///256/// [ref]: https://doc.rust-lang.org/reference/keywords.html#strict-keywords257const STRICT: &[&str] = &[258"as", "async", "await", "break", "const", "continue", "crate", "dyn", "else", "enum",259"extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move",260"mut", "pub", "ref", "return", "self", "Self", "static", "struct", "super", "trait",261"true", "type", "unsafe", "use", "where", "while",262];263264/// Reserved keywords.265///266/// > These keywords aren't used yet, but they are reserved for future use. They have the same267/// > restrictions as strict keywords. The reasoning behind this is to make current programs268/// > forward compatible with future versions of Rust by forbidding them to use these keywords.269/// >270/// > - <cite>[The Rust Reference][ref]</cite>271///272/// This list also includes keywords that were introduced in the 2018 edition of Rust.273///274/// [ref]: https://doc.rust-lang.org/reference/keywords.html#reserved-keywords275const RESERVED: &[&str] = &[276"abstract", "become", "box", "do", "final", "macro", "override", "priv", "try", "typeof",277"unsized", "virtual", "yield",278];279280/// Handle WASI's [`errno::2big`][err] variant.281///282/// This is an unfortunate edge case that must account for when generating `enum` variants.283/// This will only return `Some(_)` if the given witx identifier *is* `2big`, otherwise this284/// function will return `None`.285///286/// This functionality is a short-term fix that keeps WASI working. Instead of expanding these sort of special cases,287/// we should replace this function by having the user provide a mapping of witx identifiers to Rust identifiers in the288/// arguments to the macro.289///290/// [err]: https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md#-errno-enumu16291pub fn handle_2big_enum_variant(id: &Id) -> Option<Ident> {292if id.as_str() == "2big" {293Some(format_ident!("TooBig"))294} else {295None296}297}298}299300301