Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions gen/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ 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)?;
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" {
Expand All @@ -45,8 +45,7 @@ fn parse(input: ParseStream, modules: &mut Vec<Module>) -> Result<()> {
if cxx_bridge {
let mut module: Module = input.parse()?;
module.namespace = namespace;
attrs.extend(module.attrs);
module.attrs = attrs;
module.add_attributes(&attrs)?;
modules.push(module);
} else {
input.advance_to(&ahead);
Expand All @@ -67,6 +66,6 @@ fn parse_args(attr: &Attribute) -> Result<Namespace> {
if attr.tokens.is_empty() {
Ok(Namespace::none())
} else {
attr.parse_args()
attr.parse_args_with(Namespace::parse_bridge_attr_namespace)
}
}
10 changes: 6 additions & 4 deletions gen/src/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use self::error::{format_err, Result};
use self::file::File;
use crate::syntax::report::Errors;
use crate::syntax::{self, check, Types};
use std::mem;
use std::path::Path;

/// Options for C++ code generation.
Expand Down Expand Up @@ -104,14 +105,15 @@ fn generate_from_string(source: &str, opt: &Opt) -> Result<GeneratedCode> {
pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
proc_macro2::fallback::force();
let ref mut errors = Errors::new();
let bridge = syntax
let mut bridge = syntax
.modules
.into_iter()
.next()
.ok_or(Error::NoBridgeMod)?;
let ref namespace = bridge.namespace;
let content = mem::take(&mut bridge.content);
let trusted = bridge.unsafety.is_some();
let ref apis = syntax::parse_items(errors, bridge.content, trusted);
let ref apis = syntax::parse_items(errors, content, trusted);
let ref types = Types::collect(errors, apis);
errors.propagate()?;
check::typecheck(errors, namespace, apis, types);
Expand All @@ -121,12 +123,12 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result<GeneratedCode> {
// only need to generate one or the other.
Ok(GeneratedCode {
header: if opt.gen_header {
write::gen(namespace, apis, types, opt, true).content()
write::gen(&bridge, apis, types, opt, true).content()
} else {
Vec::new()
},
implementation: if opt.gen_implementation {
write::gen(namespace, apis, types, opt, false).content()
write::gen(&bridge, apis, types, opt, false).content()
} else {
Vec::new()
},
Expand Down
17 changes: 15 additions & 2 deletions gen/src/write.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
use crate::gen::out::OutFile;
use crate::gen::{include, Opt};
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::file::Module;
use crate::syntax::namespace::Namespace;
use crate::syntax::symbol::Symbol;
use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var};
use crate::syntax::{
mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types, Var,
};
use proc_macro2::Ident;
use std::collections::HashMap;

pub(super) fn gen(
namespace: &Namespace,
bridge: &Module,
apis: &[Api],
types: &Types,
opt: &Opt,
header: bool,
) -> OutFile {
let namespace = &bridge.namespace;
let mut out_file = OutFile::new(namespace.clone(), header);
let out = &mut out_file;

Expand Down Expand Up @@ -42,6 +46,7 @@ pub(super) fn gen(
Api::Struct(strct) => write_struct_decl(out, &strct.ident),
Api::CxxType(ety) => write_struct_using(out, &ety.ident),
Api::RustType(ety) => write_struct_decl(out, &ety.ident),
Api::TypeAlias(alias) => write_alias(out, bridge, alias),
_ => {}
}
}
Expand Down Expand Up @@ -931,6 +936,14 @@ fn write_type(out: &mut OutFile, ty: &Type) {
}
}

fn write_alias(out: &mut OutFile, bridge: &Module, alias: &TypeAlias) {
let namespace = bridge.namespace_for_alias(alias);
if namespace != &bridge.namespace {
let path = namespace.path_for_type(Some(&alias.ty_ident));
writeln!(out, "using {} = {};", alias.ident, path)
}
}

fn write_atom(out: &mut OutFile, atom: Atom) {
match atom {
Bool => write!(out, "bool"),
Expand Down
24 changes: 9 additions & 15 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
}
Api::TypeAlias(alias) => {
expanded.extend(expand_type_alias(alias));
hidden.extend(expand_type_alias_verify(namespace, alias));
hidden.extend(expand_type_alias_verify(&ffi, alias));
}
}
}
Expand Down Expand Up @@ -102,15 +102,12 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream {
});
}

let attrs = ffi
.attrs
.into_iter()
.filter(|attr| attr.path.is_ident("doc"));
let doc = &ffi.doc;
let vis = &ffi.vis;
let ident = &ffi.ident;

quote! {
#(#attrs)*
#doc
#[deny(improper_ctypes)]
#[allow(non_snake_case)]
#vis mod #ident {
Expand Down Expand Up @@ -657,16 +654,19 @@ fn expand_rust_function_shim_impl(
}

fn expand_type_alias(alias: &TypeAlias) -> TokenStream {
let doc = &alias.doc;
let ident = &alias.ident;
let ty = &alias.ty;
quote! {
#doc
pub type #ident = #ty;
}
}

fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenStream {
fn expand_type_alias_verify(ffi: &Module, alias: &TypeAlias) -> TokenStream {
let namespace = ffi.namespace_for_alias(alias);
let type_id = type_id(namespace, &alias.ty_ident);
let ident = &alias.ident;
let type_id = type_id(namespace, ident);
let begin_span = alias.type_token.span;
let end_span = alias.semi_token.span;
let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<);
Expand All @@ -678,13 +678,7 @@ fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenSt
}

fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream {
let mut path = String::new();
for name in namespace {
path += &name.to_string();
path += "::";
}
path += &ident.to_string();

let path = namespace.path_for_type(Some(ident));
quote! {
::cxx::type_id!(#path)
}
Expand Down
7 changes: 5 additions & 2 deletions macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::syntax::file::Module;
use crate::syntax::namespace::Namespace;
use crate::syntax::qualified::QualifiedName;
use proc_macro::TokenStream;
use syn::parse::{Parse, ParseStream, Result};
use syn::parse::{Parse, ParseStream, Parser, Result};
use syn::parse_macro_input;

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

let namespace = parse_macro_input!(args as Namespace);
let namespace = match Namespace::parse_bridge_attr_namespace.parse(args) {
Ok(ns) => ns,
Err(err) => return err.to_compile_error().into(),
};
let mut ffi = parse_macro_input!(input as Module);
ffi.namespace = namespace;

Expand Down
43 changes: 41 additions & 2 deletions syntax/attrs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::syntax::qualified::QualifiedName;
use crate::syntax::report::Errors;
use crate::syntax::Atom::{self, *};
use crate::syntax::{Derive, Doc};
use crate::syntax::{Derive, Doc, Namespace};
use proc_macro2::Ident;
use std::collections::HashMap;
use syn::parse::{ParseStream, Parser as _};
use syn::{Attribute, Error, LitStr, Path, Result, Token};

Expand All @@ -10,6 +12,8 @@ pub struct Parser<'a> {
pub doc: Option<&'a mut Doc>,
pub derives: Option<&'a mut Vec<Derive>>,
pub repr: Option<&'a mut Option<Atom>>,
pub alias_namespaces: Option<&'a mut HashMap<QualifiedName, Namespace>>,
pub ignore_unsupported: bool,
}

pub(super) fn parse_doc(cx: &mut Errors, attrs: &[Attribute]) -> Doc {
Expand Down Expand Up @@ -57,11 +61,39 @@ pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) {
}
Err(err) => return cx.push(err),
}
} else if is_cxx_alias_namespace_attr(attr) {
match attr.parse_args_with(parse_namespace_attribute) {
Ok((name, namespace)) => {
if let Some(map) = &mut parser.alias_namespaces {
if let Some(existing) = map.get(&name) {
return cx.error(
attr,
format!(
"conflicting cxx::alias_namespace attributes for {}: {}, {}",
name,
existing.path_for_type(None),
namespace.path_for_type(None)
),
);
}
map.insert(name, namespace);
continue;
}
}
Err(err) => return cx.push(err),
}
}
if !parser.ignore_unsupported {
return cx.error(attr, "unsupported attribute");
}
return cx.error(attr, "unsupported attribute");
}
}

fn is_cxx_alias_namespace_attr(attr: &Attribute) -> bool {
let path = &attr.path.segments;
path.len() == 2 && path[0].ident == "cxx" && path[1].ident == "alias_namespace"
}

fn parse_doc_attribute(input: ParseStream) -> Result<LitStr> {
input.parse::<Token![=]>()?;
let lit: LitStr = input.parse()?;
Expand Down Expand Up @@ -99,3 +131,10 @@ fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
"unrecognized repr",
))
}

fn parse_namespace_attribute(input: ParseStream) -> Result<(QualifiedName, Namespace)> {
let name = QualifiedName::parse_quoted_or_unquoted(input)?;
input.parse::<Token![=]>()?;
let namespace = input.parse::<Namespace>()?;
Ok((name, namespace))
}
4 changes: 4 additions & 0 deletions syntax/doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ impl Doc {
self.fragments.push(lit);
}

pub fn extend(&mut self, doc: Doc) {
self.fragments.extend(doc.fragments)
}

pub fn to_string(&self) -> String {
let mut doc = String::new();
for lit in &self.fragments {
Expand Down
39 changes: 35 additions & 4 deletions syntax/file.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use crate::syntax::namespace::Namespace;
use crate::syntax::qualified::QualifiedName;
use crate::syntax::report::Errors;
use crate::syntax::{attrs, Doc, TypeAlias};
use quote::quote;
use std::collections::HashMap;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{
braced, token, Abi, Attribute, ForeignItem, Ident, Item as RustItem, ItemEnum, ItemStruct,
Expand All @@ -8,7 +12,8 @@ use syn::{

pub struct Module {
pub namespace: Namespace,
pub attrs: Vec<Attribute>,
pub alias_namespaces: HashMap<QualifiedName, Namespace>,
pub doc: Doc,
pub vis: Visibility,
pub unsafety: Option<Token![unsafe]>,
pub mod_token: Token![mod],
Expand All @@ -33,6 +38,29 @@ pub struct ItemForeignMod {
pub items: Vec<ForeignItem>,
}

impl Module {
pub fn add_attributes(&mut self, attrs: &[Attribute]) -> Result<()> {
let ref mut errors = Errors::new();
attrs::parse(
errors,
attrs,
attrs::Parser {
doc: Some(&mut self.doc),
alias_namespaces: Some(&mut self.alias_namespaces),
ignore_unsupported: true,
..attrs::Parser::default()
},
);
errors.propagate()
}

pub fn namespace_for_alias(&self, alias: &TypeAlias) -> &Namespace {
self.alias_namespaces
.get(&alias.ty_path)
.unwrap_or(&self.namespace)
}
}

impl Parse for Module {
fn parse(input: ParseStream) -> Result<Self> {
let namespace = Namespace::none();
Expand Down Expand Up @@ -60,16 +88,19 @@ impl Parse for Module {
items.push(content.parse()?);
}

Ok(Module {
let mut module = Module {
namespace,
attrs,
alias_namespaces: HashMap::new(),
doc: Doc::new(),
vis,
unsafety,
mod_token,
ident,
brace_token,
content: items,
})
};
module.add_attributes(&attrs)?;
Ok(module)
}
}

Expand Down
9 changes: 7 additions & 2 deletions syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ use self::parse::kw;
use proc_macro2::{Ident, Span};
use syn::punctuated::Punctuated;
use syn::token::{Brace, Bracket, Paren};
use syn::{Expr, Lifetime, Token, Type as RustType};
use syn::{Expr, Lifetime, Token, TypePath};

pub use self::atom::Atom;
pub use self::derive::Derive;
pub use self::doc::Doc;
pub use self::namespace::Namespace;
pub use self::parse::parse_items;
pub use self::qualified::QualifiedName;
pub use self::types::Types;

pub enum Api {
Expand Down Expand Up @@ -79,10 +81,13 @@ pub struct ExternFn {
}

pub struct TypeAlias {
pub doc: Doc,
pub type_token: Token![type],
pub ident: Ident,
pub eq_token: Token![=],
pub ty: RustType,
pub ty: TypePath,
pub ty_path: QualifiedName,
pub ty_ident: Ident,
pub semi_token: Token![;],
}

Expand Down
Loading