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