• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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