diff --git a/gen/src/mod.rs b/gen/src/mod.rs index 2fee0e91d..92e8ec84b 100644 --- a/gen/src/mod.rs +++ b/gen/src/mod.rs @@ -5,6 +5,7 @@ pub(super) mod error; mod file; pub(super) mod fs; pub(super) mod include; +mod namespace_organizer; pub(super) mod out; mod write; @@ -109,22 +110,22 @@ pub(super) fn generate(syntax: File, opt: &Opt) -> Result { .ok_or(Error::NoBridgeMod)?; let ref namespace = bridge.namespace; let trusted = bridge.unsafety.is_some(); - let ref apis = syntax::parse_items(errors, bridge.content, trusted); + let ref apis = syntax::parse_items(errors, bridge.content, trusted, namespace); let ref types = Types::collect(errors, apis); errors.propagate()?; - check::typecheck(errors, namespace, apis, types); + check::typecheck(errors, apis, types); errors.propagate()?; // Some callers may wish to generate both header and C++ // from the same token stream to avoid parsing twice. But others // 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(apis, types, opt, true).content() } else { Vec::new() }, implementation: if opt.gen_implementation { - write::gen(namespace, apis, types, opt, false).content() + write::gen(apis, types, opt, false).content() } else { Vec::new() }, diff --git a/gen/src/namespace_organizer.rs b/gen/src/namespace_organizer.rs new file mode 100644 index 000000000..d13aa3b49 --- /dev/null +++ b/gen/src/namespace_organizer.rs @@ -0,0 +1,40 @@ +use crate::syntax::Api; +use proc_macro2::Ident; +use std::collections::HashMap; + +pub(crate) struct NamespaceEntries<'a> { + pub(crate) entries: Vec<&'a Api>, + pub(crate) children: HashMap<&'a Ident, NamespaceEntries<'a>>, +} + +pub(crate) fn sort_by_namespace(apis: &[Api]) -> NamespaceEntries { + let api_refs = apis.iter().collect::>(); + sort_by_inner_namespace(api_refs, 0) +} + +fn sort_by_inner_namespace(apis: Vec<&Api>, depth: usize) -> NamespaceEntries { + let mut root = NamespaceEntries { + entries: Vec::new(), + children: HashMap::new(), + }; + + let mut kids_by_child_ns = HashMap::new(); + for api in apis { + if let Some(ns) = api.get_namespace() { + let first_ns_elem = ns.iter().nth(depth); + if let Some(first_ns_elem) = first_ns_elem { + let list = kids_by_child_ns.entry(first_ns_elem).or_insert(Vec::new()); + list.push(api); + continue; + } + } + root.entries.push(api); + } + + for (k, v) in kids_by_child_ns.into_iter() { + root.children + .insert(k, sort_by_inner_namespace(v, depth + 1)); + } + + root +} diff --git a/gen/src/out.rs b/gen/src/out.rs index d42ea7475..8a6bd8610 100644 --- a/gen/src/out.rs +++ b/gen/src/out.rs @@ -1,10 +1,8 @@ use crate::gen::include::Includes; -use crate::syntax::namespace::Namespace; use std::cell::RefCell; use std::fmt::{self, Arguments, Write}; pub(crate) struct OutFile { - pub namespace: Namespace, pub header: bool, pub include: Includes, pub front: Content, @@ -18,9 +16,8 @@ pub struct Content { } impl OutFile { - pub fn new(namespace: Namespace, header: bool) -> Self { + pub fn new(header: bool) -> Self { OutFile { - namespace, header, include: Includes::new(), front: Content::new(), diff --git a/gen/src/write.rs b/gen/src/write.rs index 318460461..5b2989c5d 100644 --- a/gen/src/write.rs +++ b/gen/src/write.rs @@ -1,20 +1,16 @@ +use crate::gen::namespace_organizer::{sort_by_namespace, NamespaceEntries}; use crate::gen::out::OutFile; 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, QualifiedIdent, Signature, Struct, Type, Types, Var, +}; use proc_macro2::Ident; use std::collections::HashMap; -pub(super) fn gen( - namespace: &Namespace, - apis: &[Api], - types: &Types, - opt: &Opt, - header: bool, -) -> OutFile { - let mut out_file = OutFile::new(namespace.clone(), header); +pub(super) fn gen(apis: &[Api], types: &Types, opt: &Opt, header: bool) -> OutFile { + let mut out_file = OutFile::new(header); let out = &mut out_file; if header { @@ -32,16 +28,36 @@ pub(super) fn gen( write_include_cxxbridge(out, apis, types); out.next_section(); - for name in namespace { - writeln!(out, "namespace {} {{", name); + + let apis_by_namespace = sort_by_namespace(apis); + + gen_namespace_contents(&apis_by_namespace, types, opt, header, out); + + if !header { + out.next_section(); + write_generic_instantiations(out, types); } + write!(out.front, "{}", out.include); + + out_file +} + +fn gen_namespace_contents( + ns_entries: &NamespaceEntries, + types: &Types, + opt: &Opt, + header: bool, + out: &mut OutFile, +) { + let apis = &ns_entries.entries; + out.next_section(); for api in apis { match api { - Api::Struct(strct) => write_struct_decl(out, &strct.ident), + Api::Struct(strct) => write_struct_decl(out, &strct.ident.ident), Api::CxxType(ety) => write_struct_using(out, &ety.ident), - Api::RustType(ety) => write_struct_decl(out, &ety.ident), + Api::RustType(ety) => write_struct_decl(out, &ety.ident.ident), _ => {} } } @@ -95,7 +111,7 @@ pub(super) fn gen( if !header { out.begin_block("extern \"C\""); - write_exception_glue(out, apis); + write_exception_glue(out, &apis); for api in apis { let (efn, write): (_, fn(_, _, _, _)) = match api { Api::CxxFunction(efn) => (efn, write_cxx_function_shim), @@ -116,24 +132,18 @@ pub(super) fn gen( } out.next_section(); - for name in namespace.iter().rev() { - writeln!(out, "}} // namespace {}", name); - } - if !header { - out.next_section(); - write_generic_instantiations(out, types); + for (child_ns, child_ns_entries) in &ns_entries.children { + writeln!(out, "namespace {} {{", child_ns); + gen_namespace_contents(&child_ns_entries, types, opt, header, out); + writeln!(out, "}} // namespace {}", child_ns); } - - write!(out.front, "{}", out.include); - - out_file } fn write_includes(out: &mut OutFile, types: &Types) { for ty in types { match ty { - Type::Ident(ident) => match Atom::from(ident) { + Type::Ident(ident) => match Atom::from_qualified_ident(ident) { Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32) | Some(I64) => out.include.cstdint = true, Some(Usize) => out.include.cstddef = true, @@ -333,13 +343,13 @@ fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) { } fn write_struct(out: &mut OutFile, strct: &Struct) { - let guard = format!("CXXBRIDGE05_STRUCT_{}{}", out.namespace, strct.ident); + let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in strct.doc.to_string().lines() { writeln!(out, "//{}", line); } - writeln!(out, "struct {} final {{", strct.ident); + writeln!(out, "struct {} final {{", strct.ident.ident); for field in &strct.fields { write!(out, " "); write_type_space(out, &field.ty); @@ -353,24 +363,33 @@ fn write_struct_decl(out: &mut OutFile, ident: &Ident) { writeln!(out, "struct {};", ident); } -fn write_struct_using(out: &mut OutFile, ident: &Ident) { - writeln!(out, "using {} = {};", ident, ident); +fn write_struct_using(out: &mut OutFile, ident: &QualifiedIdent) { + writeln!( + out, + "using {} = {};", + ident.ident, + ident.to_fully_qualified() + ); } fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) { - let guard = format!("CXXBRIDGE05_STRUCT_{}{}", out.namespace, ety.ident); + let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in ety.doc.to_string().lines() { writeln!(out, "//{}", line); } - writeln!(out, "struct {} final {{", ety.ident); - writeln!(out, " {}() = delete;", ety.ident); - writeln!(out, " {}(const {} &) = delete;", ety.ident, ety.ident); + writeln!(out, "struct {} final {{", ety.ident.ident); + writeln!(out, " {}() = delete;", ety.ident.ident); + writeln!( + out, + " {}(const {} &) = delete;", + ety.ident.ident, ety.ident.ident + ); for method in methods { write!(out, " "); let sig = &method.sig; - let local_name = method.ident.cxx.to_string(); + let local_name = method.ident.cxx.ident.to_string(); write_rust_function_shim_decl(out, &local_name, sig, false); writeln!(out, ";"); } @@ -379,13 +398,13 @@ fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&Ex } fn write_enum(out: &mut OutFile, enm: &Enum) { - let guard = format!("CXXBRIDGE05_ENUM_{}{}", out.namespace, enm.ident); + let guard = format!("CXXBRIDGE05_ENUM_{}", enm.ident.to_symbol()); writeln!(out, "#ifndef {}", guard); writeln!(out, "#define {}", guard); for line in enm.doc.to_string().lines() { writeln!(out, "//{}", line); } - write!(out, "enum class {} : ", enm.ident); + write!(out, "enum class {} : ", enm.ident.ident); write_atom(out, enm.repr); writeln!(out, " {{"); for variant in &enm.variants { @@ -396,7 +415,7 @@ fn write_enum(out: &mut OutFile, enm: &Enum) { } fn check_enum(out: &mut OutFile, enm: &Enum) { - write!(out, "static_assert(sizeof({}) == sizeof(", enm.ident); + write!(out, "static_assert(sizeof({}) == sizeof(", enm.ident.ident); write_atom(out, enm.repr); writeln!(out, "), \"incorrect size\");"); for variant in &enm.variants { @@ -405,12 +424,12 @@ fn check_enum(out: &mut OutFile, enm: &Enum) { writeln!( out, ">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");", - enm.ident, variant.ident, variant.discriminant, + enm.ident.ident, variant.ident, variant.discriminant, ); } } -fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { +fn check_trivial_extern_type(out: &mut OutFile, id: &QualifiedIdent) { // NOTE: The following two static assertions are just nice-to-have and not // necessary for soundness. That's because triviality is always declared by // the user in the form of an unsafe impl of cxx::ExternType: @@ -429,6 +448,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { // not being recognized as such by the C++ type system due to a move // constructor or destructor. + let id = &id.ident; out.include.type_traits = true; writeln!(out, "static_assert("); writeln!( @@ -450,7 +470,7 @@ fn check_trivial_extern_type(out: &mut OutFile, id: &Ident) { ); } -fn write_exception_glue(out: &mut OutFile, apis: &[Api]) { +fn write_exception_glue(out: &mut OutFile, apis: &[&Api]) { let mut has_cxx_throws = false; for api in apis { if let Api::CxxFunction(efn) = api { @@ -486,13 +506,13 @@ fn write_cxx_function_shim( } else { write_extern_return_type_space(out, &efn.ret, types); } - let mangled = mangle::extern_fn(&out.namespace, efn); + let mangled = mangle::extern_fn(efn); write!(out, "{}(", mangled); if let Some(receiver) = &efn.receiver { if receiver.mutability.is_none() { write!(out, "const "); } - write!(out, "{} &self", receiver.ty); + write!(out, "{} &self", receiver.ty.to_fully_qualified()); } for (i, arg) in efn.args.iter().enumerate() { if i > 0 || efn.receiver.is_some() { @@ -518,7 +538,12 @@ fn write_cxx_function_shim( write_return_type(out, &efn.ret); match &efn.receiver { None => write!(out, "(*{}$)(", efn.ident.rust), - Some(receiver) => write!(out, "({}::*{}$)(", receiver.ty, efn.ident.rust), + Some(receiver) => write!( + out, + "({}::*{}$)(", + receiver.ty.to_fully_qualified(), + efn.ident.rust + ), } for (i, arg) in efn.args.iter().enumerate() { if i > 0 { @@ -534,8 +559,13 @@ fn write_cxx_function_shim( } write!(out, " = "); match &efn.receiver { - None => write!(out, "{}", efn.ident.cxx), - Some(receiver) => write!(out, "&{}::{}", receiver.ty, efn.ident.cxx), + None => write!(out, "{}", efn.ident.cxx.to_fully_qualified()), + Some(receiver) => write!( + out, + "&{}::{}", + receiver.ty.to_fully_qualified(), + efn.ident.cxx.ident + ), } writeln!(out, ";"); write!(out, " "); @@ -632,17 +662,17 @@ fn write_function_pointer_trampoline( types: &Types, ) { out.next_section(); - let r_trampoline = mangle::r_trampoline(&out.namespace, efn, var); + let r_trampoline = mangle::r_trampoline(efn, var); let indirect_call = true; write_rust_function_decl_impl(out, &r_trampoline, f, types, indirect_call); out.next_section(); - let c_trampoline = mangle::c_trampoline(&out.namespace, efn, var).to_string(); + let c_trampoline = mangle::c_trampoline(efn, var).to_string(); write_rust_function_shim_impl(out, &c_trampoline, f, types, &r_trampoline, indirect_call); } fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types, _: &Option) { - let link_name = mangle::extern_fn(&out.namespace, efn); + let link_name = mangle::extern_fn(efn); let indirect_call = false; write_rust_function_decl_impl(out, &link_name, efn, types, indirect_call); } @@ -665,7 +695,7 @@ fn write_rust_function_decl_impl( if receiver.mutability.is_none() { write!(out, "const "); } - write!(out, "{} &self", receiver.ty); + write!(out, "{} &self", receiver.ty.to_fully_qualified()); needs_comma = true; } for arg in &sig.args { @@ -697,10 +727,10 @@ fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) { writeln!(out, "//{}", line); } let local_name = match &efn.sig.receiver { - None => efn.ident.cxx.to_string(), - Some(receiver) => format!("{}::{}", receiver.ty, efn.ident.cxx), + None => efn.ident.cxx.ident.to_string(), + Some(receiver) => format!("{}::{}", receiver.ty.ident, efn.ident.cxx.ident), }; - let invoke = mangle::extern_fn(&out.namespace, efn); + let invoke = mangle::extern_fn(efn); let indirect_call = false; write_rust_function_shim_impl(out, &local_name, efn, types, &invoke, indirect_call); } @@ -923,9 +953,9 @@ fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) { fn write_type(out: &mut OutFile, ty: &Type) { match ty { - Type::Ident(ident) => match Atom::from(ident) { + Type::Ident(ident) => match Atom::from_qualified_ident(ident) { Some(atom) => write_atom(out, atom), - None => write!(out, "{}", ident), + None => write!(out, "{}", ident.to_fully_qualified()), }, Type::RustBox(ty) => { write!(out, "::rust::Box<"); @@ -1025,28 +1055,20 @@ fn write_space_after_type(out: &mut OutFile, ty: &Type) { // Only called for legal referent types of unique_ptr and element types of // std::vector and Vec. -fn to_typename(namespace: &Namespace, ty: &Type) -> String { +fn to_typename(ty: &Type) -> String { match ty { - Type::Ident(ident) => { - let mut path = String::new(); - for name in namespace { - path += &name.to_string(); - path += "::"; - } - path += &ident.to_string(); - path - } - Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(namespace, &ptr.inner)), + Type::Ident(ident) => ident.to_fully_qualified(), + Type::CxxVector(ptr) => format!("::std::vector<{}>", to_typename(&ptr.inner)), _ => unreachable!(), } } // Only called for legal referent types of unique_ptr and element types of // std::vector and Vec. -fn to_mangled(namespace: &Namespace, ty: &Type) -> String { +fn to_mangled(ty: &Type) -> Symbol { match ty { - Type::Ident(_) => to_typename(namespace, ty).replace("::", "$"), - Type::CxxVector(ptr) => format!("std$vector${}", to_mangled(namespace, &ptr.inner)), + Type::Ident(ident) => ident.to_symbol(), + Type::CxxVector(ptr) => to_mangled(&ptr.inner).prefix_with("std$vector$"), _ => unreachable!(), } } @@ -1061,14 +1083,14 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } else if let Type::RustVec(ty) = ty { if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { + if Atom::from_qualified_ident(inner).is_none() { out.next_section(); write_rust_vec_extern(out, inner); } } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() + if Atom::from_qualified_ident(inner).is_none() && (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty)) { out.next_section(); @@ -1077,7 +1099,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(inner) = &ptr.inner { - if Atom::from(inner).is_none() + if Atom::from_qualified_ident(inner).is_none() && (!types.aliases.contains_key(inner) || types.explicit_impls.contains(ty)) { out.next_section(); @@ -1097,7 +1119,7 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { } } else if let Type::RustVec(ty) = ty { if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { + if Atom::from_qualified_ident(inner).is_none() { write_rust_vec_impl(out, inner); } } @@ -1107,14 +1129,9 @@ fn write_generic_instantiations(out: &mut OutFile, types: &Types) { out.end_block("namespace rust"); } -fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { - let mut inner = String::new(); - for name in &out.namespace { - inner += &name.to_string(); - inner += "::"; - } - inner += &ident.to_string(); - let instance = inner.replace("::", "$"); +fn write_rust_box_extern(out: &mut OutFile, ident: &QualifiedIdent) { + let inner = ident.to_fully_qualified(); + let instance = ident.to_symbol(); writeln!(out, "#ifndef CXXBRIDGE05_RUST_BOX_{}", instance); writeln!(out, "#define CXXBRIDGE05_RUST_BOX_{}", instance); @@ -1131,10 +1148,10 @@ fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) { writeln!(out, "#endif // CXXBRIDGE05_RUST_BOX_{}", instance); } -fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { +fn write_rust_vec_extern(out: &mut OutFile, element: &QualifiedIdent) { let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); + let inner = to_typename(&element); + let instance = to_mangled(&element); writeln!(out, "#ifndef CXXBRIDGE05_RUST_VEC_{}", instance); writeln!(out, "#define CXXBRIDGE05_RUST_VEC_{}", instance); @@ -1166,14 +1183,9 @@ fn write_rust_vec_extern(out: &mut OutFile, element: &Ident) { writeln!(out, "#endif // CXXBRIDGE05_RUST_VEC_{}", instance); } -fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { - let mut inner = String::new(); - for name in &out.namespace { - inner += &name.to_string(); - inner += "::"; - } - inner += &ident.to_string(); - let instance = inner.replace("::", "$"); +fn write_rust_box_impl(out: &mut OutFile, ident: &QualifiedIdent) { + let inner = ident.to_fully_qualified(); + let instance = ident.to_symbol(); writeln!(out, "template <>"); writeln!(out, "void Box<{}>::uninit() noexcept {{", inner); @@ -1186,10 +1198,10 @@ fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) { writeln!(out, "}}"); } -fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { +fn write_rust_vec_impl(out: &mut OutFile, element: &QualifiedIdent) { let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); + let inner = to_typename(&element); + let instance = to_mangled(&element); writeln!(out, "template <>"); writeln!(out, "Vec<{}>::Vec() noexcept {{", inner); @@ -1225,9 +1237,9 @@ fn write_rust_vec_impl(out: &mut OutFile, element: &Ident) { writeln!(out, "}}"); } -fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { +fn write_unique_ptr(out: &mut OutFile, ident: &QualifiedIdent, types: &Types) { let ty = Type::Ident(ident.clone()); - let instance = to_mangled(&out.namespace, &ty); + let instance = to_mangled(&ty); writeln!(out, "#ifndef CXXBRIDGE05_UNIQUE_PTR_{}", instance); writeln!(out, "#define CXXBRIDGE05_UNIQUE_PTR_{}", instance); @@ -1241,8 +1253,8 @@ fn write_unique_ptr(out: &mut OutFile, ident: &Ident, types: &Types) { fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { out.include.new = true; out.include.utility = true; - let inner = to_typename(&out.namespace, ty); - let instance = to_mangled(&out.namespace, ty); + let inner = to_typename(ty); + let instance = to_mangled(ty); let can_construct_from_value = match ty { // Some aliases are to opaque types; some are to trivial types. We can't @@ -1315,10 +1327,10 @@ fn write_unique_ptr_common(out: &mut OutFile, ty: &Type, types: &Types) { writeln!(out, "}}"); } -fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &Ident, types: &Types) { +fn write_cxx_vector(out: &mut OutFile, vector_ty: &Type, element: &QualifiedIdent, types: &Types) { let element = Type::Ident(element.clone()); - let inner = to_typename(&out.namespace, &element); - let instance = to_mangled(&out.namespace, &element); + let inner = to_typename(&element); + let instance = to_mangled(&element); writeln!(out, "#ifndef CXXBRIDGE05_VECTOR_{}", instance); writeln!(out, "#define CXXBRIDGE05_VECTOR_{}", instance); diff --git a/macro/src/expand.rs b/macro/src/expand.rs index 4a4ec642b..48902841f 100644 --- a/macro/src/expand.rs +++ b/macro/src/expand.rs @@ -1,12 +1,11 @@ use crate::derive::DeriveAttribute; use crate::syntax::atom::Atom::{self, *}; use crate::syntax::file::Module; -use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::symbol::Symbol; use crate::syntax::{ - self, check, mangle, Api, Enum, ExternFn, ExternType, Impl, Signature, Struct, Type, TypeAlias, - Types, + self, check, mangle, Api, Enum, ExternFn, ExternType, Impl, QualifiedIdent, Signature, Struct, + Type, TypeAlias, Types, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned, ToTokens}; @@ -17,11 +16,11 @@ pub fn bridge(mut ffi: Module) -> Result { let ref mut errors = Errors::new(); let content = mem::take(&mut ffi.content); let trusted = ffi.unsafety.is_some(); - let ref apis = syntax::parse_items(errors, content, trusted); + let namespace = &ffi.namespace; + let ref apis = syntax::parse_items(errors, content, trusted, namespace); let ref types = Types::collect(errors, apis); errors.propagate()?; - let namespace = &ffi.namespace; - check::typecheck(errors, namespace, apis, types); + check::typecheck(errors, apis, types); errors.propagate()?; Ok(expand(ffi, apis, types)) @@ -30,7 +29,7 @@ pub fn bridge(mut ffi: Module) -> Result { fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { let mut expanded = TokenStream::new(); let mut hidden = TokenStream::new(); - let namespace = &ffi.namespace; + //let namespace = &ffi.namespace; for api in apis { if let Api::RustType(ety) = api { @@ -42,23 +41,21 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { for api in apis { match api { Api::Include(_) | Api::RustType(_) | Api::Impl(_) => {} - Api::Struct(strct) => expanded.extend(expand_struct(namespace, strct)), - Api::Enum(enm) => expanded.extend(expand_enum(namespace, enm)), + Api::Struct(strct) => expanded.extend(expand_struct(strct)), + Api::Enum(enm) => expanded.extend(expand_enum(enm)), Api::CxxType(ety) => { let ident = &ety.ident; if !types.structs.contains_key(ident) && !types.enums.contains_key(ident) { - expanded.extend(expand_cxx_type(namespace, ety)); + expanded.extend(expand_cxx_type(ety)); } } Api::CxxFunction(efn) => { - expanded.extend(expand_cxx_function_shim(namespace, efn, types)); - } - Api::RustFunction(efn) => { - hidden.extend(expand_rust_function_shim(namespace, efn, types)) + expanded.extend(expand_cxx_function_shim(efn, types)); } + Api::RustFunction(efn) => hidden.extend(expand_rust_function_shim(efn, types)), Api::TypeAlias(alias) => { expanded.extend(expand_type_alias(alias)); - hidden.extend(expand_type_alias_verify(namespace, alias, types)); + hidden.extend(expand_type_alias_verify(alias, types)); } } } @@ -67,33 +64,33 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { let explicit_impl = types.explicit_impls.get(ty); if let Type::RustBox(ty) = ty { if let Type::Ident(ident) = &ty.inner { - if Atom::from(ident).is_none() { - hidden.extend(expand_rust_box(namespace, ident)); + if Atom::from_qualified_ident(ident).is_none() { + hidden.extend(expand_rust_box(ident)); } } } else if let Type::RustVec(ty) = ty { if let Type::Ident(ident) = &ty.inner { - if Atom::from(ident).is_none() { - hidden.extend(expand_rust_vec(namespace, ident)); + if Atom::from_qualified_ident(ident).is_none() { + hidden.extend(expand_rust_vec(ident)); } } } else if let Type::UniquePtr(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() + if Atom::from_qualified_ident(ident).is_none() && (explicit_impl.is_some() || !types.aliases.contains_key(ident)) { - expanded.extend(expand_unique_ptr(namespace, ident, types, explicit_impl)); + expanded.extend(expand_unique_ptr(ident, types, explicit_impl)); } } } else if let Type::CxxVector(ptr) = ty { if let Type::Ident(ident) = &ptr.inner { - if Atom::from(ident).is_none() + if Atom::from_qualified_ident(ident).is_none() && (explicit_impl.is_some() || !types.aliases.contains_key(ident)) { // Generate impl for CxxVector if T is a struct or opaque // C++ type. Impl for primitives is already provided by cxx // crate. - expanded.extend(expand_cxx_vector(namespace, ident, explicit_impl)); + expanded.extend(expand_cxx_vector(ident, explicit_impl)); } } } @@ -126,11 +123,11 @@ fn expand(ffi: Module, apis: &[Api], types: &Types) -> TokenStream { } } -fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { +fn expand_struct(strct: &Struct) -> TokenStream { let ident = &strct.ident; let doc = &strct.doc; let derives = DeriveAttribute(&strct.derives); - let type_id = type_id(namespace, ident); + let type_id = type_id(ident); let fields = strct.fields.iter().map(|field| { // This span on the pub makes "private type in public interface" errors // appear in the right place. @@ -153,11 +150,11 @@ fn expand_struct(namespace: &Namespace, strct: &Struct) -> TokenStream { } } -fn expand_enum(namespace: &Namespace, enm: &Enum) -> TokenStream { +fn expand_enum(enm: &Enum) -> TokenStream { let ident = &enm.ident; let doc = &enm.doc; let repr = enm.repr; - let type_id = type_id(namespace, ident); + let type_id = type_id(ident); let variants = enm.variants.iter().map(|variant| { let variant_ident = &variant.ident; let discriminant = &variant.discriminant; @@ -186,10 +183,10 @@ fn expand_enum(namespace: &Namespace, enm: &Enum) -> TokenStream { } } -fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { +fn expand_cxx_type(ety: &ExternType) -> TokenStream { let ident = &ety.ident; let doc = &ety.doc; - let type_id = type_id(namespace, ident); + let type_id = type_id(ident); quote! { #doc @@ -205,7 +202,7 @@ fn expand_cxx_type(namespace: &Namespace, ety: &ExternType) -> TokenStream { } } -fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { +fn expand_cxx_function_decl(efn: &ExternFn, types: &Types) -> TokenStream { let receiver = efn.receiver.iter().map(|receiver| { let receiver_type = receiver.ty(); quote!(_: #receiver_type) @@ -236,7 +233,7 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types let ret = expand_extern_type(efn.ret.as_ref().unwrap()); outparam = Some(quote!(__return: *mut #ret)); } - let link_name = mangle::extern_fn(namespace, efn); + let link_name = mangle::extern_fn(efn); let local_name = format_ident!("__{}", efn.ident.rust); quote! { #[link_name = #link_name] @@ -244,9 +241,9 @@ fn expand_cxx_function_decl(namespace: &Namespace, efn: &ExternFn, types: &Types } } -fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { +fn expand_cxx_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { let doc = &efn.doc; - let decl = expand_cxx_function_decl(namespace, efn, types); + let decl = expand_cxx_function_decl(efn, types); let receiver = efn.receiver.iter().map(|receiver| { let ampersand = receiver.ampersand; let mutability = receiver.mutability; @@ -306,9 +303,7 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types .filter_map(|arg| { if let Type::Fn(f) = &arg.ty { let var = &arg.ident; - Some(expand_function_pointer_trampoline( - namespace, efn, var, f, types, - )) + Some(expand_function_pointer_trampoline(efn, var, f, types)) } else { None } @@ -445,14 +440,13 @@ fn expand_cxx_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types } fn expand_function_pointer_trampoline( - namespace: &Namespace, efn: &ExternFn, var: &Ident, sig: &Signature, types: &Types, ) -> TokenStream { - let c_trampoline = mangle::c_trampoline(namespace, efn, var); - let r_trampoline = mangle::r_trampoline(namespace, efn, var); + let c_trampoline = mangle::c_trampoline(efn, var); + let r_trampoline = mangle::r_trampoline(efn, var); let local_name = parse_quote!(__); let catch_unwind_label = format!("::{}::{}", efn.ident.rust, var); let shim = expand_rust_function_shim_impl( @@ -508,8 +502,8 @@ fn expand_rust_type_assert_sized(ety: &ExternType) -> TokenStream { } } -fn expand_rust_function_shim(namespace: &Namespace, efn: &ExternFn, types: &Types) -> TokenStream { - let link_name = mangle::extern_fn(namespace, efn); +fn expand_rust_function_shim(efn: &ExternFn, types: &Types) -> TokenStream { + let link_name = mangle::extern_fn(efn); let local_name = format_ident!("__{}", efn.ident.rust); let catch_unwind_label = format!("::{}", efn.ident.rust); let invoke = Some(&efn.ident.rust); @@ -686,13 +680,9 @@ fn expand_type_alias(alias: &TypeAlias) -> TokenStream { } } -fn expand_type_alias_verify( - namespace: &Namespace, - alias: &TypeAlias, - types: &Types, -) -> TokenStream { +fn expand_type_alias_verify(alias: &TypeAlias, types: &Types) -> TokenStream { let ident = &alias.ident; - let type_id = type_id(namespace, ident); + let type_id = type_id(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::<); @@ -712,21 +702,15 @@ fn expand_type_alias_verify( verify } -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(); - +fn type_id(ident: &QualifiedIdent) -> TokenStream { + let path = ident.to_fully_qualified(); quote! { ::cxx::type_id!(#path) } } -fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge05$box${}{}$", namespace, ident); +fn expand_rust_box(ident: &QualifiedIdent) -> TokenStream { + let link_prefix = format!("cxxbridge05$box${}$", ident.to_symbol()); let link_uninit = format!("{}uninit", link_prefix); let link_drop = format!("{}drop", link_prefix); @@ -754,8 +738,8 @@ fn expand_rust_box(namespace: &Namespace, ident: &Ident) -> TokenStream { } } -fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { - let link_prefix = format!("cxxbridge05$rust_vec${}{}$", namespace, elem); +fn expand_rust_vec(elem: &QualifiedIdent) -> TokenStream { + let link_prefix = format!("cxxbridge05$rust_vec${}$", elem.to_symbol()); let link_new = format!("{}new", link_prefix); let link_drop = format!("{}drop", link_prefix); let link_len = format!("{}len", link_prefix); @@ -800,13 +784,12 @@ fn expand_rust_vec(namespace: &Namespace, elem: &Ident) -> TokenStream { } fn expand_unique_ptr( - namespace: &Namespace, - ident: &Ident, + ident: &QualifiedIdent, types: &Types, explicit_impl: Option<&Impl>, ) -> TokenStream { - let name = ident.to_string(); - let prefix = format!("cxxbridge05$unique_ptr${}{}$", namespace, ident); + let name = ident.to_fully_qualified(); + let prefix = format!("cxxbridge05$unique_ptr${}$", ident.to_symbol()); let link_null = format!("{}null", prefix); let link_new = format!("{}new", prefix); let link_raw = format!("{}raw", prefix); @@ -882,17 +865,13 @@ fn expand_unique_ptr( } } -fn expand_cxx_vector( - namespace: &Namespace, - elem: &Ident, - explicit_impl: Option<&Impl>, -) -> TokenStream { +fn expand_cxx_vector(elem: &QualifiedIdent, explicit_impl: Option<&Impl>) -> TokenStream { let _ = explicit_impl; - let name = elem.to_string(); - let prefix = format!("cxxbridge05$std$vector${}{}$", namespace, elem); + let name = elem.to_fully_qualified(); + let prefix = format!("cxxbridge05$std$vector${}$", elem.to_symbol()); let link_size = format!("{}size", prefix); let link_get_unchecked = format!("{}get_unchecked", prefix); - let unique_ptr_prefix = format!("cxxbridge05$unique_ptr$std$vector${}{}$", namespace, elem); + let unique_ptr_prefix = format!("cxxbridge05$unique_ptr$std$vector${}$", elem.to_symbol()); let link_unique_ptr_null = format!("{}null", unique_ptr_prefix); let link_unique_ptr_raw = format!("{}raw", unique_ptr_prefix); let link_unique_ptr_get = format!("{}get", unique_ptr_prefix); diff --git a/syntax/atom.rs b/syntax/atom.rs index 6e5fa8801..a8b537f8c 100644 --- a/syntax/atom.rs +++ b/syntax/atom.rs @@ -1,3 +1,4 @@ +use crate::syntax::QualifiedIdent; use crate::syntax::Type; use proc_macro2::Ident; use std::fmt::{self, Display}; @@ -22,6 +23,14 @@ pub enum Atom { } impl Atom { + pub fn from_qualified_ident(ident: &QualifiedIdent) -> Option { + if !ident.ns.is_empty() { + None + } else { + Self::from(&ident.ident) + } + } + pub fn from(ident: &Ident) -> Option { Self::from_str(ident.to_string().as_str()) } @@ -93,6 +102,18 @@ impl PartialEq for &Ident { } } +impl PartialEq for &QualifiedIdent { + fn eq(&self, atom: &Atom) -> bool { + self.ns.is_empty() && self.ident == atom + } +} + +impl PartialEq for QualifiedIdent { + fn eq(&self, atom: &Atom) -> bool { + self.ns.is_empty() && self.ident == atom + } +} + impl PartialEq for &Type { fn eq(&self, atom: &Atom) -> bool { *self == atom diff --git a/syntax/check.rs b/syntax/check.rs index 2f3c33483..cafcdf0f0 100644 --- a/syntax/check.rs +++ b/syntax/check.rs @@ -1,25 +1,22 @@ use crate::syntax::atom::Atom::{self, *}; -use crate::syntax::namespace::Namespace; use crate::syntax::report::Errors; use crate::syntax::types::TrivialReason; use crate::syntax::{ - error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, Receiver, Ref, Slice, Struct, Ty1, - Type, Types, + error, ident, Api, Enum, ExternFn, ExternType, Impl, Lang, QualifiedIdent, Receiver, Ref, + Slice, Struct, Ty1, Type, Types, }; -use proc_macro2::{Delimiter, Group, Ident, TokenStream}; +use proc_macro2::{Delimiter, Group, TokenStream}; use quote::{quote, ToTokens}; use std::fmt::Display; pub(crate) struct Check<'a> { - namespace: &'a Namespace, apis: &'a [Api], types: &'a Types<'a>, errors: &'a mut Errors, } -pub(crate) fn typecheck(cx: &mut Errors, namespace: &Namespace, apis: &[Api], types: &Types) { +pub(crate) fn typecheck(cx: &mut Errors, apis: &[Api], types: &Types) { do_typecheck(&mut Check { - namespace, apis, types, errors: cx, @@ -27,7 +24,7 @@ pub(crate) fn typecheck(cx: &mut Errors, namespace: &Namespace, apis: &[Api], ty } fn do_typecheck(cx: &mut Check) { - ident::check_all(cx, cx.namespace, cx.apis); + ident::check_all(cx, cx.apis); for ty in cx.types { match ty { @@ -60,14 +57,17 @@ impl Check<'_> { } } -fn check_type_ident(cx: &mut Check, ident: &Ident) { - if Atom::from(ident).is_none() +fn check_type_ident(cx: &mut Check, ident: &QualifiedIdent) { + if Atom::from_qualified_ident(ident).is_none() && !cx.types.structs.contains_key(ident) && !cx.types.enums.contains_key(ident) && !cx.types.cxx.contains(ident) && !cx.types.rust.contains(ident) { - cx.error(ident, "unsupported type"); + cx.error( + ident, + &format!("unsupported type: {}", ident.to_fully_qualified()), + ); } } @@ -80,7 +80,7 @@ fn check_type_box(cx: &mut Check, ptr: &Ty1) { cx.error(ptr, error::BOX_CXX_TYPE.msg); } - if Atom::from(ident).is_none() { + if Atom::from_qualified_ident(ident).is_none() { return; } } @@ -98,7 +98,7 @@ fn check_type_rust_vec(cx: &mut Check, ty: &Ty1) { return; } - match Atom::from(ident) { + match Atom::from_qualified_ident(ident) { None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) => return, @@ -116,7 +116,7 @@ fn check_type_unique_ptr(cx: &mut Check, ptr: &Ty1) { cx.error(ptr, "unique_ptr of a Rust type is not supported yet"); } - match Atom::from(ident) { + match Atom::from_qualified_ident(ident) { None | Some(CxxString) => return, _ => {} } @@ -136,7 +136,7 @@ fn check_type_cxx_vector(cx: &mut Check, ptr: &Ty1) { ); } - match Atom::from(ident) { + match Atom::from_qualified_ident(ident) { None | Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(Usize) | Some(I8) | Some(I16) | Some(I32) | Some(I64) | Some(Isize) | Some(F32) | Some(F64) | Some(CxxString) => return, @@ -213,7 +213,9 @@ fn check_api_type(cx: &mut Check, ety: &ExternType) { if let Some(reason) = cx.types.required_trivial.get(&ety.ident) { let what = match reason { - TrivialReason::StructField(strct) => format!("a field of `{}`", strct.ident), + TrivialReason::StructField(strct) => { + format!("a field of `{}`", strct.ident.to_fully_qualified()) + } TrivialReason::FunctionArgument(efn) => format!("an argument of `{}`", efn.ident.rust), TrivialReason::FunctionReturn(efn) => format!("a return value of `{}`", efn.ident.rust), }; @@ -229,7 +231,7 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { if let Some(receiver) = &efn.receiver { let ref span = span_for_receiver_error(receiver); - if receiver.ty == "Self" { + if receiver.ty.is_self() { let mutability = match receiver.mutability { Some(_) => "mut ", None => "", @@ -290,7 +292,7 @@ fn check_api_fn(cx: &mut Check, efn: &ExternFn) { fn check_api_impl(cx: &mut Check, imp: &Impl) { if let Type::UniquePtr(ty) | Type::CxxVector(ty) = &imp.ty { if let Type::Ident(inner) = &ty.inner { - if Atom::from(inner).is_none() { + if Atom::from_qualified_ident(inner).is_none() { return; } } @@ -344,12 +346,12 @@ fn check_multiple_arg_lifetimes(cx: &mut Check, efn: &ExternFn) { } } -fn check_reserved_name(cx: &mut Check, ident: &Ident) { +fn check_reserved_name(cx: &mut Check, ident: &QualifiedIdent) { if ident == "Box" || ident == "UniquePtr" || ident == "Vec" || ident == "CxxVector" - || Atom::from(ident).is_some() + || Atom::from_qualified_ident(ident).is_some() { cx.error(ident, "reserved name"); } @@ -410,10 +412,10 @@ fn describe(cx: &mut Check, ty: &Type) -> String { "opaque C++ type".to_owned() } else if cx.types.rust.contains(ident) { "opaque Rust type".to_owned() - } else if Atom::from(ident) == Some(CxxString) { + } else if Atom::from_qualified_ident(ident) == Some(CxxString) { "C++ string".to_owned() } else { - ident.to_string() + ident.to_fully_qualified() } } Type::RustBox(_) => "Box".to_owned(), diff --git a/syntax/ident.rs b/syntax/ident.rs index 66f736510..1538909ff 100644 --- a/syntax/ident.rs +++ b/syntax/ident.rs @@ -1,6 +1,5 @@ use crate::syntax::check::Check; -use crate::syntax::namespace::Namespace; -use crate::syntax::{error, Api}; +use crate::syntax::{error, Api, QualifiedIdent}; use proc_macro2::Ident; fn check(cx: &mut Check, ident: &Ident) { @@ -13,28 +12,31 @@ fn check(cx: &mut Check, ident: &Ident) { } } -pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { - for segment in namespace { +fn check_ident(cx: &mut Check, ident: &QualifiedIdent) { + for segment in &ident.ns { check(cx, segment); } + check(cx, &ident.ident); +} +pub(crate) fn check_all(cx: &mut Check, apis: &[Api]) { for api in apis { match api { Api::Include(_) | Api::Impl(_) => {} Api::Struct(strct) => { - check(cx, &strct.ident); + check_ident(cx, &strct.ident); for field in &strct.fields { check(cx, &field.ident); } } Api::Enum(enm) => { - check(cx, &enm.ident); + check_ident(cx, &enm.ident); for variant in &enm.variants { check(cx, &variant.ident); } } Api::CxxType(ety) | Api::RustType(ety) => { - check(cx, &ety.ident); + check_ident(cx, &ety.ident); } Api::CxxFunction(efn) | Api::RustFunction(efn) => { check(cx, &efn.ident.rust); @@ -43,7 +45,7 @@ pub(crate) fn check_all(cx: &mut Check, namespace: &Namespace, apis: &[Api]) { } } Api::TypeAlias(alias) => { - check(cx, &alias.ident); + check_ident(cx, &alias.ident); } } } diff --git a/syntax/mangle.rs b/syntax/mangle.rs index e4618875d..bf5bfdfa0 100644 --- a/syntax/mangle.rs +++ b/syntax/mangle.rs @@ -1,4 +1,3 @@ -use crate::syntax::namespace::Namespace; use crate::syntax::symbol::{self, Symbol}; use crate::syntax::ExternFn; use proc_macro2::Ident; @@ -11,19 +10,19 @@ macro_rules! join { }; } -pub fn extern_fn(namespace: &Namespace, efn: &ExternFn) -> Symbol { +pub fn extern_fn(efn: &ExternFn) -> Symbol { match &efn.receiver { - Some(receiver) => join!(namespace, CXXBRIDGE, receiver.ty, efn.ident.rust), - None => join!(namespace, CXXBRIDGE, efn.ident.rust), + Some(receiver) => join!(efn.ident.cxx.ns, CXXBRIDGE, receiver.ty, efn.ident.rust), + None => join!(efn.ident.cxx.ns, CXXBRIDGE, efn.ident.rust), } } // The C half of a function pointer trampoline. -pub fn c_trampoline(namespace: &Namespace, efn: &ExternFn, var: &Ident) -> Symbol { - join!(extern_fn(namespace, efn), var, 0) +pub fn c_trampoline(efn: &ExternFn, var: &Ident) -> Symbol { + join!(extern_fn(efn), var, 0) } // The Rust half of a function pointer trampoline. -pub fn r_trampoline(namespace: &Namespace, efn: &ExternFn, var: &Ident) -> Symbol { - join!(extern_fn(namespace, efn), var, 1) +pub fn r_trampoline(efn: &ExternFn, var: &Ident) -> Symbol { + join!(extern_fn(efn), var, 1) } diff --git a/syntax/mod.rs b/syntax/mod.rs index c8dea67f2..3009288f4 100644 --- a/syntax/mod.rs +++ b/syntax/mod.rs @@ -21,8 +21,12 @@ mod tokens; pub mod types; use self::discriminant::Discriminant; +use self::namespace::Namespace; use self::parse::kw; -use proc_macro2::{Ident, Span}; +use self::symbol::Symbol; +use core::fmt::{Formatter, Result}; +use proc_macro2::{Ident, Span, TokenStream}; +use quote::{IdentFragment, ToTokens}; use syn::punctuated::Punctuated; use syn::token::{Brace, Bracket, Paren}; use syn::{Expr, Lifetime, Token, Type as RustType}; @@ -33,6 +37,16 @@ pub use self::doc::Doc; pub use self::parse::parse_items; pub use self::types::Types; +#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] +/// A C++ identifier in a particular namespace. +/// It is intentional that this does not impl Display, +/// because we want to force users actively to decide whether to output +/// it as a qualified name or as an unqualfiied name. +pub struct QualifiedIdent { + pub ns: Namespace, + pub ident: Ident, +} + pub enum Api { Include(String), Struct(Struct), @@ -48,7 +62,7 @@ pub enum Api { pub struct ExternType { pub doc: Doc, pub type_token: Token![type], - pub ident: Ident, + pub ident: QualifiedIdent, pub semi_token: Token![;], pub trusted: bool, } @@ -57,7 +71,7 @@ pub struct Struct { pub doc: Doc, pub derives: Vec, pub struct_token: Token![struct], - pub ident: Ident, + pub ident: QualifiedIdent, pub brace_token: Brace, pub fields: Vec, } @@ -65,14 +79,14 @@ pub struct Struct { pub struct Enum { pub doc: Doc, pub enum_token: Token![enum], - pub ident: Ident, + pub ident: QualifiedIdent, pub brace_token: Brace, pub variants: Vec, pub repr: Atom, } pub struct Pair { - pub cxx: Ident, + pub cxx: QualifiedIdent, pub rust: Ident, } @@ -87,7 +101,7 @@ pub struct ExternFn { pub struct TypeAlias { pub doc: Doc, pub type_token: Token![type], - pub ident: Ident, + pub ident: QualifiedIdent, pub eq_token: Token![=], pub ty: RustType, pub semi_token: Token![;], @@ -112,7 +126,7 @@ pub struct Signature { #[derive(Eq, PartialEq, Hash)] pub struct Var { - pub ident: Ident, + pub ident: Ident, // fields and variables are not namespaced pub ty: Type, } @@ -121,7 +135,7 @@ pub struct Receiver { pub lifetime: Option, pub mutability: Option, pub var: Token![self], - pub ty: Ident, + pub ty: QualifiedIdent, pub shorthand: bool, } @@ -132,7 +146,7 @@ pub struct Variant { } pub enum Type { - Ident(Ident), + Ident(QualifiedIdent), RustBox(Box), RustVec(Box), UniquePtr(Box), @@ -146,7 +160,7 @@ pub enum Type { } pub struct Ty1 { - pub name: Ident, + pub name: QualifiedIdent, pub langle: Token![<], pub inner: Type, pub rangle: Token![>], @@ -169,3 +183,108 @@ pub enum Lang { Cxx, Rust, } + +impl Api { + pub fn get_namespace(&self) -> Option<&Namespace> { + match self { + Api::CxxFunction(cfn) => Some(&cfn.ident.cxx.ns), + Api::CxxType(cty) => Some(&cty.ident.ns), + Api::Enum(enm) => Some(&enm.ident.ns), + Api::Struct(strct) => Some(&strct.ident.ns), + Api::TypeAlias(ta) => Some(&ta.ident.ns), + Api::RustType(rty) => Some(&rty.ident.ns), + Api::RustFunction(rfn) => Some(&rfn.ident.cxx.ns), + Api::Impl(_) | Api::Include(_) => None, + } + } +} + +impl QualifiedIdent { + /// Use this constructor if the name is always qualified according to + /// the namespace. + pub fn new_never_primitive(ns: &Namespace, ident: Ident) -> Self { + Self { + ns: ns.clone(), + ident, + } + } + + /// If there's a chance that the name is not fully-qualified, but + /// is instead a built-in type (e.g. i32, CxxString, str) then + /// use this constructor. This is a temporary hack. Eventually we'll + /// need a later phase to go through and resolve all unresolved + /// idents according to the current available symbols and 'use' + /// statements that are in use (which will include an implicit + /// 'use' statement covering these standard types.) At the moment + /// there is no such resolution pass, so we aim to try to resolve + /// all idents at construction time. + pub fn new_maybe_primitive(ns: &Namespace, ident: Ident) -> Self { + let is_primitive = Atom::from(&ident).is_some() || ident == "str" || ident == "UniquePtr"; + Self { + ns: if is_primitive { + Namespace::none() + } else { + ns.clone() + }, + ident, + } + } + + pub fn make_self(span: Span) -> Self { + QualifiedIdent { + ns: Namespace::none(), + ident: Token![Self](span).into(), + } + } + + pub fn is_self(&self) -> bool { + self.ns.is_empty() && self.ident == "Self" + } + + pub fn span(&self) -> Span { + self.ident.span() + } + + fn iter_all_segments( + &self, + ) -> std::iter::Chain, std::iter::Once<&Ident>> { + self.ns.iter().chain(std::iter::once(&self.ident)) + } + + fn join(&self, sep: &str) -> String { + self.iter_all_segments() + .map(|s| s.to_string()) + .collect::>() + .join(sep) + } + + pub fn to_symbol(&self) -> Symbol { + Symbol::from_idents(self.iter_all_segments()) + } + + pub fn to_fully_qualified(&self) -> String { + format!("::{}", self.join("::")) + } +} + +impl ToTokens for QualifiedIdent { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.ident.to_tokens(tokens); + } +} + +impl PartialEq for QualifiedIdent { + fn eq(&self, other: &str) -> bool { + self.ns.is_empty() && self.ident == *other + } +} + +impl IdentFragment for QualifiedIdent { + fn fmt(&self, f: &mut Formatter) -> Result { + for seg in self.iter_all_segments() { + f.write_str(&seg.to_string())?; + f.write_str("__")?; + } + Ok(()) + } +} diff --git a/syntax/namespace.rs b/syntax/namespace.rs index 49b31d14f..a731315ee 100644 --- a/syntax/namespace.rs +++ b/syntax/namespace.rs @@ -8,8 +8,7 @@ use syn::{Ident, Token}; mod kw { syn::custom_keyword!(namespace); } - -#[derive(Clone)] +#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct Namespace { segments: Vec, } @@ -24,6 +23,10 @@ impl Namespace { pub fn iter(&self) -> Iter { self.segments.iter() } + + pub fn is_empty(&self) -> bool { + self.segments.is_empty() + } } impl Parse for Namespace { diff --git a/syntax/parse.rs b/syntax/parse.rs index 367ad059f..35f351c30 100644 --- a/syntax/parse.rs +++ b/syntax/parse.rs @@ -3,8 +3,8 @@ use crate::syntax::file::{Item, ItemForeignMod}; use crate::syntax::report::Errors; use crate::syntax::Atom::*; use crate::syntax::{ - attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Pair, Receiver, Ref, Signature, - Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, + attrs, error, Api, Doc, Enum, ExternFn, ExternType, Impl, Lang, Namespace, Pair, + QualifiedIdent, Receiver, Ref, Signature, Slice, Struct, Ty1, Type, TypeAlias, Var, Variant, }; use proc_macro2::{Delimiter, Group, TokenStream, TokenTree}; use quote::{format_ident, quote, quote_spanned}; @@ -20,20 +20,22 @@ pub mod kw { syn::custom_keyword!(Result); } -pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool) -> Vec { +pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool, ns: &Namespace) -> Vec { let mut apis = Vec::new(); for item in items { match item { - Item::Struct(item) => match parse_struct(cx, item) { + Item::Struct(item) => match parse_struct(cx, item, ns) { Ok(strct) => apis.push(strct), Err(err) => cx.push(err), }, - Item::Enum(item) => match parse_enum(cx, item) { + Item::Enum(item) => match parse_enum(cx, item, ns) { Ok(enm) => apis.push(enm), Err(err) => cx.push(err), }, - Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis, trusted), - Item::Impl(item) => match parse_impl(item) { + Item::ForeignMod(foreign_mod) => { + parse_foreign_mod(cx, foreign_mod, &mut apis, trusted, ns) + } + Item::Impl(item) => match parse_impl(item, ns) { Ok(imp) => apis.push(imp), Err(err) => cx.push(err), }, @@ -44,7 +46,7 @@ pub fn parse_items(cx: &mut Errors, items: Vec, trusted: bool) -> Vec apis } -fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { +fn parse_struct(cx: &mut Errors, item: ItemStruct, ns: &Namespace) -> Result { let generics = &item.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { let struct_token = item.struct_token; @@ -81,7 +83,7 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { doc, derives, struct_token: item.struct_token, - ident: item.ident, + ident: QualifiedIdent::new_never_primitive(ns, item.ident), brace_token: fields.brace_token, fields: fields .named @@ -89,14 +91,14 @@ fn parse_struct(cx: &mut Errors, item: ItemStruct) -> Result { .map(|field| { Ok(Var { ident: field.ident.unwrap(), - ty: parse_type(&field.ty)?, + ty: parse_type(&field.ty, ns)?, }) }) .collect::>()?, })) } -fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { +fn parse_enum(cx: &mut Errors, item: ItemEnum, ns: &Namespace) -> Result { let generics = &item.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { let enum_token = item.enum_token; @@ -167,7 +169,7 @@ fn parse_enum(cx: &mut Errors, item: ItemEnum) -> Result { Ok(Api::Enum(Enum { doc, enum_token, - ident: item.ident, + ident: QualifiedIdent::new_never_primitive(ns, item.ident), brace_token, variants, repr, @@ -179,6 +181,7 @@ fn parse_foreign_mod( foreign_mod: ItemForeignMod, out: &mut Vec, trusted: bool, + ns: &Namespace, ) { let lang = match parse_lang(&foreign_mod.abi) { Ok(lang) => lang, @@ -202,11 +205,11 @@ fn parse_foreign_mod( let mut items = Vec::new(); for foreign in &foreign_mod.items { match foreign { - ForeignItem::Type(foreign) => match parse_extern_type(cx, foreign, lang, trusted) { + ForeignItem::Type(foreign) => match parse_extern_type(cx, foreign, lang, trusted, ns) { Ok(ety) => items.push(ety), Err(err) => cx.push(err), }, - ForeignItem::Fn(foreign) => match parse_extern_fn(cx, foreign, lang) { + ForeignItem::Fn(foreign) => match parse_extern_fn(cx, foreign, lang, ns) { Ok(efn) => items.push(efn), Err(err) => cx.push(err), }, @@ -216,7 +219,7 @@ fn parse_foreign_mod( Err(err) => cx.push(err), } } - ForeignItem::Verbatim(tokens) => match parse_extern_verbatim(cx, tokens, lang) { + ForeignItem::Verbatim(tokens) => match parse_extern_verbatim(cx, tokens, lang, ns) { Ok(api) => items.push(api), Err(err) => cx.push(err), }, @@ -234,7 +237,7 @@ fn parse_foreign_mod( for item in &mut items { if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item { if let Some(receiver) = &mut efn.receiver { - if receiver.ty == "Self" { + if receiver.ty.is_self() { receiver.ty = single_type.clone(); } } @@ -267,6 +270,7 @@ fn parse_extern_type( foreign_type: &ForeignItemType, lang: Lang, trusted: bool, + ns: &Namespace, ) -> Result { let doc = attrs::parse_doc(cx, &foreign_type.attrs); let type_token = foreign_type.type_token; @@ -279,13 +283,18 @@ fn parse_extern_type( Ok(api_type(ExternType { doc, type_token, - ident, + ident: QualifiedIdent::new_never_primitive(ns, ident), semi_token, trusted, })) } -fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> Result { +fn parse_extern_fn( + cx: &mut Errors, + foreign_fn: &ForeignItemFn, + lang: Lang, + ns: &Namespace, +) -> Result { let generics = &foreign_fn.sig.generics; if !generics.params.is_empty() || generics.where_clause.is_some() { return Err(Error::new_spanned( @@ -326,7 +335,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R lifetime: lifetime.clone(), mutability: arg.mutability, var: arg.self_token, - ty: Token![Self](arg.self_token.span).into(), + ty: QualifiedIdent::make_self(arg.self_token.span), shorthand: true, }); continue; @@ -341,7 +350,7 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R } _ => return Err(Error::new_spanned(arg, "unsupported signature")), }; - let ty = parse_type(&arg.ty)?; + let ty = parse_type(&arg.ty, ns)?; if ident != "self" { args.push_value(Var { ident, ty }); if let Some(comma) = comma { @@ -368,12 +377,15 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R } let mut throws_tokens = None; - let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?; + let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens, ns)?; let throws = throws_tokens.is_some(); let unsafety = foreign_fn.sig.unsafety; let fn_token = foreign_fn.sig.fn_token; let ident = Pair { - cxx: cxx_name.unwrap_or(foreign_fn.sig.ident.clone()), + cxx: QualifiedIdent::new_never_primitive( + ns, + cxx_name.unwrap_or(foreign_fn.sig.ident.clone()), + ), rust: rust_name.unwrap_or(foreign_fn.sig.ident.clone()), }; let paren_token = foreign_fn.sig.paren_token; @@ -401,7 +413,12 @@ fn parse_extern_fn(cx: &mut Errors, foreign_fn: &ForeignItemFn, lang: Lang) -> R })) } -fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> Result { +fn parse_extern_verbatim( + cx: &mut Errors, + tokens: &TokenStream, + lang: Lang, + ns: &Namespace, +) -> Result { // type Alias = crate::path::to::Type; let parse = |input: ParseStream| -> Result { let attrs = input.call(Attribute::parse_outer)?; @@ -421,7 +438,7 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R Ok(TypeAlias { doc, type_token, - ident, + ident: QualifiedIdent::new_never_primitive(ns, ident), eq_token, ty, semi_token, @@ -440,7 +457,7 @@ fn parse_extern_verbatim(cx: &mut Errors, tokens: &TokenStream, lang: Lang) -> R } } -fn parse_impl(imp: ItemImpl) -> Result { +fn parse_impl(imp: ItemImpl, ns: &Namespace) -> Result { if !imp.items.is_empty() { let mut span = Group::new(Delimiter::Brace, TokenStream::new()); span.set_span(imp.brace_token.span); @@ -466,7 +483,7 @@ fn parse_impl(imp: ItemImpl) -> Result { Ok(Api::Impl(Impl { impl_token: imp.impl_token, - ty: parse_type(&self_ty)?, + ty: parse_type(&self_ty, ns)?, brace_token: imp.brace_token, })) } @@ -503,19 +520,19 @@ fn parse_include(input: ParseStream) -> Result { Err(input.error("expected \"quoted/path/to\" or ")) } -fn parse_type(ty: &RustType) -> Result { +fn parse_type(ty: &RustType, ns: &Namespace) -> Result { match ty { - RustType::Reference(ty) => parse_type_reference(ty), - RustType::Path(ty) => parse_type_path(ty), - RustType::Slice(ty) => parse_type_slice(ty), - RustType::BareFn(ty) => parse_type_fn(ty), + RustType::Reference(ty) => parse_type_reference(ty, ns), + RustType::Path(ty) => parse_type_path(ty, ns), + RustType::Slice(ty) => parse_type_slice(ty, ns), + RustType::BareFn(ty) => parse_type_fn(ty, ns), RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)), _ => Err(Error::new_spanned(ty, "unsupported type")), } } -fn parse_type_reference(ty: &TypeReference) -> Result { - let inner = parse_type(&ty.elem)?; +fn parse_type_reference(ty: &TypeReference, ns: &Namespace) -> Result { + let inner = parse_type(&ty.elem, ns)?; let which = match &inner { Type::Ident(ident) if ident == "str" => { if ty.mutability.is_some() { @@ -538,19 +555,20 @@ fn parse_type_reference(ty: &TypeReference) -> Result { }))) } -fn parse_type_path(ty: &TypePath) -> Result { +fn parse_type_path(ty: &TypePath, ns: &Namespace) -> Result { let path = &ty.path; if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 { let segment = &path.segments[0]; let ident = segment.ident.clone(); + let qualified_ident = QualifiedIdent::new_maybe_primitive(ns, ident.clone()); match &segment.arguments { - PathArguments::None => return Ok(Type::Ident(ident)), + PathArguments::None => return Ok(Type::Ident(qualified_ident)), PathArguments::AngleBracketed(generic) => { if ident == "UniquePtr" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::UniquePtr(Box::new(Ty1 { - name: ident, + name: qualified_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -558,9 +576,9 @@ fn parse_type_path(ty: &TypePath) -> Result { } } else if ident == "CxxVector" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::CxxVector(Box::new(Ty1 { - name: ident, + name: qualified_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -568,9 +586,9 @@ fn parse_type_path(ty: &TypePath) -> Result { } } else if ident == "Box" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::RustBox(Box::new(Ty1 { - name: ident, + name: qualified_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -578,9 +596,9 @@ fn parse_type_path(ty: &TypePath) -> Result { } } else if ident == "Vec" && generic.args.len() == 1 { if let GenericArgument::Type(arg) = &generic.args[0] { - let inner = parse_type(arg)?; + let inner = parse_type(arg, ns)?; return Ok(Type::RustVec(Box::new(Ty1 { - name: ident, + name: qualified_ident, langle: generic.lt_token, inner, rangle: generic.gt_token, @@ -591,18 +609,18 @@ fn parse_type_path(ty: &TypePath) -> Result { PathArguments::Parenthesized(_) => {} } } - Err(Error::new_spanned(ty, "unsupported type")) + Err(Error::new_spanned(ty, "unsupported type path")) } -fn parse_type_slice(ty: &TypeSlice) -> Result { - let inner = parse_type(&ty.elem)?; +fn parse_type_slice(ty: &TypeSlice, ns: &Namespace) -> Result { + let inner = parse_type(&ty.elem, ns)?; Ok(Type::Slice(Box::new(Slice { bracket: ty.bracket_token, inner, }))) } -fn parse_type_fn(ty: &TypeBareFn) -> Result { +fn parse_type_fn(ty: &TypeBareFn, ns: &Namespace) -> Result { if ty.lifetimes.is_some() { return Err(Error::new_spanned( ty, @@ -620,7 +638,7 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result { .iter() .enumerate() .map(|(i, arg)| { - let ty = parse_type(&arg.ty)?; + let ty = parse_type(&arg.ty, ns)?; let ident = match &arg.name { Some(ident) => ident.0.clone(), None => format_ident!("_{}", i), @@ -629,7 +647,7 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result { }) .collect::>()?; let mut throws_tokens = None; - let ret = parse_return_type(&ty.output, &mut throws_tokens)?; + let ret = parse_return_type(&ty.output, &mut throws_tokens, ns)?; let throws = throws_tokens.is_some(); Ok(Type::Fn(Box::new(Signature { unsafety: ty.unsafety, @@ -646,6 +664,7 @@ fn parse_type_fn(ty: &TypeBareFn) -> Result { fn parse_return_type( ty: &ReturnType, throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>, + ns: &Namespace, ) -> Result> { let mut ret = match ty { ReturnType::Default => return Ok(None), @@ -667,7 +686,7 @@ fn parse_return_type( } } } - match parse_type(ret)? { + match parse_type(ret, ns)? { Type::Void(_) => Ok(None), ty => Ok(Some(ty)), } diff --git a/syntax/qualified.rs b/syntax/qualified.rs index be9bceb43..5eefb8dbe 100644 --- a/syntax/qualified.rs +++ b/syntax/qualified.rs @@ -10,6 +10,7 @@ impl QualifiedName { pub fn parse_unquoted(input: ParseStream) -> Result { let mut segments = Vec::new(); let mut trailing_punct = true; + input.parse::>()?; while trailing_punct && input.peek(Ident::peek_any) { let ident = Ident::parse_any(input)?; segments.push(ident); diff --git a/syntax/symbol.rs b/syntax/symbol.rs index 1e5b5131d..bce40cb39 100644 --- a/syntax/symbol.rs +++ b/syntax/symbol.rs @@ -1,4 +1,5 @@ use crate::syntax::namespace::Namespace; +use crate::syntax::QualifiedIdent; use proc_macro2::{Ident, TokenStream}; use quote::ToTokens; use std::fmt::{self, Display, Write}; @@ -19,12 +20,6 @@ impl ToTokens for Symbol { } } -impl From<&Ident> for Symbol { - fn from(ident: &Ident) -> Self { - Symbol(ident.to_string()) - } -} - impl Symbol { fn push(&mut self, segment: &dyn Display) { let len_before = self.0.len(); @@ -34,18 +29,47 @@ impl Symbol { self.0.write_fmt(format_args!("{}", segment)).unwrap(); assert!(self.0.len() > len_before); } + + pub fn from_idents<'a, T: Iterator>(it: T) -> Self { + let mut symbol = Symbol(String::new()); + for segment in it { + segment.write(&mut symbol); + } + assert!(!symbol.0.is_empty()); + symbol + } + + /// For example, for taking a symbol and then making a new symbol + /// for a vec of that symbol. + pub fn prefix_with(&self, prefix: &str) -> Symbol { + Symbol(format!("{}{}", prefix, self.to_string())) + } +} + +pub trait Segment { + fn write(&self, symbol: &mut Symbol); } -pub trait Segment: Display { +impl Segment for str { + fn write(&self, symbol: &mut Symbol) { + 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); } } - -impl Segment for str {} -impl Segment for usize {} -impl Segment for Ident {} -impl Segment for Symbol {} impl Segment for Namespace { fn write(&self, symbol: &mut Symbol) { @@ -55,9 +79,16 @@ impl Segment for Namespace { } } +impl Segment for QualifiedIdent { + fn write(&self, symbol: &mut Symbol) { + self.ns.write(symbol); + self.ident.write(symbol); + } +} + impl Segment for &'_ T where - T: ?Sized + Segment, + T: ?Sized + Segment + Display, { fn write(&self, symbol: &mut Symbol) { (**self).write(symbol); diff --git a/syntax/tokens.rs b/syntax/tokens.rs index 7618e99da..59d76603a 100644 --- a/syntax/tokens.rs +++ b/syntax/tokens.rs @@ -39,7 +39,7 @@ impl ToTokens for Var { impl ToTokens for Ty1 { fn to_tokens(&self, tokens: &mut TokenStream) { let span = self.name.span(); - let name = self.name.to_string(); + let name = self.name.ident.to_string(); if let "UniquePtr" | "CxxVector" = name.as_str() { tokens.extend(quote_spanned!(span=> ::cxx::)); } else if name == "Vec" { diff --git a/syntax/types.rs b/syntax/types.rs index 5bac76e42..e8ffcc6cd 100644 --- a/syntax/types.rs +++ b/syntax/types.rs @@ -1,20 +1,22 @@ use crate::syntax::atom::Atom::{self, *}; use crate::syntax::report::Errors; use crate::syntax::set::OrderedSet as Set; -use crate::syntax::{Api, Derive, Enum, ExternFn, ExternType, Impl, Struct, Type, TypeAlias}; +use crate::syntax::{ + Api, Derive, Enum, ExternFn, ExternType, Impl, QualifiedIdent, Struct, Type, TypeAlias, +}; use proc_macro2::Ident; use quote::ToTokens; use std::collections::{BTreeMap as Map, HashSet as UnorderedSet}; pub struct Types<'a> { pub all: Set<&'a Type>, - pub structs: Map<&'a Ident, &'a Struct>, - pub enums: Map<&'a Ident, &'a Enum>, - pub cxx: Set<&'a Ident>, - pub rust: Set<&'a Ident>, - pub aliases: Map<&'a Ident, &'a TypeAlias>, - pub untrusted: Map<&'a Ident, &'a ExternType>, - pub required_trivial: Map<&'a Ident, TrivialReason<'a>>, + pub structs: Map<&'a QualifiedIdent, &'a Struct>, + pub enums: Map<&'a QualifiedIdent, &'a Enum>, + pub cxx: Set<&'a QualifiedIdent>, + pub rust: Set<&'a QualifiedIdent>, + pub aliases: Map<&'a QualifiedIdent, &'a TypeAlias>, + pub untrusted: Map<&'a QualifiedIdent, &'a ExternType>, + pub required_trivial: Map<&'a QualifiedIdent, TrivialReason<'a>>, pub explicit_impls: Set<&'a Impl>, } @@ -71,7 +73,7 @@ impl<'a> Types<'a> { // If already declared as a struct or enum, or if // colliding with something other than an extern C++ // type, then error. - duplicate_name(cx, strct, ident); + duplicate_cxx_name(cx, strct, ident); } structs.insert(ident, strct); for field in &strct.fields { @@ -88,7 +90,7 @@ impl<'a> Types<'a> { // If already declared as a struct or enum, or if // colliding with something other than an extern C++ // type, then error. - duplicate_name(cx, enm, ident); + duplicate_cxx_name(cx, enm, ident); } enums.insert(ident, enm); } @@ -101,7 +103,7 @@ impl<'a> Types<'a> { // If already declared as an extern C++ type, or if // colliding with something which is neither struct nor // enum, then error. - duplicate_name(cx, ety, ident); + duplicate_cxx_name(cx, ety, ident); } cxx.insert(ident); if !ety.trusted { @@ -111,7 +113,7 @@ impl<'a> Types<'a> { Api::RustType(ety) => { let ident = &ety.ident; if !type_names.insert(ident) { - duplicate_name(cx, ety, ident); + duplicate_cxx_name(cx, ety, ident); } rust.insert(ident); } @@ -119,7 +121,7 @@ impl<'a> Types<'a> { // Note: duplication of the C++ name is fine because C++ has // function overloading. if !function_names.insert((&efn.receiver, &efn.ident.rust)) { - duplicate_name(cx, efn, &efn.ident.rust); + duplicate_rs_name(cx, efn, &efn.ident.rust); } for arg in &efn.args { visit(&mut all, &arg.ty); @@ -131,7 +133,7 @@ impl<'a> Types<'a> { Api::TypeAlias(alias) => { let ident = &alias.ident; if !type_names.insert(ident) { - duplicate_name(cx, alias, ident); + duplicate_cxx_name(cx, alias, ident); } cxx.insert(ident); aliases.insert(ident, alias); @@ -196,7 +198,7 @@ impl<'a> Types<'a> { if let Some(strct) = self.structs.get(ident) { !self.is_pod(strct) } else { - Atom::from(ident) == Some(RustString) + Atom::from_qualified_ident(ident) == Some(RustString) } } Type::RustVec(_) => true, @@ -229,7 +231,15 @@ pub enum TrivialReason<'a> { FunctionReturn(&'a ExternFn), } -fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) { - let msg = format!("the name `{}` is defined multiple times", ident); +fn duplicate_cxx_name(cx: &mut Errors, sp: impl ToTokens, ident: &QualifiedIdent) { + let msg = format!( + "the C++ name `{}` is defined multiple times", + ident.to_fully_qualified() + ); + cx.error(sp, msg); +} + +fn duplicate_rs_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) { + let msg = format!("the Rust name `{}` is defined multiple times", ident); cx.error(sp, msg); } diff --git a/tests/ui/by_value_not_supported.stderr b/tests/ui/by_value_not_supported.stderr index 0a56dd48f..a860f3ddb 100644 --- a/tests/ui/by_value_not_supported.stderr +++ b/tests/ui/by_value_not_supported.stderr @@ -16,7 +16,7 @@ error: using C++ string by value is not supported 6 | s: CxxString, | ^^^^^^^^^^^^ -error: needs a cxx::ExternType impl in order to be used as a field of `S` +error: needs a cxx::ExternType impl in order to be used as a field of `::S` --> $DIR/by_value_not_supported.rs:10:9 | 10 | type C;