Fork of daniellemaywood.uk/gleam — Wasm codegen work
36 kB
2005 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_custom_type() {
822 assert_parse_module!(
823 r#"
824@external(erlang, "gleam_stdlib", "dict")
825@external(javascript, "./gleam_stdlib.d.ts", "Dict")
826pub type Dict(key, value)
827"#
828 );
829}
830
831#[test]
832fn external_attribute_with_non_fn_definition() {
833 assert_module_error!(
834 r#"
835@external(erlang, "module", "fun")
836pub type Fun = Fun
837"#
838 );
839}
840
841#[test]
842fn attributes_with_improper_definition() {
843 assert_module_error!(
844 r#"
845@deprecated("1")
846@external(erlang, "module", "fun")
847"#
848 );
849}
850
851#[test]
852fn const_with_function_call() {
853 assert_module_error!(
854 r#"
855pub fn wibble() { 123 }
856const wib: Int = wibble()
857"#
858 );
859}
860
861#[test]
862fn const_with_function_call_with_args() {
863 assert_module_error!(
864 r#"
865pub fn wibble() { 123 }
866const wib: Int = wibble(1, "wobble")
867"#
868 );
869}
870
871#[test]
872fn import_type() {
873 assert_parse_module!(r#"import wibble.{type Wobble, Wobble, type Wabble}"#);
874}
875
876#[test]
877fn reserved_auto() {
878 assert_module_error!(r#"const auto = 1"#);
879}
880
881#[test]
882fn reserved_delegate() {
883 assert_module_error!(r#"const delegate = 1"#);
884}
885
886#[test]
887fn reserved_derive() {
888 assert_module_error!(r#"const derive = 1"#);
889}
890
891#[test]
892fn reserved_else() {
893 assert_module_error!(r#"const else = 1"#);
894}
895
896#[test]
897fn reserved_implement() {
898 assert_module_error!(r#"const implement = 1"#);
899}
900
901#[test]
902fn reserved_macro() {
903 assert_module_error!(r#"const macro = 1"#);
904}
905
906#[test]
907fn reserved_test() {
908 assert_module_error!(r#"const test = 1"#);
909}
910
911#[test]
912fn reserved_echo() {
913 assert_module_error!(r#"const echo = 1"#);
914}
915
916#[test]
917fn capture_with_name() {
918 assert_module_error!(
919 r#"
920pub fn main() {
921 add(_name, 1)
922}
923
924fn add(x, y) {
925 x + y
926}
927"#
928 );
929}
930
931#[test]
932fn list_spread_with_no_tail_in_the_middle_of_a_list() {
933 assert_module_error!(
934 r#"
935pub fn main() -> Nil {
936 let xs = [1, 2, 3]
937 [1, 2, .., 3 + 3, 4]
938}
939"#
940 );
941}
942
943#[test]
944fn list_spread_followed_by_extra_items() {
945 assert_module_error!(
946 r#"
947pub fn main() -> Nil {
948 let xs = [1, 2, 3]
949 [1, 2, ..xs, 3 + 3, 4]
950}
951"#
952 );
953}
954
955#[test]
956fn list_spread_followed_by_extra_item_and_another_spread() {
957 assert_module_error!(
958 r#"
959pub fn main() -> Nil {
960 let xs = [1, 2, 3]
961 let ys = [5, 6, 7]
962 [..xs, 4, ..ys]
963}
964"#
965 );
966}
967
968#[test]
969fn list_spread_followed_by_other_spread() {
970 assert_module_error!(
971 r#"
972pub fn main() -> Nil {
973 let xs = [1, 2, 3]
974 let ys = [5, 6, 7]
975 [1, ..xs, ..ys]
976}
977"#
978 );
979}
980
981#[test]
982fn list_spread_as_first_item_followed_by_other_items() {
983 assert_module_error!(
984 r#"
985pub fn main() -> Nil {
986 let xs = [1, 2, 3]
987 [..xs, 3 + 3, 4]
988}
989"#
990 );
991}
992
993// Tests for nested tuples and structs in tuples
994// https://github.com/gleam-lang/gleam/issues/1980
995
996#[test]
997fn nested_tuples() {
998 assert_parse!(
999 r#"
1000let tup = #(#(5, 6))
1001{tup.0}.1
1002"#
1003 );
1004}
1005
1006#[test]
1007fn nested_tuples_no_block() {
1008 assert_parse!(
1009 r#"
1010let tup = #(#(5, 6))
1011tup.0.1
1012"#
1013 );
1014}
1015
1016#[test]
1017fn deeply_nested_tuples() {
1018 assert_parse!(
1019 r#"
1020let tup = #(#(#(#(4))))
1021{{{tup.0}.0}.0}.0
1022"#
1023 );
1024}
1025
1026#[test]
1027fn deeply_nested_tuples_no_block() {
1028 assert_parse!(
1029 r#"
1030let tup = #(#(#(#(4))))
1031tup.0.0.0.0
1032"#
1033 );
1034}
1035
1036#[test]
1037fn inner_single_quote_parses() {
1038 assert_parse!(
1039 r#"
1040let a = "inner 'quotes'"
1041"#
1042 );
1043}
1044
1045#[test]
1046fn string_single_char_suggestion() {
1047 assert_module_error!(
1048 "
1049 pub fn main() {
1050 let a = 'example'
1051 }
1052 "
1053 );
1054}
1055
1056#[test]
1057fn private_internal_const() {
1058 assert_module_error!(
1059 "
1060@internal
1061const wibble = 1
1062"
1063 );
1064}
1065
1066#[test]
1067fn private_internal_type_alias() {
1068 assert_module_error!(
1069 "
1070@internal
1071type Alias = Int
1072"
1073 );
1074}
1075
1076#[test]
1077fn private_internal_function() {
1078 assert_module_error!(
1079 "
1080@internal
1081fn wibble() { todo }
1082"
1083 );
1084}
1085
1086#[test]
1087fn private_internal_type() {
1088 assert_module_error!(
1089 "
1090@internal
1091type Wibble {
1092 Wibble
1093}
1094"
1095 );
1096}
1097
1098#[test]
1099fn wrong_record_access_pattern() {
1100 assert_module_error!(
1101 "
1102pub fn main() {
1103 case wibble {
1104 wibble.thing -> 1
1105 }
1106}
1107"
1108 );
1109}
1110
1111#[test]
1112fn tuple_invalid_expr() {
1113 assert_module_error!(
1114 "
1115fn main() {
1116 #(1, 2, const)
1117}
1118"
1119 );
1120}
1121
1122#[test]
1123fn bit_array_invalid_segment() {
1124 assert_module_error!(
1125 "
1126fn main() {
1127 <<72, 101, 108, 108, 111, 44, 32, 74, 111, 101, const>>
1128}
1129"
1130 );
1131}
1132
1133#[test]
1134fn case_invalid_expression() {
1135 assert_module_error!(
1136 "
1137fn main() {
1138 case 1, type {
1139 _, _ -> 0
1140 }
1141}
1142"
1143 );
1144}
1145
1146#[test]
1147fn case_invalid_case_pattern() {
1148 assert_module_error!(
1149 "
1150fn main() {
1151 case 1 {
1152 -> -> 0
1153 }
1154}
1155"
1156 );
1157}
1158
1159#[test]
1160fn use_invalid_assignments() {
1161 assert_module_error!(
1162 "
1163fn main() {
1164 use fn <- result.try(get_username())
1165}
1166"
1167 );
1168}
1169
1170#[test]
1171fn assignment_pattern_invalid_tuple() {
1172 assert_module_error!(
1173 "
1174fn main() {
1175 let #(a, case, c) = #(1, 2, 3)
1176}
1177"
1178 );
1179}
1180
1181#[test]
1182fn assignment_pattern_invalid_bit_segment() {
1183 assert_module_error!(
1184 "
1185fn main() {
1186 let <<b1, pub>> = <<24, 3>>
1187}
1188"
1189 );
1190}
1191
1192#[test]
1193fn case_list_pattern_after_spread() {
1194 assert_module_error!(
1195 "
1196fn main() {
1197 case somelist {
1198 [..rest, last] -> 1
1199 _ -> 2
1200 }
1201}
1202"
1203 );
1204}
1205
1206#[test]
1207fn type_invalid_constructor() {
1208 assert_module_error!(
1209 "
1210type A {
1211 A(String)
1212 type
1213}
1214"
1215 );
1216}
1217
1218// Tests whether diagnostic presents an example of how to formulate a proper
1219// record constructor based off a common user error pattern.
1220// https://github.com/gleam-lang/gleam/issues/3324
1221
1222#[test]
1223fn type_invalid_record_constructor() {
1224 assert_module_error!(
1225 "
1226pub type User {
1227 name: String,
1228}
1229"
1230 );
1231}
1232
1233#[test]
1234fn type_invalid_record_constructor_without_field_type() {
1235 assert_module_error!(
1236 "
1237pub opaque type User {
1238 name
1239}
1240"
1241 );
1242}
1243
1244#[test]
1245fn type_invalid_record_constructor_invalid_field_type() {
1246 assert_module_error!(
1247 r#"
1248type User {
1249 name: "Test User",
1250}
1251"#
1252 );
1253}
1254
1255#[test]
1256fn type_invalid_type_name() {
1257 assert_module_error!(
1258 "
1259type A(a, type) {
1260 A
1261}
1262"
1263 );
1264}
1265
1266#[test]
1267fn type_invalid_constructor_arg() {
1268 assert_module_error!(
1269 "
1270type A {
1271 A(type: String)
1272}
1273"
1274 );
1275}
1276
1277#[test]
1278fn type_invalid_record() {
1279 assert_module_error!(
1280 "
1281type A {
1282 One
1283 Two
1284 3
1285}
1286"
1287 );
1288}
1289
1290#[test]
1291fn function_type_invalid_param_type() {
1292 assert_module_error!(
1293 "
1294fn f(g: fn(Int, 1) -> Int) -> Int {
1295 g(0, 1)
1296}
1297"
1298 );
1299}
1300
1301#[test]
1302fn function_invalid_signature() {
1303 assert_module_error!(
1304 r#"
1305fn f(a, "b") -> String {
1306 a <> b
1307}
1308"#
1309 );
1310}
1311
1312#[test]
1313fn const_invalid_tuple() {
1314 assert_module_error!(
1315 "
1316const a = #(1, 2, <-)
1317"
1318 );
1319}
1320
1321#[test]
1322fn const_invalid_list() {
1323 assert_module_error!(
1324 "
1325const a = [1, 2, <-]
1326"
1327 );
1328}
1329
1330#[test]
1331fn const_invalid_bit_array_segment() {
1332 assert_module_error!(
1333 "
1334const a = <<1, 2, <->>
1335"
1336 );
1337}
1338
1339#[test]
1340fn const_invalid_record_constructor() {
1341 assert_module_error!(
1342 "
1343type A {
1344 A(String, Int)
1345}
1346const a = A(\"a\", let)
1347"
1348 );
1349}
1350
1351// record access should parse even if there is no label written
1352#[test]
1353fn record_access_no_label() {
1354 assert_parse_module!(
1355 "
1356type Wibble {
1357 Wibble(wibble: String)
1358}
1359
1360fn wobble() {
1361 Wibble(\"a\").
1362}
1363"
1364 );
1365}
1366
1367#[test]
1368fn newline_tokens() {
1369 assert_eq!(
1370 make_tokenizer("1\n\n2\n").collect_vec(),
1371 [
1372 Ok((
1373 0,
1374 Token::Int {
1375 value: "1".into(),
1376 int_value: 1.into()
1377 },
1378 1
1379 )),
1380 Ok((1, Token::NewLine, 2)),
1381 Ok((2, Token::NewLine, 3)),
1382 Ok((
1383 3,
1384 Token::Int {
1385 value: "2".into(),
1386 int_value: 2.into()
1387 },
1388 4
1389 )),
1390 Ok((4, Token::NewLine, 5))
1391 ]
1392 );
1393}
1394
1395// https://github.com/gleam-lang/gleam/issues/1756
1396#[test]
1397fn arithmetic_in_guards() {
1398 assert_parse!(
1399 "
1400case 2, 3 {
1401 x, y if x + y == 1 -> True
1402}"
1403 );
1404}
1405
1406#[test]
1407fn const_string_concat() {
1408 assert_parse_module!(
1409 "
1410const cute = \"cute\"
1411const cute_bee = cute <> \"bee\"
1412"
1413 );
1414}
1415
1416#[test]
1417fn const_string_concat_naked_right() {
1418 assert_module_error!(
1419 "
1420const no_cute_bee = \"cute\" <>
1421"
1422 );
1423}
1424
1425#[test]
1426fn function_call_in_case_clause_guard() {
1427 assert_error!(
1428 r#"
1429let my_string = "hello"
1430case my_string {
1431 _ if length(my_string) > 2 -> io.debug("doesn't work')
1432}"#
1433 );
1434}
1435
1436#[test]
1437fn dot_access_function_call_in_case_clause_guard() {
1438 assert_error!(
1439 r#"
1440let my_string = "hello"
1441case my_string {
1442 _ if string.length(my_string) > 2 -> io.debug("doesn't work')
1443}"#
1444 );
1445}
1446
1447#[test]
1448fn invalid_left_paren_in_case_clause_guard() {
1449 assert_error!(
1450 r#"
1451let my_string = "hello"
1452case my_string {
1453 _ if string.length( > 2 -> io.debug("doesn't work')
1454}"#
1455 );
1456}
1457
1458#[test]
1459fn invalid_label_shorthand() {
1460 assert_module_error!(
1461 "
1462pub fn main() {
1463 wibble(:)
1464}
1465"
1466 );
1467}
1468
1469#[test]
1470fn invalid_label_shorthand_2() {
1471 assert_module_error!(
1472 "
1473pub fn main() {
1474 wibble(:,)
1475}
1476"
1477 );
1478}
1479
1480#[test]
1481fn invalid_label_shorthand_3() {
1482 assert_module_error!(
1483 "
1484pub fn main() {
1485 wibble(:arg)
1486}
1487"
1488 );
1489}
1490
1491#[test]
1492fn invalid_label_shorthand_4() {
1493 assert_module_error!(
1494 "
1495pub fn main() {
1496 wibble(arg::)
1497}
1498"
1499 );
1500}
1501
1502#[test]
1503fn invalid_label_shorthand_5() {
1504 assert_module_error!(
1505 "
1506pub fn main() {
1507 wibble(arg::arg)
1508}
1509"
1510 );
1511}
1512
1513#[test]
1514fn invalid_pattern_label_shorthand() {
1515 assert_module_error!(
1516 "
1517pub fn main() {
1518 let Wibble(:) = todo
1519}
1520"
1521 );
1522}
1523
1524#[test]
1525fn invalid_pattern_label_shorthand_2() {
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_3() {
1537 assert_module_error!(
1538 "
1539pub fn main() {
1540 let Wibble(arg::) = todo
1541}
1542"
1543 );
1544}
1545
1546#[test]
1547fn invalid_pattern_label_shorthand_4() {
1548 assert_module_error!(
1549 "
1550pub fn main() {
1551 let Wibble(arg: arg:) = todo
1552}
1553"
1554 );
1555}
1556
1557#[test]
1558fn invalid_pattern_label_shorthand_5() {
1559 assert_module_error!(
1560 "
1561pub fn main() {
1562 let Wibble(arg1: arg2:) = todo
1563}
1564"
1565 );
1566}
1567
1568fn first_parsed_docstring(src: &str) -> EcoString {
1569 let parsed =
1570 crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null())
1571 .expect("should parse");
1572
1573 parsed
1574 .module
1575 .definitions
1576 .first()
1577 .expect("parsed a definition")
1578 .definition
1579 .get_doc()
1580 .expect("definition without doc")
1581}
1582
1583#[test]
1584fn doc_comment_before_comment_is_not_attached_to_following_function() {
1585 assert_eq!(
1586 first_parsed_docstring(
1587 r#"
1588 /// Not included!
1589 // pub fn call()
1590
1591 /// Doc!
1592 pub fn wibble() {}
1593"#
1594 ),
1595 " Doc!\n"
1596 )
1597}
1598
1599#[test]
1600fn doc_comment_before_comment_is_not_attached_to_following_type() {
1601 assert_eq!(
1602 first_parsed_docstring(
1603 r#"
1604 /// Not included!
1605 // pub fn call()
1606
1607 /// Doc!
1608 pub type Wibble
1609"#
1610 ),
1611 " Doc!\n"
1612 )
1613}
1614
1615#[test]
1616fn doc_comment_before_comment_is_not_attached_to_following_type_alias() {
1617 assert_eq!(
1618 first_parsed_docstring(
1619 r#"
1620 /// Not included!
1621 // pub fn call()
1622
1623 /// Doc!
1624 pub type Wibble = Int
1625"#
1626 ),
1627 " Doc!\n"
1628 )
1629}
1630
1631#[test]
1632fn doc_comment_before_comment_is_not_attached_to_following_constant() {
1633 assert_eq!(
1634 first_parsed_docstring(
1635 r#"
1636 /// Not included!
1637 // pub fn call()
1638
1639 /// Doc!
1640 pub const wibble = 1
1641"#
1642 ),
1643 " Doc!\n"
1644 );
1645}
1646
1647#[test]
1648fn non_module_level_function_with_a_name() {
1649 assert_module_error!(
1650 r#"
1651pub fn main() {
1652 fn my() { 1 }
1653}
1654"#
1655 );
1656}
1657
1658#[test]
1659fn error_message_on_variable_starting_with_underscore() {
1660 // https://github.com/gleam-lang/gleam/issues/3504
1661 assert_module_error!(
1662 "
1663 pub fn main() {
1664 let val = _func_starting_with_underscore(1)
1665 }"
1666 );
1667}
1668
1669#[test]
1670fn non_module_level_function_with_not_a_name() {
1671 assert_module_error!(
1672 r#"
1673pub fn main() {
1674 fn @() { 1 } // wrong token and not a name
1675}
1676"#
1677 );
1678}
1679
1680#[test]
1681fn error_message_on_variable_starting_with_underscore2() {
1682 // https://github.com/gleam-lang/gleam/issues/3504
1683 assert_module_error!(
1684 "
1685 pub fn main() {
1686 case 1 {
1687 1 -> _with_underscore(1)
1688 }
1689 }"
1690 );
1691}
1692
1693#[test]
1694fn function_inside_a_type() {
1695 assert_module_error!(
1696 r#"
1697type Wibble {
1698 fn wobble() {}
1699}
1700"#
1701 );
1702}
1703
1704#[test]
1705fn pub_function_inside_a_type() {
1706 assert_module_error!(
1707 r#"
1708type Wibble {
1709 pub fn wobble() {}
1710}
1711"#
1712 );
1713}
1714
1715#[test]
1716fn if_like_expression() {
1717 assert_module_error!(
1718 r#"
1719pub fn main() {
1720 let a = if wibble {
1721 wobble
1722 }
1723}
1724"#
1725 );
1726}
1727
1728// https://github.com/gleam-lang/gleam/issues/3730
1729#[test]
1730fn missing_constructor_arguments() {
1731 assert_module_error!(
1732 "
1733pub type A {
1734 A(Int)
1735}
1736
1737const a = A()
1738"
1739 );
1740}
1741
1742// https://github.com/gleam-lang/gleam/issues/3796
1743#[test]
1744fn missing_type_constructor_arguments_in_type_definition() {
1745 assert_module_error!(
1746 "
1747pub type A() {
1748 A(Int)
1749}
1750"
1751 );
1752}
1753
1754#[test]
1755fn tuple_without_hash() {
1756 assert_module_error!(
1757 r#"
1758pub fn main() {
1759 let triple = (1, 2.2, "three")
1760 io.debug(triple)
1761 let (a, *, *) = triple
1762 io.debug(a)
1763 io.debug(triple.1)
1764}
1765"#
1766 );
1767}
1768
1769#[test]
1770fn deprecation_attribute_on_type_variant() {
1771 assert_parse_module!(
1772 r#"
1773type Wibble {
1774 @deprecated("1")
1775 Wibble1
1776 Wibble2
1777}
1778"#
1779 );
1780}
1781
1782#[test]
1783
1784fn float_empty_exponent() {
1785 assert_error!("1.32e");
1786}
1787
1788#[test]
1789fn multiple_deprecation_attribute_on_type_variant() {
1790 assert_module_error!(
1791 r#"
1792type Wibble {
1793 @deprecated("1")
1794 @deprecated("2")
1795 Wibble1
1796 Wibble2
1797}
1798"#
1799 );
1800}
1801
1802#[test]
1803fn target_attribute_on_type_variant() {
1804 assert_module_error!(
1805 r#"
1806type Wibble {
1807 @target(erlang)
1808 Wibble2
1809}
1810"#
1811 );
1812}
1813
1814#[test]
1815fn internal_attribute_on_type_variant() {
1816 assert_module_error!(
1817 r#"
1818type Wibble {
1819 @internal
1820 Wibble1
1821}
1822"#
1823 );
1824}
1825
1826#[test]
1827fn external_attribute_on_type_variant() {
1828 assert_module_error!(
1829 r#"
1830type Wibble {
1831 @external(erlang, "one", "two")
1832 Wibble1
1833}
1834"#
1835 );
1836}
1837
1838#[test]
1839fn multiple_unsupported_attributes_on_type_variant() {
1840 assert_module_error!(
1841 r#"
1842type Wibble {
1843 @external(erlang, "one", "two")
1844 @target(erlang)
1845 @internal
1846 Wibble1
1847}
1848"#
1849 );
1850}
1851
1852#[test]
1853// https://github.com/gleam-lang/gleam/issues/3870
1854fn nested_tuple_access_after_function() {
1855 assert_parse!("tuple().0.1");
1856}
1857
1858#[test]
1859fn case_expression_without_body() {
1860 assert_parse!("case a");
1861}
1862
1863#[test]
1864fn assert_statement() {
1865 assert_parse!("assert 10 != 11");
1866}
1867
1868#[test]
1869fn assert_statement_with_message() {
1870 assert_parse!(r#"assert False as "Uh oh""#);
1871}
1872
1873#[test]
1874fn assert_statement_without_expression() {
1875 assert_error!("assert");
1876}
1877
1878#[test]
1879fn assert_statement_followed_by_statement() {
1880 assert_error!("assert let a = 10");
1881}
1882
1883#[test]
1884fn special_error_for_pythonic_import() {
1885 assert_module_error!("import gleam.io");
1886}
1887
1888#[test]
1889fn special_error_for_pythonic_neste_import() {
1890 assert_module_error!("import one.two.three");
1891}
1892
1893#[test]
1894fn doesnt_issue_special_error_for_pythonic_import_if_slash() {
1895 assert_module_error!("import one/two.three");
1896}
1897
1898#[test]
1899fn operator_in_pattern_size() {
1900 assert_parse!("let assert <<size, payload:size(size - 1)>> = <<>>");
1901}
1902
1903#[test]
1904fn correct_precedence_in_pattern_size() {
1905 assert_parse!("let assert <<size, payload:size(size + 2 * 8)>> = <<>>");
1906}
1907
1908#[test]
1909fn private_opaque_type_is_parsed() {
1910 assert_parse_module!("opaque type Wibble { Wobble }");
1911}
1912
1913#[test]
1914fn case_guard_with_nested_blocks() {
1915 assert_parse!(
1916 "case 1 {
1917 _ if { 1 || { 1 || 1 } } || 1 -> 1
1918}"
1919 );
1920}
1921
1922#[test]
1923fn case_guard_with_empty_block() {
1924 assert_error!(
1925 "case 1 {
1926 _ if a || {} -> 1
1927}"
1928 );
1929}
1930
1931#[test]
1932fn constant_inside_function() {
1933 assert_module_error!(
1934 "
1935pub fn main() {
1936 const x = 10
1937 x
1938}
1939"
1940 );
1941}
1942
1943#[test]
1944fn function_definition_angle_generics_error() {
1945 assert_module_error!("fn id<T>(x: T) { x }");
1946}
1947
1948#[test]
1949fn type_angle_generics_usage_without_module_error() {
1950 assert_error!("let list: List<Int, String> = []");
1951}
1952
1953#[test]
1954fn type_angle_generics_usage_with_module_error() {
1955 assert_error!("let set: set.Set<Int> = set.new()");
1956}
1957
1958#[test]
1959fn type_angle_generics_definition_error() {
1960 assert_module_error!(
1961 r#"
1962type Either<a, b> {
1963 This(a)
1964 That(b)
1965}
1966"#
1967 );
1968}
1969
1970#[test]
1971fn type_angle_generics_definition_with_upname_error() {
1972 assert_module_error!(
1973 r#"
1974type Either<A, B> {
1975 This(A)
1976 That(B)
1977}
1978"#
1979 );
1980}
1981
1982#[test]
1983fn type_angle_generics_definition_error_fallback() {
1984 // Example of a more incorrect syntax, where Gleam doesn't make a suggestion.
1985 // In this case, an example type argument is used instead.
1986 assert_module_error!(
1987 r#"
1988type Either<type A, type B> {
1989 This(A)
1990 That(B)
1991}
1992"#
1993 );
1994}
1995
1996#[test]
1997fn wrong_type_of_comments_with_hash() {
1998 assert_module_error!(
1999 r#"
2000pub fn main() {
2001 # a python-style comment
2002}
2003"#
2004 );
2005}