Fork of daniellemaywood.uk/gleam — Wasm codegen work
52 kB
1635 lines
1use hexpm::version::Version;
2use rand::Rng;
3use type_::{AccessorsMap, FieldMap, RecordAccessor};
4
5use super::*;
6use crate::{
7 ast::{
8 BitArrayOption, BitArraySegment, CallArg, Constant, Publicity, SrcSpan, TypedConstant,
9 TypedConstantBitArraySegmentOption,
10 },
11 build::Origin,
12 line_numbers::LineNumbers,
13 type_::{
14 self, Deprecation, ModuleInterface, Type, TypeConstructor, TypeValueConstructor,
15 TypeValueConstructorField, TypeVariantConstructors, ValueConstructor,
16 ValueConstructorVariant, expression::Implementations,
17 },
18 uid::UniqueIdGenerator,
19};
20use std::{collections::HashMap, io::BufReader, sync::Arc};
21
22use pretty_assertions::assert_eq;
23
24fn roundtrip(input: &ModuleInterface) -> ModuleInterface {
25 let buffer = ModuleEncoder::new(input).encode().unwrap();
26 let ids = UniqueIdGenerator::new();
27 ModuleDecoder::new(ids)
28 .read(BufReader::new(buffer.as_slice()))
29 .unwrap()
30}
31
32fn constant_module(constant: TypedConstant) -> ModuleInterface {
33 ModuleInterface {
34 warnings: vec![],
35 is_internal: true,
36 package: "some_package".into(),
37 origin: Origin::Src,
38 name: "a".into(),
39 types: HashMap::new(),
40 types_value_constructors: HashMap::new(),
41 accessors: HashMap::new(),
42 values: [(
43 "one".into(),
44 ValueConstructor {
45 publicity: Publicity::Public,
46 deprecation: Deprecation::NotDeprecated,
47 type_: type_::int(),
48 variant: ValueConstructorVariant::ModuleConstant {
49 documentation: Some("Some documentation".into()),
50 literal: constant,
51 location: SrcSpan::default(),
52 module: "one/two".into(),
53 implementations: Implementations {
54 gleam: true,
55 uses_erlang_externals: false,
56 uses_javascript_externals: false,
57 can_run_on_erlang: true,
58 can_run_on_javascript: true,
59 },
60 },
61 },
62 )]
63 .into(),
64 line_numbers: LineNumbers::new(""),
65 src_path: "some_path".into(),
66 minimum_required_version: Version::new(0, 1, 0),
67 type_aliases: HashMap::new(),
68 documentation: Vec::new(),
69 }
70}
71
72fn bit_array_segment_option_module(option: TypedConstantBitArraySegmentOption) -> ModuleInterface {
73 constant_module(Constant::BitArray {
74 location: Default::default(),
75 segments: vec![BitArraySegment {
76 location: Default::default(),
77 value: Box::new(Constant::Int {
78 location: Default::default(),
79 value: "1".into(),
80 int_value: 1.into(),
81 }),
82 options: vec![option],
83 type_: type_::int(),
84 }],
85 })
86}
87
88#[test]
89fn empty_module() {
90 let module = ModuleInterface {
91 warnings: vec![],
92 is_internal: true,
93 package: "some_package".into(),
94 origin: Origin::Src,
95 name: "one/two".into(),
96 types: HashMap::new(),
97 types_value_constructors: HashMap::new(),
98 values: HashMap::new(),
99 accessors: HashMap::new(),
100 line_numbers: LineNumbers::new(""),
101 src_path: "some_path".into(),
102 minimum_required_version: Version::new(0, 1, 0),
103 type_aliases: HashMap::new(),
104 documentation: Vec::new(),
105 };
106 assert_eq!(roundtrip(&module), module);
107}
108
109#[test]
110fn with_line_numbers() {
111 let module = ModuleInterface {
112 warnings: vec![],
113 is_internal: false,
114 package: "some_package".into(),
115 origin: Origin::Src,
116 name: "one/two".into(),
117 types: HashMap::new(),
118 types_value_constructors: HashMap::new(),
119 values: HashMap::new(),
120 accessors: HashMap::new(),
121 line_numbers: LineNumbers::new(
122 "const a = 1
123 const b = 2
124 const c = 3",
125 ),
126 src_path: "some_path".into(),
127 minimum_required_version: Version::new(0, 1, 0),
128 type_aliases: HashMap::new(),
129 documentation: Vec::new(),
130 };
131 assert_eq!(roundtrip(&module), module);
132}
133
134#[test]
135fn module_with_private_type() {
136 let module = ModuleInterface {
137 warnings: vec![],
138 is_internal: false,
139 package: "some_package".into(),
140 origin: Origin::Src,
141 name: "a/b".into(),
142 types: [(
143 "ListIntType".into(),
144 TypeConstructor {
145 type_: type_::list(type_::int()),
146 publicity: Publicity::Private,
147 origin: Default::default(),
148 module: "the/module".into(),
149 parameters: vec![],
150 deprecation: Deprecation::NotDeprecated,
151 documentation: None,
152 opaque: false,
153 },
154 )]
155 .into(),
156 types_value_constructors: HashMap::new(),
157 values: HashMap::new(),
158 accessors: HashMap::new(),
159 line_numbers: LineNumbers::new(""),
160 src_path: "some_path".into(),
161 minimum_required_version: Version::new(0, 1, 0),
162 type_aliases: HashMap::new(),
163 documentation: Vec::new(),
164 };
165 assert_eq!(roundtrip(&module), module);
166}
167
168#[test]
169fn module_with_app_type() {
170 let module = ModuleInterface {
171 warnings: vec![],
172 is_internal: false,
173 package: "some_package".into(),
174 origin: Origin::Src,
175 name: "a/b".into(),
176 types: [(
177 "ListIntType".into(),
178 TypeConstructor {
179 type_: type_::list(type_::int()),
180 publicity: Publicity::Public,
181 origin: Default::default(),
182 module: "the/module".into(),
183 parameters: vec![],
184 deprecation: Deprecation::NotDeprecated,
185 documentation: None,
186 opaque: false,
187 },
188 )]
189 .into(),
190 types_value_constructors: HashMap::new(),
191 values: HashMap::new(),
192 accessors: HashMap::new(),
193 line_numbers: LineNumbers::new(""),
194 src_path: "some_path".into(),
195 minimum_required_version: Version::new(0, 1, 0),
196 type_aliases: HashMap::new(),
197 documentation: Vec::new(),
198 };
199 assert_eq!(roundtrip(&module), module);
200}
201
202#[test]
203fn module_with_fn_type() {
204 let module = ModuleInterface {
205 warnings: vec![],
206 is_internal: false,
207 package: "some_package".into(),
208 origin: Origin::Src,
209 name: "a/b".into(),
210 types: [(
211 "FnType".into(),
212 TypeConstructor {
213 type_: type_::fn_(vec![type_::nil(), type_::float()], type_::int()),
214 publicity: Publicity::Public,
215 origin: Default::default(),
216 module: "the/module".into(),
217 parameters: vec![],
218 deprecation: Deprecation::NotDeprecated,
219 documentation: None,
220 opaque: false,
221 },
222 )]
223 .into(),
224 types_value_constructors: HashMap::new(),
225 values: HashMap::new(),
226 accessors: HashMap::new(),
227 line_numbers: LineNumbers::new(""),
228 src_path: "some_path".into(),
229 minimum_required_version: Version::new(0, 1, 0),
230 type_aliases: HashMap::new(),
231 documentation: Vec::new(),
232 };
233 assert_eq!(roundtrip(&module), module);
234}
235
236#[test]
237fn module_with_tuple_type() {
238 let module = ModuleInterface {
239 warnings: vec![],
240 is_internal: false,
241 package: "some_package".into(),
242 origin: Origin::Src,
243 name: "a/b".into(),
244 types: [(
245 "TupleType".into(),
246 TypeConstructor {
247 type_: type_::tuple(vec![type_::nil(), type_::float(), type_::int()]),
248 publicity: Publicity::Public,
249 origin: Default::default(),
250 module: "the/module".into(),
251 parameters: vec![],
252 deprecation: Deprecation::NotDeprecated,
253 documentation: None,
254 opaque: false,
255 },
256 )]
257 .into(),
258 types_value_constructors: HashMap::new(),
259 values: HashMap::new(),
260 accessors: HashMap::new(),
261 line_numbers: LineNumbers::new(""),
262 src_path: "some_path".into(),
263 minimum_required_version: Version::new(0, 1, 0),
264 type_aliases: HashMap::new(),
265 documentation: Vec::new(),
266 };
267 assert_eq!(roundtrip(&module), module);
268}
269
270#[test]
271fn module_with_generic_type() {
272 let t0 = type_::generic_var(0);
273 let t1 = type_::generic_var(1);
274 let t7 = type_::generic_var(7);
275 let t8 = type_::generic_var(8);
276
277 fn make(t1: Arc<Type>, t2: Arc<Type>) -> ModuleInterface {
278 ModuleInterface {
279 warnings: vec![],
280 is_internal: false,
281 package: "some_package".into(),
282 origin: Origin::Src,
283 name: "a/b".into(),
284 types: [(
285 "TupleType".into(),
286 TypeConstructor {
287 type_: type_::tuple(vec![t1.clone(), t1.clone(), t2.clone()]),
288 publicity: Publicity::Public,
289 origin: Default::default(),
290 module: "the/module".into(),
291 parameters: vec![t1, t2],
292 deprecation: Deprecation::NotDeprecated,
293 documentation: None,
294 opaque: false,
295 },
296 )]
297 .into(),
298 types_value_constructors: HashMap::new(),
299 values: HashMap::new(),
300 accessors: HashMap::new(),
301 line_numbers: LineNumbers::new(""),
302 src_path: "some_path".into(),
303 minimum_required_version: Version::new(0, 1, 0),
304 type_aliases: HashMap::new(),
305 documentation: Vec::new(),
306 }
307 }
308
309 assert_eq!(roundtrip(&make(t7, t8)), make(t0, t1));
310}
311
312#[test]
313fn module_with_type_links() {
314 let linked_type = type_::link(type_::int());
315 let type_ = type_::int();
316
317 fn make(type_: Arc<Type>) -> ModuleInterface {
318 ModuleInterface {
319 warnings: vec![],
320 is_internal: false,
321 package: "some_package".into(),
322 origin: Origin::Src,
323 name: "a".into(),
324 types: [(
325 "SomeType".into(),
326 TypeConstructor {
327 type_,
328 publicity: Publicity::Public,
329 origin: Default::default(),
330 module: "a".into(),
331 parameters: vec![],
332 deprecation: Deprecation::NotDeprecated,
333 documentation: None,
334 opaque: false,
335 },
336 )]
337 .into(),
338 types_value_constructors: HashMap::new(),
339 values: HashMap::new(),
340 accessors: HashMap::new(),
341 line_numbers: LineNumbers::new(""),
342 src_path: "some_path".into(),
343 minimum_required_version: Version::new(0, 1, 0),
344 type_aliases: HashMap::new(),
345 documentation: Vec::new(),
346 }
347 }
348
349 assert_eq!(roundtrip(&make(linked_type)), make(type_));
350}
351
352#[test]
353fn module_with_type_constructor_documentation() {
354 let linked_type = type_::link(type_::int());
355 let type_ = type_::int();
356
357 fn make(type_: Arc<Type>) -> ModuleInterface {
358 ModuleInterface {
359 warnings: vec![],
360 is_internal: false,
361 package: "some_package".into(),
362 origin: Origin::Src,
363 name: "a".into(),
364 types: [(
365 "SomeType".into(),
366 TypeConstructor {
367 type_,
368 publicity: Publicity::Public,
369 origin: Default::default(),
370 module: "a".into(),
371 parameters: vec![],
372 deprecation: Deprecation::NotDeprecated,
373 documentation: Some("type documentation".into()),
374 opaque: false,
375 },
376 )]
377 .into(),
378 types_value_constructors: HashMap::new(),
379 values: HashMap::new(),
380 accessors: HashMap::new(),
381 line_numbers: LineNumbers::new(""),
382 src_path: "some_path".into(),
383 minimum_required_version: Version::new(0, 1, 0),
384 type_aliases: HashMap::new(),
385 documentation: Vec::new(),
386 }
387 }
388
389 assert_eq!(roundtrip(&make(linked_type)), make(type_));
390}
391
392#[test]
393fn module_with_type_constructor_origin() {
394 let linked_type = type_::link(type_::int());
395 let type_ = type_::int();
396
397 fn make(type_: Arc<Type>) -> ModuleInterface {
398 ModuleInterface {
399 warnings: vec![],
400 is_internal: false,
401 package: "some_package".into(),
402 origin: Origin::Src,
403 name: "a".into(),
404 types: [(
405 "SomeType".into(),
406 TypeConstructor {
407 type_,
408 publicity: Publicity::Public,
409 origin: SrcSpan {
410 start: 535,
411 end: 543,
412 },
413 module: "a".into(),
414 parameters: vec![],
415 deprecation: Deprecation::NotDeprecated,
416 documentation: None,
417 opaque: false,
418 },
419 )]
420 .into(),
421 types_value_constructors: HashMap::new(),
422 values: HashMap::new(),
423 accessors: HashMap::new(),
424 line_numbers: LineNumbers::new(""),
425 src_path: "some_path".into(),
426 minimum_required_version: Version::new(0, 1, 0),
427 type_aliases: HashMap::new(),
428 documentation: Vec::new(),
429 }
430 }
431
432 assert_eq!(roundtrip(&make(linked_type)), make(type_));
433}
434
435#[test]
436fn module_type_to_constructors_mapping() {
437 let module = ModuleInterface {
438 warnings: vec![],
439 is_internal: false,
440 package: "some_package".into(),
441 origin: Origin::Src,
442 name: "a".into(),
443 types: HashMap::new(),
444 types_value_constructors: [(
445 "SomeType".into(),
446 TypeVariantConstructors {
447 type_parameters_ids: vec![0, 1, 2],
448 variants: vec![TypeValueConstructor {
449 name: "One".into(),
450 parameters: vec![],
451 }],
452 },
453 )]
454 .into(),
455 accessors: HashMap::new(),
456 values: HashMap::new(),
457 line_numbers: LineNumbers::new(""),
458 src_path: "some_path".into(),
459 minimum_required_version: Version::new(0, 1, 0),
460 type_aliases: HashMap::new(),
461 documentation: Vec::new(),
462 };
463
464 assert_eq!(roundtrip(&module), module);
465}
466
467#[test]
468fn module_fn_value() {
469 let module = ModuleInterface {
470 warnings: vec![],
471 is_internal: false,
472 package: "some_package".into(),
473 origin: Origin::Src,
474 name: "a".into(),
475 types: HashMap::new(),
476 types_value_constructors: HashMap::new(),
477 accessors: HashMap::new(),
478 values: [(
479 "one".into(),
480 ValueConstructor {
481 publicity: Publicity::Public,
482 deprecation: Deprecation::NotDeprecated,
483 type_: type_::int(),
484 variant: ValueConstructorVariant::ModuleFn {
485 documentation: Some("wobble!".into()),
486 name: "one".into(),
487 field_map: None,
488 module: "a".into(),
489 arity: 5,
490 location: SrcSpan {
491 start: 535,
492 end: 1100,
493 },
494 external_erlang: None,
495 external_javascript: None,
496 implementations: Implementations {
497 gleam: true,
498 uses_erlang_externals: false,
499 uses_javascript_externals: false,
500 can_run_on_erlang: true,
501 can_run_on_javascript: true,
502 },
503 },
504 },
505 )]
506 .into(),
507 line_numbers: LineNumbers::new(""),
508 src_path: "some_path".into(),
509 minimum_required_version: Version::new(0, 1, 0),
510 type_aliases: HashMap::new(),
511 documentation: Vec::new(),
512 };
513 assert_eq!(roundtrip(&module), module);
514}
515
516#[test]
517fn deprecated_module_fn_value() {
518 let module = ModuleInterface {
519 warnings: vec![],
520 is_internal: false,
521 package: "some_package".into(),
522 origin: Origin::Src,
523 name: "a".into(),
524 types: HashMap::new(),
525 types_value_constructors: HashMap::new(),
526 accessors: HashMap::new(),
527 values: [(
528 "one".into(),
529 ValueConstructor {
530 publicity: Publicity::Public,
531 deprecation: Deprecation::Deprecated {
532 message: "wibble wobble".into(),
533 },
534 type_: type_::int(),
535 variant: ValueConstructorVariant::ModuleFn {
536 documentation: Some("wobble!".into()),
537 name: "one".into(),
538 field_map: None,
539 module: "a".into(),
540 arity: 5,
541 location: SrcSpan {
542 start: 535,
543 end: 1100,
544 },
545 external_erlang: None,
546 external_javascript: None,
547 implementations: Implementations {
548 gleam: true,
549 uses_erlang_externals: false,
550 uses_javascript_externals: false,
551 can_run_on_erlang: true,
552 can_run_on_javascript: true,
553 },
554 },
555 },
556 )]
557 .into(),
558 line_numbers: LineNumbers::new(""),
559 src_path: "some_path".into(),
560 minimum_required_version: Version::new(0, 1, 0),
561 type_aliases: HashMap::new(),
562 documentation: Vec::new(),
563 };
564 assert_eq!(roundtrip(&module), module);
565}
566
567#[test]
568fn private_module_fn_value() {
569 let module = ModuleInterface {
570 warnings: vec![],
571 is_internal: false,
572 package: "some_package".into(),
573 origin: Origin::Src,
574 name: "a".into(),
575 types: HashMap::new(),
576 types_value_constructors: HashMap::new(),
577 accessors: HashMap::new(),
578 values: [(
579 "one".into(),
580 ValueConstructor {
581 publicity: Publicity::Private,
582 deprecation: Deprecation::NotDeprecated,
583 type_: type_::int(),
584 variant: ValueConstructorVariant::ModuleFn {
585 documentation: Some("wobble!".into()),
586 name: "one".into(),
587 field_map: None,
588 module: "a".into(),
589 arity: 5,
590 location: SrcSpan {
591 start: 535,
592 end: 1100,
593 },
594 external_erlang: None,
595 external_javascript: None,
596 implementations: Implementations {
597 gleam: true,
598 uses_erlang_externals: false,
599 uses_javascript_externals: false,
600 can_run_on_erlang: true,
601 can_run_on_javascript: true,
602 },
603 },
604 },
605 )]
606 .into(),
607 line_numbers: LineNumbers::new(""),
608 src_path: "some_path".into(),
609 minimum_required_version: Version::new(0, 1, 0),
610 type_aliases: HashMap::new(),
611 documentation: Vec::new(),
612 };
613
614 assert_eq!(roundtrip(&module), module);
615}
616
617// https://github.com/gleam-lang/gleam/commit/c8f3bd0ddbf61c27ea35f37297058ecca7515f6c
618#[test]
619fn module_fn_value_regression() {
620 let module = ModuleInterface {
621 warnings: vec![],
622 is_internal: false,
623 package: "some_package".into(),
624 origin: Origin::Src,
625 name: "a/b/c".into(),
626 types: HashMap::new(),
627 types_value_constructors: HashMap::new(),
628 accessors: HashMap::new(),
629 values: [(
630 "one".into(),
631 ValueConstructor {
632 publicity: Publicity::Public,
633 deprecation: Deprecation::NotDeprecated,
634 type_: type_::int(),
635 variant: ValueConstructorVariant::ModuleFn {
636 documentation: Some("wabble!".into()),
637 name: "one".into(),
638 field_map: None,
639 module: "a".into(),
640 arity: 5,
641 location: SrcSpan {
642 start: 52,
643 end: 1100,
644 },
645 external_erlang: None,
646 external_javascript: None,
647 implementations: Implementations {
648 gleam: true,
649 uses_erlang_externals: false,
650 uses_javascript_externals: false,
651 can_run_on_erlang: true,
652 can_run_on_javascript: true,
653 },
654 },
655 },
656 )]
657 .into(),
658 line_numbers: LineNumbers::new(""),
659 src_path: "some_path".into(),
660 minimum_required_version: Version::new(0, 1, 0),
661 type_aliases: HashMap::new(),
662 documentation: Vec::new(),
663 };
664
665 assert_eq!(roundtrip(&module), module);
666}
667
668#[test]
669fn module_fn_value_with_field_map() {
670 let module = ModuleInterface {
671 warnings: vec![],
672 is_internal: false,
673 package: "some_package".into(),
674 origin: Origin::Src,
675 name: "a".into(),
676 types: HashMap::new(),
677 types_value_constructors: HashMap::new(),
678 accessors: HashMap::new(),
679 values: [(
680 "one".into(),
681 ValueConstructor {
682 publicity: Publicity::Public,
683 deprecation: Deprecation::NotDeprecated,
684 type_: type_::int(),
685 variant: ValueConstructorVariant::ModuleFn {
686 documentation: Some("wubble!".into()),
687 name: "one".into(),
688 field_map: Some(FieldMap {
689 arity: 20,
690 fields: [("ok".into(), 5), ("ko".into(), 7)].into(),
691 }),
692 external_erlang: None,
693 external_javascript: None,
694 module: "a".into(),
695 arity: 5,
696 location: SrcSpan { start: 2, end: 11 },
697 implementations: Implementations {
698 gleam: true,
699 uses_erlang_externals: false,
700 uses_javascript_externals: false,
701 can_run_on_erlang: true,
702 can_run_on_javascript: true,
703 },
704 },
705 },
706 )]
707 .into(),
708 line_numbers: LineNumbers::new(""),
709 src_path: "some_path".into(),
710 minimum_required_version: Version::new(0, 1, 0),
711 type_aliases: HashMap::new(),
712 documentation: Vec::new(),
713 };
714
715 assert_eq!(roundtrip(&module), module);
716}
717
718#[test]
719fn record_value() {
720 let mut random = rand::thread_rng();
721
722 let module = ModuleInterface {
723 warnings: vec![],
724 is_internal: false,
725 package: "some_package".into(),
726 origin: Origin::Src,
727 name: "a".into(),
728 types: HashMap::new(),
729 types_value_constructors: HashMap::new(),
730 accessors: HashMap::new(),
731 values: [(
732 "one".into(),
733 ValueConstructor {
734 publicity: Publicity::Public,
735 deprecation: Deprecation::NotDeprecated,
736 type_: type_::int(),
737 variant: ValueConstructorVariant::Record {
738 documentation: Some("webble!".into()),
739 name: "one".into(),
740 module: "themodule".into(),
741 field_map: None,
742 arity: random.r#gen(),
743 variants_count: random.r#gen(),
744 location: SrcSpan {
745 start: random.r#gen(),
746 end: random.r#gen(),
747 },
748 variant_index: random.r#gen(),
749 },
750 },
751 )]
752 .into(),
753 line_numbers: LineNumbers::new(""),
754 src_path: "some_path".into(),
755 minimum_required_version: Version::new(0, 1, 0),
756 type_aliases: HashMap::new(),
757 documentation: Vec::new(),
758 };
759
760 assert_eq!(roundtrip(&module), module);
761}
762
763#[test]
764fn record_value_with_field_map() {
765 let mut random = rand::thread_rng();
766
767 let module = ModuleInterface {
768 warnings: vec![],
769 is_internal: false,
770 package: "some_package".into(),
771 origin: Origin::Src,
772 name: "a".into(),
773 types: HashMap::new(),
774 types_value_constructors: HashMap::new(),
775 accessors: HashMap::new(),
776 values: [(
777 "one".into(),
778 ValueConstructor {
779 publicity: Publicity::Public,
780 deprecation: Deprecation::NotDeprecated,
781 type_: type_::int(),
782 variant: ValueConstructorVariant::Record {
783 documentation: Some("wybble!".into()),
784 module: "themodule".into(),
785 name: "one".into(),
786 field_map: Some(FieldMap {
787 arity: random.r#gen(),
788 fields: [("ok".into(), random.r#gen()), ("ko".into(), random.r#gen())]
789 .into(),
790 }),
791 arity: random.r#gen(),
792 variants_count: random.r#gen(),
793 variant_index: random.r#gen(),
794 location: SrcSpan {
795 start: random.r#gen(),
796 end: random.r#gen(),
797 },
798 },
799 },
800 )]
801 .into(),
802 line_numbers: LineNumbers::new(""),
803 src_path: "some_path".into(),
804 minimum_required_version: Version::new(0, 1, 0),
805 type_aliases: HashMap::new(),
806 documentation: Vec::new(),
807 };
808
809 assert_eq!(roundtrip(&module), module);
810}
811
812#[test]
813fn accessors() {
814 let accessors1 = [
815 (
816 "a".into(),
817 RecordAccessor {
818 index: 6,
819 label: "siiixxx".into(),
820 type_: type_::nil(),
821 },
822 ),
823 (
824 "a".into(),
825 RecordAccessor {
826 index: 5,
827 label: "fiveee".into(),
828 type_: type_::float(),
829 },
830 ),
831 ];
832
833 let accessors2 = [(
834 "a".into(),
835 RecordAccessor {
836 index: 1,
837 label: "ok".into(),
838 type_: type_::float(),
839 },
840 )];
841
842 let module = ModuleInterface {
843 warnings: vec![],
844 is_internal: false,
845 package: "some_package".into(),
846 origin: Origin::Src,
847 name: "a".into(),
848 types: HashMap::new(),
849 types_value_constructors: HashMap::new(),
850 values: HashMap::new(),
851 accessors: [
852 (
853 "one".into(),
854 AccessorsMap {
855 publicity: Publicity::Public,
856 type_: type_::int(),
857 shared_accessors: accessors1.clone().into(),
858 variant_specific_accessors: vec![accessors1.into()],
859 },
860 ),
861 (
862 "two".into(),
863 AccessorsMap {
864 publicity: Publicity::Public,
865 type_: type_::int(),
866 shared_accessors: accessors2.clone().into(),
867 variant_specific_accessors: vec![accessors2.into()],
868 },
869 ),
870 ]
871 .into(),
872 line_numbers: LineNumbers::new(""),
873 src_path: "some_path".into(),
874 minimum_required_version: Version::new(0, 1, 0),
875 type_aliases: HashMap::new(),
876 documentation: Vec::new(),
877 };
878
879 assert_eq!(roundtrip(&module), module);
880}
881
882#[test]
883fn private_accessors() {
884 let accessors1 = [
885 (
886 "a".into(),
887 RecordAccessor {
888 index: 6,
889 label: "siiixxx".into(),
890 type_: type_::nil(),
891 },
892 ),
893 (
894 "a".into(),
895 RecordAccessor {
896 index: 5,
897 label: "fiveee".into(),
898 type_: type_::float(),
899 },
900 ),
901 ];
902
903 let accessors2 = [(
904 "a".into(),
905 RecordAccessor {
906 index: 1,
907 label: "ok".into(),
908 type_: type_::float(),
909 },
910 )];
911
912 let module = ModuleInterface {
913 warnings: vec![],
914 is_internal: false,
915 package: "some_package".into(),
916 origin: Origin::Src,
917 name: "a".into(),
918 types: HashMap::new(),
919 types_value_constructors: HashMap::new(),
920 values: HashMap::new(),
921 accessors: [
922 (
923 "one".into(),
924 AccessorsMap {
925 publicity: Publicity::Private,
926 type_: type_::int(),
927 shared_accessors: accessors1.clone().into(),
928 variant_specific_accessors: vec![accessors1.into()],
929 },
930 ),
931 (
932 "two".into(),
933 AccessorsMap {
934 publicity: Publicity::Public,
935 type_: type_::int(),
936 shared_accessors: accessors2.clone().into(),
937 variant_specific_accessors: vec![accessors2.into()],
938 },
939 ),
940 ]
941 .into(),
942 line_numbers: LineNumbers::new(""),
943 src_path: "some_path".into(),
944 minimum_required_version: Version::new(0, 1, 0),
945 type_aliases: HashMap::new(),
946 documentation: Vec::new(),
947 };
948
949 assert_eq!(roundtrip(&module), module);
950}
951
952#[test]
953fn constant_int() {
954 let module = constant_module(Constant::Int {
955 location: Default::default(),
956 value: "100".into(),
957 int_value: 100.into(),
958 });
959
960 assert_eq!(roundtrip(&module), module);
961}
962
963#[test]
964fn constant_float() {
965 let module = constant_module(Constant::Float {
966 location: Default::default(),
967 value: "1.0".into(),
968 });
969
970 assert_eq!(roundtrip(&module), module);
971}
972
973#[test]
974fn constant_string() {
975 let module = constant_module(Constant::String {
976 location: Default::default(),
977 value: "hello".into(),
978 });
979
980 assert_eq!(roundtrip(&module), module);
981}
982
983#[test]
984fn constant_tuple() {
985 let module = constant_module(Constant::Tuple {
986 location: Default::default(),
987 elements: vec![
988 Constant::Int {
989 location: Default::default(),
990 value: "1".into(),
991 int_value: 1.into(),
992 },
993 Constant::Float {
994 location: Default::default(),
995 value: "1.0".into(),
996 },
997 Constant::Tuple {
998 location: Default::default(),
999 elements: vec![
1000 Constant::Int {
1001 location: Default::default(),
1002 value: "1".into(),
1003 int_value: 1.into(),
1004 },
1005 Constant::Float {
1006 location: Default::default(),
1007 value: "1.0".into(),
1008 },
1009 ],
1010 },
1011 ],
1012 });
1013
1014 assert_eq!(roundtrip(&module), module);
1015}
1016
1017#[test]
1018fn constant_list() {
1019 let module = constant_module(Constant::List {
1020 location: Default::default(),
1021 type_: type_::int(),
1022 elements: vec![
1023 Constant::Int {
1024 location: Default::default(),
1025 value: "1".into(),
1026 int_value: 1.into(),
1027 },
1028 Constant::Int {
1029 location: Default::default(),
1030 value: "2".into(),
1031 int_value: 2.into(),
1032 },
1033 Constant::Int {
1034 location: Default::default(),
1035 value: "3".into(),
1036 int_value: 3.into(),
1037 },
1038 ],
1039 });
1040
1041 assert_eq!(roundtrip(&module), module);
1042}
1043
1044#[test]
1045fn constant_record() {
1046 let module = constant_module(Constant::Record {
1047 location: Default::default(),
1048 module: None,
1049 name: "".into(),
1050 args: vec![
1051 CallArg {
1052 implicit: None,
1053 label: None,
1054 location: Default::default(),
1055 value: Constant::Float {
1056 location: Default::default(),
1057 value: "0.0".into(),
1058 },
1059 },
1060 CallArg {
1061 implicit: None,
1062 label: None,
1063 location: Default::default(),
1064 value: Constant::Int {
1065 location: Default::default(),
1066 value: "1".into(),
1067 int_value: 1.into(),
1068 },
1069 },
1070 ],
1071 tag: "thetag".into(),
1072 type_: type_::int(),
1073 field_map: None,
1074 });
1075
1076 assert_eq!(roundtrip(&module), module);
1077}
1078
1079#[test]
1080fn constant_var() {
1081 let one_original = Constant::Int {
1082 location: Default::default(),
1083 value: "1".into(),
1084 int_value: 1.into(),
1085 };
1086
1087 let one = Constant::Var {
1088 location: Default::default(),
1089 module: None,
1090 name: "one_original".into(),
1091 type_: type_::int(),
1092 constructor: Some(Box::from(ValueConstructor {
1093 publicity: Publicity::Public,
1094 deprecation: Deprecation::NotDeprecated,
1095 type_: type_::int(),
1096 variant: ValueConstructorVariant::ModuleConstant {
1097 documentation: Some("some doc".into()),
1098 literal: one_original.clone(),
1099 location: SrcSpan::default(),
1100 module: "one/two".into(),
1101 implementations: Implementations {
1102 gleam: true,
1103 uses_erlang_externals: false,
1104 uses_javascript_externals: false,
1105 can_run_on_erlang: true,
1106 can_run_on_javascript: true,
1107 },
1108 },
1109 })),
1110 };
1111
1112 let module = ModuleInterface {
1113 warnings: vec![],
1114 is_internal: false,
1115 package: "some_package".into(),
1116 origin: Origin::Src,
1117 name: "a".into(),
1118 types: HashMap::new(),
1119 types_value_constructors: HashMap::new(),
1120 accessors: HashMap::new(),
1121 values: [
1122 (
1123 "one".into(),
1124 ValueConstructor {
1125 publicity: Publicity::Public,
1126 deprecation: Deprecation::NotDeprecated,
1127 type_: type_::int(),
1128 variant: ValueConstructorVariant::ModuleConstant {
1129 documentation: Some("some doc!!!!!!!!!".into()),
1130 literal: one,
1131 location: SrcSpan::default(),
1132 module: "one/two".into(),
1133 implementations: Implementations {
1134 gleam: true,
1135 uses_erlang_externals: false,
1136 uses_javascript_externals: false,
1137 can_run_on_erlang: true,
1138 can_run_on_javascript: true,
1139 },
1140 },
1141 },
1142 ),
1143 (
1144 "one_original".into(),
1145 ValueConstructor {
1146 publicity: Publicity::Public,
1147 deprecation: Deprecation::NotDeprecated,
1148 type_: type_::int(),
1149 variant: ValueConstructorVariant::ModuleConstant {
1150 documentation: Some("some doc yeah".into()),
1151 literal: one_original,
1152 location: SrcSpan::default(),
1153 module: "one/two".into(),
1154 implementations: Implementations {
1155 gleam: true,
1156 uses_erlang_externals: false,
1157 uses_javascript_externals: false,
1158 can_run_on_erlang: true,
1159 can_run_on_javascript: true,
1160 },
1161 },
1162 },
1163 ),
1164 ]
1165 .into(),
1166 line_numbers: LineNumbers::new(""),
1167 src_path: "some_path".into(),
1168 minimum_required_version: Version::new(0, 1, 0),
1169 type_aliases: HashMap::new(),
1170 documentation: Vec::new(),
1171 };
1172
1173 assert_eq!(roundtrip(&module), module);
1174}
1175
1176#[test]
1177fn constant_bit_array() {
1178 let module = constant_module(Constant::BitArray {
1179 location: Default::default(),
1180 segments: vec![],
1181 });
1182 assert_eq!(roundtrip(&module), module);
1183}
1184
1185#[test]
1186fn constant_bit_array_unit() {
1187 let module = bit_array_segment_option_module(BitArrayOption::Unit {
1188 location: Default::default(),
1189 value: 234,
1190 });
1191 assert_eq!(roundtrip(&module), module);
1192}
1193
1194#[test]
1195fn constant_bit_array_float() {
1196 let module = bit_array_segment_option_module(BitArrayOption::Float {
1197 location: Default::default(),
1198 });
1199 assert_eq!(roundtrip(&module), module);
1200}
1201
1202#[test]
1203fn constant_bit_array_int() {
1204 let module = bit_array_segment_option_module(BitArrayOption::Int {
1205 location: Default::default(),
1206 });
1207 assert_eq!(roundtrip(&module), module);
1208}
1209
1210#[test]
1211fn constant_bit_array_size() {
1212 let module = bit_array_segment_option_module(BitArrayOption::Size {
1213 location: Default::default(),
1214 value: Box::new(Constant::Int {
1215 location: Default::default(),
1216 value: "1".into(),
1217 int_value: 1.into(),
1218 }),
1219 short_form: false,
1220 });
1221 assert_eq!(roundtrip(&module), module);
1222}
1223
1224#[test]
1225fn constant_bit_array_size_short_form() {
1226 let module = bit_array_segment_option_module(BitArrayOption::Size {
1227 location: Default::default(),
1228 value: Box::new(Constant::Int {
1229 location: Default::default(),
1230 value: "1".into(),
1231 int_value: 1.into(),
1232 }),
1233 short_form: true,
1234 });
1235 assert_eq!(roundtrip(&module), module);
1236}
1237
1238#[test]
1239fn constant_bit_array_bit_arry() {
1240 let module = bit_array_segment_option_module(BitArrayOption::Bits {
1241 location: Default::default(),
1242 });
1243 assert_eq!(roundtrip(&module), module);
1244}
1245
1246#[test]
1247fn constant_bit_array_utf8() {
1248 let module = bit_array_segment_option_module(BitArrayOption::Utf8 {
1249 location: Default::default(),
1250 });
1251 assert_eq!(roundtrip(&module), module);
1252}
1253
1254#[test]
1255fn constant_bit_array_utf16() {
1256 let module = bit_array_segment_option_module(BitArrayOption::Utf16 {
1257 location: Default::default(),
1258 });
1259 assert_eq!(roundtrip(&module), module);
1260}
1261
1262#[test]
1263fn constant_bit_array_utf32() {
1264 let module = bit_array_segment_option_module(BitArrayOption::Utf32 {
1265 location: Default::default(),
1266 });
1267 assert_eq!(roundtrip(&module), module);
1268}
1269
1270#[test]
1271fn constant_bit_array_utf8codepoint() {
1272 let module = bit_array_segment_option_module(BitArrayOption::Utf8Codepoint {
1273 location: Default::default(),
1274 });
1275 assert_eq!(roundtrip(&module), module);
1276}
1277
1278#[test]
1279fn constant_bit_array_utf16codepoint() {
1280 let module = bit_array_segment_option_module(BitArrayOption::Utf16Codepoint {
1281 location: Default::default(),
1282 });
1283 assert_eq!(roundtrip(&module), module);
1284}
1285
1286#[test]
1287fn constant_bit_array_utf32codepoint() {
1288 let module = bit_array_segment_option_module(BitArrayOption::Utf32Codepoint {
1289 location: Default::default(),
1290 });
1291 assert_eq!(roundtrip(&module), module);
1292}
1293
1294#[test]
1295fn constant_bit_array_signed() {
1296 let module = bit_array_segment_option_module(BitArrayOption::Signed {
1297 location: Default::default(),
1298 });
1299 assert_eq!(roundtrip(&module), module);
1300}
1301
1302#[test]
1303fn constant_bit_array_unsigned() {
1304 let module = bit_array_segment_option_module(BitArrayOption::Unsigned {
1305 location: Default::default(),
1306 });
1307 assert_eq!(roundtrip(&module), module);
1308}
1309
1310#[test]
1311fn constant_bit_array_big() {
1312 let module = bit_array_segment_option_module(BitArrayOption::Big {
1313 location: Default::default(),
1314 });
1315 assert_eq!(roundtrip(&module), module);
1316}
1317
1318#[test]
1319fn constant_bit_array_little() {
1320 let module = bit_array_segment_option_module(BitArrayOption::Little {
1321 location: Default::default(),
1322 });
1323 assert_eq!(roundtrip(&module), module);
1324}
1325
1326#[test]
1327fn constant_bit_array_native() {
1328 let module = bit_array_segment_option_module(BitArrayOption::Native {
1329 location: Default::default(),
1330 });
1331 assert_eq!(roundtrip(&module), module);
1332}
1333
1334#[test]
1335fn deprecated_type() {
1336 let module = ModuleInterface {
1337 warnings: vec![],
1338 is_internal: false,
1339 package: "some_package".into(),
1340 origin: Origin::Src,
1341 name: "a/b".into(),
1342 types: [(
1343 "ListIntType".into(),
1344 TypeConstructor {
1345 type_: type_::list(type_::int()),
1346 publicity: Publicity::Public,
1347 origin: Default::default(),
1348 module: "the/module".into(),
1349 parameters: vec![],
1350 deprecation: Deprecation::Deprecated {
1351 message: "oh no".into(),
1352 },
1353 documentation: None,
1354 opaque: false,
1355 },
1356 )]
1357 .into(),
1358 types_value_constructors: HashMap::new(),
1359 values: HashMap::new(),
1360 accessors: HashMap::new(),
1361 line_numbers: LineNumbers::new(""),
1362 src_path: "some_path".into(),
1363 minimum_required_version: Version::new(0, 1, 0),
1364 type_aliases: HashMap::new(),
1365 documentation: Vec::new(),
1366 };
1367 assert_eq!(roundtrip(&module), module);
1368}
1369
1370#[test]
1371fn module_fn_value_with_external_implementations() {
1372 let module = ModuleInterface {
1373 warnings: vec![],
1374 is_internal: false,
1375 package: "some_package".into(),
1376 origin: Origin::Src,
1377 name: "a/b/c".into(),
1378 types: HashMap::new(),
1379 types_value_constructors: HashMap::new(),
1380 accessors: HashMap::new(),
1381 values: [(
1382 "one".into(),
1383 ValueConstructor {
1384 publicity: Publicity::Public,
1385 deprecation: Deprecation::NotDeprecated,
1386 type_: type_::int(),
1387 variant: ValueConstructorVariant::ModuleFn {
1388 documentation: Some("wabble!".into()),
1389 name: "one".into(),
1390 field_map: None,
1391 module: "a".into(),
1392 arity: 5,
1393 location: SrcSpan {
1394 start: 52,
1395 end: 1100,
1396 },
1397 external_erlang: Some(("wibble".into(), "wobble".into())),
1398 external_javascript: Some(("wobble".into(), "wibble".into())),
1399 implementations: Implementations {
1400 gleam: false,
1401 uses_erlang_externals: true,
1402 uses_javascript_externals: true,
1403 can_run_on_erlang: false,
1404 can_run_on_javascript: true,
1405 },
1406 },
1407 },
1408 )]
1409 .into(),
1410 line_numbers: LineNumbers::new(""),
1411 src_path: "some_path".into(),
1412 minimum_required_version: Version::new(0, 1, 0),
1413 type_aliases: HashMap::new(),
1414 documentation: Vec::new(),
1415 };
1416
1417 assert_eq!(roundtrip(&module), module);
1418}
1419
1420#[test]
1421fn internal_module_fn() {
1422 let module = ModuleInterface {
1423 warnings: vec![],
1424 is_internal: false,
1425 package: "some_package".into(),
1426 origin: Origin::Src,
1427 name: "a/b/c".into(),
1428 types: HashMap::new(),
1429 types_value_constructors: HashMap::new(),
1430 accessors: HashMap::new(),
1431 values: [(
1432 "one".into(),
1433 ValueConstructor {
1434 publicity: Publicity::Internal {
1435 attribute_location: None,
1436 },
1437 deprecation: Deprecation::NotDeprecated,
1438 type_: type_::int(),
1439 variant: ValueConstructorVariant::ModuleFn {
1440 documentation: Some("wabble!".into()),
1441 name: "one".into(),
1442 field_map: None,
1443 module: "a".into(),
1444 arity: 5,
1445 location: SrcSpan {
1446 start: 52,
1447 end: 1100,
1448 },
1449 external_erlang: Some(("wibble".into(), "wobble".into())),
1450 external_javascript: Some(("wobble".into(), "wibble".into())),
1451 implementations: Implementations {
1452 gleam: false,
1453 uses_erlang_externals: true,
1454 uses_javascript_externals: true,
1455 can_run_on_erlang: true,
1456 can_run_on_javascript: true,
1457 },
1458 },
1459 },
1460 )]
1461 .into(),
1462 line_numbers: LineNumbers::new(""),
1463 src_path: "some_path".into(),
1464 minimum_required_version: Version::new(0, 1, 0),
1465 type_aliases: HashMap::new(),
1466 documentation: Vec::new(),
1467 };
1468
1469 assert_eq!(roundtrip(&module), module);
1470}
1471
1472#[test]
1473fn internal_annotated_module_fn() {
1474 let module = ModuleInterface {
1475 warnings: vec![],
1476 is_internal: false,
1477 package: "some_package".into(),
1478 origin: Origin::Src,
1479 name: "a/b/c".into(),
1480 types: HashMap::new(),
1481 types_value_constructors: HashMap::new(),
1482 accessors: HashMap::new(),
1483 values: [(
1484 "one".into(),
1485 ValueConstructor {
1486 publicity: Publicity::Internal {
1487 attribute_location: Some(SrcSpan {
1488 start: 11,
1489 end: 111,
1490 }),
1491 },
1492 deprecation: Deprecation::NotDeprecated,
1493 type_: type_::int(),
1494 variant: ValueConstructorVariant::ModuleFn {
1495 documentation: Some("wabble!".into()),
1496 name: "one".into(),
1497 field_map: None,
1498 module: "a".into(),
1499 arity: 5,
1500 location: SrcSpan {
1501 start: 52,
1502 end: 1100,
1503 },
1504 external_erlang: Some(("wibble".into(), "wobble".into())),
1505 external_javascript: Some(("wobble".into(), "wibble".into())),
1506 implementations: Implementations {
1507 gleam: false,
1508 uses_erlang_externals: true,
1509 uses_javascript_externals: true,
1510 can_run_on_erlang: true,
1511 can_run_on_javascript: true,
1512 },
1513 },
1514 },
1515 )]
1516 .into(),
1517 line_numbers: LineNumbers::new(""),
1518 src_path: "some_path".into(),
1519 minimum_required_version: Version::new(0, 1, 0),
1520 type_aliases: HashMap::new(),
1521 documentation: Vec::new(),
1522 };
1523
1524 assert_eq!(roundtrip(&module), module);
1525}
1526
1527// https://github.com/gleam-lang/gleam/issues/2599
1528#[test]
1529fn type_variable_ids_in_constructors_are_shared() {
1530 let module = ModuleInterface {
1531 warnings: vec![],
1532 is_internal: false,
1533 package: "some_package".into(),
1534 origin: Origin::Src,
1535 name: "a/b/c".into(),
1536 types: HashMap::new(),
1537 types_value_constructors: HashMap::from([(
1538 "SomeType".into(),
1539 TypeVariantConstructors {
1540 type_parameters_ids: vec![4, 5, 6],
1541 variants: vec![TypeValueConstructor {
1542 name: "One".into(),
1543 parameters: vec![
1544 TypeValueConstructorField {
1545 type_: type_::generic_var(6),
1546 label: None,
1547 },
1548 TypeValueConstructorField {
1549 type_: type_::int(),
1550 label: None,
1551 },
1552 TypeValueConstructorField {
1553 type_: type_::tuple(vec![type_::generic_var(4), type_::generic_var(5)]),
1554 label: None,
1555 },
1556 ],
1557 }],
1558 },
1559 )]),
1560 accessors: HashMap::new(),
1561 values: [].into(),
1562 line_numbers: LineNumbers::new(""),
1563 src_path: "some_path".into(),
1564 minimum_required_version: Version::new(0, 1, 0),
1565 type_aliases: HashMap::new(),
1566 documentation: Vec::new(),
1567 };
1568
1569 let expected = HashMap::from([(
1570 "SomeType".into(),
1571 TypeVariantConstructors {
1572 type_parameters_ids: vec![1, 2, 0],
1573 variants: vec![TypeValueConstructor {
1574 name: "One".into(),
1575 parameters: vec![
1576 TypeValueConstructorField {
1577 type_: type_::generic_var(0),
1578 label: None,
1579 },
1580 TypeValueConstructorField {
1581 type_: type_::int(),
1582 label: None,
1583 },
1584 TypeValueConstructorField {
1585 type_: type_::tuple(vec![type_::generic_var(1), type_::generic_var(2)]),
1586 label: None,
1587 },
1588 ],
1589 }],
1590 },
1591 )]);
1592
1593 assert_eq!(roundtrip(&module).types_value_constructors, expected);
1594}
1595
1596#[test]
1597fn type_with_inferred_variant() {
1598 let module = ModuleInterface {
1599 warnings: vec![],
1600 is_internal: false,
1601 package: "some_package".into(),
1602 origin: Origin::Src,
1603 name: "a/b".into(),
1604 types: [(
1605 "Wibble".into(),
1606 TypeConstructor {
1607 type_: Arc::new(Type::Named {
1608 publicity: Publicity::Public,
1609 package: "some_package".into(),
1610 module: "the/module".into(),
1611 name: "Wibble".into(),
1612 args: Vec::new(),
1613 inferred_variant: Some(1),
1614 }),
1615 publicity: Publicity::Public,
1616 origin: Default::default(),
1617 module: "the/module".into(),
1618 parameters: vec![],
1619 deprecation: Deprecation::NotDeprecated,
1620 documentation: None,
1621 opaque: false,
1622 },
1623 )]
1624 .into(),
1625 types_value_constructors: HashMap::new(),
1626 values: HashMap::new(),
1627 accessors: HashMap::new(),
1628 line_numbers: LineNumbers::new(""),
1629 src_path: "some_path".into(),
1630 minimum_required_version: Version::new(0, 1, 0),
1631 type_aliases: HashMap::new(),
1632 documentation: Vec::new(),
1633 };
1634 assert_eq!(roundtrip(&module), module);
1635}