1 use crate::syntax::attrs::OtherAttrs;
2 use crate::syntax::discriminant::DiscriminantSet;
3 use crate::syntax::file::{Item, ItemForeignMod};
4 use crate::syntax::report::Errors;
5 use crate::syntax::Atom::*;
6 use crate::syntax::{
7     attrs, error, Api, Array, Derive, Doc, Enum, ExternFn, ExternType, ForeignName, Impl, Include,
8     IncludeKind, Lang, Lifetimes, NamedType, Namespace, Pair, Ptr, Receiver, Ref, Signature,
9     SliceRef, Struct, Ty1, Type, TypeAlias, Var, Variant,
10 };
11 use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
12 use quote::{format_ident, quote, quote_spanned};
13 use std::mem;
14 use syn::parse::{ParseStream, Parser};
15 use syn::punctuated::Punctuated;
16 use syn::{
17     Abi, Attribute, Error, Expr, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType,
18     GenericArgument, GenericParam, Generics, Ident, ItemEnum, ItemImpl, ItemStruct, Lit, LitStr,
19     Pat, PathArguments, Result, ReturnType, Signature as RustSignature, Token, TraitBound,
20     TraitBoundModifier, Type as RustType, TypeArray, TypeBareFn, TypeParamBound, TypePath, TypePtr,
21     TypeReference, Variant as RustVariant, Visibility,
22 };
23 
24 pub mod kw {
25     syn::custom_keyword!(Pin);
26     syn::custom_keyword!(Result);
27 }
28 
parse_items( cx: &mut Errors, items: Vec<Item>, trusted: bool, namespace: &Namespace, ) -> Vec<Api>29 pub fn parse_items(
30     cx: &mut Errors,
31     items: Vec<Item>,
32     trusted: bool,
33     namespace: &Namespace,
34 ) -> Vec<Api> {
35     let mut apis = Vec::new();
36     for item in items {
37         match item {
38             Item::Struct(item) => match parse_struct(cx, item, namespace) {
39                 Ok(strct) => apis.push(strct),
40                 Err(err) => cx.push(err),
41             },
42             Item::Enum(item) => apis.push(parse_enum(cx, item, namespace)),
43             Item::ForeignMod(foreign_mod) => {
44                 parse_foreign_mod(cx, foreign_mod, &mut apis, trusted, namespace)
45             }
46             Item::Impl(item) => match parse_impl(item) {
47                 Ok(imp) => apis.push(imp),
48                 Err(err) => cx.push(err),
49             },
50             Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED),
51             Item::Other(item) => cx.error(item, "unsupported item"),
52         }
53     }
54     apis
55 }
56 
parse_struct(cx: &mut Errors, mut item: ItemStruct, namespace: &Namespace) -> Result<Api>57 fn parse_struct(cx: &mut Errors, mut item: ItemStruct, namespace: &Namespace) -> Result<Api> {
58     let mut doc = Doc::new();
59     let mut derives = Vec::new();
60     let mut namespace = namespace.clone();
61     let mut cxx_name = None;
62     let mut rust_name = None;
63     let attrs = attrs::parse(
64         cx,
65         mem::take(&mut item.attrs),
66         attrs::Parser {
67             doc: Some(&mut doc),
68             derives: Some(&mut derives),
69             namespace: Some(&mut namespace),
70             cxx_name: Some(&mut cxx_name),
71             rust_name: Some(&mut rust_name),
72             ..Default::default()
73         },
74     );
75 
76     let named_fields = match item.fields {
77         Fields::Named(fields) => fields,
78         Fields::Unit => return Err(Error::new_spanned(item, "unit structs are not supported")),
79         Fields::Unnamed(_) => {
80             return Err(Error::new_spanned(item, "tuple structs are not supported"));
81         }
82     };
83 
84     let mut lifetimes = Punctuated::new();
85     let mut has_unsupported_generic_param = false;
86     for pair in item.generics.params.into_pairs() {
87         let (param, punct) = pair.into_tuple();
88         match param {
89             GenericParam::Lifetime(param) => {
90                 if !param.bounds.is_empty() && !has_unsupported_generic_param {
91                     let msg = "lifetime parameter with bounds is not supported yet";
92                     cx.error(&param, msg);
93                     has_unsupported_generic_param = true;
94                 }
95                 lifetimes.push_value(param.lifetime);
96                 if let Some(punct) = punct {
97                     lifetimes.push_punct(punct);
98                 }
99             }
100             GenericParam::Type(param) => {
101                 if !has_unsupported_generic_param {
102                     let msg = "struct with generic type parameter is not supported yet";
103                     cx.error(&param, msg);
104                     has_unsupported_generic_param = true;
105                 }
106             }
107             GenericParam::Const(param) => {
108                 if !has_unsupported_generic_param {
109                     let msg = "struct with const generic parameter is not supported yet";
110                     cx.error(&param, msg);
111                     has_unsupported_generic_param = true;
112                 }
113             }
114         }
115     }
116 
117     if let Some(where_clause) = &item.generics.where_clause {
118         cx.error(
119             where_clause,
120             "struct with where-clause is not supported yet",
121         );
122     }
123 
124     let mut fields = Vec::new();
125     for field in named_fields.named {
126         let ident = field.ident.unwrap();
127         let mut doc = Doc::new();
128         let mut cxx_name = None;
129         let mut rust_name = None;
130         let attrs = attrs::parse(
131             cx,
132             field.attrs,
133             attrs::Parser {
134                 doc: Some(&mut doc),
135                 cxx_name: Some(&mut cxx_name),
136                 rust_name: Some(&mut rust_name),
137                 ..Default::default()
138             },
139         );
140         let ty = match parse_type(&field.ty) {
141             Ok(ty) => ty,
142             Err(err) => {
143                 cx.push(err);
144                 continue;
145             }
146         };
147         let visibility = visibility_pub(&field.vis, ident.span());
148         let name = pair(Namespace::default(), &ident, cxx_name, rust_name);
149         fields.push(Var {
150             doc,
151             attrs,
152             visibility,
153             name,
154             ty,
155         });
156     }
157 
158     let struct_token = item.struct_token;
159     let visibility = visibility_pub(&item.vis, struct_token.span);
160     let name = pair(namespace, &item.ident, cxx_name, rust_name);
161     let generics = Lifetimes {
162         lt_token: item.generics.lt_token,
163         lifetimes,
164         gt_token: item.generics.gt_token,
165     };
166     let brace_token = named_fields.brace_token;
167 
168     Ok(Api::Struct(Struct {
169         doc,
170         derives,
171         attrs,
172         visibility,
173         struct_token,
174         name,
175         generics,
176         brace_token,
177         fields,
178     }))
179 }
180 
parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Api181 fn parse_enum(cx: &mut Errors, item: ItemEnum, namespace: &Namespace) -> Api {
182     let mut doc = Doc::new();
183     let mut derives = Vec::new();
184     let mut repr = None;
185     let mut namespace = namespace.clone();
186     let mut cxx_name = None;
187     let mut rust_name = None;
188     let attrs = attrs::parse(
189         cx,
190         item.attrs,
191         attrs::Parser {
192             doc: Some(&mut doc),
193             derives: Some(&mut derives),
194             repr: Some(&mut repr),
195             namespace: Some(&mut namespace),
196             cxx_name: Some(&mut cxx_name),
197             rust_name: Some(&mut rust_name),
198             ..Default::default()
199         },
200     );
201 
202     if !item.generics.params.is_empty() {
203         let vis = &item.vis;
204         let enum_token = item.enum_token;
205         let ident = &item.ident;
206         let generics = &item.generics;
207         let span = quote!(#vis #enum_token #ident #generics);
208         cx.error(span, "enum with generic parameters is not supported");
209     } else if let Some(where_clause) = &item.generics.where_clause {
210         cx.error(where_clause, "enum with where-clause is not supported");
211     }
212 
213     let mut variants = Vec::new();
214     let mut discriminants = DiscriminantSet::new(repr);
215     for variant in item.variants {
216         match parse_variant(cx, variant, &mut discriminants) {
217             Ok(variant) => variants.push(variant),
218             Err(err) => cx.push(err),
219         }
220     }
221 
222     let enum_token = item.enum_token;
223     let visibility = visibility_pub(&item.vis, enum_token.span);
224     let brace_token = item.brace_token;
225 
226     let explicit_repr = repr.is_some();
227     let mut repr = U8;
228     match discriminants.inferred_repr() {
229         Ok(inferred) => repr = inferred,
230         Err(err) => {
231             let span = quote_spanned!(brace_token.span=> #enum_token {});
232             cx.error(span, err);
233             variants.clear();
234         }
235     }
236 
237     let name = pair(namespace, &item.ident, cxx_name, rust_name);
238     let repr_ident = Ident::new(repr.as_ref(), Span::call_site());
239     let repr_type = Type::Ident(NamedType::new(repr_ident));
240     let generics = Lifetimes {
241         lt_token: None,
242         lifetimes: Punctuated::new(),
243         gt_token: None,
244     };
245 
246     Api::Enum(Enum {
247         doc,
248         derives,
249         attrs,
250         visibility,
251         enum_token,
252         name,
253         generics,
254         brace_token,
255         variants,
256         repr,
257         repr_type,
258         explicit_repr,
259     })
260 }
261 
parse_variant( cx: &mut Errors, mut variant: RustVariant, discriminants: &mut DiscriminantSet, ) -> Result<Variant>262 fn parse_variant(
263     cx: &mut Errors,
264     mut variant: RustVariant,
265     discriminants: &mut DiscriminantSet,
266 ) -> Result<Variant> {
267     let mut doc = Doc::new();
268     let mut cxx_name = None;
269     let mut rust_name = None;
270     let attrs = attrs::parse(
271         cx,
272         mem::take(&mut variant.attrs),
273         attrs::Parser {
274             doc: Some(&mut doc),
275             cxx_name: Some(&mut cxx_name),
276             rust_name: Some(&mut rust_name),
277             ..Default::default()
278         },
279     );
280 
281     match variant.fields {
282         Fields::Unit => {}
283         _ => {
284             let msg = "enums with data are not supported yet";
285             return Err(Error::new_spanned(variant, msg));
286         }
287     }
288 
289     let expr = variant.discriminant.as_ref().map(|(_, expr)| expr);
290     let try_discriminant = match &expr {
291         Some(lit) => discriminants.insert(lit),
292         None => discriminants.insert_next(),
293     };
294     let discriminant = match try_discriminant {
295         Ok(discriminant) => discriminant,
296         Err(err) => return Err(Error::new_spanned(variant, err)),
297     };
298 
299     let name = pair(Namespace::ROOT, &variant.ident, cxx_name, rust_name);
300     let expr = variant.discriminant.map(|(_, expr)| expr);
301 
302     Ok(Variant {
303         doc,
304         attrs,
305         name,
306         discriminant,
307         expr,
308     })
309 }
310 
parse_foreign_mod( cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec<Api>, trusted: bool, namespace: &Namespace, )311 fn parse_foreign_mod(
312     cx: &mut Errors,
313     foreign_mod: ItemForeignMod,
314     out: &mut Vec<Api>,
315     trusted: bool,
316     namespace: &Namespace,
317 ) {
318     let lang = match parse_lang(&foreign_mod.abi) {
319         Ok(lang) => lang,
320         Err(err) => return cx.push(err),
321     };
322 
323     match lang {
324         Lang::Rust => {
325             if foreign_mod.unsafety.is_some() {
326                 let unsafety = foreign_mod.unsafety;
327                 let abi = &foreign_mod.abi;
328                 let span = quote!(#unsafety #abi);
329                 cx.error(span, "extern \"Rust\" block does not need to be unsafe");
330             }
331         }
332         Lang::Cxx => {}
333     }
334 
335     let trusted = trusted || foreign_mod.unsafety.is_some();
336 
337     let mut namespace = namespace.clone();
338     attrs::parse(
339         cx,
340         foreign_mod.attrs,
341         attrs::Parser {
342             namespace: Some(&mut namespace),
343             ..Default::default()
344         },
345     );
346 
347     let mut items = Vec::new();
348     for foreign in foreign_mod.items {
349         match foreign {
350             ForeignItem::Type(foreign) => {
351                 let ety = parse_extern_type(cx, foreign, lang, trusted, &namespace);
352                 items.push(ety);
353             }
354             ForeignItem::Fn(foreign) => {
355                 match parse_extern_fn(cx, foreign, lang, trusted, &namespace) {
356                     Ok(efn) => items.push(efn),
357                     Err(err) => cx.push(err),
358                 }
359             }
360             ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => {
361                 match foreign.mac.parse_body_with(parse_include) {
362                     Ok(include) => items.push(Api::Include(include)),
363                     Err(err) => cx.push(err),
364                 }
365             }
366             ForeignItem::Verbatim(tokens) => {
367                 match parse_extern_verbatim(cx, tokens, lang, trusted, &namespace) {
368                     Ok(api) => items.push(api),
369                     Err(err) => cx.push(err),
370                 }
371             }
372             _ => cx.error(foreign, "unsupported foreign item"),
373         }
374     }
375 
376     if !trusted
377         && items.iter().any(|api| match api {
378             Api::CxxFunction(efn) => efn.unsafety.is_none(),
379             _ => false,
380         })
381     {
382         cx.error(
383             foreign_mod.abi,
384             "block must be declared `unsafe extern \"C++\"` if it contains any safe-to-call C++ functions",
385         );
386     }
387 
388     let mut types = items.iter().filter_map(|item| match item {
389         Api::CxxType(ety) | Api::RustType(ety) => Some(&ety.name),
390         Api::TypeAlias(alias) => Some(&alias.name),
391         _ => None,
392     });
393     if let (Some(single_type), None) = (types.next(), types.next()) {
394         let single_type = single_type.clone();
395         for item in &mut items {
396             if let Api::CxxFunction(efn) | Api::RustFunction(efn) = item {
397                 if let Some(receiver) = &mut efn.receiver {
398                     if receiver.ty.rust == "Self" {
399                         receiver.ty.rust = single_type.rust.clone();
400                     }
401                 }
402             }
403         }
404     }
405 
406     out.extend(items);
407 }
408 
parse_lang(abi: &Abi) -> Result<Lang>409 fn parse_lang(abi: &Abi) -> Result<Lang> {
410     let name = match &abi.name {
411         Some(name) => name,
412         None => {
413             return Err(Error::new_spanned(
414                 abi,
415                 "ABI name is required, extern \"C++\" or extern \"Rust\"",
416             ));
417         }
418     };
419 
420     match name.value().as_str() {
421         "C++" => Ok(Lang::Cxx),
422         "Rust" => Ok(Lang::Rust),
423         _ => Err(Error::new_spanned(
424             abi,
425             "unrecognized ABI, requires either \"C++\" or \"Rust\"",
426         )),
427     }
428 }
429 
parse_extern_type( cx: &mut Errors, foreign_type: ForeignItemType, lang: Lang, trusted: bool, namespace: &Namespace, ) -> Api430 fn parse_extern_type(
431     cx: &mut Errors,
432     foreign_type: ForeignItemType,
433     lang: Lang,
434     trusted: bool,
435     namespace: &Namespace,
436 ) -> Api {
437     let mut doc = Doc::new();
438     let mut derives = Vec::new();
439     let mut namespace = namespace.clone();
440     let mut cxx_name = None;
441     let mut rust_name = None;
442     let attrs = attrs::parse(
443         cx,
444         foreign_type.attrs,
445         attrs::Parser {
446             doc: Some(&mut doc),
447             derives: Some(&mut derives),
448             namespace: Some(&mut namespace),
449             cxx_name: Some(&mut cxx_name),
450             rust_name: Some(&mut rust_name),
451             ..Default::default()
452         },
453     );
454 
455     let type_token = foreign_type.type_token;
456     let visibility = visibility_pub(&foreign_type.vis, type_token.span);
457     let name = pair(namespace, &foreign_type.ident, cxx_name, rust_name);
458     let generics = Lifetimes {
459         lt_token: None,
460         lifetimes: Punctuated::new(),
461         gt_token: None,
462     };
463     let colon_token = None;
464     let bounds = Vec::new();
465     let semi_token = foreign_type.semi_token;
466 
467     (match lang {
468         Lang::Cxx => Api::CxxType,
469         Lang::Rust => Api::RustType,
470     })(ExternType {
471         lang,
472         doc,
473         derives,
474         attrs,
475         visibility,
476         type_token,
477         name,
478         generics,
479         colon_token,
480         bounds,
481         semi_token,
482         trusted,
483     })
484 }
485 
parse_extern_fn( cx: &mut Errors, mut foreign_fn: ForeignItemFn, lang: Lang, trusted: bool, namespace: &Namespace, ) -> Result<Api>486 fn parse_extern_fn(
487     cx: &mut Errors,
488     mut foreign_fn: ForeignItemFn,
489     lang: Lang,
490     trusted: bool,
491     namespace: &Namespace,
492 ) -> Result<Api> {
493     let mut doc = Doc::new();
494     let mut namespace = namespace.clone();
495     let mut cxx_name = None;
496     let mut rust_name = None;
497     let attrs = attrs::parse(
498         cx,
499         mem::take(&mut foreign_fn.attrs),
500         attrs::Parser {
501             doc: Some(&mut doc),
502             namespace: Some(&mut namespace),
503             cxx_name: Some(&mut cxx_name),
504             rust_name: Some(&mut rust_name),
505             ..Default::default()
506         },
507     );
508 
509     let generics = &foreign_fn.sig.generics;
510     if generics.where_clause.is_some()
511         || generics.params.iter().any(|param| match param {
512             GenericParam::Lifetime(lifetime) => !lifetime.bounds.is_empty(),
513             GenericParam::Type(_) | GenericParam::Const(_) => true,
514         })
515     {
516         return Err(Error::new_spanned(
517             foreign_fn,
518             "extern function with generic parameters is not supported yet",
519         ));
520     }
521 
522     if let Some(variadic) = &foreign_fn.sig.variadic {
523         return Err(Error::new_spanned(
524             variadic,
525             "variadic function is not supported yet",
526         ));
527     }
528 
529     if foreign_fn.sig.asyncness.is_some() {
530         return Err(Error::new_spanned(
531             foreign_fn,
532             "async function is not directly supported yet, but see https://cxx.rs/async.html for a working approach",
533         ));
534     }
535 
536     if foreign_fn.sig.constness.is_some() {
537         return Err(Error::new_spanned(
538             foreign_fn,
539             "const extern function is not supported",
540         ));
541     }
542 
543     if let Some(abi) = &foreign_fn.sig.abi {
544         return Err(Error::new_spanned(
545             abi,
546             "explicit ABI on extern function is not supported",
547         ));
548     }
549 
550     let mut receiver = None;
551     let mut args = Punctuated::new();
552     for arg in foreign_fn.sig.inputs.pairs() {
553         let (arg, comma) = arg.into_tuple();
554         match arg {
555             FnArg::Receiver(arg) => {
556                 if let Some((ampersand, lifetime)) = &arg.reference {
557                     receiver = Some(Receiver {
558                         pinned: false,
559                         ampersand: *ampersand,
560                         lifetime: lifetime.clone(),
561                         mutable: arg.mutability.is_some(),
562                         var: arg.self_token,
563                         ty: NamedType::new(Ident::new("Self", arg.self_token.span)),
564                         shorthand: true,
565                         pin_tokens: None,
566                         mutability: arg.mutability,
567                     });
568                     continue;
569                 }
570                 return Err(Error::new_spanned(arg, "unsupported signature"));
571             }
572             FnArg::Typed(arg) => {
573                 let ident = match arg.pat.as_ref() {
574                     Pat::Ident(pat) => pat.ident.clone(),
575                     Pat::Wild(pat) => {
576                         Ident::new(&format!("arg{}", args.len()), pat.underscore_token.span)
577                     }
578                     _ => return Err(Error::new_spanned(arg, "unsupported signature")),
579                 };
580                 let ty = parse_type(&arg.ty)?;
581                 if ident != "self" {
582                     let doc = Doc::new();
583                     let attrs = OtherAttrs::none();
584                     let visibility = Token![pub](ident.span());
585                     let name = pair(Namespace::default(), &ident, None, None);
586                     args.push_value(Var {
587                         doc,
588                         attrs,
589                         visibility,
590                         name,
591                         ty,
592                     });
593                     if let Some(comma) = comma {
594                         args.push_punct(*comma);
595                     }
596                     continue;
597                 }
598                 if let Type::Ref(reference) = ty {
599                     if let Type::Ident(ident) = reference.inner {
600                         receiver = Some(Receiver {
601                             pinned: reference.pinned,
602                             ampersand: reference.ampersand,
603                             lifetime: reference.lifetime,
604                             mutable: reference.mutable,
605                             var: Token![self](ident.rust.span()),
606                             ty: ident,
607                             shorthand: false,
608                             pin_tokens: reference.pin_tokens,
609                             mutability: reference.mutability,
610                         });
611                         continue;
612                     }
613                 }
614                 return Err(Error::new_spanned(arg, "unsupported method receiver"));
615             }
616         }
617     }
618 
619     let mut throws_tokens = None;
620     let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?;
621     let throws = throws_tokens.is_some();
622     let unsafety = foreign_fn.sig.unsafety;
623     let fn_token = foreign_fn.sig.fn_token;
624     let inherited_span = unsafety.map_or(fn_token.span, |unsafety| unsafety.span);
625     let visibility = visibility_pub(&foreign_fn.vis, inherited_span);
626     let name = pair(namespace, &foreign_fn.sig.ident, cxx_name, rust_name);
627     let generics = generics.clone();
628     let paren_token = foreign_fn.sig.paren_token;
629     let semi_token = foreign_fn.semi_token;
630 
631     Ok(match lang {
632         Lang::Cxx => Api::CxxFunction,
633         Lang::Rust => Api::RustFunction,
634     }(ExternFn {
635         lang,
636         doc,
637         attrs,
638         visibility,
639         name,
640         sig: Signature {
641             unsafety,
642             fn_token,
643             generics,
644             receiver,
645             args,
646             ret,
647             throws,
648             paren_token,
649             throws_tokens,
650         },
651         semi_token,
652         trusted,
653     }))
654 }
655 
parse_extern_verbatim( cx: &mut Errors, tokens: TokenStream, lang: Lang, trusted: bool, namespace: &Namespace, ) -> Result<Api>656 fn parse_extern_verbatim(
657     cx: &mut Errors,
658     tokens: TokenStream,
659     lang: Lang,
660     trusted: bool,
661     namespace: &Namespace,
662 ) -> Result<Api> {
663     |input: ParseStream| -> Result<Api> {
664         let attrs = input.call(Attribute::parse_outer)?;
665         let visibility: Visibility = input.parse()?;
666         if input.peek(Token![type]) {
667             parse_extern_verbatim_type(cx, attrs, visibility, input, lang, trusted, namespace)
668         } else if input.peek(Token![fn]) {
669             parse_extern_verbatim_fn(input)
670         } else {
671             let span = input.cursor().token_stream();
672             Err(Error::new_spanned(
673                 span,
674                 "unsupported foreign item, expected `type` or `fn`",
675             ))
676         }
677     }
678     .parse2(tokens)
679 }
680 
parse_extern_verbatim_type( cx: &mut Errors, attrs: Vec<Attribute>, visibility: Visibility, input: ParseStream, lang: Lang, trusted: bool, namespace: &Namespace, ) -> Result<Api>681 fn parse_extern_verbatim_type(
682     cx: &mut Errors,
683     attrs: Vec<Attribute>,
684     visibility: Visibility,
685     input: ParseStream,
686     lang: Lang,
687     trusted: bool,
688     namespace: &Namespace,
689 ) -> Result<Api> {
690     let type_token: Token![type] = input.parse()?;
691     let ident: Ident = input.parse()?;
692     let generics: Generics = input.parse()?;
693     let mut lifetimes = Punctuated::new();
694     let mut has_unsupported_generic_param = false;
695     for pair in generics.params.into_pairs() {
696         let (param, punct) = pair.into_tuple();
697         match param {
698             GenericParam::Lifetime(param) => {
699                 if !param.bounds.is_empty() && !has_unsupported_generic_param {
700                     let msg = "lifetime parameter with bounds is not supported yet";
701                     cx.error(&param, msg);
702                     has_unsupported_generic_param = true;
703                 }
704                 lifetimes.push_value(param.lifetime);
705                 if let Some(punct) = punct {
706                     lifetimes.push_punct(punct);
707                 }
708             }
709             GenericParam::Type(param) => {
710                 if !has_unsupported_generic_param {
711                     let msg = "extern type with generic type parameter is not supported yet";
712                     cx.error(&param, msg);
713                     has_unsupported_generic_param = true;
714                 }
715             }
716             GenericParam::Const(param) => {
717                 if !has_unsupported_generic_param {
718                     let msg = "extern type with const generic parameter is not supported yet";
719                     cx.error(&param, msg);
720                     has_unsupported_generic_param = true;
721                 }
722             }
723         }
724     }
725     let lifetimes = Lifetimes {
726         lt_token: generics.lt_token,
727         lifetimes,
728         gt_token: generics.gt_token,
729     };
730     let lookahead = input.lookahead1();
731     if lookahead.peek(Token![=]) {
732         // type Alias = crate::path::to::Type;
733         parse_type_alias(
734             cx, attrs, visibility, type_token, ident, lifetimes, input, lang, namespace,
735         )
736     } else if lookahead.peek(Token![:]) || lookahead.peek(Token![;]) {
737         // type Opaque: Bound2 + Bound2;
738         parse_extern_type_bounded(
739             cx, attrs, visibility, type_token, ident, lifetimes, input, lang, trusted, namespace,
740         )
741     } else {
742         Err(lookahead.error())
743     }
744 }
745 
parse_extern_verbatim_fn(input: ParseStream) -> Result<Api>746 fn parse_extern_verbatim_fn(input: ParseStream) -> Result<Api> {
747     input.parse::<RustSignature>()?;
748     input.parse::<Token![;]>()?;
749     unreachable!()
750 }
751 
parse_type_alias( cx: &mut Errors, attrs: Vec<Attribute>, visibility: Visibility, type_token: Token![type], ident: Ident, generics: Lifetimes, input: ParseStream, lang: Lang, namespace: &Namespace, ) -> Result<Api>752 fn parse_type_alias(
753     cx: &mut Errors,
754     attrs: Vec<Attribute>,
755     visibility: Visibility,
756     type_token: Token![type],
757     ident: Ident,
758     generics: Lifetimes,
759     input: ParseStream,
760     lang: Lang,
761     namespace: &Namespace,
762 ) -> Result<Api> {
763     let eq_token: Token![=] = input.parse()?;
764     let ty: RustType = input.parse()?;
765     let semi_token: Token![;] = input.parse()?;
766 
767     let mut doc = Doc::new();
768     let mut derives = Vec::new();
769     let mut namespace = namespace.clone();
770     let mut cxx_name = None;
771     let mut rust_name = None;
772     let attrs = attrs::parse(
773         cx,
774         attrs,
775         attrs::Parser {
776             doc: Some(&mut doc),
777             derives: Some(&mut derives),
778             namespace: Some(&mut namespace),
779             cxx_name: Some(&mut cxx_name),
780             rust_name: Some(&mut rust_name),
781             ..Default::default()
782         },
783     );
784 
785     if lang == Lang::Rust {
786         let span = quote!(#type_token #semi_token);
787         let msg = "type alias in extern \"Rust\" block is not supported";
788         return Err(Error::new_spanned(span, msg));
789     }
790 
791     let visibility = visibility_pub(&visibility, type_token.span);
792     let name = pair(namespace, &ident, cxx_name, rust_name);
793 
794     Ok(Api::TypeAlias(TypeAlias {
795         doc,
796         derives,
797         attrs,
798         visibility,
799         type_token,
800         name,
801         generics,
802         eq_token,
803         ty,
804         semi_token,
805     }))
806 }
807 
parse_extern_type_bounded( cx: &mut Errors, attrs: Vec<Attribute>, visibility: Visibility, type_token: Token![type], ident: Ident, generics: Lifetimes, input: ParseStream, lang: Lang, trusted: bool, namespace: &Namespace, ) -> Result<Api>808 fn parse_extern_type_bounded(
809     cx: &mut Errors,
810     attrs: Vec<Attribute>,
811     visibility: Visibility,
812     type_token: Token![type],
813     ident: Ident,
814     generics: Lifetimes,
815     input: ParseStream,
816     lang: Lang,
817     trusted: bool,
818     namespace: &Namespace,
819 ) -> Result<Api> {
820     let mut bounds = Vec::new();
821     let colon_token: Option<Token![:]> = input.parse()?;
822     if colon_token.is_some() {
823         loop {
824             match input.parse()? {
825                 TypeParamBound::Trait(TraitBound {
826                     paren_token: None,
827                     modifier: TraitBoundModifier::None,
828                     lifetimes: None,
829                     path,
830                 }) if if let Some(derive) = path.get_ident().and_then(Derive::from) {
831                     bounds.push(derive);
832                     true
833                 } else {
834                     false
835                 } => {}
836                 bound @ TypeParamBound::Trait(_) | bound @ TypeParamBound::Lifetime(_) => {
837                     cx.error(bound, "unsupported trait");
838                 }
839             }
840 
841             let lookahead = input.lookahead1();
842             if lookahead.peek(Token![+]) {
843                 input.parse::<Token![+]>()?;
844             } else if lookahead.peek(Token![;]) {
845                 break;
846             } else {
847                 return Err(lookahead.error());
848             }
849         }
850     }
851     let semi_token: Token![;] = input.parse()?;
852 
853     let mut doc = Doc::new();
854     let mut derives = Vec::new();
855     let mut namespace = namespace.clone();
856     let mut cxx_name = None;
857     let mut rust_name = None;
858     let attrs = attrs::parse(
859         cx,
860         attrs,
861         attrs::Parser {
862             doc: Some(&mut doc),
863             derives: Some(&mut derives),
864             namespace: Some(&mut namespace),
865             cxx_name: Some(&mut cxx_name),
866             rust_name: Some(&mut rust_name),
867             ..Default::default()
868         },
869     );
870 
871     let visibility = visibility_pub(&visibility, type_token.span);
872     let name = pair(namespace, &ident, cxx_name, rust_name);
873 
874     Ok(match lang {
875         Lang::Cxx => Api::CxxType,
876         Lang::Rust => Api::RustType,
877     }(ExternType {
878         lang,
879         doc,
880         derives,
881         attrs,
882         visibility,
883         type_token,
884         name,
885         generics,
886         colon_token,
887         bounds,
888         semi_token,
889         trusted,
890     }))
891 }
892 
parse_impl(imp: ItemImpl) -> Result<Api>893 fn parse_impl(imp: ItemImpl) -> Result<Api> {
894     let impl_token = imp.impl_token;
895 
896     if !imp.items.is_empty() {
897         let mut span = Group::new(Delimiter::Brace, TokenStream::new());
898         span.set_span(imp.brace_token.span);
899         return Err(Error::new_spanned(span, "expected an empty impl block"));
900     }
901 
902     if let Some((bang, path, for_token)) = &imp.trait_ {
903         let self_ty = &imp.self_ty;
904         let span = quote!(#bang #path #for_token #self_ty);
905         return Err(Error::new_spanned(
906             span,
907             "unexpected impl, expected something like `impl UniquePtr<T> {}`",
908         ));
909     }
910 
911     if let Some(where_clause) = imp.generics.where_clause {
912         return Err(Error::new_spanned(
913             where_clause,
914             "where-clause on an impl is not supported yet",
915         ));
916     }
917     let mut impl_generics = Lifetimes {
918         lt_token: imp.generics.lt_token,
919         lifetimes: Punctuated::new(),
920         gt_token: imp.generics.gt_token,
921     };
922     for pair in imp.generics.params.into_pairs() {
923         let (param, punct) = pair.into_tuple();
924         match param {
925             GenericParam::Lifetime(def) if def.bounds.is_empty() => {
926                 impl_generics.lifetimes.push_value(def.lifetime);
927                 if let Some(punct) = punct {
928                     impl_generics.lifetimes.push_punct(punct);
929                 }
930             }
931             _ => {
932                 let span = quote!(#impl_token #impl_generics);
933                 return Err(Error::new_spanned(
934                     span,
935                     "generic parameter on an impl is not supported yet",
936                 ));
937             }
938         }
939     }
940 
941     let mut negative_token = None;
942     let mut self_ty = *imp.self_ty;
943     if let RustType::Verbatim(ty) = &self_ty {
944         let mut iter = ty.clone().into_iter();
945         if let Some(TokenTree::Punct(punct)) = iter.next() {
946             if punct.as_char() == '!' {
947                 let ty = iter.collect::<TokenStream>();
948                 if !ty.is_empty() {
949                     negative_token = Some(Token![!](punct.span()));
950                     self_ty = syn::parse2(ty)?;
951                 }
952             }
953         }
954     }
955 
956     let ty = parse_type(&self_ty)?;
957     let ty_generics = match &ty {
958         Type::RustBox(ty)
959         | Type::RustVec(ty)
960         | Type::UniquePtr(ty)
961         | Type::SharedPtr(ty)
962         | Type::WeakPtr(ty)
963         | Type::CxxVector(ty) => match &ty.inner {
964             Type::Ident(ident) => ident.generics.clone(),
965             _ => Lifetimes::default(),
966         },
967         Type::Ident(_)
968         | Type::Ref(_)
969         | Type::Ptr(_)
970         | Type::Str(_)
971         | Type::Fn(_)
972         | Type::Void(_)
973         | Type::SliceRef(_)
974         | Type::Array(_) => Lifetimes::default(),
975     };
976 
977     let negative = negative_token.is_some();
978     let brace_token = imp.brace_token;
979 
980     Ok(Api::Impl(Impl {
981         impl_token,
982         impl_generics,
983         negative,
984         ty,
985         ty_generics,
986         brace_token,
987         negative_token,
988     }))
989 }
990 
parse_include(input: ParseStream) -> Result<Include>991 fn parse_include(input: ParseStream) -> Result<Include> {
992     if input.peek(LitStr) {
993         let lit: LitStr = input.parse()?;
994         let span = lit.span();
995         return Ok(Include {
996             path: lit.value(),
997             kind: IncludeKind::Quoted,
998             begin_span: span,
999             end_span: span,
1000         });
1001     }
1002 
1003     if input.peek(Token![<]) {
1004         let mut path = String::new();
1005 
1006         let langle: Token![<] = input.parse()?;
1007         while !input.is_empty() && !input.peek(Token![>]) {
1008             let token: TokenTree = input.parse()?;
1009             match token {
1010                 TokenTree::Ident(token) => path += &token.to_string(),
1011                 TokenTree::Literal(token)
1012                     if token
1013                         .to_string()
1014                         .starts_with(|ch: char| ch.is_ascii_digit()) =>
1015                 {
1016                     path += &token.to_string();
1017                 }
1018                 TokenTree::Punct(token) => path.push(token.as_char()),
1019                 _ => return Err(Error::new(token.span(), "unexpected token in include path")),
1020             }
1021         }
1022         let rangle: Token![>] = input.parse()?;
1023 
1024         return Ok(Include {
1025             path,
1026             kind: IncludeKind::Bracketed,
1027             begin_span: langle.span,
1028             end_span: rangle.span,
1029         });
1030     }
1031 
1032     Err(input.error("expected \"quoted/path/to\" or <bracketed/path/to>"))
1033 }
1034 
parse_type(ty: &RustType) -> Result<Type>1035 fn parse_type(ty: &RustType) -> Result<Type> {
1036     match ty {
1037         RustType::Reference(ty) => parse_type_reference(ty),
1038         RustType::Ptr(ty) => parse_type_ptr(ty),
1039         RustType::Path(ty) => parse_type_path(ty),
1040         RustType::Array(ty) => parse_type_array(ty),
1041         RustType::BareFn(ty) => parse_type_fn(ty),
1042         RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)),
1043         _ => Err(Error::new_spanned(ty, "unsupported type")),
1044     }
1045 }
1046 
parse_type_reference(ty: &TypeReference) -> Result<Type>1047 fn parse_type_reference(ty: &TypeReference) -> Result<Type> {
1048     let ampersand = ty.and_token;
1049     let lifetime = ty.lifetime.clone();
1050     let mutable = ty.mutability.is_some();
1051     let mutability = ty.mutability;
1052 
1053     if let RustType::Slice(slice) = ty.elem.as_ref() {
1054         let inner = parse_type(&slice.elem)?;
1055         let bracket = slice.bracket_token;
1056         return Ok(Type::SliceRef(Box::new(SliceRef {
1057             ampersand,
1058             lifetime,
1059             mutable,
1060             bracket,
1061             inner,
1062             mutability,
1063         })));
1064     }
1065 
1066     let inner = parse_type(&ty.elem)?;
1067     let pinned = false;
1068     let pin_tokens = None;
1069 
1070     Ok(match &inner {
1071         Type::Ident(ident) if ident.rust == "str" => {
1072             if ty.mutability.is_some() {
1073                 return Err(Error::new_spanned(ty, "unsupported type"));
1074             } else {
1075                 Type::Str
1076             }
1077         }
1078         _ => Type::Ref,
1079     }(Box::new(Ref {
1080         pinned,
1081         ampersand,
1082         lifetime,
1083         mutable,
1084         inner,
1085         pin_tokens,
1086         mutability,
1087     })))
1088 }
1089 
parse_type_ptr(ty: &TypePtr) -> Result<Type>1090 fn parse_type_ptr(ty: &TypePtr) -> Result<Type> {
1091     let star = ty.star_token;
1092     let mutable = ty.mutability.is_some();
1093     let constness = ty.const_token;
1094     let mutability = ty.mutability;
1095 
1096     let inner = parse_type(&ty.elem)?;
1097 
1098     Ok(Type::Ptr(Box::new(Ptr {
1099         star,
1100         mutable,
1101         inner,
1102         mutability,
1103         constness,
1104     })))
1105 }
1106 
parse_type_path(ty: &TypePath) -> Result<Type>1107 fn parse_type_path(ty: &TypePath) -> Result<Type> {
1108     let path = &ty.path;
1109     if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
1110         let segment = &path.segments[0];
1111         let ident = segment.ident.clone();
1112         match &segment.arguments {
1113             PathArguments::None => return Ok(Type::Ident(NamedType::new(ident))),
1114             PathArguments::AngleBracketed(generic) => {
1115                 if ident == "UniquePtr" && generic.args.len() == 1 {
1116                     if let GenericArgument::Type(arg) = &generic.args[0] {
1117                         let inner = parse_type(arg)?;
1118                         return Ok(Type::UniquePtr(Box::new(Ty1 {
1119                             name: ident,
1120                             langle: generic.lt_token,
1121                             inner,
1122                             rangle: generic.gt_token,
1123                         })));
1124                     }
1125                 } else if ident == "SharedPtr" && generic.args.len() == 1 {
1126                     if let GenericArgument::Type(arg) = &generic.args[0] {
1127                         let inner = parse_type(arg)?;
1128                         return Ok(Type::SharedPtr(Box::new(Ty1 {
1129                             name: ident,
1130                             langle: generic.lt_token,
1131                             inner,
1132                             rangle: generic.gt_token,
1133                         })));
1134                     }
1135                 } else if ident == "WeakPtr" && generic.args.len() == 1 {
1136                     if let GenericArgument::Type(arg) = &generic.args[0] {
1137                         let inner = parse_type(arg)?;
1138                         return Ok(Type::WeakPtr(Box::new(Ty1 {
1139                             name: ident,
1140                             langle: generic.lt_token,
1141                             inner,
1142                             rangle: generic.gt_token,
1143                         })));
1144                     }
1145                 } else if ident == "CxxVector" && generic.args.len() == 1 {
1146                     if let GenericArgument::Type(arg) = &generic.args[0] {
1147                         let inner = parse_type(arg)?;
1148                         return Ok(Type::CxxVector(Box::new(Ty1 {
1149                             name: ident,
1150                             langle: generic.lt_token,
1151                             inner,
1152                             rangle: generic.gt_token,
1153                         })));
1154                     }
1155                 } else if ident == "Box" && generic.args.len() == 1 {
1156                     if let GenericArgument::Type(arg) = &generic.args[0] {
1157                         let inner = parse_type(arg)?;
1158                         return Ok(Type::RustBox(Box::new(Ty1 {
1159                             name: ident,
1160                             langle: generic.lt_token,
1161                             inner,
1162                             rangle: generic.gt_token,
1163                         })));
1164                     }
1165                 } else if ident == "Vec" && generic.args.len() == 1 {
1166                     if let GenericArgument::Type(arg) = &generic.args[0] {
1167                         let inner = parse_type(arg)?;
1168                         return Ok(Type::RustVec(Box::new(Ty1 {
1169                             name: ident,
1170                             langle: generic.lt_token,
1171                             inner,
1172                             rangle: generic.gt_token,
1173                         })));
1174                     }
1175                 } else if ident == "Pin" && generic.args.len() == 1 {
1176                     if let GenericArgument::Type(arg) = &generic.args[0] {
1177                         let inner = parse_type(arg)?;
1178                         let pin_token = kw::Pin(ident.span());
1179                         if let Type::Ref(mut inner) = inner {
1180                             inner.pinned = true;
1181                             inner.pin_tokens =
1182                                 Some((pin_token, generic.lt_token, generic.gt_token));
1183                             return Ok(Type::Ref(inner));
1184                         }
1185                     }
1186                 } else {
1187                     let mut lifetimes = Punctuated::new();
1188                     let mut only_lifetimes = true;
1189                     for pair in generic.args.pairs() {
1190                         let (param, punct) = pair.into_tuple();
1191                         if let GenericArgument::Lifetime(param) = param {
1192                             lifetimes.push_value(param.clone());
1193                             if let Some(punct) = punct {
1194                                 lifetimes.push_punct(*punct);
1195                             }
1196                         } else {
1197                             only_lifetimes = false;
1198                             break;
1199                         }
1200                     }
1201                     if only_lifetimes {
1202                         return Ok(Type::Ident(NamedType {
1203                             rust: ident,
1204                             generics: Lifetimes {
1205                                 lt_token: Some(generic.lt_token),
1206                                 lifetimes,
1207                                 gt_token: Some(generic.gt_token),
1208                             },
1209                         }));
1210                     }
1211                 }
1212             }
1213             PathArguments::Parenthesized(_) => {}
1214         }
1215     }
1216 
1217     Err(Error::new_spanned(ty, "unsupported type"))
1218 }
1219 
parse_type_array(ty: &TypeArray) -> Result<Type>1220 fn parse_type_array(ty: &TypeArray) -> Result<Type> {
1221     let inner = parse_type(&ty.elem)?;
1222 
1223     let len_expr = if let Expr::Lit(lit) = &ty.len {
1224         lit
1225     } else {
1226         let msg = "unsupported expression, array length must be an integer literal";
1227         return Err(Error::new_spanned(&ty.len, msg));
1228     };
1229 
1230     let len_token = if let Lit::Int(int) = &len_expr.lit {
1231         int.clone()
1232     } else {
1233         let msg = "array length must be an integer literal";
1234         return Err(Error::new_spanned(len_expr, msg));
1235     };
1236 
1237     let len = len_token.base10_parse::<usize>()?;
1238     if len == 0 {
1239         let msg = "array with zero size is not supported";
1240         return Err(Error::new_spanned(ty, msg));
1241     }
1242 
1243     let bracket = ty.bracket_token;
1244     let semi_token = ty.semi_token;
1245 
1246     Ok(Type::Array(Box::new(Array {
1247         bracket,
1248         inner,
1249         semi_token,
1250         len,
1251         len_token,
1252     })))
1253 }
1254 
parse_type_fn(ty: &TypeBareFn) -> Result<Type>1255 fn parse_type_fn(ty: &TypeBareFn) -> Result<Type> {
1256     if ty.lifetimes.is_some() {
1257         return Err(Error::new_spanned(
1258             ty,
1259             "function pointer with lifetime parameters is not supported yet",
1260         ));
1261     }
1262 
1263     if ty.variadic.is_some() {
1264         return Err(Error::new_spanned(
1265             ty,
1266             "variadic function pointer is not supported yet",
1267         ));
1268     }
1269 
1270     let args = ty
1271         .inputs
1272         .iter()
1273         .enumerate()
1274         .map(|(i, arg)| {
1275             let ty = parse_type(&arg.ty)?;
1276             let ident = match &arg.name {
1277                 Some(ident) => ident.0.clone(),
1278                 None => format_ident!("arg{}", i),
1279             };
1280             let doc = Doc::new();
1281             let attrs = OtherAttrs::none();
1282             let visibility = Token![pub](ident.span());
1283             let name = pair(Namespace::default(), &ident, None, None);
1284             Ok(Var {
1285                 doc,
1286                 attrs,
1287                 visibility,
1288                 name,
1289                 ty,
1290             })
1291         })
1292         .collect::<Result<_>>()?;
1293 
1294     let mut throws_tokens = None;
1295     let ret = parse_return_type(&ty.output, &mut throws_tokens)?;
1296     let throws = throws_tokens.is_some();
1297 
1298     let unsafety = ty.unsafety;
1299     let fn_token = ty.fn_token;
1300     let generics = Generics::default();
1301     let receiver = None;
1302     let paren_token = ty.paren_token;
1303 
1304     Ok(Type::Fn(Box::new(Signature {
1305         unsafety,
1306         fn_token,
1307         generics,
1308         receiver,
1309         args,
1310         ret,
1311         throws,
1312         paren_token,
1313         throws_tokens,
1314     })))
1315 }
1316 
parse_return_type( ty: &ReturnType, throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>, ) -> Result<Option<Type>>1317 fn parse_return_type(
1318     ty: &ReturnType,
1319     throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>,
1320 ) -> Result<Option<Type>> {
1321     let mut ret = match ty {
1322         ReturnType::Default => return Ok(None),
1323         ReturnType::Type(_, ret) => ret.as_ref(),
1324     };
1325 
1326     if let RustType::Path(ty) = ret {
1327         let path = &ty.path;
1328         if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
1329             let segment = &path.segments[0];
1330             let ident = segment.ident.clone();
1331             if let PathArguments::AngleBracketed(generic) = &segment.arguments {
1332                 if ident == "Result" && generic.args.len() == 1 {
1333                     if let GenericArgument::Type(arg) = &generic.args[0] {
1334                         ret = arg;
1335                         *throws_tokens =
1336                             Some((kw::Result(ident.span()), generic.lt_token, generic.gt_token));
1337                     }
1338                 }
1339             }
1340         }
1341     }
1342 
1343     match parse_type(ret)? {
1344         Type::Void(_) => Ok(None),
1345         ty => Ok(Some(ty)),
1346     }
1347 }
1348 
visibility_pub(vis: &Visibility, inherited: Span) -> Token![pub]1349 fn visibility_pub(vis: &Visibility, inherited: Span) -> Token![pub] {
1350     Token![pub](match vis {
1351         Visibility::Public(vis) => vis.pub_token.span,
1352         Visibility::Crate(vis) => vis.crate_token.span,
1353         Visibility::Restricted(vis) => vis.pub_token.span,
1354         Visibility::Inherited => inherited,
1355     })
1356 }
1357 
pair( namespace: Namespace, default: &Ident, cxx: Option<ForeignName>, rust: Option<Ident>, ) -> Pair1358 fn pair(
1359     namespace: Namespace,
1360     default: &Ident,
1361     cxx: Option<ForeignName>,
1362     rust: Option<Ident>,
1363 ) -> Pair {
1364     Pair {
1365         namespace,
1366         cxx: cxx
1367             .unwrap_or_else(|| ForeignName::parse(&default.to_string(), default.span()).unwrap()),
1368         rust: rust.unwrap_or_else(|| default.clone()),
1369     }
1370 }
1371