Parse full file using the new Module parser

This commit is contained in:
David Tolnay 2020-08-29 14:07:38 -07:00
parent 17c3230d08
commit 3c64a4e144
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
7 changed files with 87 additions and 80 deletions

View File

@ -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),
}
@ -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),
}

View File

@ -1,20 +1,71 @@
use syn::parse::{Parse, ParseStream, Result};
use syn::{Attribute, Item};
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 attrs: Vec<Attribute>,
pub items: Vec<Item>,
pub modules: Vec<Module>,
}
impl Parse for File {
fn parse(input: ParseStream) -> Result<Self> {
let attrs = input.call(Attribute::parse_inner)?;
let mut items = Vec::new();
while !input.is_empty() {
items.push(input.parse()?);
}
Ok(File { attrs, items })
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 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;
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()
}
}

View File

@ -1,47 +0,0 @@
use crate::gen::{Error, File, Input, Result};
use crate::syntax::namespace::Namespace;
use syn::{Attribute, Item};
pub(super) fn find_bridge_mod(syntax: File) -> Result<Input> {
match scan(syntax.items)? {
Some(input) => Ok(input),
None => Err(Error::NoBridgeMod),
}
}
fn scan(items: Vec<Item>) -> Result<Option<Input>> {
for item in items {
if let Item::Mod(item) = item {
for attr in &item.attrs {
let path = &attr.path.segments;
if path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "bridge" {
let module = match item.content {
Some(module) => module.1,
None => {
return Err(Error::Syn(syn::Error::new_spanned(
item,
Error::OutOfLineMod,
)));
}
};
let namespace = parse_args(attr)?;
return Ok(Some(Input { namespace, module }));
}
}
if let Some(module) = item.content {
if let Some(input) = scan(module.1)? {
return Ok(Some(input));
}
}
}
}
Ok(None)
}
fn parse_args(attr: &Attribute) -> syn::Result<Namespace> {
if attr.tokens.is_empty() {
Ok(Namespace::none())
} else {
attr.parse_args()
}
}

View File

@ -3,7 +3,6 @@
mod error;
mod file;
mod find;
pub(super) mod include;
pub(super) mod out;
mod write;
@ -13,17 +12,10 @@ mod tests;
use self::error::{format_err, Error, Result};
use self::file::File;
use crate::syntax::namespace::Namespace;
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 {
@ -64,9 +56,13 @@ fn generate(source: &str, opt: Opt, header: bool) -> Result<Vec<u8>> {
proc_macro2::fallback::force();
let ref mut errors = Errors::new();
let syntax: File = syn::parse_str(source)?;
let bridge = find::find_bridge_mod(syntax)?;
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);

View File

@ -11,21 +11,23 @@ use quote::{format_ident, quote, quote_spanned, ToTokens};
use std::mem;
use syn::{parse_quote, Result, Token};
pub fn bridge(namespace: &Namespace, mut ffi: Module) -> Result<TokenStream> {
pub fn bridge(mut ffi: Module) -> Result<TokenStream> {
let ref mut errors = Errors::new();
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: Module, 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 {

View File

@ -40,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 Module);
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()
}

View File

@ -1,8 +1,10 @@
use proc_macro2::Span;
use crate::syntax::namespace::Namespace;
use quote::quote;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{braced, token, Attribute, Ident, Item, Token, Visibility};
pub struct Module {
pub namespace: Namespace,
pub attrs: Vec<Attribute>,
pub vis: Visibility,
// TODO: unsafety
@ -14,14 +16,17 @@ pub struct Module {
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 mod_token: Token![mod] = input.parse()?;
let ident: Ident = input.parse()?;
if input.peek(Token![;]) {
return Err(Error::new(
Span::call_site(),
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",
))?;
}
@ -36,6 +41,7 @@ impl Parse for Module {
}
Ok(Module {
namespace,
attrs,
vis,
mod_token,