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
36 kB 2005 lines
1use crate::ast::SrcSpan; 2use crate::parse::error::{ 3 InvalidUnicodeEscapeError, LexicalError, LexicalErrorType, ParseError, ParseErrorType, 4}; 5use crate::parse::lexer::make_tokenizer; 6use crate::parse::token::Token; 7use crate::warning::WarningEmitter; 8use camino::Utf8PathBuf; 9 10use ecow::EcoString; 11use itertools::Itertools; 12use pretty_assertions::assert_eq; 13 14macro_rules! assert_error { 15 ($src:expr, $error:expr $(,)?) => { 16 let result = crate::parse::parse_statement_sequence($src).expect_err("should not parse"); 17 assert_eq!(($src, $error), ($src, result),); 18 }; 19 ($src:expr) => { 20 let error = $crate::parse::tests::expect_error($src); 21 let output = format!("----- SOURCE CODE\n{}\n\n----- ERROR\n{}", $src, error); 22 insta::assert_snapshot!(insta::internals::AutoName, output, $src); 23 }; 24} 25 26macro_rules! assert_module_error { 27 ($src:expr) => { 28 let error = $crate::parse::tests::expect_module_error($src); 29 let output = format!("----- SOURCE CODE\n{}\n\n----- ERROR\n{}", $src, error); 30 insta::assert_snapshot!(insta::internals::AutoName, output, $src); 31 }; 32} 33 34macro_rules! assert_parse_module { 35 ($src:expr) => { 36 let result = crate::parse::parse_module( 37 camino::Utf8PathBuf::from("test/path"), 38 $src, 39 &crate::warning::WarningEmitter::null(), 40 ) 41 .expect("should parse"); 42 insta::assert_snapshot!(insta::internals::AutoName, &format!("{:#?}", result), $src); 43 }; 44} 45 46macro_rules! assert_parse { 47 ($src:expr) => { 48 let result = crate::parse::parse_statement_sequence($src).expect("should parse"); 49 insta::assert_snapshot!(insta::internals::AutoName, &format!("{:#?}", result), $src); 50 }; 51} 52 53pub fn expect_module_error(src: &str) -> String { 54 let result = 55 crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null()) 56 .expect_err("should not parse"); 57 let error = crate::error::Error::Parse { 58 src: src.into(), 59 path: Utf8PathBuf::from("/src/parse/error.gleam"), 60 error: Box::new(result), 61 }; 62 error.pretty_string() 63} 64 65pub fn expect_error(src: &str) -> String { 66 let result = crate::parse::parse_statement_sequence(src).expect_err("should not parse"); 67 let error = crate::error::Error::Parse { 68 src: src.into(), 69 path: Utf8PathBuf::from("/src/parse/error.gleam"), 70 error: Box::new(result), 71 }; 72 error.pretty_string() 73} 74 75#[test] 76fn int_tests() { 77 // bad binary digit 78 assert_error!( 79 "0b012", 80 ParseError { 81 error: ParseErrorType::LexError { 82 error: LexicalError { 83 error: LexicalErrorType::DigitOutOfRadix, 84 location: SrcSpan { start: 4, end: 4 }, 85 } 86 }, 87 location: SrcSpan { start: 4, end: 4 }, 88 } 89 ); 90 // bad octal digit 91 assert_error!( 92 "0o12345678", 93 ParseError { 94 error: ParseErrorType::LexError { 95 error: LexicalError { 96 error: LexicalErrorType::DigitOutOfRadix, 97 location: SrcSpan { start: 9, end: 9 }, 98 } 99 }, 100 location: SrcSpan { start: 9, end: 9 }, 101 } 102 ); 103 // no int value 104 assert_error!( 105 "0x", 106 ParseError { 107 error: ParseErrorType::LexError { 108 error: LexicalError { 109 error: LexicalErrorType::RadixIntNoValue, 110 location: SrcSpan { start: 1, end: 1 }, 111 } 112 }, 113 location: SrcSpan { start: 1, end: 1 }, 114 } 115 ); 116 // trailing underscore 117 assert_error!( 118 "1_000_", 119 ParseError { 120 error: ParseErrorType::LexError { 121 error: LexicalError { 122 error: LexicalErrorType::NumTrailingUnderscore, 123 location: SrcSpan { start: 5, end: 5 }, 124 } 125 }, 126 location: SrcSpan { start: 5, end: 5 }, 127 } 128 ); 129} 130 131#[test] 132fn string_bad_character_escape() { 133 assert_error!( 134 r#""\g""#, 135 ParseError { 136 error: ParseErrorType::LexError { 137 error: LexicalError { 138 error: LexicalErrorType::BadStringEscape, 139 location: SrcSpan { start: 1, end: 2 }, 140 } 141 }, 142 location: SrcSpan { start: 1, end: 2 }, 143 } 144 ); 145} 146 147#[test] 148fn string_bad_character_escape_leading_backslash() { 149 assert_error!( 150 r#""\\\g""#, 151 ParseError { 152 error: ParseErrorType::LexError { 153 error: LexicalError { 154 error: LexicalErrorType::BadStringEscape, 155 location: SrcSpan { start: 3, end: 4 }, 156 } 157 }, 158 location: SrcSpan { start: 3, end: 4 }, 159 } 160 ); 161} 162 163#[test] 164fn string_freestanding_unicode_escape_sequence() { 165 assert_error!( 166 r#""\u""#, 167 ParseError { 168 error: ParseErrorType::LexError { 169 error: LexicalError { 170 error: LexicalErrorType::InvalidUnicodeEscape( 171 InvalidUnicodeEscapeError::MissingOpeningBrace, 172 ), 173 location: SrcSpan { start: 2, end: 3 }, 174 } 175 }, 176 location: SrcSpan { start: 2, end: 3 }, 177 } 178 ); 179} 180 181#[test] 182fn string_unicode_escape_sequence_no_braces() { 183 assert_error!( 184 r#""\u65""#, 185 ParseError { 186 error: ParseErrorType::LexError { 187 error: LexicalError { 188 error: LexicalErrorType::InvalidUnicodeEscape( 189 InvalidUnicodeEscapeError::MissingOpeningBrace, 190 ), 191 location: SrcSpan { start: 2, end: 3 }, 192 } 193 }, 194 location: SrcSpan { start: 2, end: 3 }, 195 } 196 ); 197} 198 199#[test] 200fn string_unicode_escape_sequence_invalid_hex() { 201 assert_error!( 202 r#""\u{z}""#, 203 ParseError { 204 error: ParseErrorType::LexError { 205 error: LexicalError { 206 error: LexicalErrorType::InvalidUnicodeEscape( 207 InvalidUnicodeEscapeError::ExpectedHexDigitOrCloseBrace, 208 ), 209 location: SrcSpan { start: 4, end: 5 }, 210 } 211 }, 212 location: SrcSpan { start: 4, end: 5 }, 213 } 214 ); 215} 216 217#[test] 218fn string_unclosed_unicode_escape_sequence() { 219 assert_error!( 220 r#""\u{039a""#, 221 ParseError { 222 error: ParseErrorType::LexError { 223 error: LexicalError { 224 error: LexicalErrorType::InvalidUnicodeEscape( 225 InvalidUnicodeEscapeError::ExpectedHexDigitOrCloseBrace, 226 ), 227 location: SrcSpan { start: 8, end: 9 }, 228 } 229 }, 230 location: SrcSpan { start: 8, end: 9 }, 231 } 232 ); 233} 234 235#[test] 236fn string_empty_unicode_escape_sequence() { 237 assert_error!( 238 r#""\u{}""#, 239 ParseError { 240 error: ParseErrorType::LexError { 241 error: LexicalError { 242 error: LexicalErrorType::InvalidUnicodeEscape( 243 InvalidUnicodeEscapeError::InvalidNumberOfHexDigits, 244 ), 245 location: SrcSpan { start: 1, end: 5 }, 246 } 247 }, 248 location: SrcSpan { start: 1, end: 5 }, 249 } 250 ); 251} 252 253#[test] 254fn string_overlong_unicode_escape_sequence() { 255 assert_error!( 256 r#""\u{0011f601}""#, 257 ParseError { 258 error: ParseErrorType::LexError { 259 error: LexicalError { 260 error: LexicalErrorType::InvalidUnicodeEscape( 261 InvalidUnicodeEscapeError::InvalidNumberOfHexDigits, 262 ), 263 location: SrcSpan { start: 1, end: 13 }, 264 } 265 }, 266 location: SrcSpan { start: 1, end: 13 }, 267 } 268 ); 269} 270 271#[test] 272fn string_invalid_unicode_escape_sequence() { 273 assert_error!( 274 r#""\u{110000}""#, 275 ParseError { 276 error: ParseErrorType::LexError { 277 error: LexicalError { 278 error: LexicalErrorType::InvalidUnicodeEscape( 279 InvalidUnicodeEscapeError::InvalidCodepoint, 280 ), 281 location: SrcSpan { start: 1, end: 11 }, 282 } 283 }, 284 location: SrcSpan { start: 1, end: 11 }, 285 } 286 ); 287} 288 289#[test] 290fn bit_array() { 291 // non int value in bit array unit option 292 assert_error!( 293 "let x = <<1:unit(0)>> x", 294 ParseError { 295 error: ParseErrorType::InvalidBitArrayUnit, 296 location: SrcSpan { start: 17, end: 18 } 297 } 298 ); 299} 300 301#[test] 302fn bit_array1() { 303 assert_error!( 304 "let x = <<1:unit(257)>> x", 305 ParseError { 306 error: ParseErrorType::InvalidBitArrayUnit, 307 location: SrcSpan { start: 17, end: 20 } 308 } 309 ); 310} 311 312#[test] 313fn bit_array2() { 314 // patterns cannot be nested 315 assert_error!( 316 "case <<>> { <<<<1>>:bits>> -> 1 }", 317 ParseError { 318 error: ParseErrorType::NestedBitArrayPattern, 319 location: SrcSpan { start: 14, end: 19 } 320 } 321 ); 322} 323 324// https://github.com/gleam-lang/gleam/issues/3125 325#[test] 326fn triple_equals() { 327 assert_error!( 328 "let wobble:Int = 32 329 wobble === 42", 330 ParseError { 331 error: ParseErrorType::LexError { 332 error: LexicalError { 333 error: LexicalErrorType::InvalidTripleEqual, 334 location: SrcSpan { start: 35, end: 38 }, 335 } 336 }, 337 location: SrcSpan { start: 35, end: 38 }, 338 } 339 ); 340} 341 342#[test] 343fn triple_equals_with_whitespace() { 344 assert_error!( 345 "let wobble:Int = 32 346 wobble == = 42", 347 ParseError { 348 error: ParseErrorType::OpNakedRight, 349 location: SrcSpan { start: 35, end: 37 }, 350 } 351 ); 352} 353 354// https://github.com/gleam-lang/gleam/issues/1231 355#[test] 356fn pointless_spread() { 357 assert_error!( 358 "let xs = [] [..xs]", 359 ParseError { 360 error: ParseErrorType::ListSpreadWithoutElements, 361 location: SrcSpan { start: 12, end: 18 }, 362 } 363 ); 364} 365 366// https://github.com/gleam-lang/gleam/issues/1358 367#[test] 368fn lowcase_bool_in_pattern() { 369 assert_error!( 370 "case 42 > 42 { true -> 1; false -> 2; }", 371 ParseError { 372 error: ParseErrorType::LowcaseBooleanPattern, 373 location: SrcSpan { start: 15, end: 19 }, 374 } 375 ); 376} 377 378// https://github.com/gleam-lang/gleam/issues/1613 379#[test] 380fn anonymous_function_labeled_arguments() { 381 assert_error!( 382 "let anon_subtract = fn (minuend a: Int, subtrahend b: Int) -> Int { 383 a - b 384}", 385 ParseError { 386 location: SrcSpan { start: 24, end: 31 }, 387 error: ParseErrorType::UnexpectedLabel 388 } 389 ); 390} 391 392#[test] 393fn no_let_binding() { 394 assert_error!( 395 "wibble = 32", 396 ParseError { 397 location: SrcSpan { start: 7, end: 8 }, 398 error: ParseErrorType::NoLetBinding 399 } 400 ); 401} 402 403#[test] 404fn no_let_binding1() { 405 assert_error!( 406 "wibble:Int = 32", 407 ParseError { 408 location: SrcSpan { start: 6, end: 7 }, 409 error: ParseErrorType::NoLetBinding 410 } 411 ); 412} 413 414#[test] 415fn no_let_binding2() { 416 assert_error!( 417 "let wobble:Int = 32 418 wobble = 42", 419 ParseError { 420 location: SrcSpan { start: 35, end: 36 }, 421 error: ParseErrorType::NoLetBinding 422 } 423 ); 424} 425 426#[test] 427fn no_let_binding3() { 428 assert_error!( 429 "[x] = [2]", 430 ParseError { 431 location: SrcSpan { start: 4, end: 5 }, 432 error: ParseErrorType::NoLetBinding 433 } 434 ); 435} 436 437#[test] 438fn with_let_binding3() { 439 // The same with `let assert` must parse: 440 assert_parse!("let assert [x] = [2]"); 441} 442 443#[test] 444fn with_let_binding3_and_annotation() { 445 assert_parse!("let assert [x]: List(Int) = [2]"); 446} 447 448#[test] 449fn no_eq_after_binding() { 450 assert_error!( 451 "let wibble", 452 ParseError { 453 location: SrcSpan { start: 4, end: 10 }, 454 error: ParseErrorType::ExpectedEqual 455 } 456 ); 457} 458 459#[test] 460fn no_eq_after_binding1() { 461 assert_error!( 462 "let wibble 463 wibble = 4", 464 ParseError { 465 location: SrcSpan { start: 4, end: 10 }, 466 error: ParseErrorType::ExpectedEqual 467 } 468 ); 469} 470 471#[test] 472fn echo_followed_by_expression_ends_where_expression_ends() { 473 assert_parse!("echo wibble"); 474} 475 476#[test] 477fn echo_with_simple_expression_1() { 478 assert_parse!("echo wibble as message"); 479} 480 481#[test] 482fn echo_with_simple_expression_2() { 483 assert_parse!("echo wibble as \"message\""); 484} 485 486#[test] 487fn echo_with_complex_expression() { 488 assert_parse!("echo wibble as { this <> complex }"); 489} 490 491#[test] 492fn echo_with_no_expressions_after_it() { 493 assert_parse!("echo"); 494} 495 496#[test] 497fn echo_with_no_expressions_after_it_but_a_message() { 498 assert_parse!("echo as message"); 499} 500 501#[test] 502fn echo_with_block() { 503 assert_parse!("echo { 1 + 1 }"); 504} 505 506#[test] 507fn echo_has_lower_precedence_than_binop() { 508 assert_parse!("echo 1 + 1"); 509} 510 511#[test] 512fn echo_in_a_pipeline() { 513 assert_parse!("[] |> echo |> wibble"); 514} 515 516#[test] 517fn echo_has_lower_precedence_than_pipeline() { 518 assert_parse!("echo wibble |> wobble |> woo"); 519} 520 521#[test] 522fn echo_cannot_have_an_expression_in_a_pipeline() { 523 // So this is actually two pipelines! 524 assert_parse!("[] |> echo fun |> wibble"); 525} 526 527#[test] 528fn panic_with_echo() { 529 assert_parse!("panic as echo \"string\""); 530} 531 532#[test] 533fn panic_with_echo_and_message() { 534 assert_parse!("panic as echo wibble as message"); 535} 536 537#[test] 538fn echo_with_panic() { 539 assert_parse!("echo panic as \"a\""); 540} 541 542#[test] 543fn echo_with_panic_and_message() { 544 assert_parse!("echo panic as \"a\""); 545} 546 547#[test] 548fn echo_with_panic_and_messages() { 549 assert_parse!("echo panic as \"a\" as \"b\""); 550} 551 552#[test] 553fn echo_with_assert_and_message_1() { 554 assert_parse!("assert 1 == echo 2 as this_belongs_to_echo"); 555} 556 557#[test] 558fn echo_with_assert_and_message_2() { 559 assert_parse!("assert echo True as this_belongs_to_echo"); 560} 561 562#[test] 563fn echo_with_assert_and_messages_1() { 564 assert_parse!("assert 1 == echo 2 as this_belongs_to_echo as this_belongs_to_assert"); 565} 566 567#[test] 568fn echo_with_assert_and_messages_2() { 569 assert_parse!("assert echo True as this_belongs_to_echo as this_belongs_to_assert"); 570} 571 572#[test] 573fn echo_with_assert_and_messages_3() { 574 assert_parse!("assert echo 1 == 2 as this_belongs_to_echo as this_belongs_to_assert"); 575} 576 577#[test] 578fn echo_with_let_assert_and_message() { 579 assert_parse!("let assert 1 = echo 2 as this_belongs_to_echo"); 580} 581 582#[test] 583fn echo_with_let_assert_and_messages() { 584 assert_parse!("let assert 1 = echo 1 as this_belongs_to_echo as this_belongs_to_assert"); 585} 586 587#[test] 588fn repeated_echos() { 589 assert_parse!("echo echo echo 1"); 590} 591 592#[test] 593fn echo_at_start_of_pipeline_wraps_the_whole_thing() { 594 assert_parse!("echo 1 |> wibble |> wobble"); 595} 596 597#[test] 598fn no_let_binding_snapshot_1() { 599 assert_error!("wibble = 4"); 600} 601 602#[test] 603fn no_let_binding_snapshot_2() { 604 assert_error!("wibble:Int = 4"); 605} 606 607#[test] 608fn no_let_binding_snapshot_3() { 609 assert_error!( 610 "let wobble:Int = 32 611 wobble = 42" 612 ); 613} 614 615#[test] 616fn no_eq_after_binding_snapshot_1() { 617 assert_error!("let wibble"); 618} 619#[test] 620fn no_eq_after_binding_snapshot_2() { 621 assert_error!( 622 "let wibble 623 wibble = 4" 624 ); 625} 626 627#[test] 628fn discard_left_hand_side_of_concat_pattern() { 629 assert_error!( 630 r#" 631 case "" { 632 _ <> rest -> rest 633 } 634 "# 635 ); 636} 637 638#[test] 639fn assign_left_hand_side_of_concat_pattern() { 640 assert_error!( 641 r#" 642 case "" { 643 first <> rest -> rest 644 } 645 "# 646 ); 647} 648 649// https://github.com/gleam-lang/gleam/issues/1890 650#[test] 651fn valueless_list_spread_expression() { 652 assert_error!(r#"let x = [1, 2, 3, ..]"#); 653} 654 655// https://github.com/gleam-lang/gleam/issues/2035 656#[test] 657fn semicolons() { 658 assert_error!(r#"{ 2 + 3; - -5; }"#); 659} 660 661#[test] 662fn bare_expression() { 663 assert_parse!(r#"1"#); 664} 665 666// https://github.com/gleam-lang/gleam/issues/1991 667#[test] 668fn block_of_one() { 669 assert_parse!(r#"{ 1 }"#); 670} 671 672// https://github.com/gleam-lang/gleam/issues/1991 673#[test] 674fn block_of_two() { 675 assert_parse!(r#"{ 1 2 }"#); 676} 677 678// https://github.com/gleam-lang/gleam/issues/1991 679#[test] 680fn nested_block() { 681 assert_parse!(r#"{ 1 { 1.0 2.0 } 3 }"#); 682} 683 684// https://github.com/gleam-lang/gleam/issues/1831 685#[test] 686fn argument_scope() { 687 assert_error!( 688 " 6891 + let a = 5 690a 691" 692 ); 693} 694 695#[test] 696fn multiple_external_for_same_project_erlang() { 697 assert_module_error!( 698 r#" 699@external(erlang, "one", "two") 700@external(erlang, "three", "four") 701pub fn one(x: Int) -> Int { 702 todo 703} 704"# 705 ); 706} 707 708#[test] 709fn multiple_external_for_same_project_javascript() { 710 assert_module_error!( 711 r#" 712@external(javascript, "one", "two") 713@external(javascript, "three", "four") 714pub fn one(x: Int) -> Int { 715 todo 716} 717"# 718 ); 719} 720 721#[test] 722fn unknown_external_target() { 723 assert_module_error!( 724 r#" 725@external(erl, "one", "two") 726pub fn one(x: Int) -> Int { 727 todo 728}"# 729 ); 730} 731 732#[test] 733fn unknown_target() { 734 assert_module_error!( 735 r#" 736@target(abc) 737pub fn one() {}"# 738 ); 739} 740 741#[test] 742fn missing_target() { 743 assert_module_error!( 744 r#" 745@target() 746pub fn one() {}"# 747 ); 748} 749 750#[test] 751fn missing_target_and_bracket() { 752 assert_module_error!( 753 r#" 754@target( 755pub fn one() {}"# 756 ); 757} 758 759#[test] 760fn unknown_attribute() { 761 assert_module_error!( 762 r#"@go_faster() 763pub fn main() { 1 }"# 764 ); 765} 766 767#[test] 768fn incomplete_function() { 769 assert_error!("fn()"); 770} 771 772#[test] 773fn multiple_deprecation_attributes() { 774 assert_module_error!( 775 r#" 776@deprecated("1") 777@deprecated("2") 778pub fn main() -> Nil { 779 Nil 780} 781"# 782 ); 783} 784 785#[test] 786fn deprecation_without_message() { 787 assert_module_error!( 788 r#" 789@deprecated() 790pub fn main() -> Nil { 791 Nil 792} 793"# 794 ); 795} 796 797#[test] 798fn multiple_internal_attributes() { 799 assert_module_error!( 800 r#" 801@internal 802@internal 803pub fn main() -> Nil { 804 Nil 805} 806"# 807 ); 808} 809 810#[test] 811fn attributes_with_no_definition() { 812 assert_module_error!( 813 r#" 814@deprecated("1") 815@target(erlang) 816"# 817 ); 818} 819 820#[test] 821fn external_attribute_with_custom_type() { 822 assert_parse_module!( 823 r#" 824@external(erlang, "gleam_stdlib", "dict") 825@external(javascript, "./gleam_stdlib.d.ts", "Dict") 826pub type Dict(key, value) 827"# 828 ); 829} 830 831#[test] 832fn external_attribute_with_non_fn_definition() { 833 assert_module_error!( 834 r#" 835@external(erlang, "module", "fun") 836pub type Fun = Fun 837"# 838 ); 839} 840 841#[test] 842fn attributes_with_improper_definition() { 843 assert_module_error!( 844 r#" 845@deprecated("1") 846@external(erlang, "module", "fun") 847"# 848 ); 849} 850 851#[test] 852fn const_with_function_call() { 853 assert_module_error!( 854 r#" 855pub fn wibble() { 123 } 856const wib: Int = wibble() 857"# 858 ); 859} 860 861#[test] 862fn const_with_function_call_with_args() { 863 assert_module_error!( 864 r#" 865pub fn wibble() { 123 } 866const wib: Int = wibble(1, "wobble") 867"# 868 ); 869} 870 871#[test] 872fn import_type() { 873 assert_parse_module!(r#"import wibble.{type Wobble, Wobble, type Wabble}"#); 874} 875 876#[test] 877fn reserved_auto() { 878 assert_module_error!(r#"const auto = 1"#); 879} 880 881#[test] 882fn reserved_delegate() { 883 assert_module_error!(r#"const delegate = 1"#); 884} 885 886#[test] 887fn reserved_derive() { 888 assert_module_error!(r#"const derive = 1"#); 889} 890 891#[test] 892fn reserved_else() { 893 assert_module_error!(r#"const else = 1"#); 894} 895 896#[test] 897fn reserved_implement() { 898 assert_module_error!(r#"const implement = 1"#); 899} 900 901#[test] 902fn reserved_macro() { 903 assert_module_error!(r#"const macro = 1"#); 904} 905 906#[test] 907fn reserved_test() { 908 assert_module_error!(r#"const test = 1"#); 909} 910 911#[test] 912fn reserved_echo() { 913 assert_module_error!(r#"const echo = 1"#); 914} 915 916#[test] 917fn capture_with_name() { 918 assert_module_error!( 919 r#" 920pub fn main() { 921 add(_name, 1) 922} 923 924fn add(x, y) { 925 x + y 926} 927"# 928 ); 929} 930 931#[test] 932fn list_spread_with_no_tail_in_the_middle_of_a_list() { 933 assert_module_error!( 934 r#" 935pub fn main() -> Nil { 936 let xs = [1, 2, 3] 937 [1, 2, .., 3 + 3, 4] 938} 939"# 940 ); 941} 942 943#[test] 944fn list_spread_followed_by_extra_items() { 945 assert_module_error!( 946 r#" 947pub fn main() -> Nil { 948 let xs = [1, 2, 3] 949 [1, 2, ..xs, 3 + 3, 4] 950} 951"# 952 ); 953} 954 955#[test] 956fn list_spread_followed_by_extra_item_and_another_spread() { 957 assert_module_error!( 958 r#" 959pub fn main() -> Nil { 960 let xs = [1, 2, 3] 961 let ys = [5, 6, 7] 962 [..xs, 4, ..ys] 963} 964"# 965 ); 966} 967 968#[test] 969fn list_spread_followed_by_other_spread() { 970 assert_module_error!( 971 r#" 972pub fn main() -> Nil { 973 let xs = [1, 2, 3] 974 let ys = [5, 6, 7] 975 [1, ..xs, ..ys] 976} 977"# 978 ); 979} 980 981#[test] 982fn list_spread_as_first_item_followed_by_other_items() { 983 assert_module_error!( 984 r#" 985pub fn main() -> Nil { 986 let xs = [1, 2, 3] 987 [..xs, 3 + 3, 4] 988} 989"# 990 ); 991} 992 993// Tests for nested tuples and structs in tuples 994// https://github.com/gleam-lang/gleam/issues/1980 995 996#[test] 997fn nested_tuples() { 998 assert_parse!( 999 r#" 1000let tup = #(#(5, 6)) 1001{tup.0}.1 1002"# 1003 ); 1004} 1005 1006#[test] 1007fn nested_tuples_no_block() { 1008 assert_parse!( 1009 r#" 1010let tup = #(#(5, 6)) 1011tup.0.1 1012"# 1013 ); 1014} 1015 1016#[test] 1017fn deeply_nested_tuples() { 1018 assert_parse!( 1019 r#" 1020let tup = #(#(#(#(4)))) 1021{{{tup.0}.0}.0}.0 1022"# 1023 ); 1024} 1025 1026#[test] 1027fn deeply_nested_tuples_no_block() { 1028 assert_parse!( 1029 r#" 1030let tup = #(#(#(#(4)))) 1031tup.0.0.0.0 1032"# 1033 ); 1034} 1035 1036#[test] 1037fn inner_single_quote_parses() { 1038 assert_parse!( 1039 r#" 1040let a = "inner 'quotes'" 1041"# 1042 ); 1043} 1044 1045#[test] 1046fn string_single_char_suggestion() { 1047 assert_module_error!( 1048 " 1049 pub fn main() { 1050 let a = 'example' 1051 } 1052 " 1053 ); 1054} 1055 1056#[test] 1057fn private_internal_const() { 1058 assert_module_error!( 1059 " 1060@internal 1061const wibble = 1 1062" 1063 ); 1064} 1065 1066#[test] 1067fn private_internal_type_alias() { 1068 assert_module_error!( 1069 " 1070@internal 1071type Alias = Int 1072" 1073 ); 1074} 1075 1076#[test] 1077fn private_internal_function() { 1078 assert_module_error!( 1079 " 1080@internal 1081fn wibble() { todo } 1082" 1083 ); 1084} 1085 1086#[test] 1087fn private_internal_type() { 1088 assert_module_error!( 1089 " 1090@internal 1091type Wibble { 1092 Wibble 1093} 1094" 1095 ); 1096} 1097 1098#[test] 1099fn wrong_record_access_pattern() { 1100 assert_module_error!( 1101 " 1102pub fn main() { 1103 case wibble { 1104 wibble.thing -> 1 1105 } 1106} 1107" 1108 ); 1109} 1110 1111#[test] 1112fn tuple_invalid_expr() { 1113 assert_module_error!( 1114 " 1115fn main() { 1116 #(1, 2, const) 1117} 1118" 1119 ); 1120} 1121 1122#[test] 1123fn bit_array_invalid_segment() { 1124 assert_module_error!( 1125 " 1126fn main() { 1127 <<72, 101, 108, 108, 111, 44, 32, 74, 111, 101, const>> 1128} 1129" 1130 ); 1131} 1132 1133#[test] 1134fn case_invalid_expression() { 1135 assert_module_error!( 1136 " 1137fn main() { 1138 case 1, type { 1139 _, _ -> 0 1140 } 1141} 1142" 1143 ); 1144} 1145 1146#[test] 1147fn case_invalid_case_pattern() { 1148 assert_module_error!( 1149 " 1150fn main() { 1151 case 1 { 1152 -> -> 0 1153 } 1154} 1155" 1156 ); 1157} 1158 1159#[test] 1160fn use_invalid_assignments() { 1161 assert_module_error!( 1162 " 1163fn main() { 1164 use fn <- result.try(get_username()) 1165} 1166" 1167 ); 1168} 1169 1170#[test] 1171fn assignment_pattern_invalid_tuple() { 1172 assert_module_error!( 1173 " 1174fn main() { 1175 let #(a, case, c) = #(1, 2, 3) 1176} 1177" 1178 ); 1179} 1180 1181#[test] 1182fn assignment_pattern_invalid_bit_segment() { 1183 assert_module_error!( 1184 " 1185fn main() { 1186 let <<b1, pub>> = <<24, 3>> 1187} 1188" 1189 ); 1190} 1191 1192#[test] 1193fn case_list_pattern_after_spread() { 1194 assert_module_error!( 1195 " 1196fn main() { 1197 case somelist { 1198 [..rest, last] -> 1 1199 _ -> 2 1200 } 1201} 1202" 1203 ); 1204} 1205 1206#[test] 1207fn type_invalid_constructor() { 1208 assert_module_error!( 1209 " 1210type A { 1211 A(String) 1212 type 1213} 1214" 1215 ); 1216} 1217 1218// Tests whether diagnostic presents an example of how to formulate a proper 1219// record constructor based off a common user error pattern. 1220// https://github.com/gleam-lang/gleam/issues/3324 1221 1222#[test] 1223fn type_invalid_record_constructor() { 1224 assert_module_error!( 1225 " 1226pub type User { 1227 name: String, 1228} 1229" 1230 ); 1231} 1232 1233#[test] 1234fn type_invalid_record_constructor_without_field_type() { 1235 assert_module_error!( 1236 " 1237pub opaque type User { 1238 name 1239} 1240" 1241 ); 1242} 1243 1244#[test] 1245fn type_invalid_record_constructor_invalid_field_type() { 1246 assert_module_error!( 1247 r#" 1248type User { 1249 name: "Test User", 1250} 1251"# 1252 ); 1253} 1254 1255#[test] 1256fn type_invalid_type_name() { 1257 assert_module_error!( 1258 " 1259type A(a, type) { 1260 A 1261} 1262" 1263 ); 1264} 1265 1266#[test] 1267fn type_invalid_constructor_arg() { 1268 assert_module_error!( 1269 " 1270type A { 1271 A(type: String) 1272} 1273" 1274 ); 1275} 1276 1277#[test] 1278fn type_invalid_record() { 1279 assert_module_error!( 1280 " 1281type A { 1282 One 1283 Two 1284 3 1285} 1286" 1287 ); 1288} 1289 1290#[test] 1291fn function_type_invalid_param_type() { 1292 assert_module_error!( 1293 " 1294fn f(g: fn(Int, 1) -> Int) -> Int { 1295 g(0, 1) 1296} 1297" 1298 ); 1299} 1300 1301#[test] 1302fn function_invalid_signature() { 1303 assert_module_error!( 1304 r#" 1305fn f(a, "b") -> String { 1306 a <> b 1307} 1308"# 1309 ); 1310} 1311 1312#[test] 1313fn const_invalid_tuple() { 1314 assert_module_error!( 1315 " 1316const a = #(1, 2, <-) 1317" 1318 ); 1319} 1320 1321#[test] 1322fn const_invalid_list() { 1323 assert_module_error!( 1324 " 1325const a = [1, 2, <-] 1326" 1327 ); 1328} 1329 1330#[test] 1331fn const_invalid_bit_array_segment() { 1332 assert_module_error!( 1333 " 1334const a = <<1, 2, <->> 1335" 1336 ); 1337} 1338 1339#[test] 1340fn const_invalid_record_constructor() { 1341 assert_module_error!( 1342 " 1343type A { 1344 A(String, Int) 1345} 1346const a = A(\"a\", let) 1347" 1348 ); 1349} 1350 1351// record access should parse even if there is no label written 1352#[test] 1353fn record_access_no_label() { 1354 assert_parse_module!( 1355 " 1356type Wibble { 1357 Wibble(wibble: String) 1358} 1359 1360fn wobble() { 1361 Wibble(\"a\"). 1362} 1363" 1364 ); 1365} 1366 1367#[test] 1368fn newline_tokens() { 1369 assert_eq!( 1370 make_tokenizer("1\n\n2\n").collect_vec(), 1371 [ 1372 Ok(( 1373 0, 1374 Token::Int { 1375 value: "1".into(), 1376 int_value: 1.into() 1377 }, 1378 1 1379 )), 1380 Ok((1, Token::NewLine, 2)), 1381 Ok((2, Token::NewLine, 3)), 1382 Ok(( 1383 3, 1384 Token::Int { 1385 value: "2".into(), 1386 int_value: 2.into() 1387 }, 1388 4 1389 )), 1390 Ok((4, Token::NewLine, 5)) 1391 ] 1392 ); 1393} 1394 1395// https://github.com/gleam-lang/gleam/issues/1756 1396#[test] 1397fn arithmetic_in_guards() { 1398 assert_parse!( 1399 " 1400case 2, 3 { 1401 x, y if x + y == 1 -> True 1402}" 1403 ); 1404} 1405 1406#[test] 1407fn const_string_concat() { 1408 assert_parse_module!( 1409 " 1410const cute = \"cute\" 1411const cute_bee = cute <> \"bee\" 1412" 1413 ); 1414} 1415 1416#[test] 1417fn const_string_concat_naked_right() { 1418 assert_module_error!( 1419 " 1420const no_cute_bee = \"cute\" <> 1421" 1422 ); 1423} 1424 1425#[test] 1426fn function_call_in_case_clause_guard() { 1427 assert_error!( 1428 r#" 1429let my_string = "hello" 1430case my_string { 1431 _ if length(my_string) > 2 -> io.debug("doesn't work') 1432}"# 1433 ); 1434} 1435 1436#[test] 1437fn dot_access_function_call_in_case_clause_guard() { 1438 assert_error!( 1439 r#" 1440let my_string = "hello" 1441case my_string { 1442 _ if string.length(my_string) > 2 -> io.debug("doesn't work') 1443}"# 1444 ); 1445} 1446 1447#[test] 1448fn invalid_left_paren_in_case_clause_guard() { 1449 assert_error!( 1450 r#" 1451let my_string = "hello" 1452case my_string { 1453 _ if string.length( > 2 -> io.debug("doesn't work') 1454}"# 1455 ); 1456} 1457 1458#[test] 1459fn invalid_label_shorthand() { 1460 assert_module_error!( 1461 " 1462pub fn main() { 1463 wibble(:) 1464} 1465" 1466 ); 1467} 1468 1469#[test] 1470fn invalid_label_shorthand_2() { 1471 assert_module_error!( 1472 " 1473pub fn main() { 1474 wibble(:,) 1475} 1476" 1477 ); 1478} 1479 1480#[test] 1481fn invalid_label_shorthand_3() { 1482 assert_module_error!( 1483 " 1484pub fn main() { 1485 wibble(:arg) 1486} 1487" 1488 ); 1489} 1490 1491#[test] 1492fn invalid_label_shorthand_4() { 1493 assert_module_error!( 1494 " 1495pub fn main() { 1496 wibble(arg::) 1497} 1498" 1499 ); 1500} 1501 1502#[test] 1503fn invalid_label_shorthand_5() { 1504 assert_module_error!( 1505 " 1506pub fn main() { 1507 wibble(arg::arg) 1508} 1509" 1510 ); 1511} 1512 1513#[test] 1514fn invalid_pattern_label_shorthand() { 1515 assert_module_error!( 1516 " 1517pub fn main() { 1518 let Wibble(:) = todo 1519} 1520" 1521 ); 1522} 1523 1524#[test] 1525fn invalid_pattern_label_shorthand_2() { 1526 assert_module_error!( 1527 " 1528pub fn main() { 1529 let Wibble(:arg) = todo 1530} 1531" 1532 ); 1533} 1534 1535#[test] 1536fn invalid_pattern_label_shorthand_3() { 1537 assert_module_error!( 1538 " 1539pub fn main() { 1540 let Wibble(arg::) = todo 1541} 1542" 1543 ); 1544} 1545 1546#[test] 1547fn invalid_pattern_label_shorthand_4() { 1548 assert_module_error!( 1549 " 1550pub fn main() { 1551 let Wibble(arg: arg:) = todo 1552} 1553" 1554 ); 1555} 1556 1557#[test] 1558fn invalid_pattern_label_shorthand_5() { 1559 assert_module_error!( 1560 " 1561pub fn main() { 1562 let Wibble(arg1: arg2:) = todo 1563} 1564" 1565 ); 1566} 1567 1568fn first_parsed_docstring(src: &str) -> EcoString { 1569 let parsed = 1570 crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null()) 1571 .expect("should parse"); 1572 1573 parsed 1574 .module 1575 .definitions 1576 .first() 1577 .expect("parsed a definition") 1578 .definition 1579 .get_doc() 1580 .expect("definition without doc") 1581} 1582 1583#[test] 1584fn doc_comment_before_comment_is_not_attached_to_following_function() { 1585 assert_eq!( 1586 first_parsed_docstring( 1587 r#" 1588 /// Not included! 1589 // pub fn call() 1590 1591 /// Doc! 1592 pub fn wibble() {} 1593"# 1594 ), 1595 " Doc!\n" 1596 ) 1597} 1598 1599#[test] 1600fn doc_comment_before_comment_is_not_attached_to_following_type() { 1601 assert_eq!( 1602 first_parsed_docstring( 1603 r#" 1604 /// Not included! 1605 // pub fn call() 1606 1607 /// Doc! 1608 pub type Wibble 1609"# 1610 ), 1611 " Doc!\n" 1612 ) 1613} 1614 1615#[test] 1616fn doc_comment_before_comment_is_not_attached_to_following_type_alias() { 1617 assert_eq!( 1618 first_parsed_docstring( 1619 r#" 1620 /// Not included! 1621 // pub fn call() 1622 1623 /// Doc! 1624 pub type Wibble = Int 1625"# 1626 ), 1627 " Doc!\n" 1628 ) 1629} 1630 1631#[test] 1632fn doc_comment_before_comment_is_not_attached_to_following_constant() { 1633 assert_eq!( 1634 first_parsed_docstring( 1635 r#" 1636 /// Not included! 1637 // pub fn call() 1638 1639 /// Doc! 1640 pub const wibble = 1 1641"# 1642 ), 1643 " Doc!\n" 1644 ); 1645} 1646 1647#[test] 1648fn non_module_level_function_with_a_name() { 1649 assert_module_error!( 1650 r#" 1651pub fn main() { 1652 fn my() { 1 } 1653} 1654"# 1655 ); 1656} 1657 1658#[test] 1659fn error_message_on_variable_starting_with_underscore() { 1660 // https://github.com/gleam-lang/gleam/issues/3504 1661 assert_module_error!( 1662 " 1663 pub fn main() { 1664 let val = _func_starting_with_underscore(1) 1665 }" 1666 ); 1667} 1668 1669#[test] 1670fn non_module_level_function_with_not_a_name() { 1671 assert_module_error!( 1672 r#" 1673pub fn main() { 1674 fn @() { 1 } // wrong token and not a name 1675} 1676"# 1677 ); 1678} 1679 1680#[test] 1681fn error_message_on_variable_starting_with_underscore2() { 1682 // https://github.com/gleam-lang/gleam/issues/3504 1683 assert_module_error!( 1684 " 1685 pub fn main() { 1686 case 1 { 1687 1 -> _with_underscore(1) 1688 } 1689 }" 1690 ); 1691} 1692 1693#[test] 1694fn function_inside_a_type() { 1695 assert_module_error!( 1696 r#" 1697type Wibble { 1698 fn wobble() {} 1699} 1700"# 1701 ); 1702} 1703 1704#[test] 1705fn pub_function_inside_a_type() { 1706 assert_module_error!( 1707 r#" 1708type Wibble { 1709 pub fn wobble() {} 1710} 1711"# 1712 ); 1713} 1714 1715#[test] 1716fn if_like_expression() { 1717 assert_module_error!( 1718 r#" 1719pub fn main() { 1720 let a = if wibble { 1721 wobble 1722 } 1723} 1724"# 1725 ); 1726} 1727 1728// https://github.com/gleam-lang/gleam/issues/3730 1729#[test] 1730fn missing_constructor_arguments() { 1731 assert_module_error!( 1732 " 1733pub type A { 1734 A(Int) 1735} 1736 1737const a = A() 1738" 1739 ); 1740} 1741 1742// https://github.com/gleam-lang/gleam/issues/3796 1743#[test] 1744fn missing_type_constructor_arguments_in_type_definition() { 1745 assert_module_error!( 1746 " 1747pub type A() { 1748 A(Int) 1749} 1750" 1751 ); 1752} 1753 1754#[test] 1755fn tuple_without_hash() { 1756 assert_module_error!( 1757 r#" 1758pub fn main() { 1759 let triple = (1, 2.2, "three") 1760 io.debug(triple) 1761 let (a, *, *) = triple 1762 io.debug(a) 1763 io.debug(triple.1) 1764} 1765"# 1766 ); 1767} 1768 1769#[test] 1770fn deprecation_attribute_on_type_variant() { 1771 assert_parse_module!( 1772 r#" 1773type Wibble { 1774 @deprecated("1") 1775 Wibble1 1776 Wibble2 1777} 1778"# 1779 ); 1780} 1781 1782#[test] 1783 1784fn float_empty_exponent() { 1785 assert_error!("1.32e"); 1786} 1787 1788#[test] 1789fn multiple_deprecation_attribute_on_type_variant() { 1790 assert_module_error!( 1791 r#" 1792type Wibble { 1793 @deprecated("1") 1794 @deprecated("2") 1795 Wibble1 1796 Wibble2 1797} 1798"# 1799 ); 1800} 1801 1802#[test] 1803fn target_attribute_on_type_variant() { 1804 assert_module_error!( 1805 r#" 1806type Wibble { 1807 @target(erlang) 1808 Wibble2 1809} 1810"# 1811 ); 1812} 1813 1814#[test] 1815fn internal_attribute_on_type_variant() { 1816 assert_module_error!( 1817 r#" 1818type Wibble { 1819 @internal 1820 Wibble1 1821} 1822"# 1823 ); 1824} 1825 1826#[test] 1827fn external_attribute_on_type_variant() { 1828 assert_module_error!( 1829 r#" 1830type Wibble { 1831 @external(erlang, "one", "two") 1832 Wibble1 1833} 1834"# 1835 ); 1836} 1837 1838#[test] 1839fn multiple_unsupported_attributes_on_type_variant() { 1840 assert_module_error!( 1841 r#" 1842type Wibble { 1843 @external(erlang, "one", "two") 1844 @target(erlang) 1845 @internal 1846 Wibble1 1847} 1848"# 1849 ); 1850} 1851 1852#[test] 1853// https://github.com/gleam-lang/gleam/issues/3870 1854fn nested_tuple_access_after_function() { 1855 assert_parse!("tuple().0.1"); 1856} 1857 1858#[test] 1859fn case_expression_without_body() { 1860 assert_parse!("case a"); 1861} 1862 1863#[test] 1864fn assert_statement() { 1865 assert_parse!("assert 10 != 11"); 1866} 1867 1868#[test] 1869fn assert_statement_with_message() { 1870 assert_parse!(r#"assert False as "Uh oh""#); 1871} 1872 1873#[test] 1874fn assert_statement_without_expression() { 1875 assert_error!("assert"); 1876} 1877 1878#[test] 1879fn assert_statement_followed_by_statement() { 1880 assert_error!("assert let a = 10"); 1881} 1882 1883#[test] 1884fn special_error_for_pythonic_import() { 1885 assert_module_error!("import gleam.io"); 1886} 1887 1888#[test] 1889fn special_error_for_pythonic_neste_import() { 1890 assert_module_error!("import one.two.three"); 1891} 1892 1893#[test] 1894fn doesnt_issue_special_error_for_pythonic_import_if_slash() { 1895 assert_module_error!("import one/two.three"); 1896} 1897 1898#[test] 1899fn operator_in_pattern_size() { 1900 assert_parse!("let assert <<size, payload:size(size - 1)>> = <<>>"); 1901} 1902 1903#[test] 1904fn correct_precedence_in_pattern_size() { 1905 assert_parse!("let assert <<size, payload:size(size + 2 * 8)>> = <<>>"); 1906} 1907 1908#[test] 1909fn private_opaque_type_is_parsed() { 1910 assert_parse_module!("opaque type Wibble { Wobble }"); 1911} 1912 1913#[test] 1914fn case_guard_with_nested_blocks() { 1915 assert_parse!( 1916 "case 1 { 1917 _ if { 1 || { 1 || 1 } } || 1 -> 1 1918}" 1919 ); 1920} 1921 1922#[test] 1923fn case_guard_with_empty_block() { 1924 assert_error!( 1925 "case 1 { 1926 _ if a || {} -> 1 1927}" 1928 ); 1929} 1930 1931#[test] 1932fn constant_inside_function() { 1933 assert_module_error!( 1934 " 1935pub fn main() { 1936 const x = 10 1937 x 1938} 1939" 1940 ); 1941} 1942 1943#[test] 1944fn function_definition_angle_generics_error() { 1945 assert_module_error!("fn id<T>(x: T) { x }"); 1946} 1947 1948#[test] 1949fn type_angle_generics_usage_without_module_error() { 1950 assert_error!("let list: List<Int, String> = []"); 1951} 1952 1953#[test] 1954fn type_angle_generics_usage_with_module_error() { 1955 assert_error!("let set: set.Set<Int> = set.new()"); 1956} 1957 1958#[test] 1959fn type_angle_generics_definition_error() { 1960 assert_module_error!( 1961 r#" 1962type Either<a, b> { 1963 This(a) 1964 That(b) 1965} 1966"# 1967 ); 1968} 1969 1970#[test] 1971fn type_angle_generics_definition_with_upname_error() { 1972 assert_module_error!( 1973 r#" 1974type Either<A, B> { 1975 This(A) 1976 That(B) 1977} 1978"# 1979 ); 1980} 1981 1982#[test] 1983fn type_angle_generics_definition_error_fallback() { 1984 // Example of a more incorrect syntax, where Gleam doesn't make a suggestion. 1985 // In this case, an example type argument is used instead. 1986 assert_module_error!( 1987 r#" 1988type Either<type A, type B> { 1989 This(A) 1990 That(B) 1991} 1992"# 1993 ); 1994} 1995 1996#[test] 1997fn wrong_type_of_comments_with_hash() { 1998 assert_module_error!( 1999 r#" 2000pub fn main() { 2001 # a python-style comment 2002} 2003"# 2004 ); 2005}