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