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