Fork of daniellemaywood.uk/gleam — Wasm codegen work
32 kB
1172 lines
1use std::sync::Arc;
2
3use camino::Utf8PathBuf;
4use ecow::EcoString;
5
6use crate::analyse::TargetSupport;
7use crate::build::{ExpressionPosition, Origin, Target};
8use crate::config::PackageConfig;
9use crate::line_numbers::LineNumbers;
10use crate::type_::error::{VariableDeclaration, VariableOrigin, VariableSyntax};
11use crate::type_::expression::{FunctionDefinition, Purity};
12use crate::type_::{Deprecation, PRELUDE_MODULE_NAME, Problems};
13use crate::warning::WarningEmitter;
14use crate::{
15 ast::{SrcSpan, TypedExpr},
16 build::Located,
17 type_::{
18 self, AccessorsMap, EnvironmentArguments, ExprTyper, FieldMap, ModuleValueConstructor,
19 RecordAccessor, Type, ValueConstructor, ValueConstructorVariant,
20 },
21 uid::UniqueIdGenerator,
22 warning::TypeWarningEmitter,
23};
24
25use super::{Publicity, Statement, TypedModule, TypedStatement};
26
27fn compile_module(src: &str) -> TypedModule {
28 use crate::type_::build_prelude;
29 let parsed =
30 crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null())
31 .expect("syntax error");
32 let ast = parsed.module;
33 let ids = UniqueIdGenerator::new();
34 let mut config = PackageConfig::default();
35 config.name = "thepackage".into();
36 let mut modules = im::HashMap::new();
37 // DUPE: preludeinsertion
38 // TODO: Currently we do this here and also in the tests. It would be better
39 // to have one place where we create all this required state for use in each
40 // place.
41 let _ = modules.insert(PRELUDE_MODULE_NAME.into(), build_prelude(&ids));
42 let line_numbers = LineNumbers::new(src);
43 let mut config = PackageConfig::default();
44 config.name = "thepackage".into();
45
46 crate::analyse::ModuleAnalyzerConstructor::<()> {
47 target: Target::Erlang,
48 ids: &ids,
49 origin: Origin::Src,
50 importable_modules: &modules,
51 warnings: &TypeWarningEmitter::null(),
52 direct_dependencies: &std::collections::HashMap::new(),
53 dev_dependencies: &std::collections::HashSet::new(),
54 target_support: TargetSupport::Enforced,
55 package_config: &config,
56 }
57 .infer_module(ast, line_numbers, "".into())
58 .expect("should successfully infer")
59}
60
61fn get_bare_expression(statement: &TypedStatement) -> &TypedExpr {
62 match statement {
63 Statement::Expression(expression) => expression,
64 Statement::Use(_) | Statement::Assignment(_) | Statement::Assert(_) => {
65 panic!("Expected expression, got {statement:?}")
66 }
67 }
68}
69
70fn compile_expression(src: &str) -> TypedStatement {
71 let ast = crate::parse::parse_statement_sequence(src).expect("syntax error");
72
73 let mut modules = im::HashMap::new();
74 let ids = UniqueIdGenerator::new();
75 // DUPE: preludeinsertion
76 // TODO: Currently we do this here and also in the tests. It would be better
77 // to have one place where we create all this required state for use in each
78 // place.
79 let _ = modules.insert(PRELUDE_MODULE_NAME.into(), type_::build_prelude(&ids));
80 let dev_dependencies = std::collections::HashSet::new();
81
82 let mut environment = EnvironmentArguments {
83 ids,
84 current_package: "thepackage".into(),
85 gleam_version: None,
86 current_module: "mymod".into(),
87 target: Target::Erlang,
88 importable_modules: &modules,
89 target_support: TargetSupport::Enforced,
90 current_origin: Origin::Src,
91 dev_dependencies: &dev_dependencies,
92 }
93 .build();
94
95 // Insert a cat record to use in the tests
96 let cat_type = Arc::new(Type::Named {
97 publicity: Publicity::Public,
98 package: "mypackage".into(),
99 module: "mymod".into(),
100 name: "Cat".into(),
101 arguments: vec![],
102 inferred_variant: None,
103 });
104 let variant = ValueConstructorVariant::Record {
105 documentation: Some("wibble".into()),
106 variants_count: 1,
107 name: "Cat".into(),
108 arity: 2,
109 location: SrcSpan { start: 12, end: 15 },
110 field_map: Some(FieldMap {
111 arity: 2,
112 fields: [("name".into(), 0), ("age".into(), 1)].into(),
113 }),
114 module: "mymod".into(),
115 variant_index: 0,
116 };
117 environment.insert_variable(
118 "Cat".into(),
119 variant,
120 type_::fn_(vec![type_::string(), type_::int()], cat_type.clone()),
121 Publicity::Public,
122 Deprecation::NotDeprecated,
123 );
124
125 let accessors = [
126 (
127 "name".into(),
128 RecordAccessor {
129 index: 0,
130 label: "name".into(),
131 type_: type_::string(),
132 documentation: None,
133 },
134 ),
135 (
136 "age".into(),
137 RecordAccessor {
138 index: 1,
139 label: "age".into(),
140 type_: type_::int(),
141 documentation: None,
142 },
143 ),
144 ];
145
146 environment.insert_accessors(
147 "Cat".into(),
148 AccessorsMap {
149 publicity: Publicity::Public,
150 type_: cat_type,
151 shared_accessors: accessors.clone().into(),
152 variant_specific_accessors: vec![accessors.into()],
153 variant_positional_accessors: vec![vec![]],
154 },
155 );
156 let mut problems = Problems::new();
157 ExprTyper::new(
158 &mut environment,
159 FunctionDefinition {
160 has_body: true,
161 has_erlang_external: false,
162 has_javascript_external: false,
163 },
164 &mut problems,
165 )
166 .infer_statements(ast)
167 .first()
168 .clone()
169}
170
171#[test]
172fn find_node_todo() {
173 let statement = compile_expression(r#" todo "#);
174 let expr = get_bare_expression(&statement);
175 assert_eq!(expr.find_node(0), None);
176 assert_eq!(
177 expr.find_node(1),
178 Some(Located::Expression {
179 expression: expr,
180 position: ExpressionPosition::Expression
181 })
182 );
183 assert_eq!(
184 expr.find_node(4),
185 Some(Located::Expression {
186 expression: expr,
187 position: ExpressionPosition::Expression
188 })
189 );
190 assert_eq!(
191 expr.find_node(5),
192 Some(Located::Expression {
193 expression: expr,
194 position: ExpressionPosition::Expression
195 })
196 );
197 assert_eq!(expr.find_node(6), None);
198}
199
200#[test]
201fn find_node_todo_with_string() {
202 let statement = compile_expression(r#" todo as "ok" "#);
203 let expr = get_bare_expression(&statement);
204 let message = TypedExpr::String {
205 location: SrcSpan { start: 9, end: 13 },
206 type_: type_::string(),
207 value: "ok".into(),
208 };
209
210 assert_eq!(expr.find_node(0), None);
211 assert_eq!(
212 expr.find_node(1),
213 Some(Located::Expression {
214 expression: expr,
215 position: ExpressionPosition::Expression
216 })
217 );
218 assert_eq!(
219 expr.find_node(12),
220 Some(Located::Expression {
221 expression: &message,
222 position: ExpressionPosition::Expression
223 })
224 );
225 assert_eq!(
226 expr.find_node(13),
227 Some(Located::Expression {
228 expression: &message,
229 position: ExpressionPosition::Expression
230 })
231 );
232 assert_eq!(expr.find_node(14), None);
233}
234
235#[test]
236fn find_node_string() {
237 let statement = compile_expression(r#" "ok" "#);
238 let expr = get_bare_expression(&statement);
239 assert_eq!(expr.find_node(0), None);
240 assert_eq!(
241 expr.find_node(1),
242 Some(Located::Expression {
243 expression: expr,
244 position: ExpressionPosition::Expression
245 })
246 );
247 assert_eq!(
248 expr.find_node(4),
249 Some(Located::Expression {
250 expression: expr,
251 position: ExpressionPosition::Expression
252 })
253 );
254 assert_eq!(
255 expr.find_node(5),
256 Some(Located::Expression {
257 expression: expr,
258 position: ExpressionPosition::Expression
259 })
260 );
261 assert_eq!(expr.find_node(6), None);
262}
263
264#[test]
265fn find_node_float() {
266 let statement = compile_expression(r#" 1.02 "#);
267 let expr = get_bare_expression(&statement);
268 assert_eq!(expr.find_node(0), None);
269 assert_eq!(
270 expr.find_node(1),
271 Some(Located::Expression {
272 expression: expr,
273 position: ExpressionPosition::Expression
274 })
275 );
276 assert_eq!(
277 expr.find_node(4),
278 Some(Located::Expression {
279 expression: expr,
280 position: ExpressionPosition::Expression
281 })
282 );
283 assert_eq!(
284 expr.find_node(5),
285 Some(Located::Expression {
286 expression: expr,
287 position: ExpressionPosition::Expression
288 })
289 );
290 assert_eq!(expr.find_node(6), None);
291}
292
293#[test]
294fn find_node_int() {
295 let statement = compile_expression(r#" 1302 "#);
296 let expr = get_bare_expression(&statement);
297 assert_eq!(expr.find_node(0), None);
298 assert_eq!(
299 expr.find_node(1),
300 Some(Located::Expression {
301 expression: expr,
302 position: ExpressionPosition::Expression
303 })
304 );
305 assert_eq!(
306 expr.find_node(4),
307 Some(Located::Expression {
308 expression: expr,
309 position: ExpressionPosition::Expression
310 })
311 );
312 assert_eq!(
313 expr.find_node(5),
314 Some(Located::Expression {
315 expression: expr,
316 position: ExpressionPosition::Expression
317 })
318 );
319 assert_eq!(expr.find_node(6), None);
320}
321
322#[test]
323fn find_node_var() {
324 let statement = compile_expression(
325 r#"{let wibble = 1
326wibble}"#,
327 );
328 let expr = get_bare_expression(&statement);
329
330 let int1 = TypedExpr::Int {
331 location: SrcSpan { start: 14, end: 15 },
332 value: "1".into(),
333 int_value: 1.into(),
334 type_: type_::int(),
335 };
336
337 let var = TypedExpr::Var {
338 location: SrcSpan { start: 16, end: 22 },
339 constructor: ValueConstructor {
340 deprecation: Deprecation::NotDeprecated,
341 publicity: Publicity::Private,
342 variant: ValueConstructorVariant::LocalVariable {
343 location: SrcSpan { start: 5, end: 11 },
344 origin: VariableOrigin {
345 syntax: VariableSyntax::Variable("wibble".into()),
346 declaration: VariableDeclaration::LetPattern,
347 },
348 },
349 type_: type_::int(),
350 },
351 name: "wibble".into(),
352 };
353
354 assert_eq!(
355 expr.find_node(15),
356 Some(Located::Expression {
357 expression: &int1,
358 position: ExpressionPosition::Expression
359 })
360 );
361 assert_eq!(
362 expr.find_node(16),
363 Some(Located::Expression {
364 expression: &var,
365 position: ExpressionPosition::Expression
366 })
367 );
368 assert_eq!(
369 expr.find_node(21),
370 Some(Located::Expression {
371 expression: &var,
372 position: ExpressionPosition::Expression
373 })
374 );
375 assert_eq!(
376 expr.find_node(22),
377 Some(Located::Expression {
378 expression: &var,
379 position: ExpressionPosition::Expression
380 })
381 );
382}
383
384#[test]
385fn find_node_sequence() {
386 let block = compile_expression(r#"{ 1 2 3 }"#);
387 assert!(block.find_node(0).is_none());
388 assert!(block.find_node(1).is_none());
389 assert!(block.find_node(2).is_some());
390 assert!(block.find_node(3).is_some());
391 assert!(block.find_node(4).is_some());
392 assert!(block.find_node(5).is_some());
393 assert!(block.find_node(6).is_some());
394 assert!(block.find_node(7).is_some());
395}
396
397#[test]
398fn find_node_list() {
399 let statement = compile_expression(r#"[1, 2, 3]"#);
400 let list = get_bare_expression(&statement);
401
402 let int1 = TypedExpr::Int {
403 location: SrcSpan { start: 1, end: 2 },
404 type_: type_::int(),
405 value: "1".into(),
406 int_value: 1.into(),
407 };
408 let int2 = TypedExpr::Int {
409 location: SrcSpan { start: 4, end: 5 },
410 type_: type_::int(),
411 value: "2".into(),
412 int_value: 2.into(),
413 };
414 let int3 = TypedExpr::Int {
415 location: SrcSpan { start: 7, end: 8 },
416 type_: type_::int(),
417 value: "3".into(),
418 int_value: 3.into(),
419 };
420
421 assert_eq!(
422 list.find_node(0),
423 Some(Located::Expression {
424 expression: list,
425 position: ExpressionPosition::Expression
426 })
427 );
428 assert_eq!(
429 list.find_node(1),
430 Some(Located::Expression {
431 expression: &int1,
432 position: ExpressionPosition::Expression
433 })
434 );
435 assert_eq!(
436 list.find_node(2),
437 Some(Located::Expression {
438 expression: &int1,
439 position: ExpressionPosition::Expression
440 })
441 );
442 assert_eq!(
443 list.find_node(3),
444 Some(Located::Expression {
445 expression: list,
446 position: ExpressionPosition::Expression
447 })
448 );
449 assert_eq!(
450 list.find_node(4),
451 Some(Located::Expression {
452 expression: &int2,
453 position: ExpressionPosition::Expression
454 })
455 );
456 assert_eq!(
457 list.find_node(5),
458 Some(Located::Expression {
459 expression: &int2,
460 position: ExpressionPosition::Expression
461 })
462 );
463 assert_eq!(
464 list.find_node(6),
465 Some(Located::Expression {
466 expression: list,
467 position: ExpressionPosition::Expression
468 })
469 );
470 assert_eq!(
471 list.find_node(7),
472 Some(Located::Expression {
473 expression: &int3,
474 position: ExpressionPosition::Expression
475 })
476 );
477 assert_eq!(
478 list.find_node(8),
479 Some(Located::Expression {
480 expression: &int3,
481 position: ExpressionPosition::Expression
482 })
483 );
484 assert_eq!(
485 list.find_node(9),
486 Some(Located::Expression {
487 expression: list,
488 position: ExpressionPosition::Expression
489 })
490 );
491}
492
493#[test]
494fn find_node_tuple() {
495 let statement = compile_expression(r#"#(1, 2, 3)"#);
496 let tuple = get_bare_expression(&statement);
497
498 let int1 = TypedExpr::Int {
499 location: SrcSpan { start: 2, end: 3 },
500 type_: type_::int(),
501 value: "1".into(),
502 int_value: 1.into(),
503 };
504 let int2 = TypedExpr::Int {
505 location: SrcSpan { start: 5, end: 6 },
506 type_: type_::int(),
507 value: "2".into(),
508 int_value: 2.into(),
509 };
510 let int3 = TypedExpr::Int {
511 location: SrcSpan { start: 8, end: 9 },
512 type_: type_::int(),
513 value: "3".into(),
514 int_value: 3.into(),
515 };
516
517 assert_eq!(
518 tuple.find_node(0),
519 Some(Located::Expression {
520 expression: tuple,
521 position: ExpressionPosition::Expression
522 })
523 );
524 assert_eq!(
525 tuple.find_node(1),
526 Some(Located::Expression {
527 expression: tuple,
528 position: ExpressionPosition::Expression
529 })
530 );
531 assert_eq!(
532 tuple.find_node(2),
533 Some(Located::Expression {
534 expression: &int1,
535 position: ExpressionPosition::Expression
536 })
537 );
538 assert_eq!(
539 tuple.find_node(3),
540 Some(Located::Expression {
541 expression: &int1,
542 position: ExpressionPosition::Expression
543 })
544 );
545 assert_eq!(
546 tuple.find_node(4),
547 Some(Located::Expression {
548 expression: tuple,
549 position: ExpressionPosition::Expression
550 })
551 );
552 assert_eq!(
553 tuple.find_node(5),
554 Some(Located::Expression {
555 expression: &int2,
556 position: ExpressionPosition::Expression
557 })
558 );
559 assert_eq!(
560 tuple.find_node(6),
561 Some(Located::Expression {
562 expression: &int2,
563 position: ExpressionPosition::Expression
564 })
565 );
566 assert_eq!(
567 tuple.find_node(7),
568 Some(Located::Expression {
569 expression: tuple,
570 position: ExpressionPosition::Expression
571 })
572 );
573 assert_eq!(
574 tuple.find_node(8),
575 Some(Located::Expression {
576 expression: &int3,
577 position: ExpressionPosition::Expression
578 })
579 );
580 assert_eq!(
581 tuple.find_node(9),
582 Some(Located::Expression {
583 expression: &int3,
584 position: ExpressionPosition::Expression
585 })
586 );
587 assert_eq!(
588 tuple.find_node(10),
589 Some(Located::Expression {
590 expression: tuple,
591 position: ExpressionPosition::Expression
592 })
593 );
594}
595
596#[test]
597fn find_node_binop() {
598 let statement = compile_expression(r#"1 + 2"#);
599 let expr = get_bare_expression(&statement);
600 assert!(expr.find_node(0).is_some());
601 assert!(expr.find_node(1).is_some());
602 assert!(expr.find_node(2).is_none());
603 assert!(expr.find_node(3).is_none());
604 assert!(expr.find_node(4).is_some());
605 assert!(expr.find_node(5).is_some());
606}
607
608#[test]
609fn find_node_tuple_index() {
610 let statement = compile_expression(r#"#(1).0"#);
611 let expr = get_bare_expression(&statement);
612
613 let int = TypedExpr::Int {
614 location: SrcSpan { start: 2, end: 3 },
615 value: "1".into(),
616 int_value: 1.into(),
617 type_: type_::int(),
618 };
619
620 assert_eq!(
621 expr.find_node(2),
622 Some(Located::Expression {
623 expression: &int,
624 position: ExpressionPosition::Expression
625 })
626 );
627 assert_eq!(
628 expr.find_node(5),
629 Some(Located::Expression {
630 expression: expr,
631 position: ExpressionPosition::Expression
632 })
633 );
634 assert_eq!(
635 expr.find_node(6),
636 Some(Located::Expression {
637 expression: expr,
638 position: ExpressionPosition::Expression
639 })
640 );
641}
642
643#[test]
644fn find_node_module_select() {
645 let expr = TypedExpr::ModuleSelect {
646 location: SrcSpan { start: 1, end: 4 },
647 field_start: 2,
648 type_: type_::int(),
649 label: "label".into(),
650 module_name: "name".into(),
651 module_alias: "alias".into(),
652 constructor: ModuleValueConstructor::Fn {
653 module: "module".into(),
654 name: "function".into(),
655 external_erlang: None,
656 external_javascript: None,
657 location: SrcSpan { start: 1, end: 55 },
658 documentation: None,
659 field_map: None,
660 purity: Purity::Pure,
661 },
662 };
663
664 assert_eq!(expr.find_node(0), None);
665 assert_eq!(
666 expr.find_node(1),
667 Some(Located::ModuleName {
668 location: SrcSpan::new(1, 1),
669 name: &"name".into(),
670 layer: super::Layer::Value
671 })
672 );
673 assert_eq!(
674 expr.find_node(2),
675 Some(Located::Expression {
676 expression: &expr,
677 position: ExpressionPosition::Expression
678 })
679 );
680 assert_eq!(
681 expr.find_node(3),
682 Some(Located::Expression {
683 expression: &expr,
684 position: ExpressionPosition::Expression
685 })
686 );
687}
688
689#[test]
690fn find_node_fn() {
691 let statement = compile_expression("fn() { 1 }");
692 let expr = get_bare_expression(&statement);
693
694 let int = TypedExpr::Int {
695 location: SrcSpan { start: 7, end: 8 },
696 value: "1".into(),
697 int_value: 1.into(),
698 type_: type_::int(),
699 };
700
701 assert_eq!(
702 expr.find_node(0),
703 Some(Located::Expression {
704 expression: expr,
705 position: ExpressionPosition::Expression
706 })
707 );
708 assert_eq!(
709 expr.find_node(6),
710 Some(Located::Expression {
711 expression: expr,
712 position: ExpressionPosition::Expression
713 })
714 );
715 assert_eq!(
716 expr.find_node(7),
717 Some(Located::Expression {
718 expression: &int,
719 position: ExpressionPosition::Expression
720 })
721 );
722 assert_eq!(
723 expr.find_node(8),
724 Some(Located::Expression {
725 expression: &int,
726 position: ExpressionPosition::Expression
727 })
728 );
729 assert_eq!(
730 expr.find_node(9),
731 Some(Located::Expression {
732 expression: expr,
733 position: ExpressionPosition::Expression
734 })
735 );
736 assert_eq!(
737 expr.find_node(10),
738 Some(Located::Expression {
739 expression: expr,
740 position: ExpressionPosition::Expression
741 })
742 );
743}
744
745#[test]
746fn find_node_call() {
747 let statement = compile_expression("fn(_, _) { 1 }(1, 2)");
748 let expr = get_bare_expression(&statement);
749
750 let return_ = TypedExpr::Int {
751 location: SrcSpan { start: 11, end: 12 },
752 value: "1".into(),
753 int_value: 1.into(),
754 type_: type_::int(),
755 };
756
757 let arg1 = TypedExpr::Int {
758 location: SrcSpan { start: 15, end: 16 },
759 value: "1".into(),
760 int_value: 1.into(),
761 type_: type_::int(),
762 };
763
764 let arg2 = TypedExpr::Int {
765 location: SrcSpan { start: 18, end: 19 },
766 value: "2".into(),
767 int_value: 2.into(),
768 type_: type_::int(),
769 };
770
771 let TypedExpr::Call {
772 fun: called_function,
773 arguments: function_arguments,
774 ..
775 } = expr
776 else {
777 panic!("Expression was not a function call");
778 };
779
780 assert_eq!(
781 expr.find_node(11),
782 Some(Located::Expression {
783 expression: &return_,
784 position: ExpressionPosition::Expression
785 })
786 );
787 assert_eq!(
788 expr.find_node(15),
789 Some(Located::Expression {
790 expression: &arg1,
791 position: ExpressionPosition::Expression
792 })
793 );
794 assert_eq!(
795 expr.find_node(16),
796 Some(Located::Expression {
797 expression: &arg1,
798 position: ExpressionPosition::ArgumentOrLabel {
799 called_function,
800 function_arguments
801 }
802 })
803 );
804 assert_eq!(
805 expr.find_node(17),
806 Some(Located::Expression {
807 expression: expr,
808 position: ExpressionPosition::Expression
809 })
810 );
811 assert_eq!(
812 expr.find_node(18),
813 Some(Located::Expression {
814 expression: &arg2,
815 position: ExpressionPosition::Expression
816 })
817 );
818 assert_eq!(
819 expr.find_node(19),
820 Some(Located::Expression {
821 expression: &arg2,
822 position: ExpressionPosition::ArgumentOrLabel {
823 called_function,
824 function_arguments
825 }
826 })
827 );
828 assert_eq!(
829 expr.find_node(20),
830 Some(Located::Expression {
831 expression: expr,
832 position: ExpressionPosition::Expression
833 })
834 );
835}
836
837#[test]
838fn find_node_record_access() {
839 let statement = compile_expression(r#"Cat("Nubi", 3).name"#);
840 let access = get_bare_expression(&statement);
841
842 let string = TypedExpr::String {
843 location: SrcSpan { start: 4, end: 10 },
844 value: "Nubi".into(),
845 type_: type_::string(),
846 };
847
848 let int = TypedExpr::Int {
849 location: SrcSpan { start: 12, end: 13 },
850 value: "3".into(),
851 int_value: 3.into(),
852 type_: type_::int(),
853 };
854
855 assert_eq!(
856 access.find_node(4),
857 Some(Located::Expression {
858 expression: &string,
859 position: ExpressionPosition::Expression
860 })
861 );
862 assert_eq!(
863 access.find_node(9),
864 Some(Located::Expression {
865 expression: &string,
866 position: ExpressionPosition::Expression
867 })
868 );
869 assert_eq!(
870 access.find_node(12),
871 Some(Located::Expression {
872 expression: &int,
873 position: ExpressionPosition::Expression
874 })
875 );
876 assert_eq!(
877 access.find_node(15),
878 Some(Located::Expression {
879 expression: access,
880 position: ExpressionPosition::Expression
881 })
882 );
883 assert_eq!(
884 access.find_node(18),
885 Some(Located::Expression {
886 expression: access,
887 position: ExpressionPosition::Expression
888 })
889 );
890 assert_eq!(
891 access.find_node(19),
892 Some(Located::Expression {
893 expression: access,
894 position: ExpressionPosition::Expression
895 })
896 );
897}
898
899#[test]
900fn find_node_record_update() {
901 let statement = compile_expression(r#"Cat(..Cat("Nubi", 3), age: 4)"#);
902 let update = get_bare_expression(&statement);
903
904 let cat = TypedExpr::Var {
905 location: SrcSpan { start: 0, end: 3 },
906 constructor: ValueConstructor {
907 publicity: Publicity::Public,
908 deprecation: Deprecation::NotDeprecated,
909 variant: ValueConstructorVariant::Record {
910 name: "Cat".into(),
911 arity: 2,
912 field_map: Some(FieldMap {
913 arity: 2,
914 fields: [(EcoString::from("age"), 1), (EcoString::from("name"), 0)].into(),
915 }),
916 location: SrcSpan { start: 12, end: 15 },
917 module: "mymod".into(),
918 variants_count: 1,
919 variant_index: 0,
920 documentation: Some("wibble".into()),
921 },
922 type_: type_::fn_(
923 vec![type_::string(), type_::int()],
924 type_::named("mypackage", "mymod", "Cat", Publicity::Public, vec![]),
925 ),
926 },
927 name: "Cat".into(),
928 };
929
930 let int = TypedExpr::Int {
931 location: SrcSpan { start: 27, end: 28 },
932 value: "4".into(),
933 int_value: 4.into(),
934 type_: type_::int(),
935 };
936
937 assert_eq!(
938 update.find_node(0),
939 Some(Located::Expression {
940 expression: &cat,
941 position: ExpressionPosition::Expression
942 })
943 );
944 assert_eq!(
945 update.find_node(3),
946 Some(Located::Expression {
947 expression: &cat,
948 position: ExpressionPosition::Expression
949 })
950 );
951 assert_eq!(
952 update.find_node(27),
953 Some(Located::Expression {
954 expression: &int,
955 position: ExpressionPosition::Expression
956 })
957 );
958 assert_eq!(
959 update.find_node(28),
960 Some(Located::Expression {
961 expression: &int,
962 position: ExpressionPosition::Expression
963 })
964 );
965 assert_eq!(
966 update.find_node(29),
967 Some(Located::Expression {
968 expression: update,
969 position: ExpressionPosition::Expression
970 })
971 );
972}
973
974#[test]
975fn find_node_case() {
976 let statement = compile_expression(
977 r#"
978case 1, 2 {
979 _, _ -> 3
980}
981"#,
982 );
983 let case = get_bare_expression(&statement);
984
985 let int1 = TypedExpr::Int {
986 location: SrcSpan { start: 6, end: 7 },
987 value: "1".into(),
988 int_value: 1.into(),
989 type_: type_::int(),
990 };
991
992 let int2 = TypedExpr::Int {
993 location: SrcSpan { start: 9, end: 10 },
994 value: "2".into(),
995 int_value: 2.into(),
996 type_: type_::int(),
997 };
998
999 let int3 = TypedExpr::Int {
1000 location: SrcSpan { start: 23, end: 24 },
1001 value: "3".into(),
1002 int_value: 3.into(),
1003 type_: type_::int(),
1004 };
1005
1006 assert_eq!(
1007 case.find_node(1),
1008 Some(Located::Expression {
1009 expression: case,
1010 position: ExpressionPosition::Expression
1011 })
1012 );
1013 assert_eq!(
1014 case.find_node(6),
1015 Some(Located::Expression {
1016 expression: &int1,
1017 position: ExpressionPosition::Expression
1018 })
1019 );
1020 assert_eq!(
1021 case.find_node(9),
1022 Some(Located::Expression {
1023 expression: &int2,
1024 position: ExpressionPosition::Expression
1025 })
1026 );
1027 assert_eq!(
1028 case.find_node(23),
1029 Some(Located::Expression {
1030 expression: &int3,
1031 position: ExpressionPosition::Expression
1032 })
1033 );
1034 assert_eq!(
1035 case.find_node(25),
1036 Some(Located::Expression {
1037 expression: case,
1038 position: ExpressionPosition::Expression
1039 })
1040 );
1041 assert_eq!(
1042 case.find_node(26),
1043 Some(Located::Expression {
1044 expression: case,
1045 position: ExpressionPosition::Expression
1046 })
1047 );
1048 assert_eq!(case.find_node(27), None);
1049}
1050
1051#[test]
1052fn find_node_bool() {
1053 let statement = compile_expression(r#"!True"#);
1054 let negate = get_bare_expression(&statement);
1055
1056 let bool = TypedExpr::Var {
1057 location: SrcSpan { start: 1, end: 5 },
1058 constructor: ValueConstructor {
1059 deprecation: Deprecation::NotDeprecated,
1060 publicity: Publicity::Public,
1061 variant: ValueConstructorVariant::Record {
1062 documentation: None,
1063 variants_count: 2,
1064 name: "True".into(),
1065 arity: 0,
1066 field_map: None,
1067 location: SrcSpan { start: 0, end: 0 },
1068 module: PRELUDE_MODULE_NAME.into(),
1069 variant_index: 0,
1070 },
1071 type_: type_::bool_with_variant(Some(true)),
1072 },
1073 name: "True".into(),
1074 };
1075
1076 assert_eq!(
1077 negate.find_node(0),
1078 Some(Located::Expression {
1079 expression: negate,
1080 position: ExpressionPosition::Expression
1081 })
1082 );
1083 assert_eq!(
1084 negate.find_node(1),
1085 Some(Located::Expression {
1086 expression: &bool,
1087 position: ExpressionPosition::Expression
1088 })
1089 );
1090 assert_eq!(
1091 negate.find_node(2),
1092 Some(Located::Expression {
1093 expression: &bool,
1094 position: ExpressionPosition::Expression
1095 })
1096 );
1097 assert_eq!(
1098 negate.find_node(3),
1099 Some(Located::Expression {
1100 expression: &bool,
1101 position: ExpressionPosition::Expression
1102 })
1103 );
1104 assert_eq!(
1105 negate.find_node(4),
1106 Some(Located::Expression {
1107 expression: &bool,
1108 position: ExpressionPosition::Expression
1109 })
1110 );
1111 assert_eq!(
1112 negate.find_node(5),
1113 Some(Located::Expression {
1114 expression: &bool,
1115 position: ExpressionPosition::Expression
1116 })
1117 );
1118}
1119
1120#[test]
1121fn find_node_statement_fn() {
1122 let module = compile_module(
1123 r#"
1124
1125pub fn main() {
1126 Nil
1127}
1128
1129"#,
1130 );
1131
1132 assert!(module.find_node(0).is_none());
1133 assert!(module.find_node(1).is_none());
1134
1135 // The fn
1136 assert!(module.find_node(2).is_some());
1137 assert!(module.find_node(24).is_some());
1138 assert!(module.find_node(25).is_some());
1139 assert!(module.find_node(26).is_none());
1140}
1141
1142#[test]
1143fn find_node_statement_import() {
1144 let module = compile_module(
1145 r#"
1146import gleam
1147"#,
1148 );
1149
1150 assert!(module.find_node(0).is_none());
1151
1152 // The import
1153 assert!(module.find_node(1).is_some());
1154 assert!(module.find_node(12).is_some());
1155 assert!(module.find_node(13).is_some());
1156 assert!(module.find_node(14).is_none());
1157}
1158
1159#[test]
1160fn find_node_use() {
1161 let use_ = compile_expression(
1162 r#"
1163use x <- fn(f) { f(1) }
1164124
1165"#,
1166 );
1167
1168 assert!(use_.find_node(0).is_none());
1169 assert!(use_.find_node(1).is_some()); // The use
1170 assert!(use_.find_node(23).is_some());
1171 assert!(use_.find_node(26).is_some()); // The int
1172}