Fork of daniellemaywood.uk/gleam — Wasm codegen work
46 kB
2498 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/1613
367#[test]
368fn anonymous_function_labeled_arguments() {
369 assert_error!(
370 "let anon_subtract = fn (minuend a: Int, subtrahend b: Int) -> Int {
371 a - b
372}",
373 ParseError {
374 location: SrcSpan { start: 24, end: 31 },
375 error: ParseErrorType::UnexpectedLabel
376 }
377 );
378}
379
380#[test]
381fn no_let_binding() {
382 assert_error!(
383 "wibble = 32",
384 ParseError {
385 location: SrcSpan { start: 7, end: 8 },
386 error: ParseErrorType::NoLetBinding
387 }
388 );
389}
390
391#[test]
392fn no_let_binding1() {
393 assert_error!(
394 "wibble:Int = 32",
395 ParseError {
396 location: SrcSpan { start: 6, end: 7 },
397 error: ParseErrorType::NoLetBinding
398 }
399 );
400}
401
402#[test]
403fn no_let_binding2() {
404 assert_error!(
405 "let wobble:Int = 32
406 wobble = 42",
407 ParseError {
408 location: SrcSpan { start: 35, end: 36 },
409 error: ParseErrorType::NoLetBinding
410 }
411 );
412}
413
414#[test]
415fn no_let_binding3() {
416 assert_error!(
417 "[x] = [2]",
418 ParseError {
419 location: SrcSpan { start: 4, end: 5 },
420 error: ParseErrorType::NoLetBinding
421 }
422 );
423}
424
425#[test]
426fn with_let_binding3() {
427 // The same with `let assert` must parse:
428 assert_parse!("let assert [x] = [2]");
429}
430
431#[test]
432fn with_let_binding3_and_annotation() {
433 assert_parse!("let assert [x]: List(Int) = [2]");
434}
435
436#[test]
437fn no_eq_after_binding() {
438 assert_error!(
439 "let wibble",
440 ParseError {
441 location: SrcSpan { start: 4, end: 10 },
442 error: ParseErrorType::ExpectedEqual
443 }
444 );
445}
446
447#[test]
448fn no_eq_after_binding1() {
449 assert_error!(
450 "let wibble
451 wibble = 4",
452 ParseError {
453 location: SrcSpan { start: 4, end: 10 },
454 error: ParseErrorType::ExpectedEqual
455 }
456 );
457}
458
459#[test]
460fn echo_followed_by_expression_ends_where_expression_ends() {
461 assert_parse!("echo wibble");
462}
463
464#[test]
465fn echo_with_simple_expression_1() {
466 assert_parse!("echo wibble as message");
467}
468
469#[test]
470fn echo_with_simple_expression_2() {
471 assert_parse!("echo wibble as \"message\"");
472}
473
474#[test]
475fn echo_with_complex_expression() {
476 assert_parse!("echo wibble as { this <> complex }");
477}
478
479#[test]
480fn echo_with_no_expressions_after_it() {
481 assert_parse!("echo");
482}
483
484#[test]
485fn echo_with_no_expressions_after_it_but_a_message() {
486 assert_parse!("echo as message");
487}
488
489#[test]
490fn echo_with_block() {
491 assert_parse!("echo { 1 + 1 }");
492}
493
494#[test]
495fn echo_has_lower_precedence_than_binop() {
496 assert_parse!("echo 1 + 1");
497}
498
499#[test]
500fn echo_in_a_pipeline() {
501 assert_parse!("[] |> echo |> wibble");
502}
503
504#[test]
505fn echo_has_lower_precedence_than_pipeline() {
506 assert_parse!("echo wibble |> wobble |> woo");
507}
508
509#[test]
510fn echo_cannot_have_an_expression_in_a_pipeline() {
511 // So this is actually two pipelines!
512 assert_parse!("[] |> echo fun |> wibble");
513}
514
515#[test]
516fn panic_with_echo() {
517 assert_parse!("panic as echo \"string\"");
518}
519
520#[test]
521fn panic_with_echo_and_message() {
522 assert_parse!("panic as echo wibble as message");
523}
524
525#[test]
526fn echo_with_panic() {
527 assert_parse!("echo panic as \"a\"");
528}
529
530#[test]
531fn echo_with_panic_and_message() {
532 assert_parse!("echo panic as \"a\"");
533}
534
535#[test]
536fn echo_with_panic_and_messages() {
537 assert_parse!("echo panic as \"a\" as \"b\"");
538}
539
540#[test]
541fn echo_with_assert_and_message_1() {
542 assert_parse!("assert 1 == echo 2 as this_belongs_to_echo");
543}
544
545#[test]
546fn echo_with_assert_and_message_2() {
547 assert_parse!("assert echo True as this_belongs_to_echo");
548}
549
550#[test]
551fn echo_with_assert_and_messages_1() {
552 assert_parse!("assert 1 == echo 2 as this_belongs_to_echo as this_belongs_to_assert");
553}
554
555#[test]
556fn echo_with_assert_and_messages_2() {
557 assert_parse!("assert echo True as this_belongs_to_echo as this_belongs_to_assert");
558}
559
560#[test]
561fn echo_with_assert_and_messages_3() {
562 assert_parse!("assert echo 1 == 2 as this_belongs_to_echo as this_belongs_to_assert");
563}
564
565#[test]
566fn echo_with_let_assert_and_message() {
567 assert_parse!("let assert 1 = echo 2 as this_belongs_to_echo");
568}
569
570#[test]
571fn echo_with_let_assert_and_messages() {
572 assert_parse!("let assert 1 = echo 1 as this_belongs_to_echo as this_belongs_to_assert");
573}
574
575#[test]
576fn repeated_echos() {
577 assert_parse!("echo echo echo 1");
578}
579
580#[test]
581fn echo_at_start_of_pipeline_wraps_the_whole_thing() {
582 assert_parse!("echo 1 |> wibble |> wobble");
583}
584
585#[test]
586fn no_let_binding_snapshot_1() {
587 assert_error!("wibble = 4");
588}
589
590#[test]
591fn no_let_binding_snapshot_2() {
592 assert_error!("wibble:Int = 4");
593}
594
595#[test]
596fn no_let_binding_snapshot_3() {
597 assert_error!(
598 "let wobble:Int = 32
599 wobble = 42"
600 );
601}
602
603#[test]
604fn no_eq_after_binding_snapshot_1() {
605 assert_error!("let wibble");
606}
607#[test]
608fn no_eq_after_binding_snapshot_2() {
609 assert_error!(
610 "let wibble
611 wibble = 4"
612 );
613}
614
615#[test]
616fn discard_left_hand_side_of_concat_pattern() {
617 assert_error!(
618 r#"
619 case "" {
620 _ <> rest -> rest
621 }
622 "#
623 );
624}
625
626#[test]
627fn assign_left_hand_side_of_concat_pattern() {
628 assert_error!(
629 r#"
630 case "" {
631 first <> rest -> rest
632 }
633 "#
634 );
635}
636
637#[test]
638fn discard_infix_and_match_suffix_of_concat_pattern() {
639 assert_error!(
640 r#"case "" {
641 "prefix" <> _ <> "suffix" -> Nil
642}"#
643 );
644}
645
646#[test]
647fn assign_infix_and_match_suffix_of_concat_pattern() {
648 assert_error!(
649 r#"case "" {
650 "prefix" <> infix <> "suffix" -> infix
651}"#
652 );
653}
654
655#[test]
656fn incomplete_suffix_match_in_concat_pattern() {
657 assert_error!(
658 r#"case "" {
659 "prefix" <> wibble <> -> wibble
660}"#
661 );
662}
663
664// https://github.com/gleam-lang/gleam/issues/1890
665#[test]
666fn valueless_list_spread_expression() {
667 assert_error!(r#"let x = [1, 2, 3, ..]"#);
668}
669
670// https://github.com/gleam-lang/gleam/issues/2035
671#[test]
672fn semicolons() {
673 assert_error!(r#"{ 2 + 3; - -5; }"#);
674}
675
676#[test]
677fn bare_expression() {
678 assert_parse!(r#"1"#);
679}
680
681// https://github.com/gleam-lang/gleam/issues/1991
682#[test]
683fn block_of_one() {
684 assert_parse!(r#"{ 1 }"#);
685}
686
687// https://github.com/gleam-lang/gleam/issues/1991
688#[test]
689fn block_of_two() {
690 assert_parse!(r#"{ 1 2 }"#);
691}
692
693// https://github.com/gleam-lang/gleam/issues/1991
694#[test]
695fn nested_block() {
696 assert_parse!(r#"{ 1 { 1.0 2.0 } 3 }"#);
697}
698
699// https://github.com/gleam-lang/gleam/issues/1831
700#[test]
701fn argument_scope() {
702 assert_error!(
703 "
7041 + let a = 5
705a
706"
707 );
708}
709
710#[test]
711fn multiple_external_for_same_project_erlang() {
712 assert_module_error!(
713 r#"
714@external(erlang, "one", "two")
715@external(erlang, "three", "four")
716pub fn one(x: Int) -> Int {
717 todo
718}
719"#
720 );
721}
722
723#[test]
724fn multiple_external_for_same_project_javascript() {
725 assert_module_error!(
726 r#"
727@external(javascript, "one", "two")
728@external(javascript, "three", "four")
729pub fn one(x: Int) -> Int {
730 todo
731}
732"#
733 );
734}
735
736#[test]
737fn unknown_external_target() {
738 assert_module_error!(
739 r#"
740@external(erl, "one", "two")
741pub fn one(x: Int) -> Int {
742 todo
743}"#
744 );
745}
746
747#[test]
748fn unknown_target() {
749 assert_module_error!(
750 r#"
751@target(abc)
752pub fn one() {}"#
753 );
754}
755
756#[test]
757fn missing_target() {
758 assert_module_error!(
759 r#"
760@target()
761pub fn one() {}"#
762 );
763}
764
765#[test]
766fn missing_target_and_bracket() {
767 assert_module_error!(
768 r#"
769@target(
770pub fn one() {}"#
771 );
772}
773
774#[test]
775fn unknown_attribute() {
776 assert_module_error!(
777 r#"@go_faster()
778pub fn main() { 1 }"#
779 );
780}
781
782#[test]
783fn incomplete_function() {
784 assert_error!("fn()");
785}
786
787#[test]
788fn multiple_deprecation_attributes() {
789 assert_module_error!(
790 r#"
791@deprecated("1")
792@deprecated("2")
793pub fn main() -> Nil {
794 Nil
795}
796"#
797 );
798}
799
800#[test]
801fn deprecation_with_no_message() {
802 assert_module_error!(
803 r#"
804@deprecated
805pub fn main() -> Nil {
806 Nil
807}
808"#
809 );
810}
811
812#[test]
813fn deprecation_with_no_message_on_constructor() {
814 assert_module_error!(
815 r#"
816pub type HashAlgorithm {
817 @deprecated
818 Md5
819 Sha224
820 Sha512
821}
822"#
823 );
824}
825
826#[test]
827fn deprecation_without_message() {
828 assert_module_error!(
829 r#"
830@deprecated()
831pub fn main() -> Nil {
832 Nil
833}
834"#
835 );
836}
837
838#[test]
839fn multiple_internal_attributes() {
840 assert_module_error!(
841 r#"
842@internal
843@internal
844pub fn main() -> Nil {
845 Nil
846}
847"#
848 );
849}
850
851#[test]
852fn attributes_with_no_definition() {
853 assert_module_error!(
854 r#"
855@deprecated("1")
856@target(erlang)
857"#
858 );
859}
860
861#[test]
862fn external_attribute_with_custom_type() {
863 assert_parse_module!(
864 r#"
865@external(erlang, "gleam_stdlib", "dict")
866@external(javascript, "./gleam_stdlib.d.ts", "Dict")
867pub type Dict(key, value)
868"#
869 );
870}
871
872#[test]
873fn external_attribute_with_non_fn_definition() {
874 assert_module_error!(
875 r#"
876@external(erlang, "module", "fun")
877pub type Fun = Fun
878"#
879 );
880}
881
882#[test]
883fn attributes_with_improper_definition() {
884 assert_module_error!(
885 r#"
886@deprecated("1")
887@external(erlang, "module", "fun")
888"#
889 );
890}
891
892#[test]
893fn const_with_function_call() {
894 assert_module_error!(
895 r#"
896pub fn wibble() { 123 }
897const wib: Int = wibble()
898"#
899 );
900}
901
902#[test]
903fn const_with_function_call_with_args() {
904 assert_module_error!(
905 r#"
906pub fn wibble() { 123 }
907const wib: Int = wibble(1, "wobble")
908"#
909 );
910}
911
912#[test]
913fn import_type() {
914 assert_parse_module!(r#"import wibble.{type Wobble, Wobble, type Wabble}"#);
915}
916
917#[test]
918fn reserved_auto() {
919 assert_module_error!(r#"const auto = 1"#);
920}
921
922#[test]
923fn reserved_delegate() {
924 assert_module_error!(r#"const delegate = 1"#);
925}
926
927#[test]
928fn reserved_derive() {
929 assert_module_error!(r#"const derive = 1"#);
930}
931
932#[test]
933fn reserved_else() {
934 assert_module_error!(r#"const else = 1"#);
935}
936
937#[test]
938fn reserved_implement() {
939 assert_module_error!(r#"const implement = 1"#);
940}
941
942#[test]
943fn reserved_macro() {
944 assert_module_error!(r#"const macro = 1"#);
945}
946
947#[test]
948fn reserved_test() {
949 assert_module_error!(r#"const test = 1"#);
950}
951
952#[test]
953fn reserved_echo() {
954 assert_module_error!(r#"const echo = 1"#);
955}
956
957#[test]
958fn capture_with_name() {
959 assert_module_error!(
960 r#"
961pub fn main() {
962 add(_name, 1)
963}
964
965fn add(x, y) {
966 x + y
967}
968"#
969 );
970}
971
972#[test]
973fn list_spread_with_no_tail_in_the_middle_of_a_list() {
974 assert_module_error!(
975 r#"
976pub fn main() -> Nil {
977 let xs = [1, 2, 3]
978 [1, 2, .., 3 + 3, 4]
979}
980"#
981 );
982}
983
984#[test]
985fn list_spread_followed_by_extra_items() {
986 assert_module_error!(
987 r#"
988pub fn main() -> Nil {
989 let xs = [1, 2, 3]
990 [1, 2, ..xs, 3 + 3, 4]
991}
992"#
993 );
994}
995
996#[test]
997fn list_spread_followed_by_extra_item_and_another_spread() {
998 assert_module_error!(
999 r#"
1000pub fn main() -> Nil {
1001 let xs = [1, 2, 3]
1002 let ys = [5, 6, 7]
1003 [..xs, 4, ..ys]
1004}
1005"#
1006 );
1007}
1008
1009#[test]
1010fn list_spread_followed_by_other_spread() {
1011 assert_module_error!(
1012 r#"
1013pub fn main() -> Nil {
1014 let xs = [1, 2, 3]
1015 let ys = [5, 6, 7]
1016 [1, ..xs, ..ys]
1017}
1018"#
1019 );
1020}
1021
1022#[test]
1023fn list_spread_as_first_item_followed_by_other_items() {
1024 assert_module_error!(
1025 r#"
1026pub fn main() -> Nil {
1027 let xs = [1, 2, 3]
1028 [..xs, 3 + 3, 4]
1029}
1030"#
1031 );
1032}
1033
1034// Tests for nested tuples and structs in tuples
1035// https://github.com/gleam-lang/gleam/issues/1980
1036
1037#[test]
1038fn nested_tuples() {
1039 assert_parse!(
1040 r#"
1041let tup = #(#(5, 6))
1042{tup.0}.1
1043"#
1044 );
1045}
1046
1047#[test]
1048fn nested_tuples_no_block() {
1049 assert_parse!(
1050 r#"
1051let tup = #(#(5, 6))
1052tup.0.1
1053"#
1054 );
1055}
1056
1057#[test]
1058fn deeply_nested_tuples() {
1059 assert_parse!(
1060 r#"
1061let tup = #(#(#(#(4))))
1062{{{tup.0}.0}.0}.0
1063"#
1064 );
1065}
1066
1067#[test]
1068fn deeply_nested_tuples_no_block() {
1069 assert_parse!(
1070 r#"
1071let tup = #(#(#(#(4))))
1072tup.0.0.0.0
1073"#
1074 );
1075}
1076
1077#[test]
1078fn inner_single_quote_parses() {
1079 assert_parse!(
1080 r#"
1081let a = "inner 'quotes'"
1082"#
1083 );
1084}
1085
1086#[test]
1087fn string_single_char_suggestion() {
1088 assert_module_error!(
1089 "
1090 pub fn main() {
1091 let a = 'example'
1092 }
1093 "
1094 );
1095}
1096
1097#[test]
1098fn private_internal_const() {
1099 assert_module_error!(
1100 "
1101@internal
1102const wibble = 1
1103"
1104 );
1105}
1106
1107#[test]
1108fn private_internal_type_alias() {
1109 assert_module_error!(
1110 "
1111@internal
1112type Alias = Int
1113"
1114 );
1115}
1116
1117#[test]
1118fn private_internal_function() {
1119 assert_module_error!(
1120 "
1121@internal
1122fn wibble() { todo }
1123"
1124 );
1125}
1126
1127#[test]
1128fn private_internal_type() {
1129 assert_module_error!(
1130 "
1131@internal
1132type Wibble {
1133 Wibble
1134}
1135"
1136 );
1137}
1138
1139#[test]
1140fn wrong_record_access_pattern() {
1141 assert_module_error!(
1142 "
1143pub fn main() {
1144 case wibble {
1145 wibble.thing -> 1
1146 }
1147}
1148"
1149 );
1150}
1151
1152#[test]
1153fn tuple_invalid_expr() {
1154 assert_module_error!(
1155 "
1156fn main() {
1157 #(1, 2, const)
1158}
1159"
1160 );
1161}
1162
1163#[test]
1164fn bit_array_invalid_segment() {
1165 assert_module_error!(
1166 "
1167fn main() {
1168 <<72, 101, 108, 108, 111, 44, 32, 74, 111, 101, const>>
1169}
1170"
1171 );
1172}
1173
1174#[test]
1175fn case_invalid_expression() {
1176 assert_module_error!(
1177 "
1178fn main() {
1179 case 1, type {
1180 _, _ -> 0
1181 }
1182}
1183"
1184 );
1185}
1186
1187#[test]
1188fn case_invalid_case_pattern() {
1189 assert_module_error!(
1190 "
1191fn main() {
1192 case 1 {
1193 -> -> 0
1194 }
1195}
1196"
1197 );
1198}
1199
1200#[test]
1201fn case_clause_no_subject() {
1202 assert_module_error!(
1203 "
1204fn main() {
1205 case 1 {
1206 -> 1
1207 _ -> 2
1208 }
1209}
1210"
1211 );
1212}
1213
1214#[test]
1215fn case_alternative_clause_no_subject() {
1216 assert_module_error!(
1217 "
1218fn main() {
1219 case 1 {
1220 1 | -> 1
1221 _ -> 1
1222 }
1223}
1224"
1225 );
1226}
1227
1228#[test]
1229fn use_invalid_assignments() {
1230 assert_module_error!(
1231 "
1232fn main() {
1233 use fn <- result.try(get_username())
1234}
1235"
1236 );
1237}
1238
1239#[test]
1240fn assignment_pattern_invalid_tuple() {
1241 assert_module_error!(
1242 "
1243fn main() {
1244 let #(a, case, c) = #(1, 2, 3)
1245}
1246"
1247 );
1248}
1249
1250#[test]
1251fn assignment_pattern_invalid_bit_segment() {
1252 assert_module_error!(
1253 "
1254fn main() {
1255 let <<b1, pub>> = <<24, 3>>
1256}
1257"
1258 );
1259}
1260
1261#[test]
1262fn case_list_pattern_after_spread() {
1263 assert_module_error!(
1264 "
1265fn main() {
1266 case somelist {
1267 [..rest, last] -> 1
1268 _ -> 2
1269 }
1270}
1271"
1272 );
1273}
1274
1275#[test]
1276fn type_invalid_constructor() {
1277 assert_module_error!(
1278 "
1279type A {
1280 A(String)
1281 type
1282}
1283"
1284 );
1285}
1286
1287// Tests whether diagnostic presents an example of how to formulate a proper
1288// record constructor based off a common user error pattern.
1289// https://github.com/gleam-lang/gleam/issues/3324
1290
1291#[test]
1292fn type_invalid_record_constructor() {
1293 assert_module_error!(
1294 "
1295pub type User {
1296 name: String,
1297}
1298"
1299 );
1300}
1301
1302#[test]
1303fn type_invalid_record_constructor_without_field_type() {
1304 assert_module_error!(
1305 "
1306pub opaque type User {
1307 name
1308}
1309"
1310 );
1311}
1312
1313#[test]
1314fn type_invalid_record_constructor_invalid_field_type() {
1315 assert_module_error!(
1316 r#"
1317type User {
1318 name: "Test User",
1319}
1320"#
1321 );
1322}
1323
1324#[test]
1325fn type_invalid_type_name() {
1326 assert_module_error!(
1327 "
1328type A(a, type) {
1329 A
1330}
1331"
1332 );
1333}
1334
1335#[test]
1336fn type_invalid_constructor_arg() {
1337 assert_module_error!(
1338 "
1339type A {
1340 A(type: String)
1341}
1342"
1343 );
1344}
1345
1346#[test]
1347fn type_invalid_record() {
1348 assert_module_error!(
1349 "
1350type A {
1351 One
1352 Two
1353 3
1354}
1355"
1356 );
1357}
1358
1359#[test]
1360fn function_type_invalid_param_type() {
1361 assert_module_error!(
1362 "
1363fn f(g: fn(Int, 1) -> Int) -> Int {
1364 g(0, 1)
1365}
1366"
1367 );
1368}
1369
1370#[test]
1371fn function_invalid_signature() {
1372 assert_module_error!(
1373 r#"
1374fn f(a, "b") -> String {
1375 a <> b
1376}
1377"#
1378 );
1379}
1380
1381#[test]
1382fn const_invalid_tuple() {
1383 assert_module_error!(
1384 "
1385const a = #(1, 2, <-)
1386"
1387 );
1388}
1389
1390#[test]
1391fn const_invalid_list() {
1392 assert_module_error!(
1393 "
1394const a = [1, 2, <-]
1395"
1396 );
1397}
1398
1399#[test]
1400fn const_invalid_bit_array_segment() {
1401 assert_module_error!(
1402 "
1403const a = <<1, 2, <->>
1404"
1405 );
1406}
1407
1408#[test]
1409fn const_invalid_record_constructor() {
1410 assert_module_error!(
1411 "
1412type A {
1413 A(String, Int)
1414}
1415const a = A(\"a\", let)
1416"
1417 );
1418}
1419
1420// record access should parse even if there is no label written
1421#[test]
1422fn record_access_no_label() {
1423 assert_parse_module!(
1424 "
1425type Wibble {
1426 Wibble(wibble: String)
1427}
1428
1429fn wobble() {
1430 Wibble(\"a\").
1431}
1432"
1433 );
1434}
1435
1436#[test]
1437fn newline_tokens() {
1438 assert_eq!(
1439 make_tokenizer("1\n\n2\n").collect_vec(),
1440 [
1441 Ok((
1442 0,
1443 Token::Int {
1444 value: "1".into(),
1445 int_value: 1.into()
1446 },
1447 1
1448 )),
1449 Ok((1, Token::NewLine, 2)),
1450 Ok((2, Token::NewLine, 3)),
1451 Ok((
1452 3,
1453 Token::Int {
1454 value: "2".into(),
1455 int_value: 2.into()
1456 },
1457 4
1458 )),
1459 Ok((4, Token::NewLine, 5))
1460 ]
1461 );
1462}
1463
1464// https://github.com/gleam-lang/gleam/issues/1756
1465#[test]
1466fn arithmetic_in_guards() {
1467 assert_parse!(
1468 "
1469case 2, 3 {
1470 x, y if x + y == 1 -> True
1471}"
1472 );
1473}
1474
1475#[test]
1476fn const_string_concat() {
1477 assert_parse_module!(
1478 "
1479const cute = \"cute\"
1480const cute_bee = cute <> \"bee\"
1481"
1482 );
1483}
1484
1485#[test]
1486fn const_string_concat_naked_right() {
1487 assert_module_error!(
1488 "
1489const no_cute_bee = \"cute\" <>
1490"
1491 );
1492}
1493
1494#[test]
1495fn function_call_in_case_clause_guard() {
1496 assert_error!(
1497 r#"
1498let my_string = "hello"
1499case my_string {
1500 _ if length(my_string) > 2 -> io.debug("doesn't work')
1501}"#
1502 );
1503}
1504
1505#[test]
1506fn dot_access_function_call_in_case_clause_guard() {
1507 assert_error!(
1508 r#"
1509let my_string = "hello"
1510case my_string {
1511 _ if string.length(my_string) > 2 -> io.debug("doesn't work')
1512}"#
1513 );
1514}
1515
1516#[test]
1517fn invalid_left_paren_in_case_clause_guard() {
1518 assert_error!(
1519 r#"
1520let my_string = "hello"
1521case my_string {
1522 _ if string.length( > 2 -> io.debug("doesn't work')
1523}"#
1524 );
1525}
1526
1527#[test]
1528fn string_concatenation_in_case_clause_guard() {
1529 assert_parse!(
1530 r#"
1531let my_string = "hello "
1532case my_string {
1533 _ if my_string <> "world" == "hello world" -> io.debug("ok")
1534}"#
1535 );
1536}
1537
1538#[test]
1539fn constant_record_with_empty_arguments_is_record() {
1540 assert_parse_module!("pub const wibble = Wibble()");
1541}
1542
1543#[test]
1544fn calling_module_constant_as_constructor() {
1545 assert_module_error!(
1546 "
1547pub type Wibble { Wibble(Int) }
1548pub const wibble = Wibble
1549pub const a = wibble(1)
1550"
1551 );
1552}
1553
1554#[test]
1555fn invalid_label_shorthand() {
1556 assert_module_error!(
1557 "
1558pub fn main() {
1559 wibble(:)
1560}
1561"
1562 );
1563}
1564
1565#[test]
1566fn invalid_label_shorthand_2() {
1567 assert_module_error!(
1568 "
1569pub fn main() {
1570 wibble(:,)
1571}
1572"
1573 );
1574}
1575
1576#[test]
1577fn invalid_label_shorthand_3() {
1578 assert_module_error!(
1579 "
1580pub fn main() {
1581 wibble(:arg)
1582}
1583"
1584 );
1585}
1586
1587#[test]
1588fn invalid_label_shorthand_4() {
1589 assert_module_error!(
1590 "
1591pub fn main() {
1592 wibble(arg::)
1593}
1594"
1595 );
1596}
1597
1598#[test]
1599fn invalid_label_shorthand_5() {
1600 assert_module_error!(
1601 "
1602pub fn main() {
1603 wibble(arg::arg)
1604}
1605"
1606 );
1607}
1608
1609#[test]
1610fn invalid_pattern_label_shorthand() {
1611 assert_module_error!(
1612 "
1613pub fn main() {
1614 let Wibble(:) = todo
1615}
1616"
1617 );
1618}
1619
1620#[test]
1621fn invalid_pattern_label_shorthand_2() {
1622 assert_module_error!(
1623 "
1624pub fn main() {
1625 let Wibble(:arg) = todo
1626}
1627"
1628 );
1629}
1630
1631#[test]
1632fn invalid_pattern_label_shorthand_3() {
1633 assert_module_error!(
1634 "
1635pub fn main() {
1636 let Wibble(arg::) = todo
1637}
1638"
1639 );
1640}
1641
1642#[test]
1643fn invalid_pattern_label_shorthand_4() {
1644 assert_module_error!(
1645 "
1646pub fn main() {
1647 let Wibble(arg: arg:) = todo
1648}
1649"
1650 );
1651}
1652
1653#[test]
1654fn invalid_pattern_label_shorthand_5() {
1655 assert_module_error!(
1656 "
1657pub fn main() {
1658 let Wibble(arg1: arg2:) = todo
1659}
1660"
1661 );
1662}
1663
1664fn first_parsed_docstring(src: &str) -> EcoString {
1665 let parsed =
1666 crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null())
1667 .expect("should parse");
1668
1669 parsed
1670 .module
1671 .definitions
1672 .first()
1673 .expect("parsed a definition")
1674 .definition
1675 .get_doc()
1676 .expect("definition without doc")
1677}
1678
1679#[test]
1680fn doc_comment_before_comment_is_not_attached_to_following_function() {
1681 assert_eq!(
1682 first_parsed_docstring(
1683 r#"
1684 /// Not included!
1685 // pub fn call()
1686
1687 /// Doc!
1688 pub fn wibble() {}
1689"#
1690 ),
1691 " Doc!\n"
1692 )
1693}
1694
1695#[test]
1696fn doc_comment_before_comment_is_not_attached_to_following_type() {
1697 assert_eq!(
1698 first_parsed_docstring(
1699 r#"
1700 /// Not included!
1701 // pub fn call()
1702
1703 /// Doc!
1704 pub type Wibble
1705"#
1706 ),
1707 " Doc!\n"
1708 )
1709}
1710
1711#[test]
1712fn doc_comment_before_comment_is_not_attached_to_following_type_alias() {
1713 assert_eq!(
1714 first_parsed_docstring(
1715 r#"
1716 /// Not included!
1717 // pub fn call()
1718
1719 /// Doc!
1720 pub type Wibble = Int
1721"#
1722 ),
1723 " Doc!\n"
1724 )
1725}
1726
1727#[test]
1728fn doc_comment_before_comment_is_not_attached_to_following_constant() {
1729 assert_eq!(
1730 first_parsed_docstring(
1731 r#"
1732 /// Not included!
1733 // pub fn call()
1734
1735 /// Doc!
1736 pub const wibble = 1
1737"#
1738 ),
1739 " Doc!\n"
1740 );
1741}
1742
1743#[test]
1744fn non_module_level_function_with_a_name() {
1745 assert_module_error!(
1746 r#"
1747pub fn main() {
1748 fn my() { 1 }
1749}
1750"#
1751 );
1752}
1753
1754#[test]
1755fn error_message_on_variable_starting_with_underscore() {
1756 // https://github.com/gleam-lang/gleam/issues/3504
1757 assert_module_error!(
1758 "
1759 pub fn main() {
1760 let val = _func_starting_with_underscore(1)
1761 }"
1762 );
1763}
1764
1765#[test]
1766fn non_module_level_function_with_not_a_name() {
1767 assert_module_error!(
1768 r#"
1769pub fn main() {
1770 fn @() { 1 } // wrong token and not a name
1771}
1772"#
1773 );
1774}
1775
1776#[test]
1777fn error_message_on_variable_starting_with_underscore2() {
1778 // https://github.com/gleam-lang/gleam/issues/3504
1779 assert_module_error!(
1780 "
1781 pub fn main() {
1782 case 1 {
1783 1 -> _with_underscore(1)
1784 }
1785 }"
1786 );
1787}
1788
1789#[test]
1790fn function_inside_a_type() {
1791 assert_module_error!(
1792 r#"
1793type Wibble {
1794 fn wobble() {}
1795}
1796"#
1797 );
1798}
1799
1800#[test]
1801fn pub_function_inside_a_type() {
1802 assert_module_error!(
1803 r#"
1804type Wibble {
1805 pub fn wobble() {}
1806}
1807"#
1808 );
1809}
1810
1811#[test]
1812fn if_like_expression() {
1813 assert_module_error!(
1814 r#"
1815pub fn main() {
1816 let a = if wibble {
1817 wobble
1818 }
1819}
1820"#
1821 );
1822}
1823
1824// https://github.com/gleam-lang/gleam/issues/3796
1825#[test]
1826fn missing_type_constructor_arguments_in_type_definition() {
1827 assert_module_error!(
1828 "
1829pub type A() {
1830 A(Int)
1831}
1832"
1833 );
1834}
1835
1836#[test]
1837fn tuple_without_hash() {
1838 assert_module_error!(
1839 r#"
1840pub fn main() {
1841 let triple = (1, 2.2, "three")
1842 io.debug(triple)
1843 let (a, *, *) = triple
1844 io.debug(a)
1845 io.debug(triple.1)
1846}
1847"#
1848 );
1849}
1850
1851#[test]
1852fn deprecation_attribute_on_type_variant() {
1853 assert_parse_module!(
1854 r#"
1855type Wibble {
1856 @deprecated("1")
1857 Wibble1
1858 Wibble2
1859}
1860"#
1861 );
1862}
1863
1864#[test]
1865
1866fn float_empty_exponent() {
1867 assert_error!("1.32e");
1868}
1869
1870#[test]
1871fn multiple_deprecation_attribute_on_type_variant() {
1872 assert_module_error!(
1873 r#"
1874type Wibble {
1875 @deprecated("1")
1876 @deprecated("2")
1877 Wibble1
1878 Wibble2
1879}
1880"#
1881 );
1882}
1883
1884#[test]
1885fn target_attribute_on_type_variant() {
1886 assert_module_error!(
1887 r#"
1888type Wibble {
1889 @target(erlang)
1890 Wibble2
1891}
1892"#
1893 );
1894}
1895
1896#[test]
1897fn internal_attribute_on_type_variant() {
1898 assert_module_error!(
1899 r#"
1900type Wibble {
1901 @internal
1902 Wibble1
1903}
1904"#
1905 );
1906}
1907
1908#[test]
1909fn external_attribute_on_type_variant() {
1910 assert_module_error!(
1911 r#"
1912type Wibble {
1913 @external(erlang, "one", "two")
1914 Wibble1
1915}
1916"#
1917 );
1918}
1919
1920#[test]
1921fn multiple_unsupported_attributes_on_type_variant() {
1922 assert_module_error!(
1923 r#"
1924type Wibble {
1925 @external(erlang, "one", "two")
1926 @target(erlang)
1927 @internal
1928 Wibble1
1929}
1930"#
1931 );
1932}
1933
1934#[test]
1935// https://github.com/gleam-lang/gleam/issues/3870
1936fn nested_tuple_access_after_function() {
1937 assert_parse!("tuple().0.1");
1938}
1939
1940#[test]
1941fn case_expression_without_body() {
1942 assert_parse!("case a");
1943}
1944
1945#[test]
1946fn assert_statement() {
1947 assert_parse!("assert 10 != 11");
1948}
1949
1950#[test]
1951fn assert_statement_with_message() {
1952 assert_parse!(r#"assert False as "Uh oh""#);
1953}
1954
1955#[test]
1956fn assert_statement_without_expression() {
1957 assert_error!("assert");
1958}
1959
1960#[test]
1961fn assert_statement_followed_by_statement() {
1962 assert_error!("assert let a = 10");
1963}
1964
1965#[test]
1966fn special_error_for_pythonic_import() {
1967 assert_module_error!("import gleam.io");
1968}
1969
1970#[test]
1971fn special_error_for_pythonic_neste_import() {
1972 assert_module_error!("import one.two.three");
1973}
1974
1975#[test]
1976fn doesnt_issue_special_error_for_pythonic_import_if_slash() {
1977 assert_module_error!("import one/two.three");
1978}
1979
1980#[test]
1981fn operator_in_pattern_size() {
1982 assert_parse!("let assert <<size, payload:size(size - 1)>> = <<>>");
1983}
1984
1985#[test]
1986fn correct_precedence_in_pattern_size() {
1987 assert_parse!("let assert <<size, payload:size(size + 2 * 8)>> = <<>>");
1988}
1989
1990#[test]
1991fn private_opaque_type_is_parsed() {
1992 assert_parse_module!("opaque type Wibble { Wobble }");
1993}
1994
1995#[test]
1996fn case_guard_with_nested_blocks() {
1997 assert_parse!(
1998 "case 1 {
1999 _ if { 1 || { 1 || 1 } } || 1 -> 1
2000}"
2001 );
2002}
2003
2004#[test]
2005fn case_guard_with_empty_block() {
2006 assert_error!(
2007 "case 1 {
2008 _ if a || {} -> 1
2009}"
2010 );
2011}
2012
2013#[test]
2014fn constant_inside_function() {
2015 assert_module_error!(
2016 "
2017pub fn main() {
2018 const x = 10
2019 x
2020}
2021"
2022 );
2023}
2024
2025#[test]
2026fn function_definition_angle_generics_error() {
2027 assert_module_error!("fn id<T>(x: T) { x }");
2028}
2029
2030#[test]
2031fn type_angle_generics_usage_without_module_error() {
2032 assert_error!("let list: List<Int, String> = []");
2033}
2034
2035#[test]
2036fn type_angle_generics_usage_with_module_error() {
2037 assert_error!("let set: set.Set<Int> = set.new()");
2038}
2039
2040#[test]
2041fn type_angle_generics_definition_error() {
2042 assert_module_error!(
2043 r#"
2044type Either<a, b> {
2045 This(a)
2046 That(b)
2047}
2048"#
2049 );
2050}
2051
2052#[test]
2053fn type_angle_generics_definition_with_upname_error() {
2054 assert_module_error!(
2055 r#"
2056type Either<A, B> {
2057 This(A)
2058 That(B)
2059}
2060"#
2061 );
2062}
2063
2064#[test]
2065fn type_angle_generics_definition_error_fallback() {
2066 // Example of a more incorrect syntax, where Gleam doesn't make a suggestion.
2067 // In this case, an example type argument is used instead.
2068 assert_module_error!(
2069 r#"
2070type Either<type A, type B> {
2071 This(A)
2072 That(B)
2073}
2074"#
2075 );
2076}
2077
2078#[test]
2079fn wrong_type_of_comments_with_hash() {
2080 assert_module_error!(
2081 r#"
2082pub fn main() {
2083 # a python-style comment
2084}
2085"#
2086 );
2087}
2088
2089#[test]
2090fn wrong_function_return_type_declaration_using_colon_instead_of_right_arrow() {
2091 assert_module_error!(
2092 r#"
2093pub fn main(): Nil {}
2094 "#
2095 );
2096}
2097
2098#[test]
2099fn const_record_update_basic() {
2100 assert_parse_module!(
2101 r#"
2102type Person {
2103 Person(name: String, age: Int)
2104}
2105
2106const alice = Person("Alice", 30)
2107const bob = Person(..alice, name: "Bob")
2108"#
2109 );
2110}
2111
2112#[test]
2113fn const_record_update_all_fields() {
2114 assert_parse_module!(
2115 r#"
2116type Person {
2117 Person(name: String, age: Int, city: String)
2118}
2119
2120const base = Person("Alice", 30, "London")
2121const updated = Person(..base, name: "Bob", age: 25, city: "Paris")
2122"#
2123 );
2124}
2125
2126#[test]
2127fn const_record_update_only() {
2128 assert_parse_module!(
2129 r#"
2130type Person {
2131 Person(name: String, age: Int)
2132}
2133
2134const alice = Person("Alice", 30)
2135const bob = Person(..alice)
2136"#
2137 );
2138}
2139
2140#[test]
2141fn const_record_update_with_module() {
2142 assert_parse_module!(
2143 r#"
2144const local_const = other.Record(..other.base, field: value)
2145"#
2146 );
2147}
2148
2149// https://github.com/gleam-lang/gleam/issues/5391
2150#[test]
2151fn byte_order_mark() {
2152 assert_parse!("\u{feff}todo");
2153}
2154
2155// https://github.com/gleam-lang/gleam/issues/5391
2156#[test]
2157fn byte_order_mark_module() {
2158 assert_parse_module!(
2159 "\u{feff}
2160const local_const = other.Record(..other.base, field: value)
2161"
2162 );
2163}
2164
2165#[test]
2166fn prepend_to_const_list_without_comma() {
2167 // While this is valid (but deprecated) syntax for expressions, prepending to
2168 // constant lists wasn't added until after the deprecation, so it was never
2169 // valid in the first place.
2170 assert_module_error!(
2171 "
2172const wibble = [2, 3]
2173const wobble = [1 ..wibble]
2174"
2175 );
2176}
2177
2178#[test]
2179fn prepend_to_const_list_with_multiple_spreads() {
2180 assert_module_error!(
2181 "
2182const wibble = [2, 3]
2183const wobble = [0, 1]
2184const wubble = [..wobble, ..wibble]
2185"
2186 );
2187}
2188
2189#[test]
2190fn prepend_to_const_list_with_no_tail() {
2191 assert_module_error!(
2192 "
2193const wibble = [1, 2, ..]
2194"
2195 );
2196}
2197
2198#[test]
2199fn prepend_no_elements_to_const_list() {
2200 assert_module_error!(
2201 "
2202const wibble = [2, 3]
2203const wobble = [..wibble]
2204"
2205 );
2206}
2207
2208#[test]
2209fn append_to_const_list() {
2210 assert_module_error!(
2211 "
2212const wibble = [2, 3]
2213const wobble = [..wibble, 4, 5]
2214"
2215 );
2216}
2217
2218#[test]
2219fn parse_error_for_greater_sign_after_invalid_qualified_type() {
2220 assert_module_error!("pub fn main() -> wibble.<a> { todo }");
2221}
2222
2223#[test]
2224fn parse_error_for_type_list_after_invalid_qualified_type_1() {
2225 assert_module_error!("pub fn main() -> wibble.() { todo }");
2226}
2227
2228#[test]
2229fn parse_error_for_type_list_after_invalid_qualified_type_2() {
2230 assert_module_error!("pub fn main() -> wibble.(a, b) { todo }");
2231}
2232
2233#[test]
2234fn unicode_left_double_quotation_mark() {
2235 assert_module_error!("pub fn main() { \u{201C}hello\u{201D} }");
2236}
2237
2238#[test]
2239fn unicode_right_double_quotation_mark() {
2240 assert_module_error!("pub fn main() { \u{201D}hello }");
2241}
2242
2243#[test]
2244fn unicode_left_single_quotation_mark() {
2245 assert_module_error!("pub fn main() { \u{2018}hello\u{2019} }");
2246}
2247
2248#[test]
2249fn unicode_right_single_quotation_mark() {
2250 assert_module_error!("pub fn main() { \u{2019}hello }");
2251}
2252
2253#[test]
2254fn unicode_en_dash() {
2255 assert_module_error!("pub fn main() { 1\u{2013}2 }");
2256}
2257
2258#[test]
2259fn unicode_em_dash() {
2260 assert_module_error!("pub fn main() { 1\u{2014}2 }");
2261}
2262
2263#[test]
2264fn unicode_asterisk_operator() {
2265 assert_module_error!("pub fn main() { 1\u{2217}2 }");
2266}
2267
2268#[test]
2269fn unicode_division_slash() {
2270 assert_module_error!("pub fn main() { 1\u{2215}2 }");
2271}
2272
2273#[test]
2274fn unicode_non_breaking_space() {
2275 assert_module_error!("pub fn main() { let\u{00A0}x = 1\n x }");
2276}
2277
2278#[test]
2279fn unicode_zero_width_space() {
2280 assert_module_error!("pub fn main() { let\u{200B}x = 1\n x }");
2281}
2282
2283#[test]
2284fn unicode_cyrillic_a() {
2285 assert_module_error!("pub fn main() { \u{0430} }");
2286}
2287
2288#[test]
2289fn unicode_cyrillic_e() {
2290 assert_module_error!("pub fn main() { \u{0435} }");
2291}
2292
2293#[test]
2294fn unicode_cyrillic_o() {
2295 assert_module_error!("pub fn main() { \u{043E} }");
2296}
2297
2298#[test]
2299fn unicode_cyrillic_p() {
2300 assert_module_error!("pub fn main() { \u{0440} }");
2301}
2302
2303#[test]
2304fn unicode_modifier_letter_capital_i() {
2305 assert_module_error!("pub fn main() { \u{1D35} }");
2306}
2307
2308#[test]
2309fn unicode_low_single_comma_quotation_mark() {
2310 assert_module_error!("pub fn main() { #(1\u{201A} 2) }");
2311}
2312
2313#[test]
2314fn unicode_fullwidth_comma() {
2315 assert_module_error!("pub fn main() { #(1\u{FF0C} 2) }");
2316}
2317
2318#[test]
2319fn unicode_ideographic_comma() {
2320 assert_module_error!("pub fn main() { #(1\u{3001} 2) }");
2321}
2322
2323#[test]
2324fn unicode_fullwidth_exclamation_mark() {
2325 assert_module_error!("pub fn main() { \u{FF01}= True }");
2326}
2327
2328#[test]
2329fn unicode_fullwidth_left_square_bracket() {
2330 assert_module_error!("pub fn main() { \u{FF3B}1, 2] }");
2331}
2332
2333#[test]
2334fn unicode_fullwidth_right_square_bracket() {
2335 assert_module_error!("pub fn main() { [1, 2\u{FF3D} }");
2336}
2337
2338#[test]
2339fn unicode_fullwidth_left_parenthesis() {
2340 assert_module_error!("pub fn main\u{FF08}) { Nil }");
2341}
2342
2343#[test]
2344fn unicode_fullwidth_right_parenthesis() {
2345 assert_module_error!("pub fn main(\u{FF09} { Nil }");
2346}
2347
2348#[test]
2349fn unicode_fullwidth_full_stop() {
2350 assert_module_error!("pub fn main() { io\u{FF0E}println(\"hi\") }");
2351}
2352
2353#[test]
2354fn unicode_ideographic_full_stop() {
2355 assert_module_error!("pub fn main() { io\u{3002}println(\"hi\") }");
2356}
2357
2358#[test]
2359fn unicode_fullwidth_less_than_sign() {
2360 assert_module_error!("pub fn main() { 1 \u{FF1C} 2 }");
2361}
2362
2363#[test]
2364fn unicode_fullwidth_greater_than_sign() {
2365 assert_module_error!("pub fn main() { 1 \u{FF1E} 2 }");
2366}
2367
2368#[test]
2369fn unicode_fullwidth_vertical_line() {
2370 assert_module_error!("pub fn main() { True \u{FF5C}\u{FF5C} False }");
2371}
2372
2373#[test]
2374fn unicode_fullwidth_at_sign() {
2375 assert_module_error!("\u{FF20}deprecated(\"old\")\npub fn main() { Nil }");
2376}
2377
2378#[test]
2379fn unicode_fullwidth_caret() {
2380 assert_module_error!("pub fn main() { 0b1 \u{FF3E} 0b1 }");
2381}
2382
2383#[test]
2384fn unicode_fullwidth_colon() {
2385 assert_module_error!("pub fn wibble(x\u{FF1A} Int) { x }");
2386}
2387
2388#[test]
2389fn missing_todo_constant_message() {
2390 assert_module_error!("pub const wibble = todo as");
2391}
2392
2393#[test]
2394fn missing_todo_constant_message_2() {
2395 assert_module_error!(
2396 "pub const wibble = todo as
2397pub fn wibble() {}"
2398 );
2399}
2400
2401// https://github.com/gleam-lang/gleam/issues/5711
2402#[test]
2403fn parsing_bit_array_constant_with_non_integer_size() {
2404 assert_module_error!("pub const wibble = <<1:size(something)>>");
2405}
2406
2407#[test]
2408fn parsing_bit_array_constant_with_non_integer_unit() {
2409 assert_module_error!("pub const wibble = <<1:unit(something)>>");
2410}
2411
2412#[test]
2413fn parsing_bit_array_expression_with_invalid_size() {
2414 assert_module_error!(
2415 "pub fn wibble() {
2416 let assert <<1:size(Upname)>> = todo
2417}"
2418 );
2419}
2420
2421#[test]
2422fn parsing_bit_array_expression_with_invalid_unit() {
2423 assert_module_error!(
2424 "pub fn wibble() {
2425 <<1:unit(Upname)>>
2426}"
2427 );
2428}
2429
2430#[test]
2431fn parsing_bit_array_pattern_with_invalid_unit() {
2432 assert_module_error!(
2433 "pub fn wibble() {
2434 let assert <<1:unit(Upname)>> = todo
2435}"
2436 );
2437}
2438
2439#[test]
2440fn parsing_bit_array_constant_with_invalid_segment_type() {
2441 assert_module_error!("pub const wibble = <<1:Upname>>");
2442}
2443
2444#[test]
2445fn parsing_invalid_external_module_name() {
2446 assert_module_error!(
2447 "
2448@external(erlang, Upname, [])
2449fn wibble() -> Nil
2450"
2451 );
2452}
2453
2454#[test]
2455fn parsing_invalid_external_function_name() {
2456 assert_module_error!(
2457 r#"
2458@external(erlang, "module", Upname)
2459fn wibble() -> Nil
2460"#
2461 );
2462}
2463
2464#[test]
2465fn parsing_invalid_deprecation_message() {
2466 assert_module_error!(
2467 r#"
2468@deprecated(Upname)
2469fn wibble() -> Nil
2470"#
2471 );
2472}
2473
2474#[test]
2475fn error_message_when_using_discard_pattern_for_function_name() {
2476 assert_module_error!("fn _wibble() -> Nil");
2477}
2478
2479#[test]
2480fn error_message_when_using_discard_pattern_for_constant_name() {
2481 assert_module_error!("const _wibble = 1");
2482}
2483
2484#[test]
2485fn error_message_when_using_discard_pattern_for_module_name() {
2486 assert_module_error!("import _wibble");
2487}
2488
2489#[test]
2490fn error_message_when_using_discard_pattern_for_as_pattern() {
2491 assert_module_error!(
2492 "pub fn main() {
2493 case todo {
2494 wibble as _wobble -> todo
2495 }
2496}"
2497 );
2498}