Skip to content

Commit 17a8230

Browse files
committed
Support type aliases in different namespaces
1 parent 8446a2a commit 17a8230

File tree

10 files changed

+100
-22
lines changed

10 files changed

+100
-22
lines changed

gen/src/write.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use crate::gen::{include, Opt};
33
use crate::syntax::atom::Atom::{self, *};
44
use crate::syntax::namespace::Namespace;
55
use crate::syntax::symbol::Symbol;
6-
use crate::syntax::{mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, Types, Var};
6+
use crate::syntax::{
7+
mangle, Api, Enum, ExternFn, ExternType, Signature, Struct, Type, TypeAlias, Types, Var,
8+
};
79
use proc_macro2::Ident;
810
use std::collections::HashMap;
911

@@ -42,6 +44,7 @@ pub(super) fn gen(
4244
Api::Struct(strct) => write_struct_decl(out, &strct.ident),
4345
Api::CxxType(ety) => write_struct_using(out, &ety.ident),
4446
Api::RustType(ety) => write_struct_decl(out, &ety.ident),
47+
Api::TypeAlias(alias) => write_alias(out, alias),
4548
_ => {}
4649
}
4750
}
@@ -931,6 +934,16 @@ fn write_type(out: &mut OutFile, ty: &Type) {
931934
}
932935
}
933936

937+
fn write_alias(out: &mut OutFile, alias: &TypeAlias) {
938+
if let Some(namespace) = &alias.namespace {
939+
// Review TODO: Is this unwrap fine? i.e. is it ok to assume that, if
940+
// the TypePath parsed, that it has at least one segment?
941+
let remote_type = &alias.ty.path.segments.last().unwrap().ident;
942+
let path = namespace.path_for_type(remote_type);
943+
writeln!(out, "using {} = {};", alias.ident, path)
944+
}
945+
}
946+
934947
fn write_atom(out: &mut OutFile, atom: Atom) {
935948
match atom {
936949
Bool => write!(out, "bool"),

macro/src/expand.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -667,8 +667,12 @@ fn expand_type_alias(alias: &TypeAlias) -> TokenStream {
667667
}
668668

669669
fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenStream {
670+
let namespace = alias.namespace.as_ref().unwrap_or(namespace);
671+
// Review TODO: Is this unwrap fine? i.e. is it ok to assume that, if the
672+
// TypePath parsed, that it has at least one segment?
673+
let remote_type = &alias.ty.path.segments.last().unwrap().ident;
674+
let type_id = type_id(namespace, remote_type);
670675
let ident = &alias.ident;
671-
let type_id = type_id(namespace, ident);
672676
let begin_span = alias.type_token.span;
673677
let end_span = alias.semi_token.span;
674678
let begin = quote_spanned!(begin_span=> ::cxx::private::verify_extern_type::<);
@@ -680,13 +684,7 @@ fn expand_type_alias_verify(namespace: &Namespace, alias: &TypeAlias) -> TokenSt
680684
}
681685

682686
fn type_id(namespace: &Namespace, ident: &Ident) -> TokenStream {
683-
let mut path = String::new();
684-
for name in namespace {
685-
path += &name.to_string();
686-
path += "::";
687-
}
688-
path += &ident.to_string();
689-
687+
let path = namespace.path_for_type(ident);
690688
quote! {
691689
::cxx::type_id!(#path)
692690
}

syntax/attrs.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
use crate::syntax::qualified::QualifiedName;
12
use crate::syntax::report::Errors;
23
use crate::syntax::Atom::{self, *};
3-
use crate::syntax::{Derive, Doc};
4+
use crate::syntax::{Derive, Doc, Namespace};
45
use proc_macro2::Ident;
56
use syn::parse::{ParseStream, Parser as _};
67
use syn::{Attribute, Error, LitStr, Path, Result, Token};
@@ -10,6 +11,7 @@ pub struct Parser<'a> {
1011
pub doc: Option<&'a mut Doc>,
1112
pub derives: Option<&'a mut Vec<Derive>>,
1213
pub repr: Option<&'a mut Option<Atom>>,
14+
pub namespace: Option<&'a mut Option<Namespace>>,
1315
}
1416

1517
pub(super) fn parse_doc(cx: &mut Errors, attrs: &[Attribute]) -> Doc {
@@ -57,6 +59,16 @@ pub(super) fn parse(cx: &mut Errors, attrs: &[Attribute], mut parser: Parser) {
5759
}
5860
Err(err) => return cx.push(err),
5961
}
62+
} else if attr.path.is_ident("namespace") {
63+
match parse_namespace_attribute.parse2(attr.tokens.clone()) {
64+
Ok(namespace) => {
65+
if let Some(ns) = &mut parser.namespace {
66+
**ns = Some(Namespace::from(namespace));
67+
continue;
68+
}
69+
}
70+
Err(err) => return cx.push(err),
71+
}
6072
}
6173
return cx.error(attr, "unsupported attribute");
6274
}
@@ -99,3 +111,9 @@ fn parse_repr_attribute(input: ParseStream) -> Result<Atom> {
99111
"unrecognized repr",
100112
))
101113
}
114+
115+
fn parse_namespace_attribute(input: ParseStream) -> Result<Namespace> {
116+
input.parse::<Token![=]>()?;
117+
let name = input.call(QualifiedName::parse_quoted_or_unquoted)?;
118+
Ok(Namespace::from(name))
119+
}

syntax/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ use self::parse::kw;
2525
use proc_macro2::{Ident, Span};
2626
use syn::punctuated::Punctuated;
2727
use syn::token::{Brace, Bracket, Paren};
28-
use syn::{Expr, Lifetime, Token, Type as RustType};
28+
use syn::{Expr, Lifetime, Token, TypePath};
2929

3030
pub use self::atom::Atom;
3131
pub use self::derive::Derive;
3232
pub use self::doc::Doc;
33+
pub use self::namespace::Namespace;
3334
pub use self::parse::parse_items;
3435
pub use self::types::Types;
3536

@@ -80,10 +81,11 @@ pub struct ExternFn {
8081

8182
pub struct TypeAlias {
8283
pub doc: Doc,
84+
pub namespace: Option<Namespace>,
8385
pub type_token: Token![type],
8486
pub ident: Ident,
8587
pub eq_token: Token![=],
86-
pub ty: RustType,
88+
pub ty: TypePath,
8789
pub semi_token: Token![;],
8890
}
8991

syntax/namespace.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,32 @@ impl Namespace {
2424
pub fn iter(&self) -> Iter<Ident> {
2525
self.segments.iter()
2626
}
27+
28+
pub fn path_for_type(&self, ident: &Ident) -> String {
29+
let mut segments = self.iter().map(ToString::to_string).collect::<Vec<_>>();
30+
segments.push(ident.to_string());
31+
segments.join("::")
32+
}
33+
}
34+
35+
impl From<QualifiedName> for Namespace {
36+
fn from(value: QualifiedName) -> Namespace {
37+
Namespace {
38+
segments: value.segments,
39+
}
40+
}
2741
}
2842

2943
impl Parse for Namespace {
3044
fn parse(input: ParseStream) -> Result<Self> {
31-
let mut segments = Vec::new();
3245
if !input.is_empty() {
3346
input.parse::<kw::namespace>()?;
3447
input.parse::<Token![=]>()?;
35-
segments = input
36-
.call(QualifiedName::parse_quoted_or_unquoted)?
37-
.segments;
48+
let name = input.call(QualifiedName::parse_quoted_or_unquoted)?;
3849
input.parse::<Option<Token![,]>>()?;
50+
return Ok(Namespace::from(name));
3951
}
40-
Ok(Namespace { segments })
52+
Ok(Namespace::none())
4153
}
4254
}
4355

syntax/parse.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -394,12 +394,24 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R
394394
};
395395
let ident: Ident = input.parse()?;
396396
let eq_token: Token![=] = input.parse()?;
397-
let ty: RustType = input.parse()?;
397+
let ty: TypePath = input.parse()?;
398398
let semi_token: Token![;] = input.parse()?;
399-
let doc = attrs::parse_doc(cx, &attrs);
399+
400+
let mut doc = Doc::new();
401+
let mut namespace = None;
402+
attrs::parse(
403+
cx,
404+
&attrs,
405+
attrs::Parser {
406+
doc: Some(&mut doc),
407+
namespace: Some(&mut namespace),
408+
..Default::default()
409+
},
410+
);
400411

401412
Ok(TypeAlias {
402413
doc,
414+
namespace,
403415
type_token,
404416
ident,
405417
eq_token,

tests/ffi/alias.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
// Rustfmt mangles the extern type alias.
22
// https://github.com/rust-lang/rustfmt/issues/4159
33
#[rustfmt::skip]
4-
#[cxx::bridge(namespace = tests)]
4+
#[cxx::bridge(namespace = alias_tests)]
55
pub mod ffi {
66
extern "C" {
77
include!("cxx-test-suite/tests.h");
88

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

11-
fn c_take_unique_ptr(c: UniquePtr<C>);
14+
#[namespace = "tests"]
15+
type SameC = crate::ffi::C;
16+
17+
fn c_return_unique_ptr() -> UniquePtr<C>;
18+
fn c_take_unique_ptr(c: UniquePtr<SameC>);
1219
}
1320
}

tests/ffi/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ pub mod ffi {
6060
fn c_take_str(s: &str);
6161
fn c_take_sliceu8(s: &[u8]);
6262
fn c_take_rust_string(s: String);
63+
fn c_take_unique_ptr(c: UniquePtr<C>);
6364
fn c_take_unique_ptr_string(s: UniquePtr<CxxString>);
6465
fn c_take_unique_ptr_vector_u8(v: UniquePtr<CxxVector<u8>>);
6566
fn c_take_unique_ptr_vector_f64(v: UniquePtr<CxxVector<f64>>);

tests/ffi/tests.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,3 +102,12 @@ rust::Vec<rust::String> c_try_return_rust_vec_string();
102102
const rust::Vec<uint8_t> &c_try_return_ref_rust_vec(const C &c);
103103

104104
} // namespace tests
105+
106+
namespace alias_tests {
107+
108+
// These aliases on the C++ side aren't under test, there's just no reason to
109+
// duplicate these functions
110+
using tests::c_return_unique_ptr;
111+
using tests::c_take_unique_ptr;
112+
113+
} // namespace alias_tests

tests/test.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ fn test_c_take() {
8989
check!(ffi::c_take_shared(ffi::Shared { z: 2020 }));
9090
check!(ffi::c_take_box(Box::new(2020)));
9191
check!(ffi::c_take_ref_c(&unique_ptr));
92-
check!(alias::ffi::c_take_unique_ptr(unique_ptr));
92+
check!(ffi::c_take_unique_ptr(unique_ptr));
9393
check!(ffi::c_take_str("2020"));
9494
check!(ffi::c_take_sliceu8(b"2020"));
9595
check!(ffi::c_take_rust_string("2020".to_owned()));
@@ -172,6 +172,12 @@ fn test_enum_representations() {
172172
assert_eq!(2021, ffi::Enum::CVal.repr);
173173
}
174174

175+
#[test]
176+
fn test_alias() {
177+
let unique_ptr = alias::ffi::c_return_unique_ptr();
178+
check!(alias::ffi::c_take_unique_ptr(unique_ptr));
179+
}
180+
175181
#[no_mangle]
176182
extern "C" fn cxx_test_suite_get_box() -> *mut cxx_test_suite::R {
177183
Box::into_raw(Box::new(2020usize))

0 commit comments

Comments
 (0)