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