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