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