cxx_gen/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 | Lang::CxxUnwind => {}
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 Some(name) = &abi.name else {
440        return Err(Error::new_spanned(
441            abi,
442            "ABI name is required, extern \"C++\" or extern \"Rust\"",
443        ));
444    };
445
446    match name.value().as_str() {
447        "C++" => Ok(Lang::Cxx),
448        "C++-unwind" => Ok(Lang::CxxUnwind),
449        "Rust" => Ok(Lang::Rust),
450        _ => Err(Error::new_spanned(
451            abi,
452            "unrecognized ABI, requires either \"C++\" or \"Rust\"",
453        )),
454    }
455}
456
457fn parse_extern_type(
458    cx: &mut Errors,
459    foreign_type: ForeignItemType,
460    lang: Lang,
461    trusted: bool,
462    extern_block_cfg: &CfgExpr,
463    namespace: &Namespace,
464    attrs: &OtherAttrs,
465) -> Api {
466    let mut cfg = extern_block_cfg.clone();
467    let mut doc = Doc::new();
468    let mut derives = Vec::new();
469    let mut namespace = namespace.clone();
470    let mut cxx_name = None;
471    let mut rust_name = None;
472    let mut attrs = attrs.clone();
473    attrs.extend(attrs::parse(
474        cx,
475        foreign_type.attrs,
476        attrs::Parser {
477            cfg: Some(&mut cfg),
478            doc: Some(&mut doc),
479            derives: Some(&mut derives),
480            namespace: Some(&mut namespace),
481            cxx_name: Some(&mut cxx_name),
482            rust_name: Some(&mut rust_name),
483            ..Default::default()
484        },
485    ));
486
487    let type_token = foreign_type.type_token;
488    let visibility = visibility_pub(&foreign_type.vis, type_token.span);
489    let name = pair(namespace, &foreign_type.ident, cxx_name, rust_name);
490    let generics = extern_type_lifetimes(cx, foreign_type.generics);
491    let colon_token = None;
492    let bounds = Vec::new();
493    let semi_token = foreign_type.semi_token;
494
495    (match lang {
496        Lang::Cxx | Lang::CxxUnwind => Api::CxxType,
497        Lang::Rust => Api::RustType,
498    })(ExternType {
499        cfg,
500        lang,
501        doc,
502        derives,
503        attrs,
504        visibility,
505        type_token,
506        name,
507        generics,
508        colon_token,
509        bounds,
510        semi_token,
511        trusted,
512    })
513}
514
515fn parse_extern_fn(
516    cx: &mut Errors,
517    mut foreign_fn: ForeignItemFn,
518    lang: Lang,
519    trusted: bool,
520    extern_block_cfg: &CfgExpr,
521    namespace: &Namespace,
522    attrs: &OtherAttrs,
523) -> Result<Api> {
524    let mut cfg = extern_block_cfg.clone();
525    let mut doc = Doc::new();
526    let mut namespace = namespace.clone();
527    let mut cxx_name = None;
528    let mut rust_name = None;
529    let mut attrs = attrs.clone();
530    attrs.extend(attrs::parse(
531        cx,
532        mem::take(&mut foreign_fn.attrs),
533        attrs::Parser {
534            cfg: Some(&mut cfg),
535            doc: Some(&mut doc),
536            namespace: Some(&mut namespace),
537            cxx_name: Some(&mut cxx_name),
538            rust_name: Some(&mut rust_name),
539            ..Default::default()
540        },
541    ));
542
543    let generics = &foreign_fn.sig.generics;
544    if generics.where_clause.is_some()
545        || generics.params.iter().any(|param| match param {
546            GenericParam::Lifetime(lifetime) => !lifetime.bounds.is_empty(),
547            GenericParam::Type(_) | GenericParam::Const(_) => true,
548        })
549    {
550        return Err(Error::new_spanned(
551            foreign_fn,
552            "extern function with generic parameters is not supported yet",
553        ));
554    }
555
556    if let Some(variadic) = &foreign_fn.sig.variadic {
557        return Err(Error::new_spanned(
558            variadic,
559            "variadic function is not supported yet",
560        ));
561    }
562
563    if foreign_fn.sig.asyncness.is_some() && !cfg!(feature = "experimental-async-fn") {
564        return Err(Error::new_spanned(
565            foreign_fn,
566            "async function is not directly supported yet, but see https://cxx.rs/async.html \
567            for a working approach, and https://github.com/pcwalton/cxx-async for some helpers; \
568            eventually what you wrote will work but it isn't integrated into the cxx::bridge \
569            macro yet",
570        ));
571    }
572
573    if foreign_fn.sig.constness.is_some() {
574        return Err(Error::new_spanned(
575            foreign_fn,
576            "const extern function is not supported",
577        ));
578    }
579
580    if let Some(abi) = &foreign_fn.sig.abi {
581        return Err(Error::new_spanned(
582            abi,
583            "explicit ABI on extern function is not supported",
584        ));
585    }
586
587    let mut receiver = None;
588    let mut args = Punctuated::new();
589    for arg in foreign_fn.sig.inputs.pairs() {
590        let (arg, comma) = arg.into_tuple();
591        match arg {
592            FnArg::Receiver(arg) => {
593                if let Some((ampersand, lifetime)) = &arg.reference {
594                    receiver = Some(Receiver {
595                        pinned: false,
596                        ampersand: *ampersand,
597                        lifetime: lifetime.clone(),
598                        mutable: arg.mutability.is_some(),
599                        var: arg.self_token,
600                        colon_token: Token![:](arg.self_token.span),
601                        ty: NamedType::new(Ident::new("Self", arg.self_token.span)),
602                        shorthand: true,
603                        pin_tokens: None,
604                        mutability: arg.mutability,
605                    });
606                    continue;
607                }
608                if let Some(colon_token) = arg.colon_token {
609                    let ty = parse_type(&arg.ty)?;
610                    if let Type::Ref(reference) = ty {
611                        if let Type::Ident(ident) = reference.inner {
612                            receiver = Some(Receiver {
613                                pinned: reference.pinned,
614                                ampersand: reference.ampersand,
615                                lifetime: reference.lifetime,
616                                mutable: reference.mutable,
617                                var: Token![self](ident.rust.span()),
618                                colon_token,
619                                ty: ident,
620                                shorthand: false,
621                                pin_tokens: reference.pin_tokens,
622                                mutability: reference.mutability,
623                            });
624                            continue;
625                        }
626                    }
627                }
628                return Err(Error::new_spanned(arg, "unsupported method receiver"));
629            }
630            FnArg::Typed(arg) => {
631                let ident = match arg.pat.as_ref() {
632                    Pat::Ident(pat) => pat.ident.clone(),
633                    Pat::Wild(pat) => {
634                        Ident::new(&format!("arg{}", args.len()), pat.underscore_token.span)
635                    }
636                    _ => return Err(Error::new_spanned(arg, "unsupported signature")),
637                };
638                let ty = parse_type(&arg.ty)?;
639                let cfg = CfgExpr::Unconditional;
640                let doc = Doc::new();
641                let attrs = OtherAttrs::none();
642                let visibility = Token![pub](ident.span());
643                let name = pair(Namespace::default(), &ident, None, None);
644                let colon_token = arg.colon_token;
645                args.push_value(Var {
646                    cfg,
647                    doc,
648                    attrs,
649                    visibility,
650                    name,
651                    colon_token,
652                    ty,
653                });
654                if let Some(comma) = comma {
655                    args.push_punct(*comma);
656                }
657            }
658        }
659    }
660
661    let mut throws_tokens = None;
662    let ret = parse_return_type(&foreign_fn.sig.output, &mut throws_tokens)?;
663    let throws = throws_tokens.is_some();
664    let asyncness = foreign_fn.sig.asyncness;
665    let unsafety = foreign_fn.sig.unsafety;
666    let fn_token = foreign_fn.sig.fn_token;
667    let inherited_span = unsafety.map_or(fn_token.span, |unsafety| unsafety.span);
668    let visibility = visibility_pub(&foreign_fn.vis, inherited_span);
669    let name = pair(namespace, &foreign_fn.sig.ident, cxx_name, rust_name);
670    let generics = generics.clone();
671    let paren_token = foreign_fn.sig.paren_token;
672    let semi_token = foreign_fn.semi_token;
673
674    Ok(match lang {
675        Lang::Cxx | Lang::CxxUnwind => Api::CxxFunction,
676        Lang::Rust => Api::RustFunction,
677    }(ExternFn {
678        cfg,
679        lang,
680        doc,
681        attrs,
682        visibility,
683        name,
684        sig: Signature {
685            asyncness,
686            unsafety,
687            fn_token,
688            generics,
689            receiver,
690            args,
691            ret,
692            throws,
693            paren_token,
694            throws_tokens,
695        },
696        semi_token,
697        trusted,
698    }))
699}
700
701fn parse_extern_verbatim(
702    cx: &mut Errors,
703    tokens: TokenStream,
704    lang: Lang,
705    trusted: bool,
706    extern_block_cfg: &CfgExpr,
707    namespace: &Namespace,
708    attrs: &OtherAttrs,
709) -> Result<Api> {
710    |input: ParseStream| -> Result<Api> {
711        let unparsed_attrs = input.call(Attribute::parse_outer)?;
712        let visibility: Visibility = input.parse()?;
713        if input.peek(Token![type]) {
714            parse_extern_verbatim_type(
715                cx,
716                unparsed_attrs,
717                visibility,
718                input,
719                lang,
720                trusted,
721                extern_block_cfg,
722                namespace,
723                attrs,
724            )
725        } else if input.peek(Token![fn]) {
726            parse_extern_verbatim_fn(input)
727        } else {
728            let span = input.cursor().token_stream();
729            Err(Error::new_spanned(
730                span,
731                "unsupported foreign item, expected `type` or `fn`",
732            ))
733        }
734    }
735    .parse2(tokens)
736}
737
738fn parse_extern_verbatim_type(
739    cx: &mut Errors,
740    unparsed_attrs: Vec<Attribute>,
741    visibility: Visibility,
742    input: ParseStream,
743    lang: Lang,
744    trusted: bool,
745    extern_block_cfg: &CfgExpr,
746    namespace: &Namespace,
747    attrs: &OtherAttrs,
748) -> Result<Api> {
749    let type_token: Token![type] = input.parse()?;
750    let ident: Ident = input.parse()?;
751    let generics: Generics = input.parse()?;
752    let lifetimes = extern_type_lifetimes(cx, generics);
753    let lookahead = input.lookahead1();
754    if lookahead.peek(Token![=]) {
755        // type Alias = crate::path::to::Type;
756        parse_type_alias(
757            cx,
758            unparsed_attrs,
759            visibility,
760            type_token,
761            ident,
762            lifetimes,
763            input,
764            lang,
765            extern_block_cfg,
766            namespace,
767            attrs,
768        )
769    } else if lookahead.peek(Token![:]) {
770        // type Opaque: Bound2 + Bound2;
771        parse_extern_type_bounded(
772            cx,
773            unparsed_attrs,
774            visibility,
775            type_token,
776            ident,
777            lifetimes,
778            input,
779            lang,
780            trusted,
781            extern_block_cfg,
782            namespace,
783            attrs,
784        )
785    } else {
786        Err(lookahead.error())
787    }
788}
789
790fn extern_type_lifetimes(cx: &mut Errors, generics: Generics) -> Lifetimes {
791    let mut lifetimes = Punctuated::new();
792    let mut has_unsupported_generic_param = false;
793    for pair in generics.params.into_pairs() {
794        let (param, punct) = pair.into_tuple();
795        match param {
796            GenericParam::Lifetime(param) => {
797                if !param.bounds.is_empty() && !has_unsupported_generic_param {
798                    let msg = "lifetime parameter with bounds is not supported yet";
799                    cx.error(&param, msg);
800                    has_unsupported_generic_param = true;
801                }
802                lifetimes.push_value(param.lifetime);
803                if let Some(punct) = punct {
804                    lifetimes.push_punct(punct);
805                }
806            }
807            GenericParam::Type(param) => {
808                if !has_unsupported_generic_param {
809                    let msg = "extern type with generic type parameter is not supported yet";
810                    cx.error(&param, msg);
811                    has_unsupported_generic_param = true;
812                }
813            }
814            GenericParam::Const(param) => {
815                if !has_unsupported_generic_param {
816                    let msg = "extern type with const generic parameter is not supported yet";
817                    cx.error(&param, msg);
818                    has_unsupported_generic_param = true;
819                }
820            }
821        }
822    }
823    Lifetimes {
824        lt_token: generics.lt_token,
825        lifetimes,
826        gt_token: generics.gt_token,
827    }
828}
829
830fn parse_extern_verbatim_fn(input: ParseStream) -> Result<Api> {
831    input.parse::<RustSignature>()?;
832    input.parse::<Token![;]>()?;
833    unreachable!()
834}
835
836fn parse_type_alias(
837    cx: &mut Errors,
838    unparsed_attrs: Vec<Attribute>,
839    visibility: Visibility,
840    type_token: Token![type],
841    ident: Ident,
842    generics: Lifetimes,
843    input: ParseStream,
844    lang: Lang,
845    extern_block_cfg: &CfgExpr,
846    namespace: &Namespace,
847    attrs: &OtherAttrs,
848) -> Result<Api> {
849    let eq_token: Token![=] = input.parse()?;
850    let ty: RustType = input.parse()?;
851    let semi_token: Token![;] = input.parse()?;
852
853    let mut cfg = extern_block_cfg.clone();
854    let mut doc = Doc::new();
855    let mut derives = Vec::new();
856    let mut namespace = namespace.clone();
857    let mut cxx_name = None;
858    let mut rust_name = None;
859    let mut attrs = attrs.clone();
860    attrs.extend(attrs::parse(
861        cx,
862        unparsed_attrs,
863        attrs::Parser {
864            cfg: Some(&mut cfg),
865            doc: Some(&mut doc),
866            derives: Some(&mut derives),
867            namespace: Some(&mut namespace),
868            cxx_name: Some(&mut cxx_name),
869            rust_name: Some(&mut rust_name),
870            ..Default::default()
871        },
872    ));
873
874    if lang == Lang::Rust {
875        let span = quote!(#type_token #semi_token);
876        let msg = "type alias in extern \"Rust\" block is not supported";
877        return Err(Error::new_spanned(span, msg));
878    }
879
880    let visibility = visibility_pub(&visibility, type_token.span);
881    let name = pair(namespace, &ident, cxx_name, rust_name);
882
883    Ok(Api::TypeAlias(TypeAlias {
884        cfg,
885        doc,
886        derives,
887        attrs,
888        visibility,
889        type_token,
890        name,
891        generics,
892        eq_token,
893        ty,
894        semi_token,
895    }))
896}
897
898fn parse_extern_type_bounded(
899    cx: &mut Errors,
900    unparsed_attrs: Vec<Attribute>,
901    visibility: Visibility,
902    type_token: Token![type],
903    ident: Ident,
904    generics: Lifetimes,
905    input: ParseStream,
906    lang: Lang,
907    trusted: bool,
908    extern_block_cfg: &CfgExpr,
909    namespace: &Namespace,
910    attrs: &OtherAttrs,
911) -> Result<Api> {
912    let mut bounds = Vec::new();
913    let colon_token: Option<Token![:]> = input.parse()?;
914    if colon_token.is_some() {
915        loop {
916            match input.parse()? {
917                TypeParamBound::Trait(TraitBound {
918                    paren_token: None,
919                    modifier: TraitBoundModifier::None,
920                    lifetimes: None,
921                    path,
922                }) if if let Some(derive) = path.get_ident().and_then(Derive::from) {
923                    bounds.push(derive);
924                    true
925                } else {
926                    false
927                } => {}
928                bound => cx.error(bound, "unsupported trait"),
929            }
930
931            let lookahead = input.lookahead1();
932            if lookahead.peek(Token![+]) {
933                input.parse::<Token![+]>()?;
934            } else if lookahead.peek(Token![;]) {
935                break;
936            } else {
937                return Err(lookahead.error());
938            }
939        }
940    }
941    let semi_token: Token![;] = input.parse()?;
942
943    let mut cfg = extern_block_cfg.clone();
944    let mut doc = Doc::new();
945    let mut derives = Vec::new();
946    let mut namespace = namespace.clone();
947    let mut cxx_name = None;
948    let mut rust_name = None;
949    let mut attrs = attrs.clone();
950    attrs.extend(attrs::parse(
951        cx,
952        unparsed_attrs,
953        attrs::Parser {
954            cfg: Some(&mut cfg),
955            doc: Some(&mut doc),
956            derives: Some(&mut derives),
957            namespace: Some(&mut namespace),
958            cxx_name: Some(&mut cxx_name),
959            rust_name: Some(&mut rust_name),
960            ..Default::default()
961        },
962    ));
963
964    let visibility = visibility_pub(&visibility, type_token.span);
965    let name = pair(namespace, &ident, cxx_name, rust_name);
966
967    Ok(match lang {
968        Lang::Cxx | Lang::CxxUnwind => Api::CxxType,
969        Lang::Rust => Api::RustType,
970    }(ExternType {
971        cfg,
972        lang,
973        doc,
974        derives,
975        attrs,
976        visibility,
977        type_token,
978        name,
979        generics,
980        colon_token,
981        bounds,
982        semi_token,
983        trusted,
984    }))
985}
986
987fn parse_impl(cx: &mut Errors, imp: ItemImpl) -> Result<Api> {
988    let impl_token = imp.impl_token;
989
990    let mut cfg = CfgExpr::Unconditional;
991    attrs::parse(
992        cx,
993        imp.attrs,
994        attrs::Parser {
995            cfg: Some(&mut cfg),
996            ..Default::default()
997        },
998    );
999
1000    if !imp.items.is_empty() {
1001        let mut span = Group::new(Delimiter::Brace, TokenStream::new());
1002        span.set_span(imp.brace_token.span.join());
1003        return Err(Error::new_spanned(span, "expected an empty impl block"));
1004    }
1005
1006    if let Some((bang, path, for_token)) = &imp.trait_ {
1007        let self_ty = &imp.self_ty;
1008        let span = quote!(#bang #path #for_token #self_ty);
1009        return Err(Error::new_spanned(
1010            span,
1011            "unexpected impl, expected something like `impl UniquePtr<T> {}`",
1012        ));
1013    }
1014
1015    if let Some(where_clause) = imp.generics.where_clause {
1016        return Err(Error::new_spanned(
1017            where_clause,
1018            "where-clause on an impl is not supported yet",
1019        ));
1020    }
1021    let mut impl_generics = Lifetimes {
1022        lt_token: imp.generics.lt_token,
1023        lifetimes: Punctuated::new(),
1024        gt_token: imp.generics.gt_token,
1025    };
1026    for pair in imp.generics.params.into_pairs() {
1027        let (param, punct) = pair.into_tuple();
1028        match param {
1029            GenericParam::Lifetime(def) if def.bounds.is_empty() => {
1030                impl_generics.lifetimes.push_value(def.lifetime);
1031                if let Some(punct) = punct {
1032                    impl_generics.lifetimes.push_punct(punct);
1033                }
1034            }
1035            _ => {
1036                let span = quote!(#impl_token #impl_generics);
1037                return Err(Error::new_spanned(
1038                    span,
1039                    "generic parameter on an impl is not supported yet",
1040                ));
1041            }
1042        }
1043    }
1044
1045    let mut negative_token = None;
1046    let mut self_ty = *imp.self_ty;
1047    if let RustType::Verbatim(ty) = &self_ty {
1048        let mut iter = ty.clone().into_iter();
1049        if let Some(TokenTree::Punct(punct)) = iter.next() {
1050            if punct.as_char() == '!' {
1051                let ty = iter.collect::<TokenStream>();
1052                if !ty.is_empty() {
1053                    negative_token = Some(Token![!](punct.span()));
1054                    self_ty = syn::parse2(ty)?;
1055                }
1056            }
1057        }
1058    }
1059
1060    let ty = parse_type(&self_ty)?;
1061    let ty_generics = match &ty {
1062        Type::RustBox(ty)
1063        | Type::RustVec(ty)
1064        | Type::UniquePtr(ty)
1065        | Type::SharedPtr(ty)
1066        | Type::WeakPtr(ty)
1067        | Type::CxxVector(ty) => match &ty.inner {
1068            Type::Ident(ident) => ident.generics.clone(),
1069            _ => Lifetimes::default(),
1070        },
1071        Type::Ident(_)
1072        | Type::Ref(_)
1073        | Type::Ptr(_)
1074        | Type::Str(_)
1075        | Type::Fn(_)
1076        | Type::Void(_)
1077        | Type::SliceRef(_)
1078        | Type::Array(_) => Lifetimes::default(),
1079    };
1080
1081    let negative = negative_token.is_some();
1082    let brace_token = imp.brace_token;
1083
1084    Ok(Api::Impl(Impl {
1085        cfg,
1086        impl_token,
1087        impl_generics,
1088        negative,
1089        ty,
1090        ty_generics,
1091        brace_token,
1092        negative_token,
1093    }))
1094}
1095
1096fn parse_include(input: ParseStream) -> Result<Include> {
1097    if input.peek(LitStr) {
1098        let lit: LitStr = input.parse()?;
1099        let span = lit.span();
1100        return Ok(Include {
1101            cfg: CfgExpr::Unconditional,
1102            path: lit.value(),
1103            kind: IncludeKind::Quoted,
1104            begin_span: span,
1105            end_span: span,
1106        });
1107    }
1108
1109    if input.peek(Token![<]) {
1110        let mut path = String::new();
1111
1112        let langle: Token![<] = input.parse()?;
1113        while !input.is_empty() && !input.peek(Token![>]) {
1114            let token: TokenTree = input.parse()?;
1115            match token {
1116                TokenTree::Ident(token) => path += &token.to_string(),
1117                TokenTree::Literal(token)
1118                    if token
1119                        .to_string()
1120                        .starts_with(|ch: char| ch.is_ascii_digit()) =>
1121                {
1122                    path += &token.to_string();
1123                }
1124                TokenTree::Punct(token) => path.push(token.as_char()),
1125                _ => return Err(Error::new(token.span(), "unexpected token in include path")),
1126            }
1127        }
1128        let rangle: Token![>] = input.parse()?;
1129
1130        return Ok(Include {
1131            cfg: CfgExpr::Unconditional,
1132            path,
1133            kind: IncludeKind::Bracketed,
1134            begin_span: langle.span,
1135            end_span: rangle.span,
1136        });
1137    }
1138
1139    Err(input.error("expected \"quoted/path/to\" or <bracketed/path/to>"))
1140}
1141
1142fn parse_type(ty: &RustType) -> Result<Type> {
1143    match ty {
1144        RustType::Reference(ty) => parse_type_reference(ty),
1145        RustType::Ptr(ty) => parse_type_ptr(ty),
1146        RustType::Path(ty) => parse_type_path(ty),
1147        RustType::Array(ty) => parse_type_array(ty),
1148        RustType::BareFn(ty) => parse_type_fn(ty),
1149        RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span.join())),
1150        _ => Err(Error::new_spanned(ty, "unsupported type")),
1151    }
1152}
1153
1154fn parse_type_reference(ty: &TypeReference) -> Result<Type> {
1155    let ampersand = ty.and_token;
1156    let lifetime = ty.lifetime.clone();
1157    let mutable = ty.mutability.is_some();
1158    let mutability = ty.mutability;
1159
1160    if let RustType::Slice(slice) = ty.elem.as_ref() {
1161        let inner = parse_type(&slice.elem)?;
1162        let bracket = slice.bracket_token;
1163        return Ok(Type::SliceRef(Box::new(SliceRef {
1164            ampersand,
1165            lifetime,
1166            mutable,
1167            bracket,
1168            inner,
1169            mutability,
1170        })));
1171    }
1172
1173    let inner = parse_type(&ty.elem)?;
1174    let pinned = false;
1175    let pin_tokens = None;
1176
1177    Ok(match &inner {
1178        Type::Ident(ident) if ident.rust == "str" => {
1179            if ty.mutability.is_some() {
1180                return Err(Error::new_spanned(ty, "unsupported type"));
1181            } else {
1182                Type::Str
1183            }
1184        }
1185        _ => Type::Ref,
1186    }(Box::new(Ref {
1187        pinned,
1188        ampersand,
1189        lifetime,
1190        mutable,
1191        inner,
1192        pin_tokens,
1193        mutability,
1194    })))
1195}
1196
1197fn parse_type_ptr(ty: &TypePtr) -> Result<Type> {
1198    let star = ty.star_token;
1199    let mutable = ty.mutability.is_some();
1200    let constness = ty.const_token;
1201    let mutability = ty.mutability;
1202
1203    let inner = parse_type(&ty.elem)?;
1204
1205    Ok(Type::Ptr(Box::new(Ptr {
1206        star,
1207        mutable,
1208        inner,
1209        mutability,
1210        constness,
1211    })))
1212}
1213
1214fn parse_type_path(ty: &TypePath) -> Result<Type> {
1215    let path = &ty.path;
1216    if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
1217        let segment = &path.segments[0];
1218        let ident = segment.ident.clone();
1219        match &segment.arguments {
1220            PathArguments::None => return Ok(Type::Ident(NamedType::new(ident))),
1221            PathArguments::AngleBracketed(generic) => {
1222                if ident == "UniquePtr" && generic.args.len() == 1 {
1223                    if let GenericArgument::Type(arg) = &generic.args[0] {
1224                        let inner = parse_type(arg)?;
1225                        return Ok(Type::UniquePtr(Box::new(Ty1 {
1226                            name: ident,
1227                            langle: generic.lt_token,
1228                            inner,
1229                            rangle: generic.gt_token,
1230                        })));
1231                    }
1232                } else if ident == "SharedPtr" && generic.args.len() == 1 {
1233                    if let GenericArgument::Type(arg) = &generic.args[0] {
1234                        let inner = parse_type(arg)?;
1235                        return Ok(Type::SharedPtr(Box::new(Ty1 {
1236                            name: ident,
1237                            langle: generic.lt_token,
1238                            inner,
1239                            rangle: generic.gt_token,
1240                        })));
1241                    }
1242                } else if ident == "WeakPtr" && generic.args.len() == 1 {
1243                    if let GenericArgument::Type(arg) = &generic.args[0] {
1244                        let inner = parse_type(arg)?;
1245                        return Ok(Type::WeakPtr(Box::new(Ty1 {
1246                            name: ident,
1247                            langle: generic.lt_token,
1248                            inner,
1249                            rangle: generic.gt_token,
1250                        })));
1251                    }
1252                } else if ident == "CxxVector" && generic.args.len() == 1 {
1253                    if let GenericArgument::Type(arg) = &generic.args[0] {
1254                        let inner = parse_type(arg)?;
1255                        return Ok(Type::CxxVector(Box::new(Ty1 {
1256                            name: ident,
1257                            langle: generic.lt_token,
1258                            inner,
1259                            rangle: generic.gt_token,
1260                        })));
1261                    }
1262                } else if ident == "Box" && generic.args.len() == 1 {
1263                    if let GenericArgument::Type(arg) = &generic.args[0] {
1264                        let inner = parse_type(arg)?;
1265                        return Ok(Type::RustBox(Box::new(Ty1 {
1266                            name: ident,
1267                            langle: generic.lt_token,
1268                            inner,
1269                            rangle: generic.gt_token,
1270                        })));
1271                    }
1272                } else if ident == "Vec" && generic.args.len() == 1 {
1273                    if let GenericArgument::Type(arg) = &generic.args[0] {
1274                        let inner = parse_type(arg)?;
1275                        return Ok(Type::RustVec(Box::new(Ty1 {
1276                            name: ident,
1277                            langle: generic.lt_token,
1278                            inner,
1279                            rangle: generic.gt_token,
1280                        })));
1281                    }
1282                } else if ident == "Pin" && generic.args.len() == 1 {
1283                    if let GenericArgument::Type(arg) = &generic.args[0] {
1284                        let inner = parse_type(arg)?;
1285                        let pin_token = kw::Pin(ident.span());
1286                        if let Type::Ref(mut inner) = inner {
1287                            inner.pinned = true;
1288                            inner.pin_tokens =
1289                                Some((pin_token, generic.lt_token, generic.gt_token));
1290                            return Ok(Type::Ref(inner));
1291                        }
1292                    }
1293                } else {
1294                    let mut lifetimes = Punctuated::new();
1295                    let mut only_lifetimes = true;
1296                    for pair in generic.args.pairs() {
1297                        let (param, punct) = pair.into_tuple();
1298                        if let GenericArgument::Lifetime(param) = param {
1299                            lifetimes.push_value(param.clone());
1300                            if let Some(punct) = punct {
1301                                lifetimes.push_punct(*punct);
1302                            }
1303                        } else {
1304                            only_lifetimes = false;
1305                            break;
1306                        }
1307                    }
1308                    if only_lifetimes {
1309                        return Ok(Type::Ident(NamedType {
1310                            rust: ident,
1311                            generics: Lifetimes {
1312                                lt_token: Some(generic.lt_token),
1313                                lifetimes,
1314                                gt_token: Some(generic.gt_token),
1315                            },
1316                        }));
1317                    }
1318                }
1319            }
1320            PathArguments::Parenthesized(_) => {}
1321        }
1322    }
1323
1324    if ty.qself.is_none() && path.segments.len() == 2 && path.segments[0].ident == "cxx" {
1325        return Err(Error::new_spanned(
1326            ty,
1327            "unexpected `cxx::` qualifier found in a `#[cxx::bridge]`",
1328        ));
1329    }
1330
1331    Err(Error::new_spanned(ty, "unsupported type"))
1332}
1333
1334fn parse_type_array(ty: &TypeArray) -> Result<Type> {
1335    let inner = parse_type(&ty.elem)?;
1336
1337    let Expr::Lit(len_expr) = &ty.len else {
1338        let msg = "unsupported expression, array length must be an integer literal";
1339        return Err(Error::new_spanned(&ty.len, msg));
1340    };
1341
1342    let Lit::Int(len_token) = &len_expr.lit else {
1343        let msg = "array length must be an integer literal";
1344        return Err(Error::new_spanned(len_expr, msg));
1345    };
1346
1347    let len = len_token.base10_parse::<usize>()?;
1348    if len == 0 {
1349        let msg = "array with zero size is not supported";
1350        return Err(Error::new_spanned(ty, msg));
1351    }
1352
1353    let bracket = ty.bracket_token;
1354    let semi_token = ty.semi_token;
1355
1356    Ok(Type::Array(Box::new(Array {
1357        bracket,
1358        inner,
1359        semi_token,
1360        len,
1361        len_token: len_token.clone(),
1362    })))
1363}
1364
1365fn parse_type_fn(ty: &TypeBareFn) -> Result<Type> {
1366    if ty.lifetimes.is_some() {
1367        return Err(Error::new_spanned(
1368            ty,
1369            "function pointer with lifetime parameters is not supported yet",
1370        ));
1371    }
1372
1373    if ty.variadic.is_some() {
1374        return Err(Error::new_spanned(
1375            ty,
1376            "variadic function pointer is not supported yet",
1377        ));
1378    }
1379
1380    let args = ty
1381        .inputs
1382        .iter()
1383        .enumerate()
1384        .map(|(i, arg)| {
1385            let (ident, colon_token) = match &arg.name {
1386                Some((ident, colon_token)) => (ident.clone(), *colon_token),
1387                None => {
1388                    let fn_span = ty.paren_token.span.join();
1389                    let ident = format_ident!("arg{}", i, span = fn_span);
1390                    let colon_token = Token![:](fn_span);
1391                    (ident, colon_token)
1392                }
1393            };
1394            let ty = parse_type(&arg.ty)?;
1395            let cfg = CfgExpr::Unconditional;
1396            let doc = Doc::new();
1397            let attrs = OtherAttrs::none();
1398            let visibility = Token![pub](ident.span());
1399            let name = pair(Namespace::default(), &ident, None, None);
1400            Ok(Var {
1401                cfg,
1402                doc,
1403                attrs,
1404                visibility,
1405                name,
1406                colon_token,
1407                ty,
1408            })
1409        })
1410        .collect::<Result<_>>()?;
1411
1412    let mut throws_tokens = None;
1413    let ret = parse_return_type(&ty.output, &mut throws_tokens)?;
1414    let throws = throws_tokens.is_some();
1415
1416    let asyncness = None;
1417    let unsafety = ty.unsafety;
1418    let fn_token = ty.fn_token;
1419    let generics = Generics::default();
1420    let receiver = None;
1421    let paren_token = ty.paren_token;
1422
1423    Ok(Type::Fn(Box::new(Signature {
1424        asyncness,
1425        unsafety,
1426        fn_token,
1427        generics,
1428        receiver,
1429        args,
1430        ret,
1431        throws,
1432        paren_token,
1433        throws_tokens,
1434    })))
1435}
1436
1437fn parse_return_type(
1438    ty: &ReturnType,
1439    throws_tokens: &mut Option<(kw::Result, Token![<], Token![>])>,
1440) -> Result<Option<Type>> {
1441    let mut ret = match ty {
1442        ReturnType::Default => return Ok(None),
1443        ReturnType::Type(_, ret) => ret.as_ref(),
1444    };
1445
1446    if let RustType::Path(ty) = ret {
1447        let path = &ty.path;
1448        if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
1449            let segment = &path.segments[0];
1450            let ident = segment.ident.clone();
1451            if let PathArguments::AngleBracketed(generic) = &segment.arguments {
1452                if ident == "Result" && generic.args.len() == 1 {
1453                    if let GenericArgument::Type(arg) = &generic.args[0] {
1454                        ret = arg;
1455                        *throws_tokens =
1456                            Some((kw::Result(ident.span()), generic.lt_token, generic.gt_token));
1457                    }
1458                }
1459            }
1460        }
1461    }
1462
1463    match parse_type(ret)? {
1464        Type::Void(_) => Ok(None),
1465        ty => Ok(Some(ty)),
1466    }
1467}
1468
1469fn visibility_pub(vis: &Visibility, inherited: Span) -> Token![pub] {
1470    Token![pub](match vis {
1471        Visibility::Public(vis) => vis.span,
1472        Visibility::Restricted(vis) => vis.pub_token.span,
1473        Visibility::Inherited => inherited,
1474    })
1475}
1476
1477fn pair(
1478    namespace: Namespace,
1479    default: &Ident,
1480    cxx: Option<ForeignName>,
1481    rust: Option<Ident>,
1482) -> Pair {
1483    Pair {
1484        namespace,
1485        cxx: cxx
1486            .unwrap_or_else(|| ForeignName::parse(&default.to_string(), default.span()).unwrap()),
1487        rust: rust.unwrap_or_else(|| default.clone()),
1488    }
1489}