Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / compiler-core / src / parse / tests.rs
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}