Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse unsafe modules and unsafe extern blocks #270

Merged
merged 9 commits into from
Aug 29, 2020
2 changes: 0 additions & 2 deletions gen/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ pub(super) type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug)]
pub(super) enum Error {
NoBridgeMod,
OutOfLineMod,
Io(io::Error),
Syn(syn::Error),
}
Expand All @@ -25,7 +24,6 @@ impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::NoBridgeMod => write!(f, "no #[cxx::bridge] module found"),
Error::OutOfLineMod => write!(f, "#[cxx::bridge] module must have inline contents"),
Error::Io(err) => err.fmt(f),
Error::Syn(err) => err.fmt(f),
}
Expand Down
72 changes: 72 additions & 0 deletions gen/src/file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use crate::syntax::file::Module;
use crate::syntax::namespace::Namespace;
use syn::parse::discouraged::Speculative;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{braced, Attribute, Ident, Item, Token, Visibility};

pub struct File {
pub modules: Vec<Module>,
}

impl Parse for File {
fn parse(input: ParseStream) -> Result<Self> {
let mut modules = Vec::new();
input.call(Attribute::parse_inner)?;
parse(input, &mut modules)?;
Ok(File { modules })
}
}

fn parse(input: ParseStream, modules: &mut Vec<Module>) -> Result<()> {
while !input.is_empty() {
let mut cxx_bridge = false;
let mut namespace = Namespace::none();
let mut attrs = input.call(Attribute::parse_outer)?;
for attr in &attrs {
let path = &attr.path.segments;
if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" {
cxx_bridge = true;
namespace = parse_args(attr)?;
break;
}
}

let ahead = input.fork();
ahead.parse::<Visibility>()?;
ahead.parse::<Option<Token![unsafe]>>()?;
if !ahead.peek(Token![mod]) {
let item: Item = input.parse()?;
if cxx_bridge {
return Err(Error::new_spanned(item, "expected a module"));
}
continue;
}

if cxx_bridge {
let mut module: Module = input.parse()?;
module.namespace = namespace;
attrs.extend(module.attrs);
module.attrs = attrs;
modules.push(module);
} else {
input.advance_to(&ahead);
input.parse::<Token![mod]>()?;
input.parse::<Ident>()?;
let semi: Option<Token![;]> = input.parse()?;
if semi.is_none() {
let content;
braced!(content in input);
parse(&content, modules)?;
}
}
}
Ok(())
}

fn parse_args(attr: &Attribute) -> Result<Namespace> {
if attr.tokens.is_empty() {
Ok(Namespace::none())
} else {
attr.parse_args()
}
}
47 changes: 0 additions & 47 deletions gen/src/find.rs

This file was deleted.

29 changes: 16 additions & 13 deletions gen/src/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// the cxxbridge CLI command.

mod error;
mod find;
mod file;
pub(super) mod include;
pub(super) mod out;
mod write;
Expand All @@ -11,17 +11,11 @@ mod write;
mod tests;

use self::error::{format_err, Error, Result};
use crate::syntax::namespace::Namespace;
use self::file::File;
use crate::syntax::report::Errors;
use crate::syntax::{self, check, Types};
use std::fs;
use std::path::Path;
use syn::Item;

struct Input {
namespace: Namespace,
module: Vec<Item>,
}

#[derive(Default)]
pub(super) struct Opt {
Expand All @@ -47,19 +41,28 @@ fn generate_from_path(path: &Path, opt: Opt, header: bool) -> Vec<u8> {
Ok(source) => source,
Err(err) => format_err(path, "", Error::Io(err)),
};
match generate(&source, opt, header) {
let mut source = source.as_str();
if source.starts_with("#!") && !source.starts_with("#![") {
let shebang_end = source.find('\n').unwrap_or(source.len());
source = &source[shebang_end..];
}
match generate(source, opt, header) {
Ok(out) => out,
Err(err) => format_err(path, &source, err),
Err(err) => format_err(path, source, err),
}
}

fn generate(source: &str, opt: Opt, header: bool) -> Result<Vec<u8>> {
proc_macro2::fallback::force();
let ref mut errors = Errors::new();
let syntax = syn::parse_file(&source)?;
let bridge = find::find_bridge_mod(syntax)?;
let syntax: File = syn::parse_str(source)?;
let bridge = syntax
.modules
.into_iter()
.next()
.ok_or(Error::NoBridgeMod)?;
let ref namespace = bridge.namespace;
let ref apis = syntax::parse_items(errors, bridge.module);
let ref apis = syntax::parse_items(errors, bridge.content);
let ref types = Types::collect(errors, apis);
errors.propagate()?;
check::typecheck(errors, namespace, apis, types);
Expand Down
21 changes: 11 additions & 10 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,33 @@
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::file::Module;
use crate::syntax::namespace::Namespace;
use crate::syntax::report::Errors;
use crate::syntax::symbol::Symbol;
use crate::syntax::{
self, check, mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types,
};
use proc_macro2::{Ident, Span, TokenStream};
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote, quote_spanned, ToTokens};
use syn::{parse_quote, Error, ItemMod, Result, Token};
use std::mem;
use syn::{parse_quote, Result, Token};

pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result<TokenStream> {
pub fn bridge(mut ffi: Module) -> Result<TokenStream> {
let ref mut errors = Errors::new();
let content = ffi.content.take().ok_or(Error::new(
Span::call_site(),
"#[cxx::bridge] module must have inline contents",
))?;
let ref apis = syntax::parse_items(errors, content.1);
let content = mem::take(&mut ffi.content);
let ref apis = syntax::parse_items(errors, content);
let ref types = Types::collect(errors, apis);
errors.propagate()?;
let namespace = &ffi.namespace;
check::typecheck(errors, namespace, apis, types);
errors.propagate()?;

Ok(expand(namespace, ffi, apis, types))
Ok(expand(ffi, apis, types))
}

fn expand(namespace: &Namespace, ffi: ItemMod, apis: &[Api], types: &Types) -> TokenStream {
fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
let mut expanded = TokenStream::new();
let mut hidden = TokenStream::new();
let namespace = &ffi.namespace;

for api in apis {
if let Api::RustType(ety) = api {
Expand Down
8 changes: 5 additions & 3 deletions macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ mod expand;
mod syntax;
mod type_id;

use crate::syntax::file::Module;
use crate::syntax::namespace::Namespace;
use proc_macro::TokenStream;
use syn::{parse_macro_input, ItemMod, LitStr};
use syn::{parse_macro_input, LitStr};

/// `#[cxx::bridge] mod ffi { ... }`
///
Expand All @@ -39,9 +40,10 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream {
let _ = syntax::error::ERRORS;

let namespace = parse_macro_input!(args as Namespace);
let ffi = parse_macro_input!(input as ItemMod);
let mut ffi = parse_macro_input!(input as Module);
ffi.namespace = namespace;

expand::bridge(&namespace, ffi)
expand::bridge(ffi)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
Expand Down
106 changes: 106 additions & 0 deletions syntax/file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use crate::syntax::namespace::Namespace;
use quote::quote;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{
braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemStruct,
ItemUse, LitStr, Token, Visibility,
};

pub struct Module {
pub namespace: Namespace,
pub attrs: Vec<Attribute>,
pub vis: Visibility,
pub unsafety: Option<Token![unsafe]>,
pub mod_token: Token![mod],
pub ident: Ident,
pub brace_token: token::Brace,
pub content: Vec<Item>,
}

pub enum Item {
Struct(ItemStruct),
Enum(ItemEnum),
ForeignMod(ItemForeignMod),
Use(ItemUse),
Other(RustItem),
}

pub struct ItemForeignMod {
pub attrs: Vec<Attribute>,
pub unsafety: Option<Token![unsafe]>,
pub abi: Abi,
pub brace_token: token::Brace,
pub items: Vec<ForeignItem>,
}

impl Parse for Module {
fn parse(input: ParseStream) -> Result<Self> {
let namespace = Namespace::none();
let mut attrs = input.call(Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
let unsafety: Option<Token![unsafe]> = input.parse()?;
let mod_token: Token![mod] = input.parse()?;
let ident: Ident = input.parse()?;

let semi: Option<Token![;]> = input.parse()?;
if let Some(semi) = semi {
let span = quote!(#vis #mod_token #semi);
return Err(Error::new_spanned(
span,
"#[cxx::bridge] module must have inline contents",
))?;
}

let content;
let brace_token = braced!(content in input);
attrs.extend(content.call(Attribute::parse_inner)?);

let mut items = Vec::new();
while !content.is_empty() {
items.push(content.parse()?);
}

Ok(Module {
namespace,
attrs,
vis,
unsafety,
mod_token,
ident,
brace_token,
content: items,
})
}
}

impl Parse for Item {
fn parse(input: ParseStream) -> Result<Self> {
let attrs = input.call(Attribute::parse_outer)?;

let ahead = input.fork();
let unsafety = if ahead.parse::<Option<Token![unsafe]>>()?.is_some()
&& ahead.parse::<Option<Token![extern]>>()?.is_some()
&& ahead.parse::<Option<LitStr>>().is_ok()
&& ahead.peek(token::Brace)
{
Some(input.parse()?)
} else {
None
};

let item = input.parse()?;
match item {
RustItem::Struct(item) => Ok(Item::Struct(ItemStruct { attrs, ..item })),
RustItem::Enum(item) => Ok(Item::Enum(ItemEnum { attrs, ..item })),
RustItem::ForeignMod(item) => Ok(Item::ForeignMod(ItemForeignMod {
attrs: item.attrs,
unsafety,
abi: item.abi,
brace_token: item.brace_token,
items: item.items,
})),
RustItem::Use(item) => Ok(Item::Use(ItemUse { attrs, ..item })),
other => Ok(Item::Other(other)),
}
}
}
1 change: 1 addition & 0 deletions syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod derive;
mod discriminant;
mod doc;
pub mod error;
pub mod file;
pub mod ident;
mod impls;
pub mod mangle;
Expand Down
Loading