Skip to content

Commit

Permalink
Support type aliases in different namespaces
Browse files Browse the repository at this point in the history
  • Loading branch information
sbrocket committed Sep 13, 2020
1 parent 8446a2a commit de3702b
Show file tree
Hide file tree
Showing 10 changed files with 89 additions and 20 deletions.
12 changes: 11 additions & 1 deletion gen/src/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use crate::gen::{include, Opt};
use crate::syntax::atom::Atom::{self, *};
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;

Expand Down Expand Up @@ -42,6 +44,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, alias),
_ => {}
}
}
Expand Down Expand Up @@ -931,6 +934,13 @@ fn write_type(out: &mut OutFile, ty: &Type) {
}
}

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

fn write_atom(out: &mut OutFile, atom: Atom) {
match atom {
Bool => write!(out, "bool"),
Expand Down
9 changes: 2 additions & 7 deletions macro/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,7 @@ fn expand_type_alias(alias: &TypeAlias) -> TokenStream {
}

fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenStream {
let namespace = alias.namespace.as_ref().unwrap_or(namespace);
let ident = &alias.ident;
let type_id = type_id(namespace, ident);
let begin_span = alias.type_token.span;
Expand All @@ -680,13 +681,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(ident);
quote! {
::cxx::type_id!(#path)
}
Expand Down
20 changes: 19 additions & 1 deletion syntax/attrs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
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 syn::parse::{ParseStream, Parser as _};
use syn::{Attribute, Error, LitStr, Path, Result, Token};
Expand All @@ -10,6 +11,7 @@ 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 namespace: Option<&'a mut Option<Namespace>>,
}

pub(super) fn parse_doc(cx: &mut Errors, attrs: &[Attribute]) -> Doc {
Expand Down Expand Up @@ -57,6 +59,16 @@ pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) {
}
Err(err) => return cx.push(err),
}
} else if attr.path.is_ident("namespace") {
match parse_namespace_attribute.parse2(attr.tokens.clone()) {
Ok(namespace) => {
if let Some(ns) = &mut parser.namespace {
**ns = Some(Namespace::from(namespace));
continue;
}
}
Err(err) => return cx.push(err),
}
}
return cx.error(attr, "unsupported attribute");
}
Expand Down Expand Up @@ -99,3 +111,9 @@ fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
"unrecognized repr",
))
}

fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
input.parse::<Token![=]>()?;
let name = input.call(QualifiedName::parse_quoted_or_unquoted)?;
Ok(Namespace::from(name))
}
6 changes: 4 additions & 2 deletions syntax/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ 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::types::Types;

Expand Down Expand Up @@ -80,10 +81,11 @@ pub struct ExternFn {

pub struct TypeAlias {
pub doc: Doc,
pub namespace: Option<Namespace>,
pub type_token: Token![type],
pub ident: Ident,
pub eq_token: Token![=],
pub ty: RustType,
pub ty: TypePath,
pub semi_token: Token![;],
}

Expand Down
22 changes: 17 additions & 5 deletions syntax/namespace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,32 @@ impl Namespace {
pub fn iter(&self) -> Iter<Ident> {
self.segments.iter()
}

pub fn path_for_type(&self, ident: &Ident) -> String {
let mut segments = self.iter().map(ToString::to_string).collect::<Vec<_>>();
segments.push(ident.to_string());
segments.join("::")
}
}

impl From<QualifiedName> for Namespace {
fn from(value: QualifiedName) -> Namespace {
Namespace {
segments: value.segments,
}
}
}

impl Parse for Namespace {
fn parse(input: ParseStream) -> Result<Self> {
let mut segments = Vec::new();
if !input.is_empty() {
input.parse::<kw::namespace>()?;
input.parse::<Token![=]>()?;
segments = input
.call(QualifiedName::parse_quoted_or_unquoted)?
.segments;
let name = input.call(QualifiedName::parse_quoted_or_unquoted)?;
input.parse::<Option<Token![,]>>()?;
return Ok(Namespace::from(name));
}
Ok(Namespace { segments })
Ok(Namespace::none())
}
}

Expand Down
16 changes: 14 additions & 2 deletions syntax/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -394,12 +394,24 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R
};
let ident: Ident = input.parse()?;
let eq_token: Token![=] = input.parse()?;
let ty: RustType = input.parse()?;
let ty: TypePath = input.parse()?;
let semi_token: Token![;] = input.parse()?;
let doc = attrs::parse_doc(cx, &attrs);

let mut doc = Doc::new();
let mut namespace = None;
attrs::parse(
cx,
&attrs,
attrs::Parser {
doc: Some(&mut doc),
namespace: Some(&mut namespace),
..Default::default()
},
);

Ok(TypeAlias {
doc,
namespace,
type_token,
ident,
eq_token,
Expand Down
6 changes: 5 additions & 1 deletion tests/ffi/alias.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
// Rustfmt mangles the extern type alias.
// https://github.com/rust-lang/rustfmt/issues/4159
#[rustfmt::skip]
#[cxx::bridge(namespace = tests)]
#[cxx::bridge(namespace = alias_tests)]
pub mod ffi {
extern "C" {
include!("cxx-test-suite/tests.h");

// Review TODO: Unquoted namespace here doesn't work, is that expected or a bug
// in my parsing?
#[namespace = "tests"]
type C = crate::ffi::C;

fn c_return_unique_ptr() -> UniquePtr<C>;
fn c_take_unique_ptr(c: UniquePtr<C>);
}
}
1 change: 1 addition & 0 deletions tests/ffi/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ pub mod ffi {
fn c_take_str(s: &str);
fn c_take_sliceu8(s: &[u8]);
fn c_take_rust_string(s: String);
fn c_take_unique_ptr(c: UniquePtr<C>);
fn c_take_unique_ptr_string(s: UniquePtr<CxxString>);
fn c_take_unique_ptr_vector_u8(v: UniquePtr<CxxVector<u8>>);
fn c_take_unique_ptr_vector_f64(v: UniquePtr<CxxVector<f64>>);
Expand Down
9 changes: 9 additions & 0 deletions tests/ffi/tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,12 @@ rust::Vec<rust::String> c_try_return_rust_vec_string();
const rust::Vec<uint8_t> &c_try_return_ref_rust_vec(const C &c);

} // namespace tests

namespace alias_tests {

// These aliases on the C++ side aren't under test, there's just no reason to
// duplicate these functions
using tests::c_return_unique_ptr;
using tests::c_take_unique_ptr;

} // namespace alias_tests
8 changes: 7 additions & 1 deletion tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ fn test_c_take() {
check!(ffi::c_take_shared(ffi::Shared { z: 2020 }));
check!(ffi::c_take_box(Box::new(2020)));
check!(ffi::c_take_ref_c(&unique_ptr));
check!(alias::ffi::c_take_unique_ptr(unique_ptr));
check!(ffi::c_take_unique_ptr(unique_ptr));
check!(ffi::c_take_str("2020"));
check!(ffi::c_take_sliceu8(b"2020"));
check!(ffi::c_take_rust_string("2020".to_owned()));
Expand Down Expand Up @@ -172,6 +172,12 @@ fn test_enum_representations() {
assert_eq!(2021, ffi::Enum::CVal.repr);
}

#[test]
fn test_alias() {
let unique_ptr = alias::ffi::c_return_unique_ptr();
check!(alias::ffi::c_take_unique_ptr(unique_ptr));
}

#[no_mangle]
extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R {
Box::into_raw(Box::new(2020usize))
Expand Down

0 comments on commit de3702b

Please sign in to comment.