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