1 use std::collections::{BTreeMap, HashSet};
2 use std::fmt::Write as _;
3
4 use quote::ToTokens;
5 use serde::Serialize;
6 use syn::{ImplItem, Item, ItemMod, UseTree, Visibility};
7
8 use super::{
9 AttrInheritContext, Attrs, CustomType, Enum, Ident, Method, ModSymbol, Mutability, OpaqueType,
10 Path, PathType, RustLink, Struct, Trait,
11 };
12 use crate::environment::*;
13
14 /// Custom Diplomat attribute that can be placed on a struct definition.
15 #[derive(Debug)]
16 enum DiplomatStructAttribute {
17 /// The `#[diplomat::out]` attribute, used for non-opaque structs that
18 /// contain an owned opaque in the form of a `Box`.
19 Out,
20 /// An attribute that can correspond to a type (struct or enum).
21 TypeAttr(DiplomatTypeAttribute),
22 }
23
24 /// Custom Diplomat attribute that can be placed on an enum or struct definition.
25 #[derive(Debug)]
26 enum DiplomatTypeAttribute {
27 /// The `#[diplomat::opaque]` attribute, used for marking a type as opaque.
28 /// Note that opaque structs can be borrowed in return types, but cannot
29 /// be passed into a function behind a mutable reference.
30 Opaque,
31 /// The `#[diplomat::opaque_mut]` attribute, used for marking a type as
32 /// opaque and mutable.
33 /// Note that mutable opaque types can never be borrowed in return types
34 /// (even immutably!), but can be passed into a function behind a mutable
35 /// reference.
36 OpaqueMut,
37 }
38
39 impl DiplomatStructAttribute {
40 /// Parses a [`DiplomatStructAttribute`] from an array of [`syn::Attribute`]s.
41 /// If more than one kind is found, an error is returned containing all the
42 /// ones encountered, since all the current attributes are disjoint.
parse(attrs: &[syn::Attribute]) -> Result<Option<Self>, Vec<Self>>43 fn parse(attrs: &[syn::Attribute]) -> Result<Option<Self>, Vec<Self>> {
44 let mut buf = String::with_capacity(32);
45 let mut res = Ok(None);
46 for attr in attrs {
47 buf.clear();
48 write!(&mut buf, "{}", attr.path().to_token_stream()).unwrap();
49 let parsed = match buf.as_str() {
50 "diplomat :: out" => Some(Self::Out),
51 "diplomat :: opaque" => Some(Self::TypeAttr(DiplomatTypeAttribute::Opaque)),
52 "diplomat :: opaque_mut" => Some(Self::TypeAttr(DiplomatTypeAttribute::OpaqueMut)),
53 _ => None,
54 };
55
56 if let Some(parsed) = parsed {
57 match res {
58 Ok(None) => res = Ok(Some(parsed)),
59 Ok(Some(first)) => res = Err(vec![first, parsed]),
60 Err(ref mut errors) => errors.push(parsed),
61 }
62 }
63 }
64
65 res
66 }
67 }
68
69 impl DiplomatTypeAttribute {
70 /// Parses a [`DiplomatTypeAttribute`] from an array of [`syn::Attribute`]s.
71 /// If more than one kind is found, an error is returned containing all the
72 /// ones encountered, since all the current attributes are disjoint.
parse(attrs: &[syn::Attribute]) -> Result<Option<Self>, Vec<Self>>73 fn parse(attrs: &[syn::Attribute]) -> Result<Option<Self>, Vec<Self>> {
74 let mut buf = String::with_capacity(32);
75 let mut res = Ok(None);
76 for attr in attrs {
77 buf.clear();
78 write!(&mut buf, "{}", attr.path().to_token_stream()).unwrap();
79 let parsed = match buf.as_str() {
80 "diplomat :: opaque" => Some(Self::Opaque),
81 "diplomat :: opaque_mut" => Some(Self::OpaqueMut),
82 _ => None,
83 };
84
85 if let Some(parsed) = parsed {
86 match res {
87 Ok(None) => res = Ok(Some(parsed)),
88 Ok(Some(first)) => res = Err(vec![first, parsed]),
89 Err(ref mut errors) => errors.push(parsed),
90 }
91 }
92 }
93
94 res
95 }
96 }
97
98 #[derive(Clone, Serialize, Debug)]
99 #[non_exhaustive]
100 pub struct Module {
101 pub name: Ident,
102 pub imports: Vec<(Path, Ident)>,
103 pub declared_types: BTreeMap<Ident, CustomType>,
104 pub declared_traits: BTreeMap<Ident, Trait>,
105 pub sub_modules: Vec<Module>,
106 pub attrs: Attrs,
107 }
108
109 impl Module {
all_rust_links(&self) -> HashSet<&RustLink>110 pub fn all_rust_links(&self) -> HashSet<&RustLink> {
111 let mut rust_links = self
112 .declared_types
113 .values()
114 .flat_map(|t| t.all_rust_links())
115 .collect::<HashSet<_>>();
116
117 self.sub_modules.iter().for_each(|m| {
118 rust_links.extend(m.all_rust_links().iter());
119 });
120 rust_links
121 }
122
insert_all_types(&self, in_path: Path, out: &mut Env)123 pub fn insert_all_types(&self, in_path: Path, out: &mut Env) {
124 let mut mod_symbols = ModuleEnv::new(self.attrs.clone());
125
126 self.imports.iter().for_each(|(path, name)| {
127 mod_symbols.insert(name.clone(), ModSymbol::Alias(path.clone()));
128 });
129
130 self.declared_types.iter().for_each(|(k, v)| {
131 if mod_symbols
132 .insert(k.clone(), ModSymbol::CustomType(v.clone()))
133 .is_some()
134 {
135 panic!("Two types were declared with the same name, this needs to be implemented");
136 }
137 });
138
139 self.declared_traits.iter().for_each(|(k, v)| {
140 if mod_symbols
141 .insert(k.clone(), ModSymbol::Trait(v.clone()))
142 .is_some()
143 {
144 panic!("Two traits were declared with the same name, this needs to be implemented");
145 }
146 });
147
148 let path_to_self = in_path.sub_path(self.name.clone());
149 self.sub_modules.iter().for_each(|m| {
150 m.insert_all_types(path_to_self.clone(), out);
151 mod_symbols.insert(m.name.clone(), ModSymbol::SubModule(m.name.clone()));
152 });
153
154 out.insert(path_to_self, mod_symbols);
155 }
156
from_syn(input: &ItemMod, force_analyze: bool) -> Module157 pub fn from_syn(input: &ItemMod, force_analyze: bool) -> Module {
158 let mut custom_types_by_name = BTreeMap::new();
159 let mut custom_traits_by_name = BTreeMap::new();
160 let mut sub_modules = Vec::new();
161 let mut imports = Vec::new();
162
163 let analyze_types = force_analyze
164 || input
165 .attrs
166 .iter()
167 .any(|a| a.path().to_token_stream().to_string() == "diplomat :: bridge");
168
169 let mod_attrs: Attrs = (&*input.attrs).into();
170
171 let impl_parent_attrs: Attrs =
172 mod_attrs.attrs_for_inheritance(AttrInheritContext::MethodOrImplFromModule);
173 let type_parent_attrs: Attrs = mod_attrs.attrs_for_inheritance(AttrInheritContext::Type);
174
175 input
176 .content
177 .as_ref()
178 .map(|t| &t.1[..])
179 .unwrap_or_default()
180 .iter()
181 .for_each(|a| match a {
182 Item::Use(u) => {
183 if analyze_types {
184 extract_imports(&Path::empty(), &u.tree, &mut imports);
185 }
186 }
187 Item::Struct(strct) => {
188 if analyze_types {
189 let custom_type = match DiplomatStructAttribute::parse(&strct.attrs[..]) {
190 Ok(None) => CustomType::Struct(Struct::new(strct, false, &type_parent_attrs)),
191 Ok(Some(DiplomatStructAttribute::Out)) => {
192 CustomType::Struct(Struct::new(strct, true, &type_parent_attrs))
193 }
194 Ok(Some(DiplomatStructAttribute::TypeAttr(DiplomatTypeAttribute::Opaque))) => {
195 CustomType::Opaque(OpaqueType::new_struct(strct, Mutability::Immutable, &type_parent_attrs))
196 }
197 Ok(Some(DiplomatStructAttribute::TypeAttr(DiplomatTypeAttribute::OpaqueMut))) => {
198 CustomType::Opaque(OpaqueType::new_struct(strct, Mutability::Mutable, &type_parent_attrs))
199 }
200 Err(errors) => {
201 panic!("Multiple conflicting Diplomat struct attributes, there can be at most one: {errors:?}");
202 }
203 };
204
205 custom_types_by_name.insert(Ident::from(&strct.ident), custom_type);
206 }
207 }
208
209 Item::Enum(enm) => {
210 if analyze_types {
211 let ident = (&enm.ident).into();
212 let custom_enum = match DiplomatTypeAttribute::parse(&enm.attrs[..]) {
213 Ok(None) => CustomType::Enum(Enum::new(enm, &type_parent_attrs)),
214 Ok(Some(DiplomatTypeAttribute::Opaque)) => {
215 CustomType::Opaque(OpaqueType::new_enum(enm, Mutability::Immutable, &type_parent_attrs))
216 }
217 Ok(Some(DiplomatTypeAttribute::OpaqueMut)) => {
218 CustomType::Opaque(OpaqueType::new_enum(enm, Mutability::Mutable, &type_parent_attrs))
219 }
220 Err(errors) => {
221 panic!("Multiple conflicting Diplomat enum attributes, there can be at most one: {errors:?}");
222 }
223 };
224 custom_types_by_name
225 .insert(ident, custom_enum);
226 }
227 }
228
229 Item::Impl(imp) => {
230 if analyze_types && imp.trait_.is_none() {
231 let self_path = match imp.self_ty.as_ref() {
232 syn::Type::Path(s) => PathType::from(s),
233 _ => panic!("Self type not found"),
234 };
235 let mut impl_attrs = impl_parent_attrs.clone();
236 impl_attrs.add_attrs(&imp.attrs);
237 let method_parent_attrs = impl_attrs.attrs_for_inheritance(AttrInheritContext::MethodFromImpl);
238 let mut new_methods = imp
239 .items
240 .iter()
241 .filter_map(|i| match i {
242 ImplItem::Fn(m) => Some(m),
243 _ => None,
244 })
245 .filter(|m| matches!(m.vis, Visibility::Public(_)))
246 .map(|m| Method::from_syn(m, self_path.clone(), Some(&imp.generics), &method_parent_attrs))
247 .collect();
248
249 let self_ident = self_path.path.elements.last().unwrap();
250
251 match custom_types_by_name.get_mut(self_ident).unwrap() {
252 CustomType::Struct(strct) => {
253 strct.methods.append(&mut new_methods);
254 }
255 CustomType::Opaque(strct) => {
256 strct.methods.append(&mut new_methods);
257 }
258 CustomType::Enum(enm) => {
259 enm.methods.append(&mut new_methods);
260 }
261 }
262 }
263 }
264 Item::Mod(item_mod) => {
265 sub_modules.push(Module::from_syn(item_mod, false));
266 }
267 Item::Trait(trt) => {
268 if analyze_types {
269 let ident = (&trt.ident).into();
270 let trt = Trait::new(trt, &type_parent_attrs);
271 custom_traits_by_name
272 .insert(ident, trt);
273 }
274 }
275 _ => {}
276 });
277
278 Module {
279 name: (&input.ident).into(),
280 imports,
281 declared_types: custom_types_by_name,
282 declared_traits: custom_traits_by_name,
283 sub_modules,
284 attrs: mod_attrs,
285 }
286 }
287 }
288
extract_imports(base_path: &Path, use_tree: &UseTree, out: &mut Vec<(Path, Ident)>)289 fn extract_imports(base_path: &Path, use_tree: &UseTree, out: &mut Vec<(Path, Ident)>) {
290 match use_tree {
291 UseTree::Name(name) => out.push((
292 base_path.sub_path((&name.ident).into()),
293 (&name.ident).into(),
294 )),
295 UseTree::Path(path) => {
296 extract_imports(&base_path.sub_path((&path.ident).into()), &path.tree, out)
297 }
298 UseTree::Glob(_) => todo!("Glob imports are not yet supported"),
299 UseTree::Group(group) => {
300 group
301 .items
302 .iter()
303 .for_each(|i| extract_imports(base_path, i, out));
304 }
305 UseTree::Rename(rename) => out.push((
306 base_path.sub_path((&rename.ident).into()),
307 (&rename.rename).into(),
308 )),
309 }
310 }
311
312 #[derive(Serialize, Clone, Debug)]
313 #[non_exhaustive]
314 pub struct File {
315 pub modules: BTreeMap<String, Module>,
316 }
317
318 impl File {
319 /// Fuses all declared types into a single environment `HashMap`.
all_types(&self) -> Env320 pub fn all_types(&self) -> Env {
321 let mut out = Env::default();
322 let mut top_symbols = ModuleEnv::new(Default::default());
323
324 self.modules.values().for_each(|m| {
325 m.insert_all_types(Path::empty(), &mut out);
326 top_symbols.insert(m.name.clone(), ModSymbol::SubModule(m.name.clone()));
327 });
328
329 out.insert(Path::empty(), top_symbols);
330
331 out
332 }
333
all_rust_links(&self) -> HashSet<&RustLink>334 pub fn all_rust_links(&self) -> HashSet<&RustLink> {
335 self.modules
336 .values()
337 .flat_map(|m| m.all_rust_links().into_iter())
338 .collect()
339 }
340 }
341
342 impl From<&syn::File> for File {
343 /// Get all custom types across all modules defined in a given file.
from(file: &syn::File) -> File344 fn from(file: &syn::File) -> File {
345 let mut out = BTreeMap::new();
346 file.items.iter().for_each(|i| {
347 if let Item::Mod(item_mod) = i {
348 out.insert(
349 item_mod.ident.to_string(),
350 Module::from_syn(item_mod, false),
351 );
352 }
353 });
354
355 File { modules: out }
356 }
357 }
358
359 #[cfg(test)]
360 mod tests {
361 use insta::{self, Settings};
362
363 use syn;
364
365 use crate::ast::{File, Module};
366
367 #[test]
simple_mod()368 fn simple_mod() {
369 let mut settings = Settings::new();
370 settings.set_sort_maps(true);
371
372 settings.bind(|| {
373 insta::assert_yaml_snapshot!(Module::from_syn(
374 &syn::parse_quote! {
375 mod ffi {
376 struct NonOpaqueStruct {
377 a: i32,
378 b: Box<NonOpaqueStruct>
379 }
380
381 impl NonOpaqueStruct {
382 pub fn new(x: i32) -> NonOpaqueStruct {
383 unimplemented!();
384 }
385
386 pub fn set_a(&mut self, new_a: i32) {
387 self.a = new_a;
388 }
389 }
390
391 #[diplomat::opaque]
392 struct OpaqueStruct {
393 a: SomeExternalType
394 }
395
396 impl OpaqueStruct {
397 pub fn new() -> Box<OpaqueStruct> {
398 unimplemented!();
399 }
400
401 pub fn get_string(&self) -> String {
402 unimplemented!()
403 }
404 }
405 }
406 },
407 true
408 ));
409 });
410 }
411
412 #[test]
method_visibility()413 fn method_visibility() {
414 let mut settings = Settings::new();
415 settings.set_sort_maps(true);
416
417 settings.bind(|| {
418 insta::assert_yaml_snapshot!(Module::from_syn(
419 &syn::parse_quote! {
420 #[diplomat::bridge]
421 mod ffi {
422 struct Foo {}
423
424 impl Foo {
425 pub fn pub_fn() {
426 unimplemented!()
427 }
428 pub(crate) fn pub_crate_fn() {
429 unimplemented!()
430 }
431 pub(super) fn pub_super_fn() {
432 unimplemented!()
433 }
434 fn priv_fn() {
435 unimplemented!()
436 }
437 }
438 }
439 },
440 true
441 ));
442 });
443 }
444
445 #[test]
import_in_non_diplomat_not_analyzed()446 fn import_in_non_diplomat_not_analyzed() {
447 let mut settings = Settings::new();
448 settings.set_sort_maps(true);
449
450 settings.bind(|| {
451 insta::assert_yaml_snapshot!(File::from(&syn::parse_quote! {
452 #[diplomat::bridge]
453 mod ffi {
454 struct Foo {}
455 }
456
457 mod other {
458 use something::*;
459 }
460 }));
461 });
462 }
463 }
464