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
35 kB 1932 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_non_fn_definition() { 822 assert_module_error!( 823 r#" 824@external(erlang, "module", "fun") 825pub type Fun 826"# 827 ); 828} 829 830#[test] 831fn attributes_with_improper_definition() { 832 assert_module_error!( 833 r#" 834@deprecated("1") 835@external(erlang, "module", "fun") 836"# 837 ); 838} 839 840#[test] 841fn const_with_function_call() { 842 assert_module_error!( 843 r#" 844pub fn wibble() { 123 } 845const wib: Int = wibble() 846"# 847 ); 848} 849 850#[test] 851fn const_with_function_call_with_args() { 852 assert_module_error!( 853 r#" 854pub fn wibble() { 123 } 855const wib: Int = wibble(1, "wobble") 856"# 857 ); 858} 859 860#[test] 861fn import_type() { 862 assert_parse_module!(r#"import wibble.{type Wobble, Wobble, type Wabble}"#); 863} 864 865#[test] 866fn reserved_auto() { 867 assert_module_error!(r#"const auto = 1"#); 868} 869 870#[test] 871fn reserved_delegate() { 872 assert_module_error!(r#"const delegate = 1"#); 873} 874 875#[test] 876fn reserved_derive() { 877 assert_module_error!(r#"const derive = 1"#); 878} 879 880#[test] 881fn reserved_else() { 882 assert_module_error!(r#"const else = 1"#); 883} 884 885#[test] 886fn reserved_implement() { 887 assert_module_error!(r#"const implement = 1"#); 888} 889 890#[test] 891fn reserved_macro() { 892 assert_module_error!(r#"const macro = 1"#); 893} 894 895#[test] 896fn reserved_test() { 897 assert_module_error!(r#"const test = 1"#); 898} 899 900#[test] 901fn reserved_echo() { 902 assert_module_error!(r#"const echo = 1"#); 903} 904 905#[test] 906fn capture_with_name() { 907 assert_module_error!( 908 r#" 909pub fn main() { 910 add(_name, 1) 911} 912 913fn add(x, y) { 914 x + y 915} 916"# 917 ); 918} 919 920#[test] 921fn list_spread_with_no_tail_in_the_middle_of_a_list() { 922 assert_module_error!( 923 r#" 924pub fn main() -> Nil { 925 let xs = [1, 2, 3] 926 [1, 2, .., 3 + 3, 4] 927} 928"# 929 ); 930} 931 932#[test] 933fn list_spread_followed_by_extra_items() { 934 assert_module_error!( 935 r#" 936pub fn main() -> Nil { 937 let xs = [1, 2, 3] 938 [1, 2, ..xs, 3 + 3, 4] 939} 940"# 941 ); 942} 943 944#[test] 945fn list_spread_followed_by_extra_item_and_another_spread() { 946 assert_module_error!( 947 r#" 948pub fn main() -> Nil { 949 let xs = [1, 2, 3] 950 let ys = [5, 6, 7] 951 [..xs, 4, ..ys] 952} 953"# 954 ); 955} 956 957#[test] 958fn list_spread_followed_by_other_spread() { 959 assert_module_error!( 960 r#" 961pub fn main() -> Nil { 962 let xs = [1, 2, 3] 963 let ys = [5, 6, 7] 964 [1, ..xs, ..ys] 965} 966"# 967 ); 968} 969 970#[test] 971fn list_spread_as_first_item_followed_by_other_items() { 972 assert_module_error!( 973 r#" 974pub fn main() -> Nil { 975 let xs = [1, 2, 3] 976 [..xs, 3 + 3, 4] 977} 978"# 979 ); 980} 981 982// Tests for nested tuples and structs in tuples 983// https://github.com/gleam-lang/gleam/issues/1980 984 985#[test] 986fn nested_tuples() { 987 assert_parse!( 988 r#" 989let tup = #(#(5, 6)) 990{tup.0}.1 991"# 992 ); 993} 994 995#[test] 996fn nested_tuples_no_block() { 997 assert_parse!( 998 r#" 999let tup = #(#(5, 6)) 1000tup.0.1 1001"# 1002 ); 1003} 1004 1005#[test] 1006fn deeply_nested_tuples() { 1007 assert_parse!( 1008 r#" 1009let tup = #(#(#(#(4)))) 1010{{{tup.0}.0}.0}.0 1011"# 1012 ); 1013} 1014 1015#[test] 1016fn deeply_nested_tuples_no_block() { 1017 assert_parse!( 1018 r#" 1019let tup = #(#(#(#(4)))) 1020tup.0.0.0.0 1021"# 1022 ); 1023} 1024 1025#[test] 1026fn inner_single_quote_parses() { 1027 assert_parse!( 1028 r#" 1029let a = "inner 'quotes'" 1030"# 1031 ); 1032} 1033 1034#[test] 1035fn string_single_char_suggestion() { 1036 assert_module_error!( 1037 " 1038 pub fn main() { 1039 let a = 'example' 1040 } 1041 " 1042 ); 1043} 1044 1045#[test] 1046fn private_internal_const() { 1047 assert_module_error!( 1048 " 1049@internal 1050const wibble = 1 1051" 1052 ); 1053} 1054 1055#[test] 1056fn private_internal_type_alias() { 1057 assert_module_error!( 1058 " 1059@internal 1060type Alias = Int 1061" 1062 ); 1063} 1064 1065#[test] 1066fn private_internal_function() { 1067 assert_module_error!( 1068 " 1069@internal 1070fn wibble() { todo } 1071" 1072 ); 1073} 1074 1075#[test] 1076fn private_internal_type() { 1077 assert_module_error!( 1078 " 1079@internal 1080type Wibble { 1081 Wibble 1082} 1083" 1084 ); 1085} 1086 1087#[test] 1088fn wrong_record_access_pattern() { 1089 assert_module_error!( 1090 " 1091pub fn main() { 1092 case wibble { 1093 wibble.thing -> 1 1094 } 1095} 1096" 1097 ); 1098} 1099 1100#[test] 1101fn tuple_invalid_expr() { 1102 assert_module_error!( 1103 " 1104fn main() { 1105 #(1, 2, const) 1106} 1107" 1108 ); 1109} 1110 1111#[test] 1112fn bit_array_invalid_segment() { 1113 assert_module_error!( 1114 " 1115fn main() { 1116 <<72, 101, 108, 108, 111, 44, 32, 74, 111, 101, const>> 1117} 1118" 1119 ); 1120} 1121 1122#[test] 1123fn case_invalid_expression() { 1124 assert_module_error!( 1125 " 1126fn main() { 1127 case 1, type { 1128 _, _ -> 0 1129 } 1130} 1131" 1132 ); 1133} 1134 1135#[test] 1136fn case_invalid_case_pattern() { 1137 assert_module_error!( 1138 " 1139fn main() { 1140 case 1 { 1141 -> -> 0 1142 } 1143} 1144" 1145 ); 1146} 1147 1148#[test] 1149fn use_invalid_assignments() { 1150 assert_module_error!( 1151 " 1152fn main() { 1153 use fn <- result.try(get_username()) 1154} 1155" 1156 ); 1157} 1158 1159#[test] 1160fn assignment_pattern_invalid_tuple() { 1161 assert_module_error!( 1162 " 1163fn main() { 1164 let #(a, case, c) = #(1, 2, 3) 1165} 1166" 1167 ); 1168} 1169 1170#[test] 1171fn assignment_pattern_invalid_bit_segment() { 1172 assert_module_error!( 1173 " 1174fn main() { 1175 let <<b1, pub>> = <<24, 3>> 1176} 1177" 1178 ); 1179} 1180 1181#[test] 1182fn case_list_pattern_after_spread() { 1183 assert_module_error!( 1184 " 1185fn main() { 1186 case somelist { 1187 [..rest, last] -> 1 1188 _ -> 2 1189 } 1190} 1191" 1192 ); 1193} 1194 1195#[test] 1196fn type_invalid_constructor() { 1197 assert_module_error!( 1198 " 1199type A { 1200 A(String) 1201 type 1202} 1203" 1204 ); 1205} 1206 1207// Tests whether diagnostic presents an example of how to formulate a proper 1208// record constructor based off a common user error pattern. 1209// https://github.com/gleam-lang/gleam/issues/3324 1210 1211#[test] 1212fn type_invalid_record_constructor() { 1213 assert_module_error!( 1214 " 1215pub type User { 1216 name: String, 1217} 1218" 1219 ); 1220} 1221 1222#[test] 1223fn type_invalid_record_constructor_without_field_type() { 1224 assert_module_error!( 1225 " 1226pub opaque type User { 1227 name 1228} 1229" 1230 ); 1231} 1232 1233#[test] 1234fn type_invalid_record_constructor_invalid_field_type() { 1235 assert_module_error!( 1236 r#" 1237type User { 1238 name: "Test User", 1239} 1240"# 1241 ); 1242} 1243 1244#[test] 1245fn type_invalid_type_name() { 1246 assert_module_error!( 1247 " 1248type A(a, type) { 1249 A 1250} 1251" 1252 ); 1253} 1254 1255#[test] 1256fn type_invalid_constructor_arg() { 1257 assert_module_error!( 1258 " 1259type A { 1260 A(type: String) 1261} 1262" 1263 ); 1264} 1265 1266#[test] 1267fn type_invalid_record() { 1268 assert_module_error!( 1269 " 1270type A { 1271 One 1272 Two 1273 3 1274} 1275" 1276 ); 1277} 1278 1279#[test] 1280fn function_type_invalid_param_type() { 1281 assert_module_error!( 1282 " 1283fn f(g: fn(Int, 1) -> Int) -> Int { 1284 g(0, 1) 1285} 1286" 1287 ); 1288} 1289 1290#[test] 1291fn function_invalid_signature() { 1292 assert_module_error!( 1293 r#" 1294fn f(a, "b") -> String { 1295 a <> b 1296} 1297"# 1298 ); 1299} 1300 1301#[test] 1302fn const_invalid_tuple() { 1303 assert_module_error!( 1304 " 1305const a = #(1, 2, <-) 1306" 1307 ); 1308} 1309 1310#[test] 1311fn const_invalid_list() { 1312 assert_module_error!( 1313 " 1314const a = [1, 2, <-] 1315" 1316 ); 1317} 1318 1319#[test] 1320fn const_invalid_bit_array_segment() { 1321 assert_module_error!( 1322 " 1323const a = <<1, 2, <->> 1324" 1325 ); 1326} 1327 1328#[test] 1329fn const_invalid_record_constructor() { 1330 assert_module_error!( 1331 " 1332type A { 1333 A(String, Int) 1334} 1335const a = A(\"a\", let) 1336" 1337 ); 1338} 1339 1340// record access should parse even if there is no label written 1341#[test] 1342fn record_access_no_label() { 1343 assert_parse_module!( 1344 " 1345type Wibble { 1346 Wibble(wibble: String) 1347} 1348 1349fn wobble() { 1350 Wibble(\"a\"). 1351} 1352" 1353 ); 1354} 1355 1356#[test] 1357fn newline_tokens() { 1358 assert_eq!( 1359 make_tokenizer("1\n\n2\n").collect_vec(), 1360 [ 1361 Ok(( 1362 0, 1363 Token::Int { 1364 value: "1".into(), 1365 int_value: 1.into() 1366 }, 1367 1 1368 )), 1369 Ok((1, Token::NewLine, 2)), 1370 Ok((2, Token::NewLine, 3)), 1371 Ok(( 1372 3, 1373 Token::Int { 1374 value: "2".into(), 1375 int_value: 2.into() 1376 }, 1377 4 1378 )), 1379 Ok((4, Token::NewLine, 5)) 1380 ] 1381 ); 1382} 1383 1384// https://github.com/gleam-lang/gleam/issues/1756 1385#[test] 1386fn arithmetic_in_guards() { 1387 assert_parse!( 1388 " 1389case 2, 3 { 1390 x, y if x + y == 1 -> True 1391}" 1392 ); 1393} 1394 1395#[test] 1396fn const_string_concat() { 1397 assert_parse_module!( 1398 " 1399const cute = \"cute\" 1400const cute_bee = cute <> \"bee\" 1401" 1402 ); 1403} 1404 1405#[test] 1406fn const_string_concat_naked_right() { 1407 assert_module_error!( 1408 " 1409const no_cute_bee = \"cute\" <> 1410" 1411 ); 1412} 1413 1414#[test] 1415fn function_call_in_case_clause_guard() { 1416 assert_error!( 1417 r#" 1418let my_string = "hello" 1419case my_string { 1420 _ if length(my_string) > 2 -> io.debug("doesn't work') 1421}"# 1422 ); 1423} 1424 1425#[test] 1426fn dot_access_function_call_in_case_clause_guard() { 1427 assert_error!( 1428 r#" 1429let my_string = "hello" 1430case my_string { 1431 _ if string.length(my_string) > 2 -> io.debug("doesn't work') 1432}"# 1433 ); 1434} 1435 1436#[test] 1437fn invalid_left_paren_in_case_clause_guard() { 1438 assert_error!( 1439 r#" 1440let my_string = "hello" 1441case my_string { 1442 _ if string.length( > 2 -> io.debug("doesn't work') 1443}"# 1444 ); 1445} 1446 1447#[test] 1448fn invalid_label_shorthand() { 1449 assert_module_error!( 1450 " 1451pub fn main() { 1452 wibble(:) 1453} 1454" 1455 ); 1456} 1457 1458#[test] 1459fn invalid_label_shorthand_2() { 1460 assert_module_error!( 1461 " 1462pub fn main() { 1463 wibble(:,) 1464} 1465" 1466 ); 1467} 1468 1469#[test] 1470fn invalid_label_shorthand_3() { 1471 assert_module_error!( 1472 " 1473pub fn main() { 1474 wibble(:arg) 1475} 1476" 1477 ); 1478} 1479 1480#[test] 1481fn invalid_label_shorthand_4() { 1482 assert_module_error!( 1483 " 1484pub fn main() { 1485 wibble(arg::) 1486} 1487" 1488 ); 1489} 1490 1491#[test] 1492fn invalid_label_shorthand_5() { 1493 assert_module_error!( 1494 " 1495pub fn main() { 1496 wibble(arg::arg) 1497} 1498" 1499 ); 1500} 1501 1502#[test] 1503fn invalid_pattern_label_shorthand() { 1504 assert_module_error!( 1505 " 1506pub fn main() { 1507 let Wibble(:) = todo 1508} 1509" 1510 ); 1511} 1512 1513#[test] 1514fn invalid_pattern_label_shorthand_2() { 1515 assert_module_error!( 1516 " 1517pub fn main() { 1518 let Wibble(:arg) = todo 1519} 1520" 1521 ); 1522} 1523 1524#[test] 1525fn invalid_pattern_label_shorthand_3() { 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_4() { 1537 assert_module_error!( 1538 " 1539pub fn main() { 1540 let Wibble(arg: arg:) = todo 1541} 1542" 1543 ); 1544} 1545 1546#[test] 1547fn invalid_pattern_label_shorthand_5() { 1548 assert_module_error!( 1549 " 1550pub fn main() { 1551 let Wibble(arg1: arg2:) = todo 1552} 1553" 1554 ); 1555} 1556 1557fn first_parsed_docstring(src: &str) -> EcoString { 1558 let parsed = 1559 crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null()) 1560 .expect("should parse"); 1561 1562 parsed 1563 .module 1564 .definitions 1565 .first() 1566 .expect("parsed a definition") 1567 .definition 1568 .get_doc() 1569 .expect("definition without doc") 1570} 1571 1572#[test] 1573fn doc_comment_before_comment_is_not_attached_to_following_function() { 1574 assert_eq!( 1575 first_parsed_docstring( 1576 r#" 1577 /// Not included! 1578 // pub fn call() 1579 1580 /// Doc! 1581 pub fn wibble() {} 1582"# 1583 ), 1584 " Doc!\n" 1585 ) 1586} 1587 1588#[test] 1589fn doc_comment_before_comment_is_not_attached_to_following_type() { 1590 assert_eq!( 1591 first_parsed_docstring( 1592 r#" 1593 /// Not included! 1594 // pub fn call() 1595 1596 /// Doc! 1597 pub type Wibble 1598"# 1599 ), 1600 " Doc!\n" 1601 ) 1602} 1603 1604#[test] 1605fn doc_comment_before_comment_is_not_attached_to_following_type_alias() { 1606 assert_eq!( 1607 first_parsed_docstring( 1608 r#" 1609 /// Not included! 1610 // pub fn call() 1611 1612 /// Doc! 1613 pub type Wibble = Int 1614"# 1615 ), 1616 " Doc!\n" 1617 ) 1618} 1619 1620#[test] 1621fn doc_comment_before_comment_is_not_attached_to_following_constant() { 1622 assert_eq!( 1623 first_parsed_docstring( 1624 r#" 1625 /// Not included! 1626 // pub fn call() 1627 1628 /// Doc! 1629 pub const wibble = 1 1630"# 1631 ), 1632 " Doc!\n" 1633 ); 1634} 1635 1636#[test] 1637fn non_module_level_function_with_a_name() { 1638 assert_module_error!( 1639 r#" 1640pub fn main() { 1641 fn my() { 1 } 1642} 1643"# 1644 ); 1645} 1646 1647#[test] 1648fn error_message_on_variable_starting_with_underscore() { 1649 // https://github.com/gleam-lang/gleam/issues/3504 1650 assert_module_error!( 1651 " 1652 pub fn main() { 1653 let val = _func_starting_with_underscore(1) 1654 }" 1655 ); 1656} 1657 1658#[test] 1659fn non_module_level_function_with_not_a_name() { 1660 assert_module_error!( 1661 r#" 1662pub fn main() { 1663 fn @() { 1 } // wrong token and not a name 1664} 1665"# 1666 ); 1667} 1668 1669#[test] 1670fn error_message_on_variable_starting_with_underscore2() { 1671 // https://github.com/gleam-lang/gleam/issues/3504 1672 assert_module_error!( 1673 " 1674 pub fn main() { 1675 case 1 { 1676 1 -> _with_underscore(1) 1677 } 1678 }" 1679 ); 1680} 1681 1682#[test] 1683fn function_inside_a_type() { 1684 assert_module_error!( 1685 r#" 1686type Wibble { 1687 fn wobble() {} 1688} 1689"# 1690 ); 1691} 1692 1693#[test] 1694fn pub_function_inside_a_type() { 1695 assert_module_error!( 1696 r#" 1697type Wibble { 1698 pub fn wobble() {} 1699} 1700"# 1701 ); 1702} 1703 1704#[test] 1705fn if_like_expression() { 1706 assert_module_error!( 1707 r#" 1708pub fn main() { 1709 let a = if wibble { 1710 wobble 1711 } 1712} 1713"# 1714 ); 1715} 1716 1717// https://github.com/gleam-lang/gleam/issues/3730 1718#[test] 1719fn missing_constructor_arguments() { 1720 assert_module_error!( 1721 " 1722pub type A { 1723 A(Int) 1724} 1725 1726const a = A() 1727" 1728 ); 1729} 1730 1731// https://github.com/gleam-lang/gleam/issues/3796 1732#[test] 1733fn missing_type_constructor_arguments_in_type_definition() { 1734 assert_module_error!( 1735 " 1736pub type A() { 1737 A(Int) 1738} 1739" 1740 ); 1741} 1742 1743#[test] 1744fn missing_type_constructor_arguments_in_type_annotation_1() { 1745 assert_module_error!("pub fn main() -> Int() {}"); 1746} 1747 1748#[test] 1749fn missing_type_constructor_arguments_in_type_annotation_2() { 1750 assert_module_error!( 1751 "pub fn main() { 1752 let a: Int() = todo 1753}" 1754 ); 1755} 1756 1757#[test] 1758fn tuple_without_hash() { 1759 assert_module_error!( 1760 r#" 1761pub fn main() { 1762 let triple = (1, 2.2, "three") 1763 io.debug(triple) 1764 let (a, *, *) = triple 1765 io.debug(a) 1766 io.debug(triple.1) 1767} 1768"# 1769 ); 1770} 1771 1772#[test] 1773fn deprecation_attribute_on_type_variant() { 1774 assert_parse_module!( 1775 r#" 1776type Wibble { 1777 @deprecated("1") 1778 Wibble1 1779 Wibble2 1780} 1781"# 1782 ); 1783} 1784 1785#[test] 1786 1787fn float_empty_exponent() { 1788 assert_error!("1.32e"); 1789} 1790 1791#[test] 1792fn multiple_deprecation_attribute_on_type_variant() { 1793 assert_module_error!( 1794 r#" 1795type Wibble { 1796 @deprecated("1") 1797 @deprecated("2") 1798 Wibble1 1799 Wibble2 1800} 1801"# 1802 ); 1803} 1804 1805#[test] 1806fn target_attribute_on_type_variant() { 1807 assert_module_error!( 1808 r#" 1809type Wibble { 1810 @target(erlang) 1811 Wibble2 1812} 1813"# 1814 ); 1815} 1816 1817#[test] 1818fn internal_attribute_on_type_variant() { 1819 assert_module_error!( 1820 r#" 1821type Wibble { 1822 @internal 1823 Wibble1 1824} 1825"# 1826 ); 1827} 1828 1829#[test] 1830fn external_attribute_on_type_variant() { 1831 assert_module_error!( 1832 r#" 1833type Wibble { 1834 @external(erlang, "one", "two") 1835 Wibble1 1836} 1837"# 1838 ); 1839} 1840 1841#[test] 1842fn multiple_unsupported_attributes_on_type_variant() { 1843 assert_module_error!( 1844 r#" 1845type Wibble { 1846 @external(erlang, "one", "two") 1847 @target(erlang) 1848 @internal 1849 Wibble1 1850} 1851"# 1852 ); 1853} 1854 1855#[test] 1856// https://github.com/gleam-lang/gleam/issues/3870 1857fn nested_tuple_access_after_function() { 1858 assert_parse!("tuple().0.1"); 1859} 1860 1861#[test] 1862fn case_expression_without_body() { 1863 assert_parse!("case a"); 1864} 1865 1866#[test] 1867fn assert_statement() { 1868 assert_parse!("assert 10 != 11"); 1869} 1870 1871#[test] 1872fn assert_statement_with_message() { 1873 assert_parse!(r#"assert False as "Uh oh""#); 1874} 1875 1876#[test] 1877fn assert_statement_without_expression() { 1878 assert_error!("assert"); 1879} 1880 1881#[test] 1882fn assert_statement_followed_by_statement() { 1883 assert_error!("assert let a = 10"); 1884} 1885 1886#[test] 1887fn special_error_for_pythonic_import() { 1888 assert_module_error!("import gleam.io"); 1889} 1890 1891#[test] 1892fn special_error_for_pythonic_neste_import() { 1893 assert_module_error!("import one.two.three"); 1894} 1895 1896#[test] 1897fn doesnt_issue_special_error_for_pythonic_import_if_slash() { 1898 assert_module_error!("import one/two.three"); 1899} 1900 1901#[test] 1902fn operator_in_pattern_size() { 1903 assert_parse!("let assert <<size, payload:size(size - 1)>> = <<>>"); 1904} 1905 1906#[test] 1907fn correct_precedence_in_pattern_size() { 1908 assert_parse!("let assert <<size, payload:size(size + 2 * 8)>> = <<>>"); 1909} 1910 1911#[test] 1912fn private_opaque_type_is_parsed() { 1913 assert_parse_module!("opaque type Wibble { Wobble }"); 1914} 1915 1916#[test] 1917fn case_guard_with_nested_blocks() { 1918 assert_parse!( 1919 "case 1 { 1920 _ if { 1 || { 1 || 1 } } || 1 -> 1 1921}" 1922 ); 1923} 1924 1925#[test] 1926fn case_guard_with_empty_block() { 1927 assert_error!( 1928 "case 1 { 1929 _ if a || {} -> 1 1930}" 1931 ); 1932}