Add ForeignName wrapper around non-Rust names

This commit is contained in:
David Tolnay 2021-01-01 14:59:40 -08:00
parent 9f84fe8bbd
commit ed6ba4a63c
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
8 changed files with 93 additions and 41 deletions

View File

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

View File

@ -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) {

View File

@ -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<Derive>>,
pub repr: Option<&'a mut Option<Atom>>,
pub namespace: Option<&'a mut Namespace>,
pub cxx_name: Option<&'a mut Option<Ident>>,
pub cxx_name: Option<&'a mut Option<ForeignName>>,
pub rust_name: Option<&'a mut Option<Ident>>,
// Suppress clippy needless_update lint ("struct update has no effect, all
@ -96,7 +96,7 @@ pub fn parse(cx: &mut Errors, attrs: Vec<Attribute>, 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<Attribute>, 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<Namespace> {
Ok(namespace)
}
fn parse_function_alias_attribute(input: ParseStream) -> Result<Ident> {
fn parse_cxx_name_attribute(input: ParseStream) -> Result<ForeignName> {
input.parse::<Token![=]>()?;
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<Ident> {
input.parse::<Token![=]>()?;
if input.peek(LitStr) {
let lit: LitStr = input.parse()?;

View File

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

View File

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

View File

@ -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<Item = &Ident> {
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<Self> {
// 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)
}
}

View File

@ -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<Ident>, rust: Option<Ident>) -> Pair {
let default = || default.clone();
fn pair(
namespace: Namespace,
default: &Ident,
cxx: Option<ForeignName>,
rust: Option<Ident>,
) -> 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()),
}
}

View File

@ -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<Item = &'a Ident>>(it: T) -> Self {
pub fn from_idents<'a>(it: impl Iterator<Item = &'a dyn Segment>) -> 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<T> Segment for &'_ T
where
T: ?Sized + Segment + Display,