Fork of daniellemaywood.uk/gleam — Wasm codegen work
35 kB
1932 lines
1use crate::ast::SrcSpan;
2use crate::parse::error::{
3 InvalidUnicodeEscapeError, LexicalError, LexicalErrorType, ParseError, ParseErrorType,
4};
5use crate::parse::lexer::make_tokenizer;
6use crate::parse::token::Token;
7use crate::warning::WarningEmitter;
8use camino::Utf8PathBuf;
9
10use ecow::EcoString;
11use itertools::Itertools;
12use pretty_assertions::assert_eq;
13
14macro_rules! assert_error {
15 ($src:expr, $error:expr $(,)?) => {
16 let result = crate::parse::parse_statement_sequence($src).expect_err("should not parse");
17 assert_eq!(($src, $error), ($src, result),);
18 };
19 ($src:expr) => {
20 let error = $crate::parse::tests::expect_error($src);
21 let output = format!("----- SOURCE CODE\n{}\n\n----- ERROR\n{}", $src, error);
22 insta::assert_snapshot!(insta::internals::AutoName, output, $src);
23 };
24}
25
26macro_rules! assert_module_error {
27 ($src:expr) => {
28 let error = $crate::parse::tests::expect_module_error($src);
29 let output = format!("----- SOURCE CODE\n{}\n\n----- ERROR\n{}", $src, error);
30 insta::assert_snapshot!(insta::internals::AutoName, output, $src);
31 };
32}
33
34macro_rules! assert_parse_module {
35 ($src:expr) => {
36 let result = crate::parse::parse_module(
37 camino::Utf8PathBuf::from("test/path"),
38 $src,
39 &crate::warning::WarningEmitter::null(),
40 )
41 .expect("should parse");
42 insta::assert_snapshot!(insta::internals::AutoName, &format!("{:#?}", result), $src);
43 };
44}
45
46macro_rules! assert_parse {
47 ($src:expr) => {
48 let result = crate::parse::parse_statement_sequence($src).expect("should parse");
49 insta::assert_snapshot!(insta::internals::AutoName, &format!("{:#?}", result), $src);
50 };
51}
52
53pub fn expect_module_error(src: &str) -> String {
54 let result =
55 crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null())
56 .expect_err("should not parse");
57 let error = crate::error::Error::Parse {
58 src: src.into(),
59 path: Utf8PathBuf::from("/src/parse/error.gleam"),
60 error: Box::new(result),
61 };
62 error.pretty_string()
63}
64
65pub fn expect_error(src: &str) -> String {
66 let result = crate::parse::parse_statement_sequence(src).expect_err("should not parse");
67 let error = crate::error::Error::Parse {
68 src: src.into(),
69 path: Utf8PathBuf::from("/src/parse/error.gleam"),
70 error: Box::new(result),
71 };
72 error.pretty_string()
73}
74
75#[test]
76fn int_tests() {
77 // bad binary digit
78 assert_error!(
79 "0b012",
80 ParseError {
81 error: ParseErrorType::LexError {
82 error: LexicalError {
83 error: LexicalErrorType::DigitOutOfRadix,
84 location: SrcSpan { start: 4, end: 4 },
85 }
86 },
87 location: SrcSpan { start: 4, end: 4 },
88 }
89 );
90 // bad octal digit
91 assert_error!(
92 "0o12345678",
93 ParseError {
94 error: ParseErrorType::LexError {
95 error: LexicalError {
96 error: LexicalErrorType::DigitOutOfRadix,
97 location: SrcSpan { start: 9, end: 9 },
98 }
99 },
100 location: SrcSpan { start: 9, end: 9 },
101 }
102 );
103 // no int value
104 assert_error!(
105 "0x",
106 ParseError {
107 error: ParseErrorType::LexError {
108 error: LexicalError {
109 error: LexicalErrorType::RadixIntNoValue,
110 location: SrcSpan { start: 1, end: 1 },
111 }
112 },
113 location: SrcSpan { start: 1, end: 1 },
114 }
115 );
116 // trailing underscore
117 assert_error!(
118 "1_000_",
119 ParseError {
120 error: ParseErrorType::LexError {
121 error: LexicalError {
122 error: LexicalErrorType::NumTrailingUnderscore,
123 location: SrcSpan { start: 5, end: 5 },
124 }
125 },
126 location: SrcSpan { start: 5, end: 5 },
127 }
128 );
129}
130
131#[test]
132fn string_bad_character_escape() {
133 assert_error!(
134 r#""\g""#,
135 ParseError {
136 error: ParseErrorType::LexError {
137 error: LexicalError {
138 error: LexicalErrorType::BadStringEscape,
139 location: SrcSpan { start: 1, end: 2 },
140 }
141 },
142 location: SrcSpan { start: 1, end: 2 },
143 }
144 );
145}
146
147#[test]
148fn string_bad_character_escape_leading_backslash() {
149 assert_error!(
150 r#""\\\g""#,
151 ParseError {
152 error: ParseErrorType::LexError {
153 error: LexicalError {
154 error: LexicalErrorType::BadStringEscape,
155 location: SrcSpan { start: 3, end: 4 },
156 }
157 },
158 location: SrcSpan { start: 3, end: 4 },
159 }
160 );
161}
162
163#[test]
164fn string_freestanding_unicode_escape_sequence() {
165 assert_error!(
166 r#""\u""#,
167 ParseError {
168 error: ParseErrorType::LexError {
169 error: LexicalError {
170 error: LexicalErrorType::InvalidUnicodeEscape(
171 InvalidUnicodeEscapeError::MissingOpeningBrace,
172 ),
173 location: SrcSpan { start: 2, end: 3 },
174 }
175 },
176 location: SrcSpan { start: 2, end: 3 },
177 }
178 );
179}
180
181#[test]
182fn string_unicode_escape_sequence_no_braces() {
183 assert_error!(
184 r#""\u65""#,
185 ParseError {
186 error: ParseErrorType::LexError {
187 error: LexicalError {
188 error: LexicalErrorType::InvalidUnicodeEscape(
189 InvalidUnicodeEscapeError::MissingOpeningBrace,
190 ),
191 location: SrcSpan { start: 2, end: 3 },
192 }
193 },
194 location: SrcSpan { start: 2, end: 3 },
195 }
196 );
197}
198
199#[test]
200fn string_unicode_escape_sequence_invalid_hex() {
201 assert_error!(
202 r#""\u{z}""#,
203 ParseError {
204 error: ParseErrorType::LexError {
205 error: LexicalError {
206 error: LexicalErrorType::InvalidUnicodeEscape(
207 InvalidUnicodeEscapeError::ExpectedHexDigitOrCloseBrace,
208 ),
209 location: SrcSpan { start: 4, end: 5 },
210 }
211 },
212 location: SrcSpan { start: 4, end: 5 },
213 }
214 );
215}
216
217#[test]
218fn string_unclosed_unicode_escape_sequence() {
219 assert_error!(
220 r#""\u{039a""#,
221 ParseError {
222 error: ParseErrorType::LexError {
223 error: LexicalError {
224 error: LexicalErrorType::InvalidUnicodeEscape(
225 InvalidUnicodeEscapeError::ExpectedHexDigitOrCloseBrace,
226 ),
227 location: SrcSpan { start: 8, end: 9 },
228 }
229 },
230 location: SrcSpan { start: 8, end: 9 },
231 }
232 );
233}
234
235#[test]
236fn string_empty_unicode_escape_sequence() {
237 assert_error!(
238 r#""\u{}""#,
239 ParseError {
240 error: ParseErrorType::LexError {
241 error: LexicalError {
242 error: LexicalErrorType::InvalidUnicodeEscape(
243 InvalidUnicodeEscapeError::InvalidNumberOfHexDigits,
244 ),
245 location: SrcSpan { start: 1, end: 5 },
246 }
247 },
248 location: SrcSpan { start: 1, end: 5 },
249 }
250 );
251}
252
253#[test]
254fn string_overlong_unicode_escape_sequence() {
255 assert_error!(
256 r#""\u{0011f601}""#,
257 ParseError {
258 error: ParseErrorType::LexError {
259 error: LexicalError {
260 error: LexicalErrorType::InvalidUnicodeEscape(
261 InvalidUnicodeEscapeError::InvalidNumberOfHexDigits,
262 ),
263 location: SrcSpan { start: 1, end: 13 },
264 }
265 },
266 location: SrcSpan { start: 1, end: 13 },
267 }
268 );
269}
270
271#[test]
272fn string_invalid_unicode_escape_sequence() {
273 assert_error!(
274 r#""\u{110000}""#,
275 ParseError {
276 error: ParseErrorType::LexError {
277 error: LexicalError {
278 error: LexicalErrorType::InvalidUnicodeEscape(
279 InvalidUnicodeEscapeError::InvalidCodepoint,
280 ),
281 location: SrcSpan { start: 1, end: 11 },
282 }
283 },
284 location: SrcSpan { start: 1, end: 11 },
285 }
286 );
287}
288
289#[test]
290fn bit_array() {
291 // non int value in bit array unit option
292 assert_error!(
293 "let x = <<1:unit(0)>> x",
294 ParseError {
295 error: ParseErrorType::InvalidBitArrayUnit,
296 location: SrcSpan { start: 17, end: 18 }
297 }
298 );
299}
300
301#[test]
302fn bit_array1() {
303 assert_error!(
304 "let x = <<1:unit(257)>> x",
305 ParseError {
306 error: ParseErrorType::InvalidBitArrayUnit,
307 location: SrcSpan { start: 17, end: 20 }
308 }
309 );
310}
311
312#[test]
313fn bit_array2() {
314 // patterns cannot be nested
315 assert_error!(
316 "case <<>> { <<<<1>>:bits>> -> 1 }",
317 ParseError {
318 error: ParseErrorType::NestedBitArrayPattern,
319 location: SrcSpan { start: 14, end: 19 }
320 }
321 );
322}
323
324// https://github.com/gleam-lang/gleam/issues/3125
325#[test]
326fn triple_equals() {
327 assert_error!(
328 "let wobble:Int = 32
329 wobble === 42",
330 ParseError {
331 error: ParseErrorType::LexError {
332 error: LexicalError {
333 error: LexicalErrorType::InvalidTripleEqual,
334 location: SrcSpan { start: 35, end: 38 },
335 }
336 },
337 location: SrcSpan { start: 35, end: 38 },
338 }
339 );
340}
341
342#[test]
343fn triple_equals_with_whitespace() {
344 assert_error!(
345 "let wobble:Int = 32
346 wobble == = 42",
347 ParseError {
348 error: ParseErrorType::OpNakedRight,
349 location: SrcSpan { start: 35, end: 37 },
350 }
351 );
352}
353
354// https://github.com/gleam-lang/gleam/issues/1231
355#[test]
356fn pointless_spread() {
357 assert_error!(
358 "let xs = [] [..xs]",
359 ParseError {
360 error: ParseErrorType::ListSpreadWithoutElements,
361 location: SrcSpan { start: 12, end: 18 },
362 }
363 );
364}
365
366// https://github.com/gleam-lang/gleam/issues/1358
367#[test]
368fn lowcase_bool_in_pattern() {
369 assert_error!(
370 "case 42 > 42 { true -> 1; false -> 2; }",
371 ParseError {
372 error: ParseErrorType::LowcaseBooleanPattern,
373 location: SrcSpan { start: 15, end: 19 },
374 }
375 );
376}
377
378// https://github.com/gleam-lang/gleam/issues/1613
379#[test]
380fn anonymous_function_labeled_arguments() {
381 assert_error!(
382 "let anon_subtract = fn (minuend a: Int, subtrahend b: Int) -> Int {
383 a - b
384}",
385 ParseError {
386 location: SrcSpan { start: 24, end: 31 },
387 error: ParseErrorType::UnexpectedLabel
388 }
389 );
390}
391
392#[test]
393fn no_let_binding() {
394 assert_error!(
395 "wibble = 32",
396 ParseError {
397 location: SrcSpan { start: 7, end: 8 },
398 error: ParseErrorType::NoLetBinding
399 }
400 );
401}
402
403#[test]
404fn no_let_binding1() {
405 assert_error!(
406 "wibble:Int = 32",
407 ParseError {
408 location: SrcSpan { start: 6, end: 7 },
409 error: ParseErrorType::NoLetBinding
410 }
411 );
412}
413
414#[test]
415fn no_let_binding2() {
416 assert_error!(
417 "let wobble:Int = 32
418 wobble = 42",
419 ParseError {
420 location: SrcSpan { start: 35, end: 36 },
421 error: ParseErrorType::NoLetBinding
422 }
423 );
424}
425
426#[test]
427fn no_let_binding3() {
428 assert_error!(
429 "[x] = [2]",
430 ParseError {
431 location: SrcSpan { start: 4, end: 5 },
432 error: ParseErrorType::NoLetBinding
433 }
434 );
435}
436
437#[test]
438fn with_let_binding3() {
439 // The same with `let assert` must parse:
440 assert_parse!("let assert [x] = [2]");
441}
442
443#[test]
444fn with_let_binding3_and_annotation() {
445 assert_parse!("let assert [x]: List(Int) = [2]");
446}
447
448#[test]
449fn no_eq_after_binding() {
450 assert_error!(
451 "let wibble",
452 ParseError {
453 location: SrcSpan { start: 4, end: 10 },
454 error: ParseErrorType::ExpectedEqual
455 }
456 );
457}
458
459#[test]
460fn no_eq_after_binding1() {
461 assert_error!(
462 "let wibble
463 wibble = 4",
464 ParseError {
465 location: SrcSpan { start: 4, end: 10 },
466 error: ParseErrorType::ExpectedEqual
467 }
468 );
469}
470
471#[test]
472fn echo_followed_by_expression_ends_where_expression_ends() {
473 assert_parse!("echo wibble");
474}
475
476#[test]
477fn echo_with_simple_expression_1() {
478 assert_parse!("echo wibble as message");
479}
480
481#[test]
482fn echo_with_simple_expression_2() {
483 assert_parse!("echo wibble as \"message\"");
484}
485
486#[test]
487fn echo_with_complex_expression() {
488 assert_parse!("echo wibble as { this <> complex }");
489}
490
491#[test]
492fn echo_with_no_expressions_after_it() {
493 assert_parse!("echo");
494}
495
496#[test]
497fn echo_with_no_expressions_after_it_but_a_message() {
498 assert_parse!("echo as message");
499}
500
501#[test]
502fn echo_with_block() {
503 assert_parse!("echo { 1 + 1 }");
504}
505
506#[test]
507fn echo_has_lower_precedence_than_binop() {
508 assert_parse!("echo 1 + 1");
509}
510
511#[test]
512fn echo_in_a_pipeline() {
513 assert_parse!("[] |> echo |> wibble");
514}
515
516#[test]
517fn echo_has_lower_precedence_than_pipeline() {
518 assert_parse!("echo wibble |> wobble |> woo");
519}
520
521#[test]
522fn echo_cannot_have_an_expression_in_a_pipeline() {
523 // So this is actually two pipelines!
524 assert_parse!("[] |> echo fun |> wibble");
525}
526
527#[test]
528fn panic_with_echo() {
529 assert_parse!("panic as echo \"string\"");
530}
531
532#[test]
533fn panic_with_echo_and_message() {
534 assert_parse!("panic as echo wibble as message");
535}
536
537#[test]
538fn echo_with_panic() {
539 assert_parse!("echo panic as \"a\"");
540}
541
542#[test]
543fn echo_with_panic_and_message() {
544 assert_parse!("echo panic as \"a\"");
545}
546
547#[test]
548fn echo_with_panic_and_messages() {
549 assert_parse!("echo panic as \"a\" as \"b\"");
550}
551
552#[test]
553fn echo_with_assert_and_message_1() {
554 assert_parse!("assert 1 == echo 2 as this_belongs_to_echo");
555}
556
557#[test]
558fn echo_with_assert_and_message_2() {
559 assert_parse!("assert echo True as this_belongs_to_echo");
560}
561
562#[test]
563fn echo_with_assert_and_messages_1() {
564 assert_parse!("assert 1 == echo 2 as this_belongs_to_echo as this_belongs_to_assert");
565}
566
567#[test]
568fn echo_with_assert_and_messages_2() {
569 assert_parse!("assert echo True as this_belongs_to_echo as this_belongs_to_assert");
570}
571
572#[test]
573fn echo_with_assert_and_messages_3() {
574 assert_parse!("assert echo 1 == 2 as this_belongs_to_echo as this_belongs_to_assert");
575}
576
577#[test]
578fn echo_with_let_assert_and_message() {
579 assert_parse!("let assert 1 = echo 2 as this_belongs_to_echo");
580}
581
582#[test]
583fn echo_with_let_assert_and_messages() {
584 assert_parse!("let assert 1 = echo 1 as this_belongs_to_echo as this_belongs_to_assert");
585}
586
587#[test]
588fn repeated_echos() {
589 assert_parse!("echo echo echo 1");
590}
591
592#[test]
593fn echo_at_start_of_pipeline_wraps_the_whole_thing() {
594 assert_parse!("echo 1 |> wibble |> wobble");
595}
596
597#[test]
598fn no_let_binding_snapshot_1() {
599 assert_error!("wibble = 4");
600}
601
602#[test]
603fn no_let_binding_snapshot_2() {
604 assert_error!("wibble:Int = 4");
605}
606
607#[test]
608fn no_let_binding_snapshot_3() {
609 assert_error!(
610 "let wobble:Int = 32
611 wobble = 42"
612 );
613}
614
615#[test]
616fn no_eq_after_binding_snapshot_1() {
617 assert_error!("let wibble");
618}
619#[test]
620fn no_eq_after_binding_snapshot_2() {
621 assert_error!(
622 "let wibble
623 wibble = 4"
624 );
625}
626
627#[test]
628fn discard_left_hand_side_of_concat_pattern() {
629 assert_error!(
630 r#"
631 case "" {
632 _ <> rest -> rest
633 }
634 "#
635 );
636}
637
638#[test]
639fn assign_left_hand_side_of_concat_pattern() {
640 assert_error!(
641 r#"
642 case "" {
643 first <> rest -> rest
644 }
645 "#
646 );
647}
648
649// https://github.com/gleam-lang/gleam/issues/1890
650#[test]
651fn valueless_list_spread_expression() {
652 assert_error!(r#"let x = [1, 2, 3, ..]"#);
653}
654
655// https://github.com/gleam-lang/gleam/issues/2035
656#[test]
657fn semicolons() {
658 assert_error!(r#"{ 2 + 3; - -5; }"#);
659}
660
661#[test]
662fn bare_expression() {
663 assert_parse!(r#"1"#);
664}
665
666// https://github.com/gleam-lang/gleam/issues/1991
667#[test]
668fn block_of_one() {
669 assert_parse!(r#"{ 1 }"#);
670}
671
672// https://github.com/gleam-lang/gleam/issues/1991
673#[test]
674fn block_of_two() {
675 assert_parse!(r#"{ 1 2 }"#);
676}
677
678// https://github.com/gleam-lang/gleam/issues/1991
679#[test]
680fn nested_block() {
681 assert_parse!(r#"{ 1 { 1.0 2.0 } 3 }"#);
682}
683
684// https://github.com/gleam-lang/gleam/issues/1831
685#[test]
686fn argument_scope() {
687 assert_error!(
688 "
6891 + let a = 5
690a
691"
692 );
693}
694
695#[test]
696fn multiple_external_for_same_project_erlang() {
697 assert_module_error!(
698 r#"
699@external(erlang, "one", "two")
700@external(erlang, "three", "four")
701pub fn one(x: Int) -> Int {
702 todo
703}
704"#
705 );
706}
707
708#[test]
709fn multiple_external_for_same_project_javascript() {
710 assert_module_error!(
711 r#"
712@external(javascript, "one", "two")
713@external(javascript, "three", "four")
714pub fn one(x: Int) -> Int {
715 todo
716}
717"#
718 );
719}
720
721#[test]
722fn unknown_external_target() {
723 assert_module_error!(
724 r#"
725@external(erl, "one", "two")
726pub fn one(x: Int) -> Int {
727 todo
728}"#
729 );
730}
731
732#[test]
733fn unknown_target() {
734 assert_module_error!(
735 r#"
736@target(abc)
737pub fn one() {}"#
738 );
739}
740
741#[test]
742fn missing_target() {
743 assert_module_error!(
744 r#"
745@target()
746pub fn one() {}"#
747 );
748}
749
750#[test]
751fn missing_target_and_bracket() {
752 assert_module_error!(
753 r#"
754@target(
755pub fn one() {}"#
756 );
757}
758
759#[test]
760fn unknown_attribute() {
761 assert_module_error!(
762 r#"@go_faster()
763pub fn main() { 1 }"#
764 );
765}
766
767#[test]
768fn incomplete_function() {
769 assert_error!("fn()");
770}
771
772#[test]
773fn multiple_deprecation_attributes() {
774 assert_module_error!(
775 r#"
776@deprecated("1")
777@deprecated("2")
778pub fn main() -> Nil {
779 Nil
780}
781"#
782 );
783}
784
785#[test]
786fn deprecation_without_message() {
787 assert_module_error!(
788 r#"
789@deprecated()
790pub fn main() -> Nil {
791 Nil
792}
793"#
794 );
795}
796
797#[test]
798fn multiple_internal_attributes() {
799 assert_module_error!(
800 r#"
801@internal
802@internal
803pub fn main() -> Nil {
804 Nil
805}
806"#
807 );
808}
809
810#[test]
811fn attributes_with_no_definition() {
812 assert_module_error!(
813 r#"
814@deprecated("1")
815@target(erlang)
816"#
817 );
818}
819
820#[test]
821fn external_attribute_with_non_fn_definition() {
822 assert_module_error!(
823 r#"
824@external(erlang, "module", "fun")
825pub type Fun
826"#
827 );
828}
829
830#[test]
831fn attributes_with_improper_definition() {
832 assert_module_error!(
833 r#"
834@deprecated("1")
835@external(erlang, "module", "fun")
836"#
837 );
838}
839
840#[test]
841fn const_with_function_call() {
842 assert_module_error!(
843 r#"
844pub fn wibble() { 123 }
845const wib: Int = wibble()
846"#
847 );
848}
849
850#[test]
851fn const_with_function_call_with_args() {
852 assert_module_error!(
853 r#"
854pub fn wibble() { 123 }
855const wib: Int = wibble(1, "wobble")
856"#
857 );
858}
859
860#[test]
861fn import_type() {
862 assert_parse_module!(r#"import wibble.{type Wobble, Wobble, type Wabble}"#);
863}
864
865#[test]
866fn reserved_auto() {
867 assert_module_error!(r#"const auto = 1"#);
868}
869
870#[test]
871fn reserved_delegate() {
872 assert_module_error!(r#"const delegate = 1"#);
873}
874
875#[test]
876fn reserved_derive() {
877 assert_module_error!(r#"const derive = 1"#);
878}
879
880#[test]
881fn reserved_else() {
882 assert_module_error!(r#"const else = 1"#);
883}
884
885#[test]
886fn reserved_implement() {
887 assert_module_error!(r#"const implement = 1"#);
888}
889
890#[test]
891fn reserved_macro() {
892 assert_module_error!(r#"const macro = 1"#);
893}
894
895#[test]
896fn reserved_test() {
897 assert_module_error!(r#"const test = 1"#);
898}
899
900#[test]
901fn reserved_echo() {
902 assert_module_error!(r#"const echo = 1"#);
903}
904
905#[test]
906fn capture_with_name() {
907 assert_module_error!(
908 r#"
909pub fn main() {
910 add(_name, 1)
911}
912
913fn add(x, y) {
914 x + y
915}
916"#
917 );
918}
919
920#[test]
921fn list_spread_with_no_tail_in_the_middle_of_a_list() {
922 assert_module_error!(
923 r#"
924pub fn main() -> Nil {
925 let xs = [1, 2, 3]
926 [1, 2, .., 3 + 3, 4]
927}
928"#
929 );
930}
931
932#[test]
933fn list_spread_followed_by_extra_items() {
934 assert_module_error!(
935 r#"
936pub fn main() -> Nil {
937 let xs = [1, 2, 3]
938 [1, 2, ..xs, 3 + 3, 4]
939}
940"#
941 );
942}
943
944#[test]
945fn list_spread_followed_by_extra_item_and_another_spread() {
946 assert_module_error!(
947 r#"
948pub fn main() -> Nil {
949 let xs = [1, 2, 3]
950 let ys = [5, 6, 7]
951 [..xs, 4, ..ys]
952}
953"#
954 );
955}
956
957#[test]
958fn list_spread_followed_by_other_spread() {
959 assert_module_error!(
960 r#"
961pub fn main() -> Nil {
962 let xs = [1, 2, 3]
963 let ys = [5, 6, 7]
964 [1, ..xs, ..ys]
965}
966"#
967 );
968}
969
970#[test]
971fn list_spread_as_first_item_followed_by_other_items() {
972 assert_module_error!(
973 r#"
974pub fn main() -> Nil {
975 let xs = [1, 2, 3]
976 [..xs, 3 + 3, 4]
977}
978"#
979 );
980}
981
982// Tests for nested tuples and structs in tuples
983// https://github.com/gleam-lang/gleam/issues/1980
984
985#[test]
986fn nested_tuples() {
987 assert_parse!(
988 r#"
989let tup = #(#(5, 6))
990{tup.0}.1
991"#
992 );
993}
994
995#[test]
996fn nested_tuples_no_block() {
997 assert_parse!(
998 r#"
999let tup = #(#(5, 6))
1000tup.0.1
1001"#
1002 );
1003}
1004
1005#[test]
1006fn deeply_nested_tuples() {
1007 assert_parse!(
1008 r#"
1009let tup = #(#(#(#(4))))
1010{{{tup.0}.0}.0}.0
1011"#
1012 );
1013}
1014
1015#[test]
1016fn deeply_nested_tuples_no_block() {
1017 assert_parse!(
1018 r#"
1019let tup = #(#(#(#(4))))
1020tup.0.0.0.0
1021"#
1022 );
1023}
1024
1025#[test]
1026fn inner_single_quote_parses() {
1027 assert_parse!(
1028 r#"
1029let a = "inner 'quotes'"
1030"#
1031 );
1032}
1033
1034#[test]
1035fn string_single_char_suggestion() {
1036 assert_module_error!(
1037 "
1038 pub fn main() {
1039 let a = 'example'
1040 }
1041 "
1042 );
1043}
1044
1045#[test]
1046fn private_internal_const() {
1047 assert_module_error!(
1048 "
1049@internal
1050const wibble = 1
1051"
1052 );
1053}
1054
1055#[test]
1056fn private_internal_type_alias() {
1057 assert_module_error!(
1058 "
1059@internal
1060type Alias = Int
1061"
1062 );
1063}
1064
1065#[test]
1066fn private_internal_function() {
1067 assert_module_error!(
1068 "
1069@internal
1070fn wibble() { todo }
1071"
1072 );
1073}
1074
1075#[test]
1076fn private_internal_type() {
1077 assert_module_error!(
1078 "
1079@internal
1080type Wibble {
1081 Wibble
1082}
1083"
1084 );
1085}
1086
1087#[test]
1088fn wrong_record_access_pattern() {
1089 assert_module_error!(
1090 "
1091pub fn main() {
1092 case wibble {
1093 wibble.thing -> 1
1094 }
1095}
1096"
1097 );
1098}
1099
1100#[test]
1101fn tuple_invalid_expr() {
1102 assert_module_error!(
1103 "
1104fn main() {
1105 #(1, 2, const)
1106}
1107"
1108 );
1109}
1110
1111#[test]
1112fn bit_array_invalid_segment() {
1113 assert_module_error!(
1114 "
1115fn main() {
1116 <<72, 101, 108, 108, 111, 44, 32, 74, 111, 101, const>>
1117}
1118"
1119 );
1120}
1121
1122#[test]
1123fn case_invalid_expression() {
1124 assert_module_error!(
1125 "
1126fn main() {
1127 case 1, type {
1128 _, _ -> 0
1129 }
1130}
1131"
1132 );
1133}
1134
1135#[test]
1136fn case_invalid_case_pattern() {
1137 assert_module_error!(
1138 "
1139fn main() {
1140 case 1 {
1141 -> -> 0
1142 }
1143}
1144"
1145 );
1146}
1147
1148#[test]
1149fn use_invalid_assignments() {
1150 assert_module_error!(
1151 "
1152fn main() {
1153 use fn <- result.try(get_username())
1154}
1155"
1156 );
1157}
1158
1159#[test]
1160fn assignment_pattern_invalid_tuple() {
1161 assert_module_error!(
1162 "
1163fn main() {
1164 let #(a, case, c) = #(1, 2, 3)
1165}
1166"
1167 );
1168}
1169
1170#[test]
1171fn assignment_pattern_invalid_bit_segment() {
1172 assert_module_error!(
1173 "
1174fn main() {
1175 let <<b1, pub>> = <<24, 3>>
1176}
1177"
1178 );
1179}
1180
1181#[test]
1182fn case_list_pattern_after_spread() {
1183 assert_module_error!(
1184 "
1185fn main() {
1186 case somelist {
1187 [..rest, last] -> 1
1188 _ -> 2
1189 }
1190}
1191"
1192 );
1193}
1194
1195#[test]
1196fn type_invalid_constructor() {
1197 assert_module_error!(
1198 "
1199type A {
1200 A(String)
1201 type
1202}
1203"
1204 );
1205}
1206
1207// Tests whether diagnostic presents an example of how to formulate a proper
1208// record constructor based off a common user error pattern.
1209// https://github.com/gleam-lang/gleam/issues/3324
1210
1211#[test]
1212fn type_invalid_record_constructor() {
1213 assert_module_error!(
1214 "
1215pub type User {
1216 name: String,
1217}
1218"
1219 );
1220}
1221
1222#[test]
1223fn type_invalid_record_constructor_without_field_type() {
1224 assert_module_error!(
1225 "
1226pub opaque type User {
1227 name
1228}
1229"
1230 );
1231}
1232
1233#[test]
1234fn type_invalid_record_constructor_invalid_field_type() {
1235 assert_module_error!(
1236 r#"
1237type User {
1238 name: "Test User",
1239}
1240"#
1241 );
1242}
1243
1244#[test]
1245fn type_invalid_type_name() {
1246 assert_module_error!(
1247 "
1248type A(a, type) {
1249 A
1250}
1251"
1252 );
1253}
1254
1255#[test]
1256fn type_invalid_constructor_arg() {
1257 assert_module_error!(
1258 "
1259type A {
1260 A(type: String)
1261}
1262"
1263 );
1264}
1265
1266#[test]
1267fn type_invalid_record() {
1268 assert_module_error!(
1269 "
1270type A {
1271 One
1272 Two
1273 3
1274}
1275"
1276 );
1277}
1278
1279#[test]
1280fn function_type_invalid_param_type() {
1281 assert_module_error!(
1282 "
1283fn f(g: fn(Int, 1) -> Int) -> Int {
1284 g(0, 1)
1285}
1286"
1287 );
1288}
1289
1290#[test]
1291fn function_invalid_signature() {
1292 assert_module_error!(
1293 r#"
1294fn f(a, "b") -> String {
1295 a <> b
1296}
1297"#
1298 );
1299}
1300
1301#[test]
1302fn const_invalid_tuple() {
1303 assert_module_error!(
1304 "
1305const a = #(1, 2, <-)
1306"
1307 );
1308}
1309
1310#[test]
1311fn const_invalid_list() {
1312 assert_module_error!(
1313 "
1314const a = [1, 2, <-]
1315"
1316 );
1317}
1318
1319#[test]
1320fn const_invalid_bit_array_segment() {
1321 assert_module_error!(
1322 "
1323const a = <<1, 2, <->>
1324"
1325 );
1326}
1327
1328#[test]
1329fn const_invalid_record_constructor() {
1330 assert_module_error!(
1331 "
1332type A {
1333 A(String, Int)
1334}
1335const a = A(\"a\", let)
1336"
1337 );
1338}
1339
1340// record access should parse even if there is no label written
1341#[test]
1342fn record_access_no_label() {
1343 assert_parse_module!(
1344 "
1345type Wibble {
1346 Wibble(wibble: String)
1347}
1348
1349fn wobble() {
1350 Wibble(\"a\").
1351}
1352"
1353 );
1354}
1355
1356#[test]
1357fn newline_tokens() {
1358 assert_eq!(
1359 make_tokenizer("1\n\n2\n").collect_vec(),
1360 [
1361 Ok((
1362 0,
1363 Token::Int {
1364 value: "1".into(),
1365 int_value: 1.into()
1366 },
1367 1
1368 )),
1369 Ok((1, Token::NewLine, 2)),
1370 Ok((2, Token::NewLine, 3)),
1371 Ok((
1372 3,
1373 Token::Int {
1374 value: "2".into(),
1375 int_value: 2.into()
1376 },
1377 4
1378 )),
1379 Ok((4, Token::NewLine, 5))
1380 ]
1381 );
1382}
1383
1384// https://github.com/gleam-lang/gleam/issues/1756
1385#[test]
1386fn arithmetic_in_guards() {
1387 assert_parse!(
1388 "
1389case 2, 3 {
1390 x, y if x + y == 1 -> True
1391}"
1392 );
1393}
1394
1395#[test]
1396fn const_string_concat() {
1397 assert_parse_module!(
1398 "
1399const cute = \"cute\"
1400const cute_bee = cute <> \"bee\"
1401"
1402 );
1403}
1404
1405#[test]
1406fn const_string_concat_naked_right() {
1407 assert_module_error!(
1408 "
1409const no_cute_bee = \"cute\" <>
1410"
1411 );
1412}
1413
1414#[test]
1415fn function_call_in_case_clause_guard() {
1416 assert_error!(
1417 r#"
1418let my_string = "hello"
1419case my_string {
1420 _ if length(my_string) > 2 -> io.debug("doesn't work')
1421}"#
1422 );
1423}
1424
1425#[test]
1426fn dot_access_function_call_in_case_clause_guard() {
1427 assert_error!(
1428 r#"
1429let my_string = "hello"
1430case my_string {
1431 _ if string.length(my_string) > 2 -> io.debug("doesn't work')
1432}"#
1433 );
1434}
1435
1436#[test]
1437fn invalid_left_paren_in_case_clause_guard() {
1438 assert_error!(
1439 r#"
1440let my_string = "hello"
1441case my_string {
1442 _ if string.length( > 2 -> io.debug("doesn't work')
1443}"#
1444 );
1445}
1446
1447#[test]
1448fn invalid_label_shorthand() {
1449 assert_module_error!(
1450 "
1451pub fn main() {
1452 wibble(:)
1453}
1454"
1455 );
1456}
1457
1458#[test]
1459fn invalid_label_shorthand_2() {
1460 assert_module_error!(
1461 "
1462pub fn main() {
1463 wibble(:,)
1464}
1465"
1466 );
1467}
1468
1469#[test]
1470fn invalid_label_shorthand_3() {
1471 assert_module_error!(
1472 "
1473pub fn main() {
1474 wibble(:arg)
1475}
1476"
1477 );
1478}
1479
1480#[test]
1481fn invalid_label_shorthand_4() {
1482 assert_module_error!(
1483 "
1484pub fn main() {
1485 wibble(arg::)
1486}
1487"
1488 );
1489}
1490
1491#[test]
1492fn invalid_label_shorthand_5() {
1493 assert_module_error!(
1494 "
1495pub fn main() {
1496 wibble(arg::arg)
1497}
1498"
1499 );
1500}
1501
1502#[test]
1503fn invalid_pattern_label_shorthand() {
1504 assert_module_error!(
1505 "
1506pub fn main() {
1507 let Wibble(:) = todo
1508}
1509"
1510 );
1511}
1512
1513#[test]
1514fn invalid_pattern_label_shorthand_2() {
1515 assert_module_error!(
1516 "
1517pub fn main() {
1518 let Wibble(:arg) = todo
1519}
1520"
1521 );
1522}
1523
1524#[test]
1525fn invalid_pattern_label_shorthand_3() {
1526 assert_module_error!(
1527 "
1528pub fn main() {
1529 let Wibble(arg::) = todo
1530}
1531"
1532 );
1533}
1534
1535#[test]
1536fn invalid_pattern_label_shorthand_4() {
1537 assert_module_error!(
1538 "
1539pub fn main() {
1540 let Wibble(arg: arg:) = todo
1541}
1542"
1543 );
1544}
1545
1546#[test]
1547fn invalid_pattern_label_shorthand_5() {
1548 assert_module_error!(
1549 "
1550pub fn main() {
1551 let Wibble(arg1: arg2:) = todo
1552}
1553"
1554 );
1555}
1556
1557fn first_parsed_docstring(src: &str) -> EcoString {
1558 let parsed =
1559 crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null())
1560 .expect("should parse");
1561
1562 parsed
1563 .module
1564 .definitions
1565 .first()
1566 .expect("parsed a definition")
1567 .definition
1568 .get_doc()
1569 .expect("definition without doc")
1570}
1571
1572#[test]
1573fn doc_comment_before_comment_is_not_attached_to_following_function() {
1574 assert_eq!(
1575 first_parsed_docstring(
1576 r#"
1577 /// Not included!
1578 // pub fn call()
1579
1580 /// Doc!
1581 pub fn wibble() {}
1582"#
1583 ),
1584 " Doc!\n"
1585 )
1586}
1587
1588#[test]
1589fn doc_comment_before_comment_is_not_attached_to_following_type() {
1590 assert_eq!(
1591 first_parsed_docstring(
1592 r#"
1593 /// Not included!
1594 // pub fn call()
1595
1596 /// Doc!
1597 pub type Wibble
1598"#
1599 ),
1600 " Doc!\n"
1601 )
1602}
1603
1604#[test]
1605fn doc_comment_before_comment_is_not_attached_to_following_type_alias() {
1606 assert_eq!(
1607 first_parsed_docstring(
1608 r#"
1609 /// Not included!
1610 // pub fn call()
1611
1612 /// Doc!
1613 pub type Wibble = Int
1614"#
1615 ),
1616 " Doc!\n"
1617 )
1618}
1619
1620#[test]
1621fn doc_comment_before_comment_is_not_attached_to_following_constant() {
1622 assert_eq!(
1623 first_parsed_docstring(
1624 r#"
1625 /// Not included!
1626 // pub fn call()
1627
1628 /// Doc!
1629 pub const wibble = 1
1630"#
1631 ),
1632 " Doc!\n"
1633 );
1634}
1635
1636#[test]
1637fn non_module_level_function_with_a_name() {
1638 assert_module_error!(
1639 r#"
1640pub fn main() {
1641 fn my() { 1 }
1642}
1643"#
1644 );
1645}
1646
1647#[test]
1648fn error_message_on_variable_starting_with_underscore() {
1649 // https://github.com/gleam-lang/gleam/issues/3504
1650 assert_module_error!(
1651 "
1652 pub fn main() {
1653 let val = _func_starting_with_underscore(1)
1654 }"
1655 );
1656}
1657
1658#[test]
1659fn non_module_level_function_with_not_a_name() {
1660 assert_module_error!(
1661 r#"
1662pub fn main() {
1663 fn @() { 1 } // wrong token and not a name
1664}
1665"#
1666 );
1667}
1668
1669#[test]
1670fn error_message_on_variable_starting_with_underscore2() {
1671 // https://github.com/gleam-lang/gleam/issues/3504
1672 assert_module_error!(
1673 "
1674 pub fn main() {
1675 case 1 {
1676 1 -> _with_underscore(1)
1677 }
1678 }"
1679 );
1680}
1681
1682#[test]
1683fn function_inside_a_type() {
1684 assert_module_error!(
1685 r#"
1686type Wibble {
1687 fn wobble() {}
1688}
1689"#
1690 );
1691}
1692
1693#[test]
1694fn pub_function_inside_a_type() {
1695 assert_module_error!(
1696 r#"
1697type Wibble {
1698 pub fn wobble() {}
1699}
1700"#
1701 );
1702}
1703
1704#[test]
1705fn if_like_expression() {
1706 assert_module_error!(
1707 r#"
1708pub fn main() {
1709 let a = if wibble {
1710 wobble
1711 }
1712}
1713"#
1714 );
1715}
1716
1717// https://github.com/gleam-lang/gleam/issues/3730
1718#[test]
1719fn missing_constructor_arguments() {
1720 assert_module_error!(
1721 "
1722pub type A {
1723 A(Int)
1724}
1725
1726const a = A()
1727"
1728 );
1729}
1730
1731// https://github.com/gleam-lang/gleam/issues/3796
1732#[test]
1733fn missing_type_constructor_arguments_in_type_definition() {
1734 assert_module_error!(
1735 "
1736pub type A() {
1737 A(Int)
1738}
1739"
1740 );
1741}
1742
1743#[test]
1744fn missing_type_constructor_arguments_in_type_annotation_1() {
1745 assert_module_error!("pub fn main() -> Int() {}");
1746}
1747
1748#[test]
1749fn missing_type_constructor_arguments_in_type_annotation_2() {
1750 assert_module_error!(
1751 "pub fn main() {
1752 let a: Int() = todo
1753}"
1754 );
1755}
1756
1757#[test]
1758fn tuple_without_hash() {
1759 assert_module_error!(
1760 r#"
1761pub fn main() {
1762 let triple = (1, 2.2, "three")
1763 io.debug(triple)
1764 let (a, *, *) = triple
1765 io.debug(a)
1766 io.debug(triple.1)
1767}
1768"#
1769 );
1770}
1771
1772#[test]
1773fn deprecation_attribute_on_type_variant() {
1774 assert_parse_module!(
1775 r#"
1776type Wibble {
1777 @deprecated("1")
1778 Wibble1
1779 Wibble2
1780}
1781"#
1782 );
1783}
1784
1785#[test]
1786
1787fn float_empty_exponent() {
1788 assert_error!("1.32e");
1789}
1790
1791#[test]
1792fn multiple_deprecation_attribute_on_type_variant() {
1793 assert_module_error!(
1794 r#"
1795type Wibble {
1796 @deprecated("1")
1797 @deprecated("2")
1798 Wibble1
1799 Wibble2
1800}
1801"#
1802 );
1803}
1804
1805#[test]
1806fn target_attribute_on_type_variant() {
1807 assert_module_error!(
1808 r#"
1809type Wibble {
1810 @target(erlang)
1811 Wibble2
1812}
1813"#
1814 );
1815}
1816
1817#[test]
1818fn internal_attribute_on_type_variant() {
1819 assert_module_error!(
1820 r#"
1821type Wibble {
1822 @internal
1823 Wibble1
1824}
1825"#
1826 );
1827}
1828
1829#[test]
1830fn external_attribute_on_type_variant() {
1831 assert_module_error!(
1832 r#"
1833type Wibble {
1834 @external(erlang, "one", "two")
1835 Wibble1
1836}
1837"#
1838 );
1839}
1840
1841#[test]
1842fn multiple_unsupported_attributes_on_type_variant() {
1843 assert_module_error!(
1844 r#"
1845type Wibble {
1846 @external(erlang, "one", "two")
1847 @target(erlang)
1848 @internal
1849 Wibble1
1850}
1851"#
1852 );
1853}
1854
1855#[test]
1856// https://github.com/gleam-lang/gleam/issues/3870
1857fn nested_tuple_access_after_function() {
1858 assert_parse!("tuple().0.1");
1859}
1860
1861#[test]
1862fn case_expression_without_body() {
1863 assert_parse!("case a");
1864}
1865
1866#[test]
1867fn assert_statement() {
1868 assert_parse!("assert 10 != 11");
1869}
1870
1871#[test]
1872fn assert_statement_with_message() {
1873 assert_parse!(r#"assert False as "Uh oh""#);
1874}
1875
1876#[test]
1877fn assert_statement_without_expression() {
1878 assert_error!("assert");
1879}
1880
1881#[test]
1882fn assert_statement_followed_by_statement() {
1883 assert_error!("assert let a = 10");
1884}
1885
1886#[test]
1887fn special_error_for_pythonic_import() {
1888 assert_module_error!("import gleam.io");
1889}
1890
1891#[test]
1892fn special_error_for_pythonic_neste_import() {
1893 assert_module_error!("import one.two.three");
1894}
1895
1896#[test]
1897fn doesnt_issue_special_error_for_pythonic_import_if_slash() {
1898 assert_module_error!("import one/two.three");
1899}
1900
1901#[test]
1902fn operator_in_pattern_size() {
1903 assert_parse!("let assert <<size, payload:size(size - 1)>> = <<>>");
1904}
1905
1906#[test]
1907fn correct_precedence_in_pattern_size() {
1908 assert_parse!("let assert <<size, payload:size(size + 2 * 8)>> = <<>>");
1909}
1910
1911#[test]
1912fn private_opaque_type_is_parsed() {
1913 assert_parse_module!("opaque type Wibble { Wobble }");
1914}
1915
1916#[test]
1917fn case_guard_with_nested_blocks() {
1918 assert_parse!(
1919 "case 1 {
1920 _ if { 1 || { 1 || 1 } } || 1 -> 1
1921}"
1922 );
1923}
1924
1925#[test]
1926fn case_guard_with_empty_block() {
1927 assert_error!(
1928 "case 1 {
1929 _ if a || {} -> 1
1930}"
1931 );
1932}