blob: 3d04674b8e00f466862b8cf269dd634f67cef714 [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use indexmap::set::IndexSet as HashSet;
10use std::fmt::Display;
11
12use crate::types::{make_ident, Namespace, QualifiedName};
13use autocxx_parser::{ExternCppType, RustFun, RustPath};
14use itertools::Itertools;
15use quote::ToTokens;
16use syn::{
17 parse::Parse,
18 punctuated::Punctuated,
19 token::{Comma, Unsafe},
20 Attribute, FnArg, Ident, ItemConst, ItemEnum, ItemStruct, ItemType, ItemUse, LitBool, LitInt,
21 Pat, ReturnType, Type, Visibility,
22};
23
24use super::{
25 analysis::{
26 fun::{
27 function_wrapper::{CppFunction, CppFunctionBody, CppFunctionKind},
28 ReceiverMutability,
29 },
30 PointerTreatment,
31 },
32 convert_error::{ConvertErrorWithContext, ErrorContext},
33 ConvertError,
34};
35
36#[derive(Copy, Clone, Eq, PartialEq)]
37pub(crate) enum TypeKind {
38 Pod, // trivial. Can be moved and copied in Rust.
39 NonPod, // has destructor or non-trivial move constructors. Can only hold by UniquePtr
40 Abstract, // has pure virtual members - can't even generate UniquePtr.
41 // It's possible that the type itself isn't pure virtual, but it inherits from
42 // some other type which is pure virtual. Alternatively, maybe we just don't
43 // know if the base class is pure virtual because it wasn't on the allowlist,
44 // in which case we'll err on the side of caution.
45}
46
47/// C++ visibility.
48#[derive(Debug, Clone, PartialEq, Eq, Copy)]
49pub(crate) enum CppVisibility {
50 Public,
51 Protected,
52 Private,
53}
54
55/// Details about a C++ struct.
56pub(crate) struct StructDetails {
57 pub(crate) vis: CppVisibility,
58 pub(crate) item: ItemStruct,
59 pub(crate) layout: Option<Layout>,
60 pub(crate) has_rvalue_reference_fields: bool,
61}
62
63/// Layout of a type, equivalent to the same type in ir/layout.rs in bindgen
64#[derive(Clone)]
65pub(crate) struct Layout {
66 /// The size (in bytes) of this layout.
67 pub(crate) size: usize,
68 /// The alignment (in bytes) of this layout.
69 pub(crate) align: usize,
70 /// Whether this layout's members are packed or not.
71 pub(crate) packed: bool,
72}
73
74impl Parse for Layout {
75 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
76 let size: LitInt = input.parse()?;
77 input.parse::<syn::token::Comma>()?;
78 let align: LitInt = input.parse()?;
79 input.parse::<syn::token::Comma>()?;
80 let packed: LitBool = input.parse()?;
81 Ok(Layout {
82 size: size.base10_parse().unwrap(),
83 align: align.base10_parse().unwrap(),
84 packed: packed.value(),
85 })
86 }
87}
88
89#[derive(Clone)]
90pub(crate) enum Virtualness {
91 None,
92 Virtual,
93 PureVirtual,
94}
95
96#[derive(Clone, Copy)]
97pub(crate) enum CastMutability {
98 ConstToConst,
99 MutToConst,
100 MutToMut,
101}
102
103/// Indicates that this function (which is synthetic) should
104/// be a trait implementation rather than a method or free function.
105#[derive(Clone)]
106pub(crate) enum TraitSynthesis {
107 Cast {
108 to_type: QualifiedName,
109 mutable: CastMutability,
110 },
111 AllocUninitialized(QualifiedName),
112 FreeUninitialized(QualifiedName),
113}
114
115/// Details of a subclass constructor.
116/// TODO: zap this; replace with an extra API.
117#[derive(Clone)]
118pub(crate) struct SubclassConstructorDetails {
119 pub(crate) subclass: SubclassName,
120 pub(crate) is_trivial: bool,
121 /// Implementation of the constructor _itself_ as distinct
122 /// from any wrapper function we create to call it.
123 pub(crate) cpp_impl: CppFunction,
124}
125
126/// Contributions to traits representing C++ superclasses that
127/// we may implement as Rust subclasses.
128#[derive(Clone)]
129pub(crate) struct SuperclassMethod {
130 pub(crate) name: Ident,
131 pub(crate) receiver: QualifiedName,
132 pub(crate) params: Punctuated<FnArg, Comma>,
133 pub(crate) param_names: Vec<Pat>,
134 pub(crate) ret_type: ReturnType,
135 pub(crate) receiver_mutability: ReceiverMutability,
136 pub(crate) requires_unsafe: UnsafetyNeeded,
137 pub(crate) is_pure_virtual: bool,
138}
139
140/// Information about references (as opposed to pointers) to be found
141/// within the function signature. This is derived from bindgen annotations
142/// which is why it's not within `FuncToConvert::inputs`
143#[derive(Default, Clone)]
144pub(crate) struct References {
145 pub(crate) rvalue_ref_params: HashSet<Ident>,
146 pub(crate) ref_params: HashSet<Ident>,
147 pub(crate) ref_return: bool,
148 pub(crate) rvalue_ref_return: bool,
149}
150
151impl References {
152 pub(crate) fn new_with_this_and_return_as_reference() -> Self {
153 Self {
154 ref_return: true,
155 ref_params: [make_ident("this")].into_iter().collect(),
156 ..Default::default()
157 }
158 }
159 pub(crate) fn param_treatment(&self, param: &Ident) -> PointerTreatment {
160 if self.rvalue_ref_params.contains(param) {
161 PointerTreatment::RValueReference
162 } else if self.ref_params.contains(param) {
163 PointerTreatment::Reference
164 } else {
165 PointerTreatment::Pointer
166 }
167 }
168 pub(crate) fn return_treatment(&self) -> PointerTreatment {
169 if self.rvalue_ref_return {
170 PointerTreatment::RValueReference
171 } else if self.ref_return {
172 PointerTreatment::Reference
173 } else {
174 PointerTreatment::Pointer
175 }
176 }
177}
178
179#[derive(Clone)]
180pub(crate) struct TraitImplSignature {
181 pub(crate) ty: Type,
182 pub(crate) trait_signature: Type,
183 /// The trait is 'unsafe' itself
184 pub(crate) unsafety: Option<Unsafe>,
185}
186
187impl Eq for TraitImplSignature {}
188
189impl PartialEq for TraitImplSignature {
190 fn eq(&self, other: &Self) -> bool {
191 totokens_equal(&self.unsafety, &other.unsafety)
192 && totokens_equal(&self.ty, &other.ty)
193 && totokens_equal(&self.trait_signature, &other.trait_signature)
194 }
195}
196
197fn totokens_to_string<T: ToTokens>(a: &T) -> String {
198 a.to_token_stream().to_string()
199}
200
201fn totokens_equal<T: ToTokens>(a: &T, b: &T) -> bool {
202 totokens_to_string(a) == totokens_to_string(b)
203}
204
205fn hash_totokens<T: ToTokens, H: std::hash::Hasher>(a: &T, state: &mut H) {
206 use std::hash::Hash;
207 totokens_to_string(a).hash(state)
208}
209
210impl std::hash::Hash for TraitImplSignature {
211 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
212 hash_totokens(&self.ty, state);
213 hash_totokens(&self.trait_signature, state);
214 hash_totokens(&self.unsafety, state);
215 }
216}
217
218#[derive(Clone, Debug)]
219pub(crate) enum SpecialMemberKind {
220 DefaultConstructor,
221 CopyConstructor,
222 MoveConstructor,
223 Destructor,
224 AssignmentOperator,
225}
226
227impl Display for SpecialMemberKind {
228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229 write!(
230 f,
231 "{}",
232 match self {
233 SpecialMemberKind::DefaultConstructor => "default constructor",
234 SpecialMemberKind::CopyConstructor => "copy constructor",
235 SpecialMemberKind::MoveConstructor => "move constructor",
236 SpecialMemberKind::Destructor => "destructor",
237 SpecialMemberKind::AssignmentOperator => "assignment operator",
238 }
239 )
240 }
241}
242
243#[derive(Clone)]
244pub(crate) enum Provenance {
245 Bindgen,
246 SynthesizedOther,
247 SynthesizedSubclassConstructor(Box<SubclassConstructorDetails>),
248}
249
250/// A C++ function for which we need to generate bindings, but haven't
251/// yet analyzed in depth. This is little more than a `ForeignItemFn`
252/// broken down into its constituent parts, plus some metadata from the
253/// surrounding bindgen parsing context.
254///
255/// Some parts of the code synthesize additional functions and then
256/// pass them through the same pipeline _as if_ they were discovered
257/// during normal bindgen parsing. If that happens, they'll create one
258/// of these structures, and typically fill in some of the
259/// `synthesized_*` members which are not filled in from bindgen.
260#[derive(Clone)]
261pub(crate) struct FuncToConvert {
262 pub(crate) provenance: Provenance,
263 pub(crate) ident: Ident,
264 pub(crate) doc_attrs: Vec<Attribute>,
265 pub(crate) inputs: Punctuated<FnArg, Comma>,
266 pub(crate) variadic: bool,
267 pub(crate) output: ReturnType,
268 pub(crate) vis: Visibility,
269 pub(crate) virtualness: Virtualness,
270 pub(crate) cpp_vis: CppVisibility,
271 pub(crate) special_member: Option<SpecialMemberKind>,
272 pub(crate) unused_template_param: bool,
273 pub(crate) references: References,
274 pub(crate) original_name: Option<String>,
275 /// Used for static functions only. For all other functons,
276 /// this is figured out from the receiver type in the inputs.
277 pub(crate) self_ty: Option<QualifiedName>,
278 /// If we wish to use a different 'this' type than the original
279 /// method receiver, e.g. because we're making a subclass
280 /// constructor, fill it in here.
281 pub(crate) synthesized_this_type: Option<QualifiedName>,
282 /// If this function should actually belong to a trait.
283 pub(crate) add_to_trait: Option<TraitSynthesis>,
284 /// If Some, this function didn't really exist in the original
285 /// C++ and instead we're synthesizing it.
286 pub(crate) synthetic_cpp: Option<(CppFunctionBody, CppFunctionKind)>,
287 pub(crate) is_deleted: bool,
288}
289
290/// Layers of analysis which may be applied to decorate each API.
291/// See description of the purpose of this trait within `Api`.
292pub(crate) trait AnalysisPhase {
293 type TypedefAnalysis;
294 type StructAnalysis;
295 type FunAnalysis;
296}
297
298/// No analysis has been applied to this API.
299pub(crate) struct NullPhase;
300
301impl AnalysisPhase for NullPhase {
302 type TypedefAnalysis = ();
303 type StructAnalysis = ();
304 type FunAnalysis = ();
305}
306
307#[derive(Clone)]
308pub(crate) enum TypedefKind {
309 Use(ItemUse, Box<Type>),
310 Type(ItemType),
311}
312
313/// Name information for an API. This includes the name by
314/// which we know it in Rust, and its C++ name, which may differ.
315#[derive(Clone, Hash, PartialEq, Eq)]
316pub(crate) struct ApiName {
317 pub(crate) name: QualifiedName,
318 cpp_name: Option<String>,
319}
320
321impl ApiName {
322 pub(crate) fn new(ns: &Namespace, id: Ident) -> Self {
323 Self::new_from_qualified_name(QualifiedName::new(ns, id))
324 }
325
326 pub(crate) fn new_with_cpp_name(ns: &Namespace, id: Ident, cpp_name: Option<String>) -> Self {
327 Self {
328 name: QualifiedName::new(ns, id),
329 cpp_name,
330 }
331 }
332
333 pub(crate) fn new_from_qualified_name(name: QualifiedName) -> Self {
334 Self {
335 name,
336 cpp_name: None,
337 }
338 }
339
340 pub(crate) fn new_in_root_namespace(id: Ident) -> Self {
341 Self::new(&Namespace::new(), id)
342 }
343
344 pub(crate) fn cpp_name(&self) -> String {
345 self.cpp_name
346 .as_ref()
347 .cloned()
348 .unwrap_or_else(|| self.name.get_final_item().to_string())
349 }
350
351 pub(crate) fn qualified_cpp_name(&self) -> String {
352 let cpp_name = self.cpp_name();
353 self.name
354 .ns_segment_iter()
355 .cloned()
356 .chain(std::iter::once(cpp_name))
357 .join("::")
358 }
359
360 pub(crate) fn cpp_name_if_present(&self) -> Option<&String> {
361 self.cpp_name.as_ref()
362 }
363}
364
365impl std::fmt::Debug for ApiName {
366 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
367 write!(f, "{}", self.name)?;
368 if let Some(cpp_name) = &self.cpp_name {
369 write!(f, " (cpp={})", cpp_name)?;
370 }
371 Ok(())
372 }
373}
374
375/// A name representing a subclass.
376/// This is a simple newtype wrapper which exists such that
377/// we can consistently generate the names of the various subsidiary
378/// types which are required both in C++ and Rust codegen.
379#[derive(Clone, Hash, PartialEq, Eq, Debug)]
380pub(crate) struct SubclassName(pub(crate) ApiName);
381
382impl SubclassName {
383 pub(crate) fn new(id: Ident) -> Self {
384 Self(ApiName::new_in_root_namespace(id))
385 }
386 pub(crate) fn from_holder_name(id: &Ident) -> Self {
387 Self::new(make_ident(id.to_string().strip_suffix("Holder").unwrap()))
388 }
389 pub(crate) fn id(&self) -> Ident {
390 self.0.name.get_final_ident()
391 }
392 /// Generate the name for the 'Holder' type
393 pub(crate) fn holder(&self) -> Ident {
394 self.with_suffix("Holder")
395 }
396 /// Generate the name for the 'Cpp' type
397 pub(crate) fn cpp(&self) -> QualifiedName {
398 let id = self.with_suffix("Cpp");
399 QualifiedName::new(self.0.name.get_namespace(), id)
400 }
401 pub(crate) fn cpp_remove_ownership(&self) -> Ident {
402 self.with_suffix("Cpp_remove_ownership")
403 }
404 pub(crate) fn remove_ownership(&self) -> Ident {
405 self.with_suffix("_remove_ownership")
406 }
407 fn with_suffix(&self, suffix: &str) -> Ident {
408 make_ident(format!("{}{}", self.0.name.get_final_item(), suffix))
409 }
410 pub(crate) fn get_trait_api_name(sup: &QualifiedName, method_name: &str) -> QualifiedName {
411 QualifiedName::new(
412 sup.get_namespace(),
413 make_ident(format!(
414 "{}_{}_trait_item",
415 sup.get_final_item(),
416 method_name
417 )),
418 )
419 }
420 // TODO this and the following should probably include both class name and method name
421 pub(crate) fn get_super_fn_name(superclass_namespace: &Namespace, id: &str) -> QualifiedName {
422 let id = make_ident(format!("{}_super", id));
423 QualifiedName::new(superclass_namespace, id)
424 }
425 pub(crate) fn get_methods_trait_name(superclass_name: &QualifiedName) -> QualifiedName {
426 Self::with_qualified_name_suffix(superclass_name, "methods")
427 }
428 pub(crate) fn get_supers_trait_name(superclass_name: &QualifiedName) -> QualifiedName {
429 Self::with_qualified_name_suffix(superclass_name, "supers")
430 }
431
432 fn with_qualified_name_suffix(name: &QualifiedName, suffix: &str) -> QualifiedName {
433 let id = make_ident(format!("{}_{}", name.get_final_item(), suffix));
434 QualifiedName::new(name.get_namespace(), id)
435 }
436}
437
438#[derive(strum_macros::Display)]
439/// Different types of API we might encounter.
440///
441/// This type is parameterized over an `ApiAnalysis`. This is any additional
442/// information which we wish to apply to our knowledge of our APIs later
443/// during analysis phases.
444///
445/// This is not as high-level as the equivalent types in `cxx` or `bindgen`,
446/// because sometimes we pass on the `bindgen` output directly in the
447/// Rust codegen output.
448///
449/// This derives from [strum_macros::Display] because we want to be
450/// able to debug-print the enum discriminant without worrying about
451/// the fact that their payloads may not be `Debug` or `Display`.
452/// (Specifically, allowing `syn` Types to be `Debug` requires
453/// enabling syn's `extra-traits` feature which increases compile time.)
454pub(crate) enum Api<T: AnalysisPhase> {
455 /// A forward declaration, which we mustn't store in a UniquePtr.
456 ForwardDeclaration {
457 name: ApiName,
458 /// If we found a problem parsing this forward declaration, we'll
459 /// ephemerally store the error here, as opposed to immediately
460 /// converting it to an `IgnoredItem`. That's because the
461 /// 'replace_hopeless_typedef_targets' analysis phase needs to spot
462 /// cases where there was an error which was _also_ a forward declaration.
463 /// That phase will then discard such Api::ForwardDeclarations
464 /// and replace them with normal Api::IgnoredItems.
465 err: Option<ConvertErrorWithContext>,
466 },
467 /// We found a typedef to something that we didn't fully understand.
468 /// We'll treat it as an opaque unsized type.
469 OpaqueTypedef {
470 name: ApiName,
471 /// Further store whether this was a typedef to a forward declaration.
472 /// If so we can't allow it to live in a UniquePtr, just like a regular
473 /// Api::ForwardDeclaration.
474 forward_declaration: bool,
475 },
476 /// A synthetic type we've manufactured in order to
477 /// concretize some templated C++ type.
478 ConcreteType {
479 name: ApiName,
480 rs_definition: Option<Box<Type>>,
481 cpp_definition: String,
482 },
483 /// A simple note that we want to make a constructor for
484 /// a `std::string` on the heap.
485 StringConstructor { name: ApiName },
486 /// A function. May include some analysis.
487 Function {
488 name: ApiName,
489 fun: Box<FuncToConvert>,
490 analysis: T::FunAnalysis,
491 },
492 /// A constant.
493 Const {
494 name: ApiName,
495 const_item: ItemConst,
496 },
497 /// A typedef found in the bindgen output which we wish
498 /// to pass on in our output
499 Typedef {
500 name: ApiName,
501 item: TypedefKind,
502 old_tyname: Option<QualifiedName>,
503 analysis: T::TypedefAnalysis,
504 },
505 /// An enum encountered in the
506 /// `bindgen` output.
507 Enum { name: ApiName, item: ItemEnum },
508 /// A struct encountered in the
509 /// `bindgen` output.
510 Struct {
511 name: ApiName,
512 details: Box<StructDetails>,
513 analysis: T::StructAnalysis,
514 },
515 /// A variable-length C integer type (e.g. int, unsigned long).
516 CType {
517 name: ApiName,
518 typename: QualifiedName,
519 },
520 /// Some item which couldn't be processed by autocxx for some reason.
521 /// We will have emitted a warning message about this, but we want
522 /// to mark that it's ignored so that we don't attempt to process
523 /// dependent items.
524 IgnoredItem {
525 name: ApiName,
526 err: ConvertError,
527 ctx: Option<ErrorContext>,
528 },
529 /// A Rust type which is not a C++ type.
530 RustType { name: ApiName, path: RustPath },
531 /// A function for the 'extern Rust' block which is not a C++ type.
532 RustFn {
533 name: ApiName,
534 details: RustFun,
535 receiver: Option<QualifiedName>,
536 },
537 /// Some function for the extern "Rust" block.
538 RustSubclassFn {
539 name: ApiName,
540 subclass: SubclassName,
541 details: Box<RustSubclassFnDetails>,
542 },
543 /// A Rust subclass of a C++ class.
544 Subclass {
545 name: SubclassName,
546 superclass: QualifiedName,
547 },
548 /// Contributions to the traits representing superclass methods that we might
549 /// subclass in Rust.
550 SubclassTraitItem {
551 name: ApiName,
552 details: SuperclassMethod,
553 },
554 /// A type which we shouldn't ourselves generate, but can use in functions
555 /// and so-forth by referring to some definition elsewhere.
556 ExternCppType {
557 name: ApiName,
558 details: ExternCppType,
559 pod: bool,
560 },
561}
562
563pub(crate) struct RustSubclassFnDetails {
564 pub(crate) params: Punctuated<FnArg, Comma>,
565 pub(crate) ret: ReturnType,
566 pub(crate) cpp_impl: CppFunction,
567 pub(crate) method_name: Ident,
568 pub(crate) superclass: QualifiedName,
569 pub(crate) receiver_mutability: ReceiverMutability,
570 pub(crate) dependencies: Vec<QualifiedName>,
571 pub(crate) requires_unsafe: UnsafetyNeeded,
572 pub(crate) is_pure_virtual: bool,
573}
574
575#[derive(Clone, Debug)]
576pub(crate) enum UnsafetyNeeded {
577 None,
578 JustBridge,
579 Always,
580}
581
582impl<T: AnalysisPhase> Api<T> {
583 pub(crate) fn name_info(&self) -> &ApiName {
584 match self {
585 Api::ForwardDeclaration { name, .. } => name,
586 Api::OpaqueTypedef { name, .. } => name,
587 Api::ConcreteType { name, .. } => name,
588 Api::StringConstructor { name } => name,
589 Api::Function { name, .. } => name,
590 Api::Const { name, .. } => name,
591 Api::Typedef { name, .. } => name,
592 Api::Enum { name, .. } => name,
593 Api::Struct { name, .. } => name,
594 Api::CType { name, .. } => name,
595 Api::IgnoredItem { name, .. } => name,
596 Api::RustType { name, .. } => name,
597 Api::RustFn { name, .. } => name,
598 Api::RustSubclassFn { name, .. } => name,
599 Api::Subclass { name, .. } => &name.0,
600 Api::SubclassTraitItem { name, .. } => name,
601 Api::ExternCppType { name, .. } => name,
602 }
603 }
604
605 /// The name of this API as used in Rust code.
606 /// For types, it's important that this never changes, since
607 /// functions or other types may refer to this.
608 /// Yet for functions, this may not actually be the name
609 /// used in the [cxx::bridge] mod - see
610 /// [Api<FnAnalysis>::cxxbridge_name]
611 pub(crate) fn name(&self) -> &QualifiedName {
612 &self.name_info().name
613 }
614
615 /// The name recorded for use in C++, if and only if
616 /// it differs from Rust.
617 pub(crate) fn cpp_name(&self) -> &Option<String> {
618 &self.name_info().cpp_name
619 }
620
621 /// The name for use in C++, whether or not it differs
622 /// from Rust.
623 pub(crate) fn effective_cpp_name(&self) -> &str {
624 self.cpp_name()
625 .as_deref()
626 .unwrap_or_else(|| self.name().get_final_item())
627 }
628
629 /// If this API turns out to have the same QualifiedName as another,
630 /// whether it's OK to just discard it?
631 pub(crate) fn discard_duplicates(&self) -> bool {
632 matches!(self, Api::IgnoredItem { .. })
633 }
634
635 pub(crate) fn valid_types(&self) -> Box<dyn Iterator<Item = QualifiedName>> {
636 match self {
637 Api::Subclass { name, .. } => Box::new(
638 vec![
639 self.name().clone(),
640 QualifiedName::new(&Namespace::new(), name.holder()),
641 name.cpp(),
642 ]
643 .into_iter(),
644 ),
645 _ => Box::new(std::iter::once(self.name().clone())),
646 }
647 }
648}
649
650impl<T: AnalysisPhase> std::fmt::Debug for Api<T> {
651 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
652 write!(f, "{:?} (kind={})", self.name_info(), self)
653 }
654}
655
656pub(crate) type UnanalyzedApi = Api<NullPhase>;
657
658impl<T: AnalysisPhase> Api<T> {
659 pub(crate) fn typedef_unchanged(
660 name: ApiName,
661 item: TypedefKind,
662 old_tyname: Option<QualifiedName>,
663 analysis: T::TypedefAnalysis,
664 ) -> Result<Box<dyn Iterator<Item = Api<T>>>, ConvertErrorWithContext>
665 where
666 T: 'static,
667 {
668 Ok(Box::new(std::iter::once(Api::Typedef {
669 name,
670 item,
671 old_tyname,
672 analysis,
673 })))
674 }
675
676 pub(crate) fn struct_unchanged(
677 name: ApiName,
678 details: Box<StructDetails>,
679 analysis: T::StructAnalysis,
680 ) -> Result<Box<dyn Iterator<Item = Api<T>>>, ConvertErrorWithContext>
681 where
682 T: 'static,
683 {
684 Ok(Box::new(std::iter::once(Api::Struct {
685 name,
686 details,
687 analysis,
688 })))
689 }
690
691 pub(crate) fn fun_unchanged(
692 name: ApiName,
693 fun: Box<FuncToConvert>,
694 analysis: T::FunAnalysis,
695 ) -> Result<Box<dyn Iterator<Item = Api<T>>>, ConvertErrorWithContext>
696 where
697 T: 'static,
698 {
699 Ok(Box::new(std::iter::once(Api::Function {
700 name,
701 fun,
702 analysis,
703 })))
704 }
705
706 pub(crate) fn enum_unchanged(
707 name: ApiName,
708 item: ItemEnum,
709 ) -> Result<Box<dyn Iterator<Item = Api<T>>>, ConvertErrorWithContext>
710 where
711 T: 'static,
712 {
713 Ok(Box::new(std::iter::once(Api::Enum { name, item })))
714 }
715}