diff --git a/gen/src/nested.rs b/gen/src/nested.rs index c030717a..2129d10f 100644 --- a/gen/src/nested.rs +++ b/gen/src/nested.rs @@ -53,7 +53,7 @@ mod tests { use super::NamespaceEntries; use crate::syntax::attrs::OtherAttrs; use crate::syntax::namespace::Namespace; - use crate::syntax::{Api, Doc, ExternType, Lang, Lifetimes, Pair}; + use crate::syntax::{Api, Doc, ExternType, ForeignName, Lang, Lifetimes, Pair}; use proc_macro2::{Ident, Span}; use std::iter::FromIterator; use syn::punctuated::Punctuated; @@ -119,7 +119,7 @@ mod tests { fn assert_ident(api: &Api, expected: &str) { if let Api::CxxType(cxx_type) = api { - assert_eq!(cxx_type.name.cxx, expected); + assert_eq!(cxx_type.name.cxx.to_string(), expected); } else { unreachable!() } @@ -127,7 +127,6 @@ mod tests { fn make_api(ns: Option<&str>, ident: &str) -> Api { let ns = ns.map_or(Namespace::ROOT, |ns| syn::parse_str(ns).unwrap()); - let ident = Ident::new(ident, Span::call_site()); Api::CxxType(ExternType { lang: Lang::Rust, doc: Doc::new(), @@ -137,8 +136,8 @@ mod tests { type_token: Token![type](Span::call_site()), name: Pair { namespace: ns, - cxx: ident.clone(), - rust: ident, + cxx: ForeignName::parse(ident, Span::call_site()).unwrap(), + rust: Ident::new(ident, Span::call_site()), }, generics: Lifetimes { lt_token: None, diff --git a/gen/src/write.rs b/gen/src/write.rs index def0447a..5e2473a1 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -50,10 +50,10 @@ fn write_forward_declarations(out: &mut OutFile, apis: &[Api]) { for api in apis { write!(out, "{:1$}", "", indent); match api { - Api::Struct(strct) => write_struct_decl(out, &strct.name.cxx), + Api::Struct(strct) => write_struct_decl(out, &strct.name), Api::Enum(enm) => write_enum_decl(out, enm), Api::CxxType(ety) => write_struct_using(out, &ety.name), - Api::RustType(ety) => write_struct_decl(out, &ety.name.cxx), + Api::RustType(ety) => write_struct_decl(out, &ety.name), _ => unreachable!(), } } @@ -296,8 +296,8 @@ fn write_struct<'a>(out: &mut OutFile<'a>, strct: &'a Struct, methods: &[&Extern writeln!(out, "#endif // {}", guard); } -fn write_struct_decl(out: &mut OutFile, ident: &Ident) { - writeln!(out, "struct {};", ident); +fn write_struct_decl(out: &mut OutFile, ident: &Pair) { + writeln!(out, "struct {};", ident.cxx); } fn write_enum_decl(out: &mut OutFile, enm: &Enum) { diff --git a/syntax/attrs.rs b/syntax/attrs.rs index d3e2bb0d..4808f2e1 100644 --- a/syntax/attrs.rs +++ b/syntax/attrs.rs @@ -1,7 +1,7 @@ use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::Atom::{self, *}; -use crate::syntax::{Derive, Doc}; +use crate::syntax::{Derive, Doc, ForeignName}; use proc_macro2::{Ident, TokenStream}; use quote::ToTokens; use syn::parse::{ParseStream, Parser as _}; @@ -31,7 +31,7 @@ pub struct Parser<'a> { pub derives: Option<&'a mut Vec>, pub repr: Option<&'a mut Option>, pub namespace: Option<&'a mut Namespace>, - pub cxx_name: Option<&'a mut Option>, + pub cxx_name: Option<&'a mut Option>, pub rust_name: Option<&'a mut Option>, // Suppress clippy needless_update lint ("struct update has no effect, all @@ -96,7 +96,7 @@ pub fn parse(cx: &mut Errors, attrs: Vec, mut parser: Parser) -> Othe } } } else if attr.path.is_ident("cxx_name") { - match parse_function_alias_attribute.parse2(attr.tokens.clone()) { + match parse_cxx_name_attribute.parse2(attr.tokens.clone()) { Ok(attr) => { if let Some(cxx_name) = &mut parser.cxx_name { **cxx_name = Some(attr); @@ -109,7 +109,7 @@ pub fn parse(cx: &mut Errors, attrs: Vec, mut parser: Parser) -> Othe } } } else if attr.path.is_ident("rust_name") { - match parse_function_alias_attribute.parse2(attr.tokens.clone()) { + match parse_rust_name_attribute.parse2(attr.tokens.clone()) { Ok(attr) => { if let Some(rust_name) = &mut parser.rust_name { **rust_name = Some(attr); @@ -192,7 +192,18 @@ fn parse_namespace_attribute(input: ParseStream) -> Result { Ok(namespace) } -fn parse_function_alias_attribute(input: ParseStream) -> Result { +fn parse_cxx_name_attribute(input: ParseStream) -> Result { + input.parse::()?; + if input.peek(LitStr) { + let lit: LitStr = input.parse()?; + ForeignName::parse(&lit.value(), lit.span()) + } else { + let ident: Ident = input.parse()?; + ForeignName::parse(&ident.to_string(), ident.span()) + } +} + +fn parse_rust_name_attribute(input: ParseStream) -> Result { input.parse::()?; if input.peek(LitStr) { let lit: LitStr = input.parse()?; diff --git a/syntax/ident.rs b/syntax/ident.rs index 78750dc9..bb2281e7 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -1,27 +1,24 @@ use crate::syntax::check::Check; use crate::syntax::{error, Api, Pair}; -use proc_macro2::Ident; fn check(cx: &mut Check, name: &Pair) { for segment in &name.namespace { - check_cxx_ident(cx, segment); + check_cxx_ident(cx, &segment.to_string()); } - check_cxx_ident(cx, &name.cxx); - check_rust_ident(cx, &name.rust); + check_cxx_ident(cx, &name.cxx.to_string()); + check_rust_ident(cx, &name.rust.to_string()); - fn check_cxx_ident(cx: &mut Check, ident: &Ident) { - let s = ident.to_string(); - if s.starts_with("cxxbridge") { + fn check_cxx_ident(cx: &mut Check, ident: &str) { + if ident.starts_with("cxxbridge") { cx.error(ident, error::CXXBRIDGE_RESERVED.msg); } - if s.contains("__") { + if ident.contains("__") { cx.error(ident, error::DOUBLE_UNDERSCORE.msg); } } - fn check_rust_ident(cx: &mut Check, ident: &Ident) { - let s = ident.to_string(); - if s.starts_with("cxxbridge") { + fn check_rust_ident(cx: &mut Check, ident: &str) { + if ident.starts_with("cxxbridge") { cx.error(ident, error::CXXBRIDGE_RESERVED.msg); } } diff --git a/syntax/mod.rs b/syntax/mod.rs index 9b4a1012..57ed4fce 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -41,6 +41,7 @@ use syn::{Expr, Generics, Lifetime, LitInt, Token, Type as RustType}; pub use self::atom::Atom; pub use self::derive::{Derive, Trait}; pub use self::doc::Doc; +pub use self::names::ForeignName; pub use self::parse::parse_items; pub use self::types::Types; @@ -252,7 +253,7 @@ pub enum Lang { #[derive(Clone)] pub struct Pair { pub namespace: Namespace, - pub cxx: Ident, + pub cxx: ForeignName, pub rust: Ident, } diff --git a/syntax/names.rs b/syntax/names.rs index c4cbcff2..b2ae0139 100644 --- a/syntax/names.rs +++ b/syntax/names.rs @@ -1,25 +1,37 @@ +use crate::syntax::symbol::Segment; use crate::syntax::{Lifetimes, NamedType, Pair, Symbol}; use proc_macro2::{Ident, Span}; +use std::fmt::{self, Display}; use std::iter; +use syn::parse::Result; use syn::punctuated::Punctuated; +#[derive(Clone)] +pub struct ForeignName { + text: String, + span: Span, +} + impl Pair { pub fn to_symbol(&self) -> Symbol { - Symbol::from_idents(self.iter_all_segments()) + let segments = self + .namespace + .iter() + .map(|ident| ident as &dyn Segment) + .chain(iter::once(&self.cxx as &dyn Segment)); + Symbol::from_idents(segments) } pub fn to_fully_qualified(&self) -> String { let mut fully_qualified = String::new(); - for segment in self.iter_all_segments() { + for segment in &self.namespace { fully_qualified += "::"; fully_qualified += &segment.to_string(); } + fully_qualified += "::"; + fully_qualified += &self.cxx.to_string(); fully_qualified } - - fn iter_all_segments(&self) -> impl Iterator { - self.namespace.iter().chain(iter::once(&self.cxx)) - } } impl NamedType { @@ -36,3 +48,19 @@ impl NamedType { self.rust.span() } } + +impl ForeignName { + pub fn parse(text: &str, span: Span) -> Result { + // TODO: support C++ names containing whitespace (`unsigned int`) or + // non-alphanumeric characters (`operator++`). + let ident: Ident = syn::parse_str(text)?; + let text = ident.to_string(); + Ok(ForeignName { text, span }) + } +} + +impl Display for ForeignName { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str(&self.text) + } +} diff --git a/syntax/parse.rs b/syntax/parse.rs index 3d2c6389..795a5189 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -4,9 +4,9 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Array, Derive, Doc, Enum, ExternFn, ExternType, Impl, Include, IncludeKind, - Lang, Lifetimes, NamedType, Namespace, Pair, Receiver, Ref, Signature, SliceRef, Struct, Ty1, - Type, TypeAlias, Var, Variant, + attrs, error, Api, Array, Derive, Doc, Enum, ExternFn, ExternType, ForeignName, Impl, Include, + IncludeKind, Lang, Lifetimes, NamedType, Namespace, Pair, Receiver, Ref, Signature, SliceRef, + Struct, Ty1, Type, TypeAlias, Var, Variant, }; use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; @@ -1258,11 +1258,16 @@ fn visibility_pub(vis: &Visibility, inherited: &Ident) -> Token![pub] { }) } -fn pair(namespace: Namespace, default: &Ident, cxx: Option, rust: Option) -> Pair { - let default = || default.clone(); +fn pair( + namespace: Namespace, + default: &Ident, + cxx: Option, + rust: Option, +) -> Pair { Pair { namespace, - cxx: cxx.unwrap_or_else(default), - rust: rust.unwrap_or_else(default), + cxx: cxx + .unwrap_or_else(|| ForeignName::parse(&default.to_string(), default.span()).unwrap()), + rust: rust.unwrap_or_else(|| default.clone()), } } diff --git a/syntax/symbol.rs b/syntax/symbol.rs index 9a0a4a1e..a13a4f3c 100644 --- a/syntax/symbol.rs +++ b/syntax/symbol.rs @@ -1,5 +1,5 @@ use crate::syntax::namespace::Namespace; -use crate::syntax::Pair; +use crate::syntax::{ForeignName, Pair}; use proc_macro2::{Ident, TokenStream}; use quote::ToTokens; use std::fmt::{self, Display, Write}; @@ -30,7 +30,7 @@ impl Symbol { assert!(self.0.len() > len_before); } - pub fn from_idents<'a, T: Iterator>(it: T) -> Self { + pub fn from_idents<'a>(it: impl Iterator) -> Self { let mut symbol = Symbol(String::new()); for segment in it { segment.write(&mut symbol); @@ -55,16 +55,19 @@ impl Segment for str { symbol.push(&self); } } + impl Segment for usize { fn write(&self, symbol: &mut Symbol) { symbol.push(&self); } } + impl Segment for Ident { fn write(&self, symbol: &mut Symbol) { symbol.push(&self); } } + impl Segment for Symbol { fn write(&self, symbol: &mut Symbol) { symbol.push(&self); @@ -86,6 +89,14 @@ impl Segment for Pair { } } +impl Segment for ForeignName { + fn write(&self, symbol: &mut Symbol) { + // TODO: support C++ names containing whitespace (`unsigned int`) or + // non-alphanumeric characters (`operator++`). + self.to_string().write(symbol); + } +} + impl Segment for &'_ T where T: ?Sized + Segment + Display,