1use std::collections::HashSet;
44
45use proc_macro2::TokenStream;
46use quote::quote;
47
48use crate::codegen::typemap::{self, CodegenContext, TypePosition};
49use crate::ir::{Param, TypeRef};
50use crate::parse::scope::ScopeId;
51use crate::util::naming::to_snake_case;
52
53#[derive(Clone, Copy, Debug, PartialEq, Eq)]
55pub enum SignatureKind {
56 Constructor,
57 Method,
58 StaticMethod,
59 Function,
60 Setter,
61 StaticSetter,
62}
63
64#[derive(Clone, Debug, PartialEq)]
66pub struct ConcreteParam {
67 pub name: String,
68 pub type_ref: TypeRef,
69 pub variadic: bool,
71}
72
73#[derive(Clone, Debug)]
75pub struct ExpandedSignature {
76 pub rust_name: String,
78 pub js_name: String,
80 pub params: Vec<ConcreteParam>,
82 pub catch: bool,
84 pub return_type: TypeRef,
86 pub doc: Option<String>,
88 pub kind: SignatureKind,
90}
91
92pub fn dedupe_name(candidate: &str, used_names: &mut HashSet<String>) -> String {
97 let mut name = candidate.to_string();
98 if !used_names.contains(&name) {
99 used_names.insert(name.clone());
100 return name;
101 }
102 let base = name.clone();
103 let mut counter = 1u32;
104 loop {
105 name = format!("{base}_{counter}");
106 if !used_names.contains(&name) {
107 used_names.insert(name.clone());
108 return name;
109 }
110 counter += 1;
111 }
112}
113
114#[allow(clippy::too_many_arguments)]
127pub fn expand_signatures(
128 js_name: &str,
129 overloads: &[&[Param]],
130 return_type: &TypeRef,
131 kind: SignatureKind,
132 doc: &Option<String>,
133 used_names: &mut HashSet<String>,
134 cgctx: Option<&CodegenContext<'_>>,
135 scope: ScopeId,
136) -> Vec<ExpandedSignature> {
137 let base_rust_name = match kind {
138 SignatureKind::Constructor => "new".to_string(),
139 SignatureKind::Setter | SignatureKind::StaticSetter => {
140 format!("set_{}", to_snake_case(js_name))
141 }
142 _ => to_snake_case(js_name),
143 };
144
145 let mut all_sigs: Vec<Vec<ConcreteParam>> = Vec::new();
147
148 for params in overloads {
149 let expanded = expand_single_overload(params, cgctx, scope);
150 all_sigs.extend(expanded);
151 }
152
153 let mut seen: Vec<&Vec<ConcreteParam>> = Vec::new();
155 let mut deduped: Vec<Vec<ConcreteParam>> = Vec::new();
156 for sig in &all_sigs {
157 if !seen.iter().any(|s| concrete_params_eq(s, sig)) {
158 seen.push(sig);
159 deduped.push(sig.clone());
160 }
161 }
162
163 let candidate_names = compute_rust_names(&base_rust_name, &deduped);
165 let is_constructor = kind == SignatureKind::Constructor;
166 let mut result = Vec::new();
167
168 for (i, (candidate, concrete_params)) in candidate_names.into_iter().zip(deduped).enumerate() {
169 let is_first = i == 0;
170 let rust_name = dedupe_name(&candidate, used_names);
171
172 result.push(ExpandedSignature {
173 rust_name: rust_name.clone(),
174 js_name: js_name.to_string(),
175 params: concrete_params.clone(),
176 catch: is_constructor,
177 return_type: return_type.clone(),
178 doc: if is_first { doc.clone() } else { None },
179 kind,
180 });
181
182 let emit_try = !matches!(
184 kind,
185 SignatureKind::Constructor | SignatureKind::Setter | SignatureKind::StaticSetter
186 );
187 if emit_try {
188 let try_candidate = format!("try_{rust_name}");
189 let try_name = dedupe_name(&try_candidate, used_names);
190 result.push(ExpandedSignature {
191 rust_name: try_name,
192 js_name: js_name.to_string(),
193 params: concrete_params,
194 catch: true,
195 return_type: return_type.clone(),
196 doc: None,
197 kind,
198 });
199 }
200 }
201
202 result
203}
204
205fn concrete_params_eq(a: &[ConcreteParam], b: &[ConcreteParam]) -> bool {
207 a.len() == b.len()
208 && a.iter().zip(b.iter()).all(|(pa, pb)| {
209 pa.name == pb.name && pa.type_ref == pb.type_ref && pa.variadic == pb.variadic
210 })
211}
212
213fn expand_single_overload(
218 params: &[Param],
219 cgctx: Option<&CodegenContext<'_>>,
220 scope: ScopeId,
221) -> Vec<Vec<ConcreteParam>> {
222 let (non_variadic, variadic_param) = if params.last().is_some_and(|p| p.variadic) {
224 (¶ms[..params.len() - 1], Some(¶ms[params.len() - 1]))
225 } else {
226 (params, None)
227 };
228
229 let mut sigs: Vec<Vec<ConcreteParam>> = vec![vec![]];
232
233 for (i, param) in non_variadic.iter().enumerate() {
234 let type_alternatives = flatten_type(¶m.type_ref, cgctx, scope);
235
236 if param.optional {
237 let frozen: Vec<Vec<ConcreteParam>> =
240 sigs.iter().filter(|s| s.len() < i).cloned().collect();
241 let mut extendable: Vec<Vec<ConcreteParam>> =
242 sigs.into_iter().filter(|s| s.len() == i).collect();
243 let snapshot = extendable.clone(); let cur = extendable.len();
246 for (j, alt) in type_alternatives.into_iter().enumerate() {
247 let concrete = ConcreteParam {
248 name: param.name.clone(),
249 type_ref: alt,
250 variadic: false,
251 };
252 if j == 0 {
253 for sig in extendable.iter_mut().take(cur) {
254 sig.push(concrete.clone());
255 }
256 } else {
257 for item in snapshot.iter().take(cur) {
258 let mut sig = item.clone();
259 sig.push(concrete.clone());
260 extendable.push(sig);
261 }
262 }
263 }
264
265 sigs = frozen;
267 sigs.extend(snapshot);
268 sigs.extend(extendable);
269 } else {
270 let cur = sigs.len();
272 for (j, alt) in type_alternatives.into_iter().enumerate() {
273 let concrete = ConcreteParam {
274 name: param.name.clone(),
275 type_ref: alt,
276 variadic: false,
277 };
278 if j == 0 {
279 for sig in sigs.iter_mut().take(cur) {
280 sig.push(concrete.clone());
281 }
282 } else {
283 for k in 0..cur {
284 let mut sig = sigs[k].clone();
285 sig.truncate(i);
286 sig.push(concrete.clone());
287 sigs.push(sig);
288 }
289 }
290 }
291 }
292 }
293
294 if let Some(vp) = variadic_param {
296 for sig in &mut sigs {
297 sig.push(ConcreteParam {
298 name: vp.name.clone(),
299 type_ref: vp.type_ref.clone(),
300 variadic: true,
301 });
302 }
303 }
304
305 sigs
306}
307
308fn flatten_type(ty: &TypeRef, cgctx: Option<&CodegenContext<'_>>, scope: ScopeId) -> Vec<TypeRef> {
317 match ty {
318 TypeRef::Union(members) => members
320 .iter()
321 .flat_map(|m| flatten_type(m, cgctx, scope))
322 .collect(),
323
324 TypeRef::Named(name) => {
326 if let Some(c) = cgctx {
327 if let Some(target) = c.resolve_alias(name, scope) {
328 let target = target.clone();
329 return flatten_type(&target, cgctx, scope);
330 }
331 }
332 vec![ty.clone()]
333 }
334
335 TypeRef::Nullable(inner) => flatten_type(inner, cgctx, scope)
337 .into_iter()
338 .map(|t| TypeRef::Nullable(Box::new(t)))
339 .collect(),
340
341 TypeRef::Promise(inner) => flatten_type(inner, cgctx, scope)
343 .into_iter()
344 .map(|t| TypeRef::Promise(Box::new(t)))
345 .collect(),
346 TypeRef::Array(inner) => flatten_type(inner, cgctx, scope)
347 .into_iter()
348 .map(|t| TypeRef::Array(Box::new(t)))
349 .collect(),
350 TypeRef::Set(inner) => flatten_type(inner, cgctx, scope)
351 .into_iter()
352 .map(|t| TypeRef::Set(Box::new(t)))
353 .collect(),
354 TypeRef::Record(k, v) => {
356 let ks = flatten_type(k, cgctx, scope);
357 let vs = flatten_type(v, cgctx, scope);
358 let mut result = Vec::new();
359 for k in &ks {
360 for v in &vs {
361 result.push(TypeRef::Record(Box::new(k.clone()), Box::new(v.clone())));
362 }
363 }
364 result
365 }
366 TypeRef::Map(k, v) => {
367 let ks = flatten_type(k, cgctx, scope);
368 let vs = flatten_type(v, cgctx, scope);
369 let mut result = Vec::new();
370 for k in &ks {
371 for v in &vs {
372 result.push(TypeRef::Map(Box::new(k.clone()), Box::new(v.clone())));
373 }
374 }
375 result
376 }
377
378 _ => vec![ty.clone()],
380 }
381}
382
383fn compute_rust_names(base_name: &str, signatures: &[Vec<ConcreteParam>]) -> Vec<String> {
399 if signatures.len() == 1 {
400 return vec![base_name.to_string()];
401 }
402
403 let (trim_start, trim_end) = compute_trim(signatures);
412
413 let mut names = Vec::new();
414
415 for (sig_idx, sig) in signatures.iter().enumerate() {
416 if sig_idx == 0 {
420 names.push(base_name.to_string());
421 continue;
422 }
423
424 let mut name = base_name.to_string();
425 let mut first_suffix = true;
426
427 let end = if sig.len() >= trim_end {
428 sig.len() - trim_end
429 } else {
430 sig.len()
432 };
433 let start = trim_start.min(end);
434
435 for (param_idx, param) in sig[start..end].iter().enumerate() {
436 let abs_idx = start + param_idx;
437
438 let mut any_different = false;
441 let mut any_same_name_different_type = false;
442
443 for (other_idx, other) in signatures.iter().enumerate() {
444 if other_idx == sig_idx {
445 continue;
446 }
447 match other.get(abs_idx) {
448 Some(other_param) => {
449 if other_param.name == param.name && other_param.type_ref != param.type_ref
450 {
451 any_same_name_different_type = true;
452 any_different = true;
453 } else if other_param.name != param.name {
454 any_different = true;
455 }
456 }
457 None => {
458 any_different = true;
460 }
461 }
462 }
463
464 if !any_different {
465 continue;
466 }
467
468 if first_suffix {
469 name.push_str("_with_");
470 first_suffix = false;
471 } else {
472 name.push_str("_and_");
473 }
474
475 if any_same_name_different_type {
476 name.push_str(&type_snake_name(¶m.type_ref));
478 } else {
479 name.push_str(&to_snake_case(¶m.name));
481 }
482 }
483
484 names.push(name);
485 }
486
487 names
488}
489
490fn compute_trim(signatures: &[Vec<ConcreteParam>]) -> (usize, usize) {
496 let min_len = signatures.iter().map(|s| s.len()).min().unwrap_or(0);
497
498 let mut trim_start = 0;
500 for i in 0..min_len {
501 let first = &signatures[0][i];
502 if signatures[1..].iter().all(|sig| sig[i] == *first) {
503 trim_start += 1;
504 } else {
505 break;
506 }
507 }
508
509 let mut trim_end = 0;
511 for i in 0..min_len {
512 let first = &signatures[0][signatures[0].len() - 1 - i];
513 if signatures[1..]
514 .iter()
515 .all(|sig| sig[sig.len() - 1 - i] == *first)
516 {
517 trim_end += 1;
518 } else {
519 break;
520 }
521 }
522
523 if trim_start + trim_end > min_len {
525 trim_end = min_len - trim_start;
526 }
527
528 (trim_start, trim_end)
529}
530
531fn type_snake_name(ty: &TypeRef) -> String {
533 match ty {
534 TypeRef::String => "str".to_string(),
535 TypeRef::Number => "f64".to_string(),
536 TypeRef::Boolean => "bool".to_string(),
537 TypeRef::BigInt => "big_int".to_string(),
538 TypeRef::Void | TypeRef::Undefined => "undefined".to_string(),
539 TypeRef::Null => "null".to_string(),
540 TypeRef::Any | TypeRef::Unknown => "js_value".to_string(),
541 TypeRef::Object => "object".to_string(),
542 TypeRef::Named(n) => to_snake_case(n),
543 TypeRef::ArrayBuffer => "array_buffer".to_string(),
544 TypeRef::Uint8Array => "uint8_array".to_string(),
545 TypeRef::Int8Array => "int8_array".to_string(),
546 TypeRef::Float32Array => "float32_array".to_string(),
547 TypeRef::Float64Array => "float64_array".to_string(),
548 TypeRef::Array(_) => "array".to_string(),
549 TypeRef::Promise(_) => "promise".to_string(),
550 TypeRef::Nullable(inner) => type_snake_name(inner),
551
552 TypeRef::Function(_) => "function".to_string(),
553 TypeRef::Date => "date".to_string(),
554 TypeRef::RegExp => "reg_exp".to_string(),
555 TypeRef::Error => "error".to_string(),
556 TypeRef::Map(_, _) => "map".to_string(),
557 TypeRef::Set(_) => "set".to_string(),
558 TypeRef::Record(_, _) => "record".to_string(),
559 _ => "js_value".to_string(),
560 }
561}
562
563pub fn generate_concrete_params(
569 params: &[ConcreteParam],
570 cgctx: Option<&CodegenContext<'_>>,
571 scope: ScopeId,
572) -> TokenStream {
573 let items: Vec<_> = params
574 .iter()
575 .map(|p| {
576 let name = typemap::make_ident(&p.name);
577 let ty = if p.variadic {
578 quote! { &[JsValue] }
579 } else {
580 typemap::to_syn_type(&p.type_ref, TypePosition::ARGUMENT, cgctx, scope)
581 };
582 quote! { #name: #ty }
583 })
584 .collect();
585
586 quote! { #(#items),* }
587}
588
589pub fn is_void_return(ty: &TypeRef) -> bool {
591 matches!(ty, TypeRef::Void | TypeRef::Undefined)
592}
593
594#[cfg(test)]
595mod tests {
596 use super::*;
597 use crate::codegen::typemap::CodegenContext;
598 use crate::context::GlobalContext;
599 use crate::ir::TypeRef;
600
601 fn no_used() -> HashSet<String> {
602 HashSet::new()
603 }
604
605 fn test_ctx() -> (GlobalContext, ScopeId) {
607 let mut gctx = GlobalContext::new();
608 let scope = gctx.create_root_scope();
609 (gctx, scope)
610 }
611
612 fn expand(
614 js: &str,
615 params: &[Param],
616 ret: &TypeRef,
617 kind: SignatureKind,
618 doc: &Option<String>,
619 used: &mut HashSet<String>,
620 ) -> Vec<ExpandedSignature> {
621 let (gctx, scope) = test_ctx();
622 let cgctx = CodegenContext::empty(&gctx, scope);
623 expand_signatures(js, &[params], ret, kind, doc, used, Some(&cgctx), scope)
624 }
625
626 fn expand_overloads(
628 js: &str,
629 overloads: &[&[Param]],
630 ret: &TypeRef,
631 kind: SignatureKind,
632 doc: &Option<String>,
633 used: &mut HashSet<String>,
634 ) -> Vec<ExpandedSignature> {
635 let (gctx, scope) = test_ctx();
636 let cgctx = CodegenContext::empty(&gctx, scope);
637 expand_signatures(js, overloads, ret, kind, doc, used, Some(&cgctx), scope)
638 }
639
640 fn param(name: &str) -> Param {
641 Param {
642 name: name.to_string(),
643 type_ref: TypeRef::Any,
644 optional: false,
645 variadic: false,
646 }
647 }
648
649 fn typed_param(name: &str, ty: TypeRef) -> Param {
650 Param {
651 name: name.to_string(),
652 type_ref: ty,
653 optional: false,
654 variadic: false,
655 }
656 }
657
658 fn opt_param(name: &str) -> Param {
659 Param {
660 name: name.to_string(),
661 type_ref: TypeRef::Any,
662 optional: true,
663 variadic: false,
664 }
665 }
666
667 fn opt_typed_param(name: &str, ty: TypeRef) -> Param {
668 Param {
669 name: name.to_string(),
670 type_ref: ty,
671 optional: true,
672 variadic: false,
673 }
674 }
675
676 fn variadic_param(name: &str) -> Param {
677 Param {
678 name: name.to_string(),
679 type_ref: TypeRef::Any,
680 optional: false,
681 variadic: true,
682 }
683 }
684
685 #[test]
686 fn test_no_optional_params() {
687 let mut used = no_used();
688 let sigs = expand(
689 "foo",
690 &[param("a"), param("b")],
691 &TypeRef::Void,
692 SignatureKind::Method,
693 &None,
694 &mut used,
695 );
696 assert_eq!(sigs.len(), 2);
698 assert_eq!(sigs[0].rust_name, "foo");
699 assert!(!sigs[0].catch);
700 assert_eq!(sigs[0].params.len(), 2);
701 assert_eq!(sigs[1].rust_name, "try_foo");
702 assert!(sigs[1].catch);
703 }
704
705 #[test]
706 fn test_constructor_no_try_variant() {
707 let mut used = no_used();
708 let sigs = expand(
709 "Console",
710 &[param("stdout")],
711 &TypeRef::Named("Console".into()),
712 SignatureKind::Constructor,
713 &None,
714 &mut used,
715 );
716 assert_eq!(sigs.len(), 1);
718 assert_eq!(sigs[0].rust_name, "new");
719 assert!(sigs[0].catch);
720 }
721
722 #[test]
723 fn test_optional_expansion() {
724 let mut used = no_used();
725 let sigs = expand(
726 "Console",
727 &[
728 param("stdout"),
729 opt_param("stderr"),
730 opt_param("ignoreErrors"),
731 ],
732 &TypeRef::Named("Console".into()),
733 SignatureKind::Constructor,
734 &None,
735 &mut used,
736 );
737 assert_eq!(sigs.len(), 3);
739 assert_eq!(sigs[0].rust_name, "new");
740 assert_eq!(sigs[0].params.len(), 1);
741 assert_eq!(sigs[1].rust_name, "new_with_stderr");
742 assert_eq!(sigs[1].params.len(), 2);
743 assert_eq!(sigs[2].rust_name, "new_with_stderr_and_ignore_errors");
744 assert_eq!(sigs[2].params.len(), 3);
745 }
746
747 #[test]
748 fn test_optional_method_expansion() {
749 let mut used = no_used();
750 let sigs = expand(
751 "count",
752 &[opt_param("label")],
753 &TypeRef::Void,
754 SignatureKind::Method,
755 &None,
756 &mut used,
757 );
758 assert_eq!(sigs.len(), 4);
760 assert_eq!(sigs[0].rust_name, "count");
761 assert_eq!(sigs[0].params.len(), 0);
762 assert!(!sigs[0].catch);
763 assert_eq!(sigs[1].rust_name, "try_count");
764 assert!(sigs[1].catch);
765 assert_eq!(sigs[2].rust_name, "count_with_label");
766 assert_eq!(sigs[2].params.len(), 1);
767 assert_eq!(sigs[3].rust_name, "try_count_with_label");
768 }
769
770 #[test]
771 fn test_variadic_param() {
772 let mut used = no_used();
773 let sigs = expand(
774 "log",
775 &[variadic_param("data")],
776 &TypeRef::Void,
777 SignatureKind::Method,
778 &None,
779 &mut used,
780 );
781 assert_eq!(sigs.len(), 2);
783 assert_eq!(sigs[0].rust_name, "log");
784 assert_eq!(sigs[0].params.len(), 1);
785 assert!(sigs[0].params[0].variadic);
786 assert_eq!(sigs[1].rust_name, "try_log");
787 }
788
789 #[test]
790 fn test_optional_then_variadic() {
791 let mut used = no_used();
792 let sigs = expand(
793 "timeLog",
794 &[opt_param("label"), variadic_param("data")],
795 &TypeRef::Void,
796 SignatureKind::Method,
797 &None,
798 &mut used,
799 );
800 assert_eq!(sigs.len(), 4);
803 assert_eq!(sigs[0].rust_name, "time_log");
804 assert_eq!(sigs[0].params.len(), 1); assert!(sigs[0].params[0].variadic);
806 assert_eq!(sigs[1].rust_name, "try_time_log");
807 assert_eq!(sigs[2].rust_name, "time_log_with_label");
810 assert_eq!(sigs[2].params.len(), 2); assert!(!sigs[2].params[0].variadic);
812 assert!(sigs[2].params[1].variadic);
813 assert_eq!(sigs[3].rust_name, "try_time_log_with_label");
814 }
815
816 #[test]
817 fn test_doc_only_on_first() {
818 let doc = Some("Hello".to_string());
819 let mut used = no_used();
820 let sigs = expand(
821 "count",
822 &[opt_param("label")],
823 &TypeRef::Void,
824 SignatureKind::Method,
825 &doc,
826 &mut used,
827 );
828 assert_eq!(sigs[0].doc, Some("Hello".to_string()));
829 assert_eq!(sigs[1].doc, None); assert_eq!(sigs[2].doc, None); assert_eq!(sigs[3].doc, None); }
833
834 #[test]
835 fn test_try_collision_deduped() {
836 let mut used: HashSet<String> = ["try_count".to_string()].into_iter().collect();
838 let sigs = expand(
839 "count",
840 &[param("x")],
841 &TypeRef::Void,
842 SignatureKind::Method,
843 &None,
844 &mut used,
845 );
846 assert_eq!(sigs.len(), 2);
847 assert_eq!(sigs[0].rust_name, "count");
848 assert!(!sigs[0].catch);
849 assert_eq!(sigs[1].rust_name, "try_count_1");
850 assert!(sigs[1].catch);
851 }
852
853 #[test]
854 fn test_name_collision_deduped() {
855 let mut used = no_used();
857 let sigs1 = expand(
858 "foo",
859 &[param("a")],
860 &TypeRef::Void,
861 SignatureKind::Method,
862 &None,
863 &mut used,
864 );
865 let sigs2 = expand(
866 "foo",
867 &[param("a"), param("b")],
868 &TypeRef::Void,
869 SignatureKind::Method,
870 &None,
871 &mut used,
872 );
873 assert_eq!(sigs1[0].rust_name, "foo");
874 assert_eq!(sigs2[0].rust_name, "foo_1");
875 }
876
877 #[test]
878 fn test_overloads_with_variadic() {
879 let mut used = no_used();
883 let overload1 = [
884 typed_param("callback", TypeRef::Any),
885 opt_typed_param("msDelay", TypeRef::Number),
886 ];
887 let overload2 = [
888 typed_param("callback", TypeRef::Any),
889 opt_typed_param("msDelay", TypeRef::Number),
890 variadic_param("args"),
891 ];
892 let sigs = expand_overloads(
893 "setTimeout",
894 &[&overload1, &overload2],
895 &TypeRef::Number,
896 SignatureKind::Method,
897 &None,
898 &mut used,
899 );
900
901 let non_try: Vec<_> = sigs.iter().filter(|s| !s.catch).collect();
908 assert_eq!(non_try.len(), 4);
909 assert_eq!(non_try[0].rust_name, "set_timeout");
910 assert_eq!(non_try[0].params.len(), 1);
911 assert_eq!(non_try[1].rust_name, "set_timeout_with_ms_delay");
912 assert_eq!(non_try[1].params.len(), 2);
913 assert_eq!(non_try[2].rust_name, "set_timeout_with_args");
914 assert_eq!(non_try[2].params.len(), 2);
915 assert!(non_try[2].params[1].variadic);
916 assert_eq!(non_try[3].rust_name, "set_timeout_with_ms_delay_and_args");
917 assert_eq!(non_try[3].params.len(), 3);
918 assert!(non_try[3].params[2].variadic);
919 }
920
921 #[test]
922 fn test_overloads_with_different_types() {
923 let mut used = no_used();
926 let overload1 = [typed_param("x", TypeRef::String)];
927 let overload2 = [typed_param(
928 "x",
929 TypeRef::Promise(Box::new(TypeRef::String)),
930 )];
931 let sigs = expand_overloads(
932 "foo",
933 &[&overload1, &overload2],
934 &TypeRef::Void,
935 SignatureKind::Method,
936 &None,
937 &mut used,
938 );
939
940 let non_try: Vec<_> = sigs.iter().filter(|s| !s.catch).collect();
941 assert_eq!(non_try.len(), 2);
942 assert_eq!(non_try[0].rust_name, "foo");
944 assert_eq!(non_try[1].rust_name, "foo_with_promise");
945 }
946
947 #[test]
948 fn test_overloads_shared_truncation_deduped() {
949 let mut used = no_used();
953 let overload1 = [param("a"), opt_param("b")];
954 let overload2 = [param("a"), opt_param("c")];
955 let sigs = expand_overloads(
956 "foo",
957 &[&overload1, &overload2],
958 &TypeRef::Void,
959 SignatureKind::Method,
960 &None,
961 &mut used,
962 );
963
964 let non_try: Vec<_> = sigs.iter().filter(|s| !s.catch).collect();
967 assert_eq!(non_try.len(), 3);
968 assert_eq!(non_try[0].rust_name, "foo");
969 assert_eq!(non_try[0].params.len(), 1);
970 assert_eq!(non_try[1].rust_name, "foo_with_b");
971 assert_eq!(non_try[1].params.len(), 2);
972 assert_eq!(non_try[2].rust_name, "foo_with_c");
973 assert_eq!(non_try[2].params.len(), 2);
974 }
975}