cxxbridge_macro/syntax/
parse.rs

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