flapigen/typemap/
typemap_macro.rs

1mod parse;
2#[cfg(test)]
3mod tests;
4
5use log::debug;
6use petgraph::Direction;
7use proc_macro2::{Span, TokenStream};
8use rustc_hash::FxHashSet;
9use smol_str::SmolStr;
10use std::{cell::RefCell, convert::TryInto, fmt::Write, rc::Rc};
11use syn::{
12    spanned::Spanned,
13    visit_mut::{visit_type_mut, VisitMut},
14    LitStr, Type,
15};
16
17use crate::{
18    error::{invalid_src_id_span, DiagnosticError, Result, SourceIdSpan},
19    source_registry::SourceId,
20    typemap::{
21        ast::{
22            get_trait_bounds, is_second_subst_of_first, normalize_type, parse_ty_with_given_span,
23            DisplayToTokens, GenericTypeConv, SpannedSmolStr, TyParamsSubstMap,
24        },
25        ty::TraitNamesSet,
26        TypeConvCode, UniqueName,
27    },
28    WRITE_TO_MEM_FAILED_MSG,
29};
30use parse::CItemsList;
31
32pub(crate) use parse::MacroArgs;
33
34static GENERIC_ALIAS: &str = "generic_alias";
35static SWIG_CONCAT_IDENTS: &str = "swig_concat_idents";
36static SWIG_I_TYPE: &str = "swig_i_type";
37static DEFINE_C_TYPE: &str = "define_c_type";
38static SWIG_FROM_RUST_TO_I_TYPE: &str = "swig_from_rust_to_i_type";
39static SWIG_FROM_I_TYPE_TO_RUST: &str = "swig_from_i_type_to_rust";
40static SWIG_FOREIGN_TO_I_TYPE: &str = "swig_foreign_to_i_type";
41static SWIG_FOREIGN_FROM_I_TYPE: &str = "swig_foreign_from_i_type";
42static SWIG_F_TYPE: &str = "swig_f_type";
43pub(in crate::typemap) static SWIG_SUBST_TYPE: &str = "swig_subst_type";
44
45#[derive(Debug)]
46pub(crate) struct TypeMapConvRuleInfo {
47    pub src_id: SourceId,
48    pub span: Span,
49    pub rtype_generics: Option<syn::Generics>,
50    pub rtype_left_to_right: Option<RTypeConvRule>,
51    pub rtype_right_to_left: Option<RTypeConvRule>,
52    pub ftype_left_to_right: Vec<FTypeConvRule>,
53    pub ftype_right_to_left: Vec<FTypeConvRule>,
54    /// For C++ case it is possible to introduce some C types
55    pub c_types: Option<CItems>,
56    pub generic_c_types: Option<GenericCItems>,
57    pub f_code: Vec<ForeignCode>,
58    pub generic_aliases: Vec<GenericAlias>,
59}
60
61#[derive(Debug, Clone, PartialEq)]
62pub(crate) enum CItem {
63    Struct(syn::ItemStruct),
64    Union(syn::ItemUnion),
65    Fn(syn::ItemFn),
66    Static(syn::ItemStatic),
67}
68
69#[derive(Debug, Clone, PartialEq)]
70pub(crate) struct CItems {
71    pub header_name: SmolStr,
72    pub items: Vec<CItem>,
73}
74
75#[derive(Debug, Clone)]
76pub(crate) struct ForeignCode {
77    pub sp: Span,
78    pub module_name: SmolStr,
79    pub cfg_option: Option<SpannedSmolStr>,
80    pub code: String,
81}
82
83#[derive(Debug)]
84pub(crate) struct GenericAlias {
85    pub alias: syn::Ident,
86    pub value: TokenStream,
87}
88
89#[derive(Debug, Clone)]
90pub(crate) struct ModuleName {
91    pub name: SmolStr,
92    pub sp: Span,
93}
94
95impl PartialEq for ModuleName {
96    fn eq(&self, other: &Self) -> bool {
97        self.name == other.name
98    }
99}
100
101impl Eq for ModuleName {}
102
103impl Ord for ModuleName {
104    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
105        self.name.cmp(&other.name)
106    }
107}
108
109impl PartialOrd for ModuleName {
110    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
111        Some(self.name.cmp(&other.name))
112    }
113}
114
115#[derive(Debug)]
116pub(crate) struct GenericCItems {
117    pub header_name: ModuleName,
118    pub types: TokenStream,
119}
120
121impl TypeMapConvRuleInfo {
122    pub(crate) fn is_empty(&self) -> bool {
123        self.rtype_generics.is_none()
124            && self.rtype_left_to_right.is_none()
125            && self.rtype_right_to_left.is_none()
126            && self.ftype_left_to_right.is_empty()
127            && self.ftype_right_to_left.is_empty()
128            && self.c_types.is_none()
129            && self.generic_c_types.is_none()
130            && self.f_code.is_empty()
131            && self.generic_aliases.is_empty()
132    }
133    pub(crate) fn if_simple_rtype_ftype_map(
134        &self,
135    ) -> Option<(&Type, &FTypeName, &[ModuleName], Option<&SpannedSmolStr>)> {
136        if self.rtype_right_to_left.is_some()
137            || !self.ftype_right_to_left.is_empty()
138            || self.ftype_left_to_right.len() > 1
139        {
140            return None;
141        }
142        match (
143            self.rtype_left_to_right.as_ref(),
144            self.ftype_left_to_right.first(),
145        ) {
146            (
147                Some(RTypeConvRule {
148                    left_ty: ref r_ty,
149                    right_ty: None,
150                    code: None,
151                }),
152                Some(FTypeConvRule {
153                    left_right_ty: FTypeLeftRightPair::OnlyLeft(ref f_ty),
154                    code: None,
155                    ref req_modules,
156                    ref unique_prefix,
157                    ..
158                }),
159            ) => Some((r_ty, f_ty, req_modules.as_slice(), unique_prefix.as_ref())),
160            _ => None,
161        }
162    }
163
164    /// it is possible to merge to `TypeMap` without help of language backend
165    pub(crate) fn if_simple_rtype_ftype_map_no_lang_backend(
166        &self,
167    ) -> Option<(&Type, &FTypeName, &[ModuleName], Option<&SpannedSmolStr>)> {
168        if !self.f_code.is_empty() || self.c_types.is_some() {
169            return None;
170        }
171
172        self.if_simple_rtype_ftype_map()
173    }
174
175    pub(in crate::typemap) fn contains_data_for_language_backend(&self) -> bool {
176        self.is_generic()
177            || !self.f_code.is_empty()
178            || self.c_types.is_some()
179            || self
180                .ftype_left_to_right
181                .iter()
182                .any(|x| x.cfg_option.is_some())
183            || self
184                .ftype_right_to_left
185                .iter()
186                .any(|x| x.cfg_option.is_some())
187            || self.ftype_left_to_right.len() > 1
188            || self.ftype_right_to_left.len() > 1
189    }
190
191    pub(crate) fn is_generic(&self) -> bool {
192        self.rtype_generics.is_some()
193    }
194
195    pub(crate) fn is_ty_subst_of_my_generic_rtype<TraitChecker>(
196        &self,
197        ty: &Type,
198        direction: Direction,
199        impl_trait: TraitChecker,
200    ) -> Option<TyParamsSubstMap<'_>>
201    where
202        TraitChecker: Fn(&Type, &TraitNamesSet) -> bool,
203    {
204        assert!(self.is_generic());
205        let generic_ty = if let Some((r_type, _, _, _)) = self.if_simple_rtype_ftype_map() {
206            r_type
207        } else {
208            let rule = match direction {
209                Direction::Incoming => self.rtype_right_to_left.as_ref(),
210                Direction::Outgoing => self.rtype_left_to_right.as_ref(),
211            };
212            let rule = rule?;
213            &rule.left_ty
214        };
215
216        let generics = self
217            .rtype_generics
218            .as_ref()
219            .expect("Internal error: should used only for generics");
220        let mut subst_map = TyParamsSubstMap::default();
221        for ty_p in generics.type_params() {
222            subst_map.insert(&ty_p.ident, None);
223        }
224        if !is_second_subst_of_first(generic_ty, ty, &mut subst_map) {
225            return None;
226        }
227        let bounds = get_trait_bounds(generics);
228        for b in &bounds {
229            if let Some(Some(ty)) = subst_map.get(b.ty_param.as_ref()) {
230                if !impl_trait(ty, &b.trait_names) {
231                    return None;
232                }
233            } else {
234                println!(
235                    "cargo:warning=invalid generic bounds({}) refer unknown parameter, subst. map {:?}",
236                    b.ty_param.as_ref(),
237                    subst_map
238                );
239                return None;
240            }
241        }
242
243        Some(subst_map)
244    }
245
246    pub(crate) fn subst_generic_params_to_c_items(
247        &self,
248        param_map: &TyParamsSubstMap,
249        expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
250    ) -> Result<Option<CItems>> {
251        debug!("subst_generic_params_to_c_items");
252        assert!(self.is_generic());
253        let type_aliases =
254            build_generic_aliases(self.src_id, &self.generic_aliases, param_map, expander)?;
255        let mut c_types = self.c_types.clone();
256        if let Some(generic_c_types) = self.generic_c_types.as_ref() {
257            let code_span = generic_c_types.types.span();
258            let code = expand_rust_code(
259                &generic_c_types.types.to_string(),
260                param_map,
261                expander,
262                &type_aliases,
263                (self.src_id, code_span),
264            )?;
265
266            let citems_list: CItemsList =
267                syn::LitStr::new(&code, code_span).parse().map_err(|err| {
268                    DiagnosticError::new(
269                        self.src_id,
270                        code_span,
271                        format!("can not parse this code after expand: {err}"),
272                    )
273                    .add_span_note(
274                        invalid_src_id_span(),
275                        format!("Code after expand: ```\n{code}\n```"),
276                    )
277                })?;
278            assert!(self.c_types.is_none());
279
280            let header_name = expand_module_name(
281                &generic_c_types.header_name.name,
282                (self.src_id, generic_c_types.header_name.sp),
283                &type_aliases,
284            )?;
285
286            c_types = Some(CItems {
287                header_name,
288                items: citems_list.0,
289            });
290        }
291        Ok(c_types)
292    }
293
294    pub(crate) fn subst_generic_params(
295        &self,
296        param_map: TyParamsSubstMap,
297        direction: Direction,
298        expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
299    ) -> Result<Self> {
300        debug!(
301            "subst_generic_params: direction {:?}, rule {:?}",
302            direction, *self
303        );
304        assert!(self.is_generic());
305        let type_aliases =
306            build_generic_aliases(self.src_id, &self.generic_aliases, &param_map, expander)?;
307        let (rtype_left_to_right, ftype_left_to_right) =
308            if direction == Direction::Outgoing || self.if_simple_rtype_ftype_map().is_some() {
309                (
310                    expand_rtype_rule(
311                        self.src_id,
312                        self.rtype_left_to_right.as_ref(),
313                        &param_map,
314                        expander,
315                        &type_aliases,
316                    )?,
317                    expand_ftype_rule(
318                        self.src_id,
319                        self.ftype_left_to_right.as_ref(),
320                        &param_map,
321                        expander,
322                        &type_aliases,
323                    )?,
324                )
325            } else {
326                (None, vec![])
327            };
328        let (rtype_right_to_left, ftype_right_to_left) = if direction == Direction::Incoming {
329            (
330                expand_rtype_rule(
331                    self.src_id,
332                    self.rtype_right_to_left.as_ref(),
333                    &param_map,
334                    expander,
335                    &type_aliases,
336                )?,
337                expand_ftype_rule(
338                    self.src_id,
339                    self.ftype_right_to_left.as_ref(),
340                    &param_map,
341                    expander,
342                    &type_aliases,
343                )?,
344            )
345        } else {
346            (None, vec![])
347        };
348        let f_code = expand_fcode(
349            &self.f_code,
350            &param_map,
351            expander,
352            &type_aliases,
353            self.src_id,
354        )?;
355        Ok(TypeMapConvRuleInfo {
356            src_id: self.src_id,
357            span: self.span,
358            rtype_generics: None,
359            rtype_left_to_right,
360            rtype_right_to_left,
361            ftype_left_to_right,
362            ftype_right_to_left,
363            f_code,
364            c_types: None,
365            generic_c_types: None,
366            generic_aliases: vec![],
367        })
368    }
369    pub(in crate::typemap) fn set_src_id(&mut self, src_id: SourceId) {
370        self.src_id = src_id;
371        fn rtype_change_src_id(r: Option<&mut RTypeConvRule>, src_id: SourceId) {
372            if let Some(RTypeConvRule {
373                code: Some(ref mut conv_code),
374                ..
375            }) = r
376            {
377                conv_code.span.0 = src_id;
378            }
379        }
380        fn ftype_change_src_id(rules: &mut [FTypeConvRule], src_id: SourceId) {
381            for r in rules {
382                if let Some(conv_code) = r.code.as_mut() {
383                    conv_code.span.0 = src_id;
384                }
385            }
386        }
387
388        rtype_change_src_id(self.rtype_left_to_right.as_mut(), src_id);
389        rtype_change_src_id(self.rtype_right_to_left.as_mut(), src_id);
390        ftype_change_src_id(&mut self.ftype_left_to_right, src_id);
391        ftype_change_src_id(&mut self.ftype_right_to_left, src_id);
392    }
393}
394
395impl TryInto<GenericTypeConv> for TypeMapConvRuleInfo {
396    type Error = TypeMapConvRuleInfo;
397
398    fn try_into(mut self) -> std::result::Result<GenericTypeConv, Self::Error> {
399        if !self.ftype_left_to_right.is_empty()
400            || !self.ftype_right_to_left.is_empty()
401            || self.c_types.is_some()
402            || self.generic_c_types.is_some()
403            || !self.f_code.is_empty()
404            || !self.generic_aliases.is_empty()
405        {
406            return Err(self);
407        }
408
409        let generic_params = match self.rtype_generics.take() {
410            Some(x) => x,
411            None => return Err(self),
412        };
413        let r_left_to_right = self.rtype_left_to_right.take();
414        let r_right_to_left = self.rtype_right_to_left.take();
415
416        let (from_ty, to_ty, code) = match (r_left_to_right, r_right_to_left) {
417            (Some(x), Some(y)) => {
418                self.rtype_generics = Some(generic_params);
419                self.rtype_left_to_right = Some(x);
420                self.rtype_right_to_left = Some(y);
421                return Err(self);
422            }
423            (None, None) => {
424                self.rtype_generics = Some(generic_params);
425                return Err(self);
426            }
427            (
428                Some(RTypeConvRule {
429                    left_ty: from_ty,
430                    right_ty: Some(to_ty),
431                    code: Some(conv_code),
432                }),
433                None,
434            ) => (from_ty, to_ty, conv_code),
435            (
436                None,
437                Some(RTypeConvRule {
438                    left_ty: to_ty,
439                    right_ty: Some(from_ty),
440                    code: Some(conv_code),
441                }),
442            ) => (from_ty, to_ty, conv_code),
443            (Some(x), None) => {
444                self.rtype_generics = Some(generic_params);
445                self.rtype_left_to_right = Some(x);
446                return Err(self);
447            }
448            (None, Some(y)) => {
449                self.rtype_generics = Some(generic_params);
450                self.rtype_right_to_left = Some(y);
451                return Err(self);
452            }
453        };
454
455        Ok(GenericTypeConv {
456            src_id: self.src_id,
457            from_ty,
458            to_ty,
459            code,
460            dependency: Rc::new(RefCell::new(None)),
461            generic_params,
462            to_foreigner_hint: None,
463            from_foreigner_hint: None,
464        })
465    }
466}
467
468#[derive(Debug, PartialEq)]
469pub(crate) struct RTypeConvRule {
470    pub left_ty: Type,
471    pub right_ty: Option<Type>,
472    pub code: Option<TypeConvCode>,
473}
474
475#[derive(Debug, PartialEq)]
476pub(crate) struct FTypeConvRule {
477    pub req_modules: Vec<ModuleName>,
478    pub cfg_option: Option<SpannedSmolStr>,
479    pub left_right_ty: FTypeLeftRightPair,
480    pub input_to_output: bool,
481    pub unique_prefix: Option<SpannedSmolStr>,
482    pub code: Option<TypeConvCode>,
483}
484
485#[derive(Debug, PartialEq)]
486pub(crate) enum FTypeLeftRightPair {
487    OnlyLeft(FTypeName),
488    OnlyRight(FTypeName),
489    Both(FTypeName, FTypeName),
490}
491
492impl FTypeLeftRightPair {
493    pub(crate) fn span(&self) -> Span {
494        use FTypeLeftRightPair::*;
495        match self {
496            OnlyRight(ref x) => x.sp,
497            OnlyLeft(ref x) => x.sp,
498            Both(ref x, _) => x.sp,
499        }
500    }
501}
502
503#[derive(Debug, Clone)]
504pub(crate) struct FTypeName {
505    pub name: UniqueName,
506    pub sp: Span,
507}
508
509impl PartialEq for FTypeName {
510    fn eq(&self, o: &Self) -> bool {
511        self.name == o.name
512    }
513}
514
515impl From<LitStr> for FTypeName {
516    fn from(x: LitStr) -> FTypeName {
517        FTypeName {
518            name: x.value().into(),
519            sp: x.span(),
520        }
521    }
522}
523
524pub(crate) struct ExpandedFType {
525    pub name: UniqueName,
526    pub provided_by_module: Vec<SmolStr>,
527}
528
529pub(crate) trait TypeMapConvRuleInfoExpanderHelper {
530    fn swig_i_type(&mut self, ty: &syn::Type, opt_arg: Option<&str>) -> Result<syn::Type>;
531    fn swig_from_rust_to_i_type(
532        &mut self,
533        ty: &syn::Type,
534        in_var_name: &str,
535        out_var_name: &str,
536    ) -> Result<String>;
537    fn swig_from_i_type_to_rust(
538        &mut self,
539        ty: &syn::Type,
540        in_var_name: &str,
541        out_var_name: &str,
542    ) -> Result<String>;
543    /// param1 - specific for lang backend parameter
544    fn swig_f_type(&mut self, ty: &syn::Type, param1: Option<&str>) -> Result<ExpandedFType>;
545    fn swig_foreign_to_i_type(&mut self, ty: &syn::Type, var_name: &str) -> Result<String>;
546    fn swig_foreign_from_i_type(&mut self, ty: &syn::Type, var_name: &str) -> Result<String>;
547}
548
549struct CalcGenericAlias<'a> {
550    name: &'a syn::Ident,
551    value: syn::Type,
552    req_modules: Vec<SmolStr>,
553}
554
555fn build_generic_aliases<'a>(
556    src_id: SourceId,
557    generic_aliases: &'a [GenericAlias],
558    param_map: &TyParamsSubstMap,
559    expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
560) -> Result<Vec<CalcGenericAlias<'a>>> {
561    let mut ret = vec![];
562    for ga in generic_aliases {
563        let item: GenericAliasItem = syn::parse2(ga.value.clone())
564            .map_err(|err| DiagnosticError::from_syn_err(src_id, err))?;
565        let mut req_modules = Vec::<SmolStr>::new();
566        let mut ident = String::new();
567        concat_idents(
568            src_id,
569            item,
570            param_map,
571            expander,
572            &mut req_modules,
573            &mut ident,
574        )?;
575        let ident = properly_escape_str_as_type(&ident);
576        let new_type: syn::Type =
577            parse_ty_with_given_span(&ident, ga.value.span()).map_err(|err| {
578                DiagnosticError::from_syn_err(src_id, err).add_span_note(
579                    invalid_src_id_span(),
580                    format!("trying to parse '{ident}' as type"),
581                )
582            })?;
583        ret.push(CalcGenericAlias {
584            name: &ga.alias,
585            value: new_type,
586            req_modules,
587        });
588    }
589    Ok(ret)
590}
591
592fn properly_escape_str_as_type(s: &str) -> String {
593    let data = s
594        .replace(":: std :: os :: raw ::", "")
595        .replace("std :: os :: raw ::", "");
596    let mut ret = String::with_capacity(data.len());
597    for ch in data.chars() {
598        if ch.is_ascii_alphanumeric() || ch == '_' {
599            ret.push(ch);
600        } else {
601            write!(&mut ret, "{}", u32::from(ch)).expect("write to String failed, no free mem?");
602        }
603    }
604    ret
605}
606
607#[derive(Debug)]
608enum GenericAliasItem {
609    Concat(Vec<GenericAliasItem>),
610    Ident(syn::Ident),
611    SwigIType((syn::Ident, Option<syn::Ident>)),
612    SwigFType(syn::Ident),
613}
614
615fn concat_idents(
616    src_id: SourceId,
617    item: GenericAliasItem,
618    param_map: &TyParamsSubstMap,
619    expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
620    req_modules: &mut Vec<SmolStr>,
621    ident: &mut String,
622) -> Result<()> {
623    match item {
624        GenericAliasItem::Concat(items) => {
625            for it in items {
626                concat_idents(src_id, it, param_map, expander, req_modules, ident)?;
627            }
628        }
629        GenericAliasItem::SwigIType((id, opt_arg)) => {
630            let ty = find_type_param(param_map, &id.to_string(), (src_id, id.span()))?;
631            let i_type: syn::Type = expander.swig_i_type(
632                ty.as_ref(),
633                opt_arg.as_ref().map(syn::Ident::to_string).as_deref(),
634            )?;
635            ident.push_str(&DisplayToTokens(&i_type).to_string());
636        }
637        GenericAliasItem::SwigFType(id) => {
638            let ty = find_type_param(param_map, &id.to_string(), (src_id, id.span()))?;
639            let mut f_type = expander.swig_f_type(ty.as_ref(), None)?;
640            req_modules.append(&mut f_type.provided_by_module);
641            ident.push_str(f_type.name.display());
642        }
643        GenericAliasItem::Ident(id) => ident.push_str(&id.to_string()),
644    }
645    Ok(())
646}
647
648pub(in crate::typemap) fn expand_macroses<E>(code: &str, mut expander: E) -> Result<String>
649where
650    E: FnMut(/*id*/ &str, /*params*/ Vec<&str>, /*out*/ &mut String) -> Result<()>,
651{
652    let mut prev_pos = 0;
653    let mut ret = String::with_capacity(code.len());
654    loop {
655        match (code[prev_pos..]).find(char::is_alphabetic) {
656            Some(pos) => {
657                let skip_chunk = &code[prev_pos..(prev_pos + pos)];
658                ret.push_str(skip_chunk);
659                prev_pos += pos;
660                if let Some((macro_id, params, macro_call_end)) = find_macro(&code[prev_pos..]) {
661                    expander(macro_id, params, &mut ret)?;
662                    prev_pos += macro_call_end;
663                } else {
664                    match (code[prev_pos..]).find(|ch: char| !ch.is_alphabetic()) {
665                        Some(pos) => {
666                            let skip_chunk = &code[prev_pos..(prev_pos + pos)];
667                            ret.push_str(skip_chunk);
668                            prev_pos += pos;
669                        }
670                        None => {
671                            ret.push_str(&code[prev_pos..]);
672                            break;
673                        }
674                    }
675                }
676            }
677            None => {
678                ret.push_str(&code[prev_pos..]);
679                break;
680            }
681        }
682    }
683
684    Ok(ret)
685}
686
687fn find_macro(code: &str) -> Option<(&str, Vec<&str>, usize)> {
688    let id_end = code.find(|ch: char| !(ch.is_alphanumeric() || ch == '_'))?;
689    let mut next_pos = id_end + (code[id_end..]).find(|ch: char| !ch.is_whitespace())?;
690    if &code[next_pos..=next_pos] != "!" {
691        return None;
692    }
693    next_pos += 1;
694    next_pos = next_pos + (code[next_pos..]).find(|ch: char| !ch.is_whitespace())?;
695    if &code[next_pos..=next_pos] != "(" {
696        return None;
697    }
698    next_pos += 1;
699    let cnt_start = next_pos;
700    let mut bracket_counter: usize = 1;
701    let mut close_bracket_pos = None;
702    for (idx, ch) in code[next_pos..].chars().enumerate() {
703        if ch == ')' {
704            bracket_counter -= 1;
705            if bracket_counter == 0 {
706                close_bracket_pos = Some(idx);
707                break;
708            }
709        } else if ch == '(' {
710            bracket_counter += 1;
711        }
712    }
713    next_pos += close_bracket_pos?;
714    let cnt_end = next_pos;
715    let id = &code[0..id_end];
716    let params: Vec<&str> = code[cnt_start..cnt_end]
717        .trim()
718        .split(',')
719        .filter_map(|x| {
720            let s = x.trim();
721            if !s.is_empty() {
722                Some(s)
723            } else {
724                None
725            }
726        })
727        .collect();
728    Some((id, params, next_pos + 1))
729}
730
731fn expand_rtype_rule(
732    src_id: SourceId,
733    grule: Option<&RTypeConvRule>,
734    param_map: &TyParamsSubstMap,
735    expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
736    generic_aliases: &[CalcGenericAlias],
737) -> Result<Option<RTypeConvRule>> {
738    let grule = match grule {
739        Some(x) => x,
740        None => return Ok(None),
741    };
742    let left_ty = expand_type(&grule.left_ty, src_id, param_map, expander, generic_aliases)?;
743    let right_ty: Option<Type> = match grule.right_ty.as_ref() {
744        Some(x) => Some(expand_type(
745            x,
746            src_id,
747            param_map,
748            expander,
749            generic_aliases,
750        )?),
751        None => None,
752    };
753
754    let code = match grule.code {
755        Some(ref x) => {
756            let code = expand_rust_code(
757                x.as_str(),
758                param_map,
759                expander,
760                generic_aliases,
761                (src_id, x.span()),
762            )?;
763            Some(TypeConvCode::new(code, (SourceId::none(), x.span())))
764        }
765        None => None,
766    };
767    Ok(Some(RTypeConvRule {
768        left_ty,
769        right_ty,
770        code,
771    }))
772}
773
774fn expand_type(
775    in_ty: &Type,
776    src_id: SourceId,
777    subst_map: &TyParamsSubstMap,
778    expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
779    generic_aliases: &[CalcGenericAlias],
780) -> Result<Type> {
781    struct ReplaceTypes<'a, 'b, 'c> {
782        src_id: SourceId,
783        expander: &'a mut dyn TypeMapConvRuleInfoExpanderHelper,
784        subst_map: &'a TyParamsSubstMap<'b>,
785        generic_aliases: &'a [CalcGenericAlias<'c>],
786        err: Option<DiagnosticError>,
787    }
788    impl VisitMut for ReplaceTypes<'_, '_, '_> {
789        fn visit_type_mut(&mut self, t: &mut Type) {
790            if self.err.is_some() {
791                return;
792            }
793
794            let ty_name = normalize_type(t);
795            if let Some(Some(subst)) = self.subst_map.get(&ty_name) {
796                *t = subst.clone();
797            } else {
798                visit_type_mut(self, t);
799            }
800
801            if let Type::Macro(ref type_macro) = t {
802                let ctx_span = (self.src_id, t.span());
803                match expand_macro_in_type(
804                    type_macro,
805                    ctx_span,
806                    self.subst_map,
807                    self.expander,
808                    self.generic_aliases,
809                ) {
810                    Ok(Some(new_ty)) => *t = new_ty,
811                    Ok(None) => {}
812                    Err(err) => self.err = Some(err),
813                }
814            }
815        }
816    }
817
818    let mut rt = ReplaceTypes {
819        subst_map,
820        src_id,
821        expander,
822        generic_aliases,
823        err: None,
824    };
825    let mut new_ty = in_ty.clone();
826    rt.visit_type_mut(&mut new_ty);
827    Ok(new_ty)
828}
829
830fn expand_macro_in_type(
831    type_macro: &syn::TypeMacro,
832    ctx_span: SourceIdSpan,
833    param_map: &TyParamsSubstMap,
834    expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
835    generic_aliases: &[CalcGenericAlias],
836) -> Result<Option<Type>> {
837    if type_macro.mac.path.is_ident(SWIG_I_TYPE) {
838        let param = type_macro.mac.tokens.to_string();
839        let ty = find_type_param(param_map, &param, ctx_span)?;
840        let i_type = expander.swig_i_type(ty.as_ref(), None)?;
841        Ok(Some(i_type))
842    } else {
843        let alias_idx = generic_aliases
844            .iter()
845            .position(|a| type_macro.mac.path.is_ident(a.name))
846            .ok_or_else(|| {
847                DiagnosticError::new2(
848                    ctx_span,
849                    format!(
850                        "unknown {} name {}",
851                        GENERIC_ALIAS,
852                        DisplayToTokens(&type_macro.mac.path)
853                    ),
854                )
855            })?;
856        Ok(Some(generic_aliases[alias_idx].value.clone()))
857    }
858}
859
860fn expand_ftype_rule(
861    src_id: SourceId,
862    grules: &[FTypeConvRule],
863    param_map: &TyParamsSubstMap,
864    expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
865    generic_aliases: &[CalcGenericAlias],
866) -> Result<Vec<FTypeConvRule>> {
867    let mut ret = Vec::with_capacity(grules.len());
868
869    for grule in grules {
870        use FTypeLeftRightPair::*;
871        let mut provided_by_module = Vec::<ModuleName>::new();
872        let left_right_ty = match grule.left_right_ty {
873            OnlyLeft(ref ftype) => OnlyLeft(expand_ftype_name(
874                src_id,
875                ftype,
876                param_map,
877                expander,
878                generic_aliases,
879                &mut provided_by_module,
880            )?),
881            OnlyRight(ref ftype) => OnlyRight(expand_ftype_name(
882                src_id,
883                ftype,
884                param_map,
885                expander,
886                generic_aliases,
887                &mut provided_by_module,
888            )?),
889            Both(ref fl, ref fr) => Both(
890                expand_ftype_name(
891                    src_id,
892                    fl,
893                    param_map,
894                    expander,
895                    generic_aliases,
896                    &mut provided_by_module,
897                )?,
898                expand_ftype_name(
899                    src_id,
900                    fr,
901                    param_map,
902                    expander,
903                    generic_aliases,
904                    &mut provided_by_module,
905                )?,
906            ),
907        };
908        let code = match grule.code {
909            Some(ref x) => {
910                let code = expand_foreign_type_conv_code(
911                    x,
912                    param_map,
913                    expander,
914                    generic_aliases,
915                    (src_id, x.span()),
916                )?;
917                Some(code)
918            }
919            None => None,
920        };
921        for m in &grule.req_modules {
922            let mod_name = expand_module_name(&m.name, (src_id, m.sp), generic_aliases)?;
923            provided_by_module.push(ModuleName {
924                name: mod_name,
925                sp: m.sp,
926            });
927        }
928        // preserve order of modules
929        let mut mod_uniques = FxHashSet::default();
930        provided_by_module.retain(|e| mod_uniques.insert(e.name.clone()));
931        let unique_prefix = if let Some(unique_prefix) = grule.unique_prefix.as_ref() {
932            let ctx_span = (src_id, unique_prefix.sp);
933            let mut provided_by_module = vec![];
934            let new_unique_prefix = expand_str_in_ftype_name_context(
935                ctx_span,
936                unique_prefix.as_str(),
937                param_map,
938                expander,
939                generic_aliases,
940                &mut provided_by_module,
941            )?;
942            Some(SpannedSmolStr {
943                sp: unique_prefix.sp,
944                value: new_unique_prefix.into(),
945            })
946        } else {
947            None
948        };
949
950        ret.push(FTypeConvRule {
951            unique_prefix,
952            req_modules: provided_by_module,
953            cfg_option: grule.cfg_option.clone(),
954            left_right_ty,
955            input_to_output: grule.input_to_output,
956            code,
957        });
958    }
959    Ok(ret)
960}
961
962fn call_swig_f_type(
963    ctx_sp: SourceIdSpan,
964    params: Vec<&str>,
965    out: &mut String,
966    param_map: &TyParamsSubstMap,
967    expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
968    generic_aliases: &[CalcGenericAlias],
969) -> Result<Vec<SmolStr>> {
970    let (type_name, opt_param) = match params.len() {
971        1 => (params[0], None),
972        2 => (params[0], Some(params[1])),
973        _ => {
974            return Err(DiagnosticError::new2(
975                ctx_sp,
976                format!(
977                    "{} parameters in {} instead of 1 or 2",
978                    params.len(),
979                    SWIG_F_TYPE
980                ),
981            ))
982        }
983    };
984    let ty = if type_name.ends_with("!()") {
985        let alias_name = type_name[0..type_name.len() - 3].trim();
986        let pos = generic_aliases
987            .iter()
988            .position(|a| a.name == alias_name)
989            .ok_or_else(|| {
990                DiagnosticError::new2(ctx_sp, format!("unknown type alias '{alias_name}'"))
991            })?;
992        TyValueOrRef::Ref(&generic_aliases[pos].value)
993    } else {
994        find_type_param(param_map, type_name, ctx_sp)?
995    };
996
997    let f_type = expander.swig_f_type(ty.as_ref(), opt_param)?;
998    out.push_str(f_type.name.value());
999    Ok(f_type.provided_by_module)
1000}
1001
1002fn expand_ftype_name(
1003    src_id: SourceId,
1004    ftype: &FTypeName,
1005    param_map: &TyParamsSubstMap,
1006    expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
1007    generic_aliases: &[CalcGenericAlias],
1008    provided_by_module: &mut Vec<ModuleName>,
1009) -> Result<FTypeName> {
1010    let ctx_span = (src_id, ftype.sp);
1011
1012    let new_fytpe = expand_str_in_ftype_name_context(
1013        ctx_span,
1014        ftype.name.display(),
1015        param_map,
1016        expander,
1017        generic_aliases,
1018        provided_by_module,
1019    )?;
1020
1021    Ok(FTypeName {
1022        name: new_fytpe.into(),
1023        sp: ftype.sp,
1024    })
1025}
1026
1027fn expand_str_in_ftype_name_context(
1028    ctx_span: SourceIdSpan,
1029    input: &str,
1030    param_map: &TyParamsSubstMap,
1031    expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
1032    generic_aliases: &[CalcGenericAlias],
1033    provided_by_module: &mut Vec<ModuleName>,
1034) -> Result<String> {
1035    expand_macroses(input, |id: &str, params: Vec<&str>, out: &mut String| {
1036        if id == SWIG_F_TYPE {
1037            let modules: Vec<SmolStr> =
1038                call_swig_f_type(ctx_span, params, out, param_map, expander, generic_aliases)?;
1039            provided_by_module.extend(modules.into_iter().map(|name| ModuleName {
1040                name,
1041                sp: Span::call_site(),
1042            }));
1043            Ok(())
1044        } else if id == SWIG_SUBST_TYPE {
1045            let type_name = if params.len() == 1 {
1046                params[0]
1047            } else {
1048                return Err(DiagnosticError::new2(
1049                    ctx_span,
1050                    format!("{} parameters in {} instead of 1", params.len(), id),
1051                ));
1052            };
1053            let ty = find_type_param(param_map, type_name, ctx_span)?;
1054            out.push_str(normalize_type(ty.as_ref()));
1055            Ok(())
1056        } else if let Some(pos) = generic_aliases.iter().position(|a| a.name == id) {
1057            write!(out, "{}", DisplayToTokens(&generic_aliases[pos].value))
1058                .expect(WRITE_TO_MEM_FAILED_MSG);
1059            provided_by_module.extend(generic_aliases[pos].req_modules.iter().map(|name| {
1060                ModuleName {
1061                    name: name.clone(),
1062                    sp: Span::call_site(),
1063                }
1064            }));
1065            Ok(())
1066        } else {
1067            Err(DiagnosticError::new2(
1068                ctx_span,
1069                format!("unknown macros '{id}' in this context"),
1070            ))
1071        }
1072    })
1073}
1074
1075enum TyValueOrRef<'a> {
1076    Value(Type),
1077    Ref(&'a Type),
1078}
1079
1080impl AsRef<Type> for TyValueOrRef<'_> {
1081    fn as_ref(&self) -> &Type {
1082        match self {
1083            TyValueOrRef::Value(ref ty) => ty,
1084            TyValueOrRef::Ref(ty) => ty,
1085        }
1086    }
1087}
1088
1089fn find_type_param<'b>(
1090    param_map: &'b TyParamsSubstMap,
1091    param: &str,
1092    param_span: SourceIdSpan,
1093) -> Result<TyValueOrRef<'b>> {
1094    if let Some(Some(ty)) = param_map.get(param) {
1095        return Ok(TyValueOrRef::Ref(ty));
1096    }
1097    let compound_ty: Type = parse_ty_with_given_span(param, param_span.1).map_err(|err| {
1098        DiagnosticError::new2(param_span, format!("unknown type parameter '{param}'"))
1099            .add_span_note(invalid_src_id_span(), err)
1100    })?;
1101    if let Type::Reference(ty_ref) = compound_ty {
1102        let param = DisplayToTokens(&ty_ref.elem).to_string();
1103        if let Some(Some(ty)) = param_map.get(&param) {
1104            let new_ty = Type::Reference(syn::TypeReference {
1105                and_token: ty_ref.and_token,
1106                lifetime: ty_ref.lifetime,
1107                mutability: ty_ref.mutability,
1108                elem: Box::new(ty.clone()),
1109            });
1110            return Ok(TyValueOrRef::Value(new_ty));
1111        }
1112    }
1113    Err(DiagnosticError::new2(
1114        param_span,
1115        format!("unknown type parameter '{param}'"),
1116    ))
1117}
1118
1119fn expand_module_name(
1120    generic_mod_name: &str,
1121    ctx_sp: SourceIdSpan,
1122    aliases: &[CalcGenericAlias],
1123) -> Result<SmolStr> {
1124    expand_macroses(
1125        generic_mod_name,
1126        |id: &str, params: Vec<&str>, out: &mut String| -> Result<()> {
1127            if !params.is_empty() {
1128                Err(DiagnosticError::new2(
1129                    ctx_sp,
1130                    format!("{GENERIC_ALIAS} ({id}) does not accept parameters: {params:?}"),
1131                ))
1132            } else if let Some(pos) = aliases.iter().position(|e| e.name == id) {
1133                write!(out, "{}", DisplayToTokens(&aliases[pos].value))
1134                    .expect(WRITE_TO_MEM_FAILED_MSG);
1135                Ok(())
1136            } else {
1137                Err(DiagnosticError::new2(
1138                    ctx_sp,
1139                    format!("unknown macros '{id}' in this context"),
1140                ))
1141            }
1142        },
1143    )
1144    .map(|x| x.into())
1145}
1146
1147fn expand_rust_code(
1148    code: &str,
1149    param_map: &TyParamsSubstMap,
1150    expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
1151    generic_aliases: &[CalcGenericAlias],
1152    ctx_span: SourceIdSpan,
1153) -> Result<String> {
1154    expand_macroses(
1155        code,
1156        |id: &str, params: Vec<&str>, out: &mut String| -> Result<()> {
1157            match id {
1158                _ if id == SWIG_FROM_RUST_TO_I_TYPE || id == SWIG_FROM_I_TYPE_TO_RUST => {
1159                    let (type_name, in_var_name, out_var_name) = if params.len() == 3 {
1160                        (&params[0], &params[1], &params[2])
1161                    } else {
1162                        return Err(DiagnosticError::new2(
1163                            ctx_span,
1164                            format!("{} parameters in {} instead of 2", params.len(), id),
1165                        ));
1166                    };
1167                    let ty = find_type_param(param_map, type_name, ctx_span)?;
1168                    let tt: String = if id == SWIG_FROM_RUST_TO_I_TYPE {
1169                        expander.swig_from_rust_to_i_type(ty.as_ref(), in_var_name, out_var_name)?
1170                    } else if id == SWIG_FROM_I_TYPE_TO_RUST {
1171                        expander.swig_from_i_type_to_rust(ty.as_ref(), in_var_name, out_var_name)?
1172                    } else {
1173                        unreachable!()
1174                    };
1175                    write!(out, "{tt}").expect(WRITE_TO_MEM_FAILED_MSG);
1176                }
1177                _ if id == SWIG_I_TYPE => {
1178                    let (param, opt_arg) = match params.len() {
1179                        1 => (params[0], None),
1180                        2 => (params[0], Some(params[1])),
1181                        _ => {
1182                            return Err(DiagnosticError::new2(
1183                                ctx_span,
1184                                format!(
1185                                    "{} parameters in {} instead of 1 or 2",
1186                                    params.len(),
1187                                    SWIG_I_TYPE
1188                                ),
1189                            ));
1190                        }
1191                    };
1192                    let ty = find_type_param(param_map, param, ctx_span)?;
1193                    let i_type = expander.swig_i_type(ty.as_ref(), opt_arg)?;
1194                    write!(out, "{}", normalize_type(&i_type)).expect(WRITE_TO_MEM_FAILED_MSG);
1195                }
1196                _ if id == SWIG_SUBST_TYPE => {
1197                    let type_name = if params.len() == 1 {
1198                        &params[0]
1199                    } else {
1200                        return Err(DiagnosticError::new2(
1201                            ctx_span,
1202                            format!("{} parameters in {} instead of 1", params.len(), id),
1203                        ));
1204                    };
1205                    let ty = find_type_param(param_map, type_name, ctx_span)?;
1206                    write!(out, "{}", normalize_type(ty.as_ref())).expect(WRITE_TO_MEM_FAILED_MSG);
1207                }
1208                _ => {
1209                    if let Some(alias_idx) = generic_aliases.iter().position(|a| a.name == id) {
1210                        write!(out, "{}", normalize_type(&generic_aliases[alias_idx].value))
1211                            .expect(WRITE_TO_MEM_FAILED_MSG);
1212                    } else {
1213                        write!(out, "{id}!(").expect(WRITE_TO_MEM_FAILED_MSG);
1214                        for (i, p) in params.iter().enumerate() {
1215                            if i == 0 {
1216                                out.push_str(p);
1217                            } else {
1218                                write!(out, ", {p}").expect(WRITE_TO_MEM_FAILED_MSG);
1219                            }
1220                        }
1221                        out.push(')');
1222                    }
1223                }
1224            }
1225            Ok(())
1226        },
1227    )
1228}
1229
1230fn expand_foreign_code(
1231    code: &str,
1232    param_map: &TyParamsSubstMap,
1233    expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
1234    generic_aliases: &[CalcGenericAlias],
1235    ctx_span: SourceIdSpan,
1236) -> Result<String> {
1237    expand_macroses(
1238        code,
1239        |id: &str, params: Vec<&str>, out: &mut String| -> Result<()> {
1240            match id {
1241                _ if id == SWIG_F_TYPE => {
1242                    call_swig_f_type(ctx_span, params, out, param_map, expander, generic_aliases)?;
1243                }
1244                _ if id == SWIG_I_TYPE => {
1245                    let (param, opt_arg) = match params.len() {
1246                        1 => (params[0], None),
1247                        2 => (params[0], Some(params[1])),
1248                        _ => {
1249                            return Err(DiagnosticError::new2(
1250                                ctx_span,
1251                                format!(
1252                                    "{} parameters in {} instead of 1 or 2",
1253                                    params.len(),
1254                                    SWIG_I_TYPE
1255                                ),
1256                            ));
1257                        }
1258                    };
1259                    let ty = find_type_param(param_map, param, ctx_span)?;
1260                    let i_type = expander.swig_i_type(ty.as_ref(), opt_arg)?;
1261                    let f_type = expander.swig_f_type(&i_type, opt_arg)?;
1262                    out.push_str(f_type.name.display());
1263                }
1264                _ if id == SWIG_FOREIGN_TO_I_TYPE || id == SWIG_FOREIGN_FROM_I_TYPE => {
1265                    let (type_name, var_name) = if params.len() == 2 {
1266                        (&params[0], &params[1])
1267                    } else {
1268                        return Err(DiagnosticError::new2(
1269                            ctx_span,
1270                            format!("{} parameters in {} instead of 2", params.len(), id),
1271                        ));
1272                    };
1273
1274                    let ty = find_type_param(param_map, type_name, ctx_span)?;
1275                    let tt: String = if id == SWIG_FOREIGN_TO_I_TYPE {
1276                        expander.swig_foreign_to_i_type(ty.as_ref(), var_name)?
1277                    } else if id == SWIG_FOREIGN_FROM_I_TYPE {
1278                        expander.swig_foreign_from_i_type(ty.as_ref(), var_name)?
1279                    } else {
1280                        unreachable!()
1281                    };
1282                    write!(out, "{tt}").expect(WRITE_TO_MEM_FAILED_MSG);
1283                }
1284                _ => {
1285                    if let Some(pos) = generic_aliases.iter().position(|a| a.name == id) {
1286                        write!(out, "{}", DisplayToTokens(&generic_aliases[pos].value))
1287                            .expect(WRITE_TO_MEM_FAILED_MSG);
1288                    } else {
1289                        return Err(DiagnosticError::new2(
1290                            ctx_span,
1291                            format!("unknown macro {id} in f_type conversion code"),
1292                        ));
1293                    }
1294                }
1295            }
1296            Ok(())
1297        },
1298    )
1299}
1300
1301fn expand_foreign_type_conv_code(
1302    code: &TypeConvCode,
1303    param_map: &TyParamsSubstMap,
1304    expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
1305    generic_aliases: &[CalcGenericAlias],
1306    ctx_span: SourceIdSpan,
1307) -> Result<TypeConvCode> {
1308    let ret_code = expand_foreign_code(
1309        code.as_str(),
1310        param_map,
1311        expander,
1312        generic_aliases,
1313        ctx_span,
1314    )?;
1315    Ok(TypeConvCode::with_params(
1316        ret_code,
1317        code.span,
1318        code.params().to_vec(),
1319    ))
1320}
1321
1322fn expand_fcode(
1323    f_code: &[ForeignCode],
1324    param_map: &TyParamsSubstMap,
1325    expander: &mut dyn TypeMapConvRuleInfoExpanderHelper,
1326    generic_aliases: &[CalcGenericAlias],
1327    src_id: SourceId,
1328) -> Result<Vec<ForeignCode>> {
1329    let mut ret = Vec::<ForeignCode>::with_capacity(f_code.len());
1330    for fc in f_code {
1331        let module_name: SmolStr =
1332            expand_module_name(&fc.module_name, (src_id, fc.sp), generic_aliases)?;
1333        let code = expand_foreign_code(
1334            &fc.code,
1335            param_map,
1336            expander,
1337            generic_aliases,
1338            (src_id, fc.sp),
1339        )?;
1340        ret.push(ForeignCode {
1341            sp: fc.sp,
1342            module_name,
1343            cfg_option: fc.cfg_option.clone(),
1344            code,
1345        });
1346    }
1347    Ok(ret)
1348}