wiggle: escape rust keywords, allow witx literals
# Overview
This commit makes changes to the `wiggle::from_witx` procedural in order
to allow for escaping strict and reserved Rust keywords.
Additionally, this commit introduces the ability to use a `witx_literal`
field in the `{..}` object provided as an argument to
`wiggle::from_witx`. This field allows for witx documents to be provided
as inline string literals.
Documentation comments are added to the methods of
`wiggle_generate::names::Names` struct responsible for generating
`proc_macro2::Ident` words.
## Keyword Escaping
Today, an interface that includes witx identifiers that conflict with
with Rust syntax will cause the `from_witx` macro to panic at
compilation time.
Here is a small example (adapted from
`/crates/wiggle/tests/keywords.rs`) that demonstrates this issue:
```
;; Attempts to define a module `self`, containing a trait `Self`. Both
;; of these are reserved keywords, and will thus cause a compilation
;; error.
(module $self
(@interface func (export "betchya_cant_implement_this")
)
)
```
Building off of code that (as of `master` today)
[demonstrates a strategy][esc] for escaping keywords, we introduce an
internal `escaping` module to `generate/src/config.rs` that contains
code responsible for escaping Rust keywords in a generalized manner.
[esc]: 0dd77d36f8/crates/wiggle/generate/src/names.rs (L106)
Some code related to special cases, such as accounting for
[`errno::2big`][err] while generating names for enum variants, is moved
into this module as well.
[err]: https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md#-errno-enumu16
As mentioned in the document comments of this diff, we do not include
weak keywords like `'static` or `union`. Their semantics do not impact
us in the same way from a code generation perspective.
## witx_literal
First, some background. Trait names, type names, and so on use a
camel-cased naming convention. As such, `Self` is the only keyword that
can potentially conflict with these identifiers. (See the [Rust
Reference][key] for a complete list of strict, reserved, and weak
keywords.)
When writing tests, this meant that many tests had to be outlined into
separate files, as items with the name `$self` could not be defined in
the same namespace. As such, it seemed like a worthwhile feature to
implement while the above work was being developed.
The most important function to note is the `load_document` inherent
method added to `WitxConf`, and that `WitxConf` is now an enum
containing either (a) a collection of paths, identical to its current
functionality, or (b) a single string literal.
Note that a witx document given to `from_witx` using a string literal
provided to `from_witx` cannot include `use (..)` directives, per
the `witx::parse` documentation.
(See: https://docs.rs/witx/0.8.5/witx/fn.parse.html)
Two newtypes, `Paths` and `Literal`, are introduced to facilitate the
parsing of `WitxConf` values. Their public API and trait implementations
has been kept to the minimum required to satisfy compilation in order to
limit the scope of this diff. Additional surface for external consumers
can be added in follow-up commits if deemed necessary in review.
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use proc_macro2::Span;
|
||||
use syn::{
|
||||
braced, bracketed,
|
||||
parse::{Parse, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
Error, Ident, LitStr, Result, Token,
|
||||
use {
|
||||
proc_macro2::Span,
|
||||
std::{
|
||||
iter::FromIterator,
|
||||
path::{Path, PathBuf},
|
||||
},
|
||||
syn::{
|
||||
braced, bracketed,
|
||||
parse::{Parse, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
Error, Ident, LitStr, Result, Token,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -23,7 +27,8 @@ pub enum ConfigField {
|
||||
impl ConfigField {
|
||||
pub fn parse_pair(ident: &str, value: ParseStream, err_loc: Span) -> Result<Self> {
|
||||
match ident {
|
||||
"witx" => Ok(ConfigField::Witx(value.parse()?)),
|
||||
"witx" => Ok(ConfigField::Witx(WitxConf::Paths(value.parse()?))),
|
||||
"witx_literal" => Ok(ConfigField::Witx(WitxConf::Literal(value.parse()?))),
|
||||
"ctx" => Ok(ConfigField::Ctx(value.parse()?)),
|
||||
_ => Err(Error::new(err_loc, "expected `witx` or `ctx`")),
|
||||
}
|
||||
@@ -61,6 +66,15 @@ impl Config {
|
||||
.ok_or_else(|| Error::new(err_loc, "`ctx` field required"))?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Load the `witx` document for the configuration.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if the paths given in the `witx` field were not valid documents.
|
||||
pub fn load_document(&self) -> witx::Document {
|
||||
self.witx.load_document()
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Config {
|
||||
@@ -73,31 +87,110 @@ impl Parse for Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// The witx document(s) that will be loaded from a [`Config`](struct.Config.html).
|
||||
///
|
||||
/// A witx interface definition can be provided either as a collection of relative paths to
|
||||
/// documents, or as a single inlined string literal. Note that `(use ...)` directives are not
|
||||
/// permitted when providing a string literal.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WitxConf {
|
||||
pub paths: Vec<PathBuf>,
|
||||
pub enum WitxConf {
|
||||
/// A collection of paths pointing to witx files.
|
||||
Paths(Paths),
|
||||
/// A single witx document, provided as a string literal.
|
||||
Literal(Literal),
|
||||
}
|
||||
|
||||
impl WitxConf {
|
||||
/// Load the `witx` document.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if the paths given in the `witx` field were not valid documents, or
|
||||
/// if any of the given documents were not syntactically valid.
|
||||
pub fn load_document(&self) -> witx::Document {
|
||||
match self {
|
||||
Self::Paths(paths) => witx::load(paths.as_ref()).expect("loading witx"),
|
||||
Self::Literal(doc) => witx::parse(doc.as_ref()).expect("parsing witx"),
|
||||
}
|
||||
}
|
||||
|
||||
/// If using the [`Paths`][paths] syntax, make all paths relative to a root directory.
|
||||
///
|
||||
/// [paths]: enum.WitxConf.html#variant.Paths
|
||||
pub fn make_paths_relative_to<P: AsRef<Path>>(&mut self, root: P) {
|
||||
self.paths.iter_mut().for_each(|p| {
|
||||
if !p.is_absolute() {
|
||||
*p = PathBuf::from(root.as_ref()).join(p.clone());
|
||||
}
|
||||
});
|
||||
if let Self::Paths(paths) = self {
|
||||
paths.as_mut().iter_mut().for_each(|p| {
|
||||
if !p.is_absolute() {
|
||||
*p = PathBuf::from(root.as_ref()).join(p.clone());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for WitxConf {
|
||||
/// A collection of paths, pointing to witx documents.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Paths(Vec<PathBuf>);
|
||||
|
||||
impl Paths {
|
||||
/// Create a new, empty collection of paths.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Paths {
|
||||
fn default() -> Self {
|
||||
Self(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[PathBuf]> for Paths {
|
||||
fn as_ref(&self) -> &[PathBuf] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<[PathBuf]> for Paths {
|
||||
fn as_mut(&mut self) -> &mut [PathBuf] {
|
||||
self.0.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<PathBuf> for Paths {
|
||||
fn from_iter<I>(iter: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = PathBuf>,
|
||||
{
|
||||
Self(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Paths {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let content;
|
||||
let _ = bracketed!(content in input);
|
||||
let path_lits: Punctuated<LitStr, Token![,]> = content.parse_terminated(Parse::parse)?;
|
||||
let paths = path_lits
|
||||
Ok(path_lits
|
||||
.iter()
|
||||
.map(|lit| PathBuf::from(lit.value()))
|
||||
.collect();
|
||||
Ok(WitxConf { paths })
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
/// A single witx document, provided as a string literal.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Literal(String);
|
||||
|
||||
impl AsRef<str> for Literal {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Literal {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
Ok(Self(input.parse::<syn::LitStr>()?.value()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user