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 2501 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 unknown_target() { 752 assert_module_error!( 753 r#" 754@target(abc) 755pub fn one() {}"# 756 ); 757} 758 759#[test] 760fn missing_target() { 761 assert_module_error!( 762 r#" 763@target() 764pub fn one() {}"# 765 ); 766} 767 768#[test] 769fn missing_target_and_bracket() { 770 assert_module_error!( 771 r#" 772@target( 773pub fn one() {}"# 774 ); 775} 776 777#[test] 778fn unknown_attribute() { 779 assert_module_error!( 780 r#"@go_faster() 781pub fn main() { 1 }"# 782 ); 783} 784 785#[test] 786fn incomplete_function() { 787 assert_error!("fn()"); 788} 789 790#[test] 791fn multiple_deprecation_attributes() { 792 assert_module_error!( 793 r#" 794@deprecated("1") 795@deprecated("2") 796pub fn main() -> Nil { 797 Nil 798} 799"# 800 ); 801} 802 803#[test] 804fn deprecation_with_no_message() { 805 assert_module_error!( 806 r#" 807@deprecated 808pub fn main() -> Nil { 809 Nil 810} 811"# 812 ); 813} 814 815#[test] 816fn deprecation_with_no_message_on_constructor() { 817 assert_module_error!( 818 r#" 819pub type HashAlgorithm { 820 @deprecated 821 Md5 822 Sha224 823 Sha512 824} 825"# 826 ); 827} 828 829#[test] 830fn deprecation_without_message() { 831 assert_module_error!( 832 r#" 833@deprecated() 834pub fn main() -> Nil { 835 Nil 836} 837"# 838 ); 839} 840 841#[test] 842fn multiple_internal_attributes() { 843 assert_module_error!( 844 r#" 845@internal 846@internal 847pub fn main() -> Nil { 848 Nil 849} 850"# 851 ); 852} 853 854#[test] 855fn attributes_with_no_definition() { 856 assert_module_error!( 857 r#" 858@deprecated("1") 859@target(erlang) 860"# 861 ); 862} 863 864#[test] 865fn external_attribute_with_custom_type() { 866 assert_parse_module!( 867 r#" 868@external(erlang, "gleam_stdlib", "dict") 869@external(javascript, "./gleam_stdlib.d.ts", "Dict") 870pub type Dict(key, value) 871"# 872 ); 873} 874 875#[test] 876fn external_attribute_with_non_fn_definition() { 877 assert_module_error!( 878 r#" 879@external(erlang, "module", "fun") 880pub type Fun = Fun 881"# 882 ); 883} 884 885#[test] 886fn attributes_with_improper_definition() { 887 assert_module_error!( 888 r#" 889@deprecated("1") 890@external(erlang, "module", "fun") 891"# 892 ); 893} 894 895#[test] 896fn const_with_function_call() { 897 assert_module_error!( 898 r#" 899pub fn wibble() { 123 } 900const wib: Int = wibble() 901"# 902 ); 903} 904 905#[test] 906fn const_with_function_call_with_args() { 907 assert_module_error!( 908 r#" 909pub fn wibble() { 123 } 910const wib: Int = wibble(1, "wobble") 911"# 912 ); 913} 914 915#[test] 916fn import_type() { 917 assert_parse_module!(r#"import wibble.{type Wobble, Wobble, type Wabble}"#); 918} 919 920#[test] 921fn reserved_auto() { 922 assert_module_error!(r#"const auto = 1"#); 923} 924 925#[test] 926fn reserved_delegate() { 927 assert_module_error!(r#"const delegate = 1"#); 928} 929 930#[test] 931fn reserved_derive() { 932 assert_module_error!(r#"const derive = 1"#); 933} 934 935#[test] 936fn reserved_else() { 937 assert_module_error!(r#"const else = 1"#); 938} 939 940#[test] 941fn reserved_implement() { 942 assert_module_error!(r#"const implement = 1"#); 943} 944 945#[test] 946fn reserved_macro() { 947 assert_module_error!(r#"const macro = 1"#); 948} 949 950#[test] 951fn reserved_test() { 952 assert_module_error!(r#"const test = 1"#); 953} 954 955#[test] 956fn reserved_echo() { 957 assert_module_error!(r#"const echo = 1"#); 958} 959 960#[test] 961fn capture_with_name() { 962 assert_module_error!( 963 r#" 964pub fn main() { 965 add(_name, 1) 966} 967 968fn add(x, y) { 969 x + y 970} 971"# 972 ); 973} 974 975#[test] 976fn list_spread_with_no_tail_in_the_middle_of_a_list() { 977 assert_module_error!( 978 r#" 979pub fn main() -> Nil { 980 let xs = [1, 2, 3] 981 [1, 2, .., 3 + 3, 4] 982} 983"# 984 ); 985} 986 987#[test] 988fn list_spread_followed_by_extra_items() { 989 assert_module_error!( 990 r#" 991pub fn main() -> Nil { 992 let xs = [1, 2, 3] 993 [1, 2, ..xs, 3 + 3, 4] 994} 995"# 996 ); 997} 998 999#[test] 1000fn list_spread_followed_by_extra_item_and_another_spread() { 1001 assert_module_error!( 1002 r#" 1003pub fn main() -> Nil { 1004 let xs = [1, 2, 3] 1005 let ys = [5, 6, 7] 1006 [..xs, 4, ..ys] 1007} 1008"# 1009 ); 1010} 1011 1012#[test] 1013fn list_spread_followed_by_other_spread() { 1014 assert_module_error!( 1015 r#" 1016pub fn main() -> Nil { 1017 let xs = [1, 2, 3] 1018 let ys = [5, 6, 7] 1019 [1, ..xs, ..ys] 1020} 1021"# 1022 ); 1023} 1024 1025#[test] 1026fn list_spread_as_first_item_followed_by_other_items() { 1027 assert_module_error!( 1028 r#" 1029pub fn main() -> Nil { 1030 let xs = [1, 2, 3] 1031 [..xs, 3 + 3, 4] 1032} 1033"# 1034 ); 1035} 1036 1037// Tests for nested tuples and structs in tuples 1038// https://github.com/gleam-lang/gleam/issues/1980 1039 1040#[test] 1041fn nested_tuples() { 1042 assert_parse!( 1043 r#" 1044let tup = #(#(5, 6)) 1045{tup.0}.1 1046"# 1047 ); 1048} 1049 1050#[test] 1051fn nested_tuples_no_block() { 1052 assert_parse!( 1053 r#" 1054let tup = #(#(5, 6)) 1055tup.0.1 1056"# 1057 ); 1058} 1059 1060#[test] 1061fn deeply_nested_tuples() { 1062 assert_parse!( 1063 r#" 1064let tup = #(#(#(#(4)))) 1065{{{tup.0}.0}.0}.0 1066"# 1067 ); 1068} 1069 1070#[test] 1071fn deeply_nested_tuples_no_block() { 1072 assert_parse!( 1073 r#" 1074let tup = #(#(#(#(4)))) 1075tup.0.0.0.0 1076"# 1077 ); 1078} 1079 1080#[test] 1081fn inner_single_quote_parses() { 1082 assert_parse!( 1083 r#" 1084let a = "inner 'quotes'" 1085"# 1086 ); 1087} 1088 1089#[test] 1090fn string_single_char_suggestion() { 1091 assert_module_error!( 1092 " 1093 pub fn main() { 1094 let a = 'example' 1095 } 1096 " 1097 ); 1098} 1099 1100#[test] 1101fn private_internal_const() { 1102 assert_module_error!( 1103 " 1104@internal 1105const wibble = 1 1106" 1107 ); 1108} 1109 1110#[test] 1111fn private_internal_type_alias() { 1112 assert_module_error!( 1113 " 1114@internal 1115type Alias = Int 1116" 1117 ); 1118} 1119 1120#[test] 1121fn private_internal_function() { 1122 assert_module_error!( 1123 " 1124@internal 1125fn wibble() { todo } 1126" 1127 ); 1128} 1129 1130#[test] 1131fn private_internal_type() { 1132 assert_module_error!( 1133 " 1134@internal 1135type Wibble { 1136 Wibble 1137} 1138" 1139 ); 1140} 1141 1142#[test] 1143fn wrong_record_access_pattern() { 1144 assert_module_error!( 1145 " 1146pub fn main() { 1147 case wibble { 1148 wibble.thing -> 1 1149 } 1150} 1151" 1152 ); 1153} 1154 1155#[test] 1156fn tuple_invalid_expr() { 1157 assert_module_error!( 1158 " 1159fn main() { 1160 #(1, 2, const) 1161} 1162" 1163 ); 1164} 1165 1166#[test] 1167fn bit_array_invalid_segment() { 1168 assert_module_error!( 1169 " 1170fn main() { 1171 <<72, 101, 108, 108, 111, 44, 32, 74, 111, 101, const>> 1172} 1173" 1174 ); 1175} 1176 1177#[test] 1178fn case_invalid_expression() { 1179 assert_module_error!( 1180 " 1181fn main() { 1182 case 1, type { 1183 _, _ -> 0 1184 } 1185} 1186" 1187 ); 1188} 1189 1190#[test] 1191fn case_invalid_case_pattern() { 1192 assert_module_error!( 1193 " 1194fn main() { 1195 case 1 { 1196 -> -> 0 1197 } 1198} 1199" 1200 ); 1201} 1202 1203#[test] 1204fn case_clause_no_subject() { 1205 assert_module_error!( 1206 " 1207fn main() { 1208 case 1 { 1209 -> 1 1210 _ -> 2 1211 } 1212} 1213" 1214 ); 1215} 1216 1217#[test] 1218fn case_alternative_clause_no_subject() { 1219 assert_module_error!( 1220 " 1221fn main() { 1222 case 1 { 1223 1 | -> 1 1224 _ -> 1 1225 } 1226} 1227" 1228 ); 1229} 1230 1231#[test] 1232fn use_invalid_assignments() { 1233 assert_module_error!( 1234 " 1235fn main() { 1236 use fn <- result.try(get_username()) 1237} 1238" 1239 ); 1240} 1241 1242#[test] 1243fn assignment_pattern_invalid_tuple() { 1244 assert_module_error!( 1245 " 1246fn main() { 1247 let #(a, case, c) = #(1, 2, 3) 1248} 1249" 1250 ); 1251} 1252 1253#[test] 1254fn assignment_pattern_invalid_bit_segment() { 1255 assert_module_error!( 1256 " 1257fn main() { 1258 let <<b1, pub>> = <<24, 3>> 1259} 1260" 1261 ); 1262} 1263 1264#[test] 1265fn case_list_pattern_after_spread() { 1266 assert_module_error!( 1267 " 1268fn main() { 1269 case somelist { 1270 [..rest, last] -> 1 1271 _ -> 2 1272 } 1273} 1274" 1275 ); 1276} 1277 1278#[test] 1279fn type_invalid_constructor() { 1280 assert_module_error!( 1281 " 1282type A { 1283 A(String) 1284 type 1285} 1286" 1287 ); 1288} 1289 1290// Tests whether diagnostic presents an example of how to formulate a proper 1291// record constructor based off a common user error pattern. 1292// https://github.com/gleam-lang/gleam/issues/3324 1293 1294#[test] 1295fn type_invalid_record_constructor() { 1296 assert_module_error!( 1297 " 1298pub type User { 1299 name: String, 1300} 1301" 1302 ); 1303} 1304 1305#[test] 1306fn type_invalid_record_constructor_without_field_type() { 1307 assert_module_error!( 1308 " 1309pub opaque type User { 1310 name 1311} 1312" 1313 ); 1314} 1315 1316#[test] 1317fn type_invalid_record_constructor_invalid_field_type() { 1318 assert_module_error!( 1319 r#" 1320type User { 1321 name: "Test User", 1322} 1323"# 1324 ); 1325} 1326 1327#[test] 1328fn type_invalid_type_name() { 1329 assert_module_error!( 1330 " 1331type A(a, type) { 1332 A 1333} 1334" 1335 ); 1336} 1337 1338#[test] 1339fn type_invalid_constructor_arg() { 1340 assert_module_error!( 1341 " 1342type A { 1343 A(type: String) 1344} 1345" 1346 ); 1347} 1348 1349#[test] 1350fn type_invalid_record() { 1351 assert_module_error!( 1352 " 1353type A { 1354 One 1355 Two 1356 3 1357} 1358" 1359 ); 1360} 1361 1362#[test] 1363fn function_type_invalid_param_type() { 1364 assert_module_error!( 1365 " 1366fn f(g: fn(Int, 1) -> Int) -> Int { 1367 g(0, 1) 1368} 1369" 1370 ); 1371} 1372 1373#[test] 1374fn function_invalid_signature() { 1375 assert_module_error!( 1376 r#" 1377fn f(a, "b") -> String { 1378 a <> b 1379} 1380"# 1381 ); 1382} 1383 1384#[test] 1385fn const_invalid_tuple() { 1386 assert_module_error!( 1387 " 1388const a = #(1, 2, <-) 1389" 1390 ); 1391} 1392 1393#[test] 1394fn const_invalid_list() { 1395 assert_module_error!( 1396 " 1397const a = [1, 2, <-] 1398" 1399 ); 1400} 1401 1402#[test] 1403fn const_invalid_bit_array_segment() { 1404 assert_module_error!( 1405 " 1406const a = <<1, 2, <->> 1407" 1408 ); 1409} 1410 1411#[test] 1412fn const_invalid_record_constructor() { 1413 assert_module_error!( 1414 " 1415type A { 1416 A(String, Int) 1417} 1418const a = A(\"a\", let) 1419" 1420 ); 1421} 1422 1423// record access should parse even if there is no label written 1424#[test] 1425fn record_access_no_label() { 1426 assert_parse_module!( 1427 " 1428type Wibble { 1429 Wibble(wibble: String) 1430} 1431 1432fn wobble() { 1433 Wibble(\"a\"). 1434} 1435" 1436 ); 1437} 1438 1439#[test] 1440fn newline_tokens() { 1441 assert_eq!( 1442 make_tokenizer("1\n\n2\n").collect_vec(), 1443 [ 1444 Ok(( 1445 0, 1446 Token::Int { 1447 value: "1".into(), 1448 int_value: 1.into() 1449 }, 1450 1 1451 )), 1452 Ok((1, Token::NewLine, 2)), 1453 Ok((2, Token::NewLine, 3)), 1454 Ok(( 1455 3, 1456 Token::Int { 1457 value: "2".into(), 1458 int_value: 2.into() 1459 }, 1460 4 1461 )), 1462 Ok((4, Token::NewLine, 5)) 1463 ] 1464 ); 1465} 1466 1467// https://github.com/gleam-lang/gleam/issues/1756 1468#[test] 1469fn arithmetic_in_guards() { 1470 assert_parse!( 1471 " 1472case 2, 3 { 1473 x, y if x + y == 1 -> True 1474}" 1475 ); 1476} 1477 1478#[test] 1479fn const_string_concat() { 1480 assert_parse_module!( 1481 " 1482const cute = \"cute\" 1483const cute_bee = cute <> \"bee\" 1484" 1485 ); 1486} 1487 1488#[test] 1489fn const_string_concat_naked_right() { 1490 assert_module_error!( 1491 " 1492const no_cute_bee = \"cute\" <> 1493" 1494 ); 1495} 1496 1497#[test] 1498fn function_call_in_case_clause_guard() { 1499 assert_error!( 1500 r#" 1501let my_string = "hello" 1502case my_string { 1503 _ if length(my_string) > 2 -> io.debug("doesn't work') 1504}"# 1505 ); 1506} 1507 1508#[test] 1509fn dot_access_function_call_in_case_clause_guard() { 1510 assert_error!( 1511 r#" 1512let my_string = "hello" 1513case my_string { 1514 _ if string.length(my_string) > 2 -> io.debug("doesn't work') 1515}"# 1516 ); 1517} 1518 1519#[test] 1520fn invalid_left_paren_in_case_clause_guard() { 1521 assert_error!( 1522 r#" 1523let my_string = "hello" 1524case my_string { 1525 _ if string.length( > 2 -> io.debug("doesn't work') 1526}"# 1527 ); 1528} 1529 1530#[test] 1531fn string_concatenation_in_case_clause_guard() { 1532 assert_parse!( 1533 r#" 1534let my_string = "hello " 1535case my_string { 1536 _ if my_string <> "world" == "hello world" -> io.debug("ok") 1537}"# 1538 ); 1539} 1540 1541#[test] 1542fn constant_record_with_empty_arguments_is_record() { 1543 assert_parse_module!("pub const wibble = Wibble()"); 1544} 1545 1546#[test] 1547fn calling_module_constant_as_constructor() { 1548 assert_module_error!( 1549 " 1550pub type Wibble { Wibble(Int) } 1551pub const wibble = Wibble 1552pub const a = wibble(1) 1553" 1554 ); 1555} 1556 1557#[test] 1558fn invalid_label_shorthand() { 1559 assert_module_error!( 1560 " 1561pub fn main() { 1562 wibble(:) 1563} 1564" 1565 ); 1566} 1567 1568#[test] 1569fn invalid_label_shorthand_2() { 1570 assert_module_error!( 1571 " 1572pub fn main() { 1573 wibble(:,) 1574} 1575" 1576 ); 1577} 1578 1579#[test] 1580fn invalid_label_shorthand_3() { 1581 assert_module_error!( 1582 " 1583pub fn main() { 1584 wibble(:arg) 1585} 1586" 1587 ); 1588} 1589 1590#[test] 1591fn invalid_label_shorthand_4() { 1592 assert_module_error!( 1593 " 1594pub fn main() { 1595 wibble(arg::) 1596} 1597" 1598 ); 1599} 1600 1601#[test] 1602fn invalid_label_shorthand_5() { 1603 assert_module_error!( 1604 " 1605pub fn main() { 1606 wibble(arg::arg) 1607} 1608" 1609 ); 1610} 1611 1612#[test] 1613fn invalid_pattern_label_shorthand() { 1614 assert_module_error!( 1615 " 1616pub fn main() { 1617 let Wibble(:) = todo 1618} 1619" 1620 ); 1621} 1622 1623#[test] 1624fn invalid_pattern_label_shorthand_2() { 1625 assert_module_error!( 1626 " 1627pub fn main() { 1628 let Wibble(:arg) = todo 1629} 1630" 1631 ); 1632} 1633 1634#[test] 1635fn invalid_pattern_label_shorthand_3() { 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_4() { 1647 assert_module_error!( 1648 " 1649pub fn main() { 1650 let Wibble(arg: arg:) = todo 1651} 1652" 1653 ); 1654} 1655 1656#[test] 1657fn invalid_pattern_label_shorthand_5() { 1658 assert_module_error!( 1659 " 1660pub fn main() { 1661 let Wibble(arg1: arg2:) = todo 1662} 1663" 1664 ); 1665} 1666 1667fn first_parsed_docstring(src: &str) -> EcoString { 1668 let parsed = 1669 crate::parse::parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null()) 1670 .expect("should parse"); 1671 1672 parsed 1673 .module 1674 .definitions 1675 .first() 1676 .expect("parsed a definition") 1677 .definition 1678 .get_doc() 1679 .expect("definition without doc") 1680} 1681 1682#[test] 1683fn doc_comment_before_comment_is_not_attached_to_following_function() { 1684 assert_eq!( 1685 first_parsed_docstring( 1686 r#" 1687 /// Not included! 1688 // pub fn call() 1689 1690 /// Doc! 1691 pub fn wibble() {} 1692"# 1693 ), 1694 " Doc!\n" 1695 ) 1696} 1697 1698#[test] 1699fn doc_comment_before_comment_is_not_attached_to_following_type() { 1700 assert_eq!( 1701 first_parsed_docstring( 1702 r#" 1703 /// Not included! 1704 // pub fn call() 1705 1706 /// Doc! 1707 pub type Wibble 1708"# 1709 ), 1710 " Doc!\n" 1711 ) 1712} 1713 1714#[test] 1715fn doc_comment_before_comment_is_not_attached_to_following_type_alias() { 1716 assert_eq!( 1717 first_parsed_docstring( 1718 r#" 1719 /// Not included! 1720 // pub fn call() 1721 1722 /// Doc! 1723 pub type Wibble = Int 1724"# 1725 ), 1726 " Doc!\n" 1727 ) 1728} 1729 1730#[test] 1731fn doc_comment_before_comment_is_not_attached_to_following_constant() { 1732 assert_eq!( 1733 first_parsed_docstring( 1734 r#" 1735 /// Not included! 1736 // pub fn call() 1737 1738 /// Doc! 1739 pub const wibble = 1 1740"# 1741 ), 1742 " Doc!\n" 1743 ); 1744} 1745 1746#[test] 1747fn non_module_level_function_with_a_name() { 1748 assert_module_error!( 1749 r#" 1750pub fn main() { 1751 fn my() { 1 } 1752} 1753"# 1754 ); 1755} 1756 1757#[test] 1758fn error_message_on_variable_starting_with_underscore() { 1759 // https://github.com/gleam-lang/gleam/issues/3504 1760 assert_module_error!( 1761 " 1762 pub fn main() { 1763 let val = _func_starting_with_underscore(1) 1764 }" 1765 ); 1766} 1767 1768#[test] 1769fn non_module_level_function_with_not_a_name() { 1770 assert_module_error!( 1771 r#" 1772pub fn main() { 1773 fn @() { 1 } // wrong token and not a name 1774} 1775"# 1776 ); 1777} 1778 1779#[test] 1780fn error_message_on_variable_starting_with_underscore2() { 1781 // https://github.com/gleam-lang/gleam/issues/3504 1782 assert_module_error!( 1783 " 1784 pub fn main() { 1785 case 1 { 1786 1 -> _with_underscore(1) 1787 } 1788 }" 1789 ); 1790} 1791 1792#[test] 1793fn function_inside_a_type() { 1794 assert_module_error!( 1795 r#" 1796type Wibble { 1797 fn wobble() {} 1798} 1799"# 1800 ); 1801} 1802 1803#[test] 1804fn pub_function_inside_a_type() { 1805 assert_module_error!( 1806 r#" 1807type Wibble { 1808 pub fn wobble() {} 1809} 1810"# 1811 ); 1812} 1813 1814#[test] 1815fn if_like_expression() { 1816 assert_module_error!( 1817 r#" 1818pub fn main() { 1819 let a = if wibble { 1820 wobble 1821 } 1822} 1823"# 1824 ); 1825} 1826 1827// https://github.com/gleam-lang/gleam/issues/3796 1828#[test] 1829fn missing_type_constructor_arguments_in_type_definition() { 1830 assert_module_error!( 1831 " 1832pub type A() { 1833 A(Int) 1834} 1835" 1836 ); 1837} 1838 1839#[test] 1840fn tuple_without_hash() { 1841 assert_module_error!( 1842 r#" 1843pub fn main() { 1844 let triple = (1, 2.2, "three") 1845 io.debug(triple) 1846 let (a, *, *) = triple 1847 io.debug(a) 1848 io.debug(triple.1) 1849} 1850"# 1851 ); 1852} 1853 1854#[test] 1855fn deprecation_attribute_on_type_variant() { 1856 assert_parse_module!( 1857 r#" 1858type Wibble { 1859 @deprecated("1") 1860 Wibble1 1861 Wibble2 1862} 1863"# 1864 ); 1865} 1866 1867#[test] 1868 1869fn float_empty_exponent() { 1870 assert_error!("1.32e"); 1871} 1872 1873#[test] 1874fn multiple_deprecation_attribute_on_type_variant() { 1875 assert_module_error!( 1876 r#" 1877type Wibble { 1878 @deprecated("1") 1879 @deprecated("2") 1880 Wibble1 1881 Wibble2 1882} 1883"# 1884 ); 1885} 1886 1887#[test] 1888fn target_attribute_on_type_variant() { 1889 assert_module_error!( 1890 r#" 1891type Wibble { 1892 @target(erlang) 1893 Wibble2 1894} 1895"# 1896 ); 1897} 1898 1899#[test] 1900fn internal_attribute_on_type_variant() { 1901 assert_module_error!( 1902 r#" 1903type Wibble { 1904 @internal 1905 Wibble1 1906} 1907"# 1908 ); 1909} 1910 1911#[test] 1912fn external_attribute_on_type_variant() { 1913 assert_module_error!( 1914 r#" 1915type Wibble { 1916 @external(erlang, "one", "two") 1917 Wibble1 1918} 1919"# 1920 ); 1921} 1922 1923#[test] 1924fn multiple_unsupported_attributes_on_type_variant() { 1925 assert_module_error!( 1926 r#" 1927type Wibble { 1928 @external(erlang, "one", "two") 1929 @target(erlang) 1930 @internal 1931 Wibble1 1932} 1933"# 1934 ); 1935} 1936 1937#[test] 1938// https://github.com/gleam-lang/gleam/issues/3870 1939fn nested_tuple_access_after_function() { 1940 assert_parse!("tuple().0.1"); 1941} 1942 1943#[test] 1944fn case_expression_without_body() { 1945 assert_parse!("case a"); 1946} 1947 1948#[test] 1949fn assert_statement() { 1950 assert_parse!("assert 10 != 11"); 1951} 1952 1953#[test] 1954fn assert_statement_with_message() { 1955 assert_parse!(r#"assert False as "Uh oh""#); 1956} 1957 1958#[test] 1959fn assert_statement_without_expression() { 1960 assert_error!("assert"); 1961} 1962 1963#[test] 1964fn assert_statement_followed_by_statement() { 1965 assert_error!("assert let a = 10"); 1966} 1967 1968#[test] 1969fn special_error_for_pythonic_import() { 1970 assert_module_error!("import gleam.io"); 1971} 1972 1973#[test] 1974fn special_error_for_pythonic_neste_import() { 1975 assert_module_error!("import one.two.three"); 1976} 1977 1978#[test] 1979fn doesnt_issue_special_error_for_pythonic_import_if_slash() { 1980 assert_module_error!("import one/two.three"); 1981} 1982 1983#[test] 1984fn operator_in_pattern_size() { 1985 assert_parse!("let assert <<size, payload:size(size - 1)>> = <<>>"); 1986} 1987 1988#[test] 1989fn correct_precedence_in_pattern_size() { 1990 assert_parse!("let assert <<size, payload:size(size + 2 * 8)>> = <<>>"); 1991} 1992 1993#[test] 1994fn private_opaque_type_is_parsed() { 1995 assert_parse_module!("opaque type Wibble { Wobble }"); 1996} 1997 1998#[test] 1999fn case_guard_with_nested_blocks() { 2000 assert_parse!( 2001 "case 1 { 2002 _ if { 1 || { 1 || 1 } } || 1 -> 1 2003}" 2004 ); 2005} 2006 2007#[test] 2008fn case_guard_with_empty_block() { 2009 assert_error!( 2010 "case 1 { 2011 _ if a || {} -> 1 2012}" 2013 ); 2014} 2015 2016#[test] 2017fn constant_inside_function() { 2018 assert_module_error!( 2019 " 2020pub fn main() { 2021 const x = 10 2022 x 2023} 2024" 2025 ); 2026} 2027 2028#[test] 2029fn function_definition_angle_generics_error() { 2030 assert_module_error!("fn id<T>(x: T) { x }"); 2031} 2032 2033#[test] 2034fn type_angle_generics_usage_without_module_error() { 2035 assert_error!("let list: List<Int, String> = []"); 2036} 2037 2038#[test] 2039fn type_angle_generics_usage_with_module_error() { 2040 assert_error!("let set: set.Set<Int> = set.new()"); 2041} 2042 2043#[test] 2044fn type_angle_generics_definition_error() { 2045 assert_module_error!( 2046 r#" 2047type Either<a, b> { 2048 This(a) 2049 That(b) 2050} 2051"# 2052 ); 2053} 2054 2055#[test] 2056fn type_angle_generics_definition_with_upname_error() { 2057 assert_module_error!( 2058 r#" 2059type Either<A, B> { 2060 This(A) 2061 That(B) 2062} 2063"# 2064 ); 2065} 2066 2067#[test] 2068fn type_angle_generics_definition_error_fallback() { 2069 // Example of a more incorrect syntax, where Gleam doesn't make a suggestion. 2070 // In this case, an example type argument is used instead. 2071 assert_module_error!( 2072 r#" 2073type Either<type A, type B> { 2074 This(A) 2075 That(B) 2076} 2077"# 2078 ); 2079} 2080 2081#[test] 2082fn wrong_type_of_comments_with_hash() { 2083 assert_module_error!( 2084 r#" 2085pub fn main() { 2086 # a python-style comment 2087} 2088"# 2089 ); 2090} 2091 2092#[test] 2093fn wrong_function_return_type_declaration_using_colon_instead_of_right_arrow() { 2094 assert_module_error!( 2095 r#" 2096pub fn main(): Nil {} 2097 "# 2098 ); 2099} 2100 2101#[test] 2102fn const_record_update_basic() { 2103 assert_parse_module!( 2104 r#" 2105type Person { 2106 Person(name: String, age: Int) 2107} 2108 2109const alice = Person("Alice", 30) 2110const bob = Person(..alice, name: "Bob") 2111"# 2112 ); 2113} 2114 2115#[test] 2116fn const_record_update_all_fields() { 2117 assert_parse_module!( 2118 r#" 2119type Person { 2120 Person(name: String, age: Int, city: String) 2121} 2122 2123const base = Person("Alice", 30, "London") 2124const updated = Person(..base, name: "Bob", age: 25, city: "Paris") 2125"# 2126 ); 2127} 2128 2129#[test] 2130fn const_record_update_only() { 2131 assert_parse_module!( 2132 r#" 2133type Person { 2134 Person(name: String, age: Int) 2135} 2136 2137const alice = Person("Alice", 30) 2138const bob = Person(..alice) 2139"# 2140 ); 2141} 2142 2143#[test] 2144fn const_record_update_with_module() { 2145 assert_parse_module!( 2146 r#" 2147const local_const = other.Record(..other.base, field: value) 2148"# 2149 ); 2150} 2151 2152// https://github.com/gleam-lang/gleam/issues/5391 2153#[test] 2154fn byte_order_mark() { 2155 assert_parse!("\u{feff}todo"); 2156} 2157 2158// https://github.com/gleam-lang/gleam/issues/5391 2159#[test] 2160fn byte_order_mark_module() { 2161 assert_parse_module!( 2162 "\u{feff} 2163const local_const = other.Record(..other.base, field: value) 2164" 2165 ); 2166} 2167 2168#[test] 2169fn prepend_to_const_list_without_comma() { 2170 // While this is valid (but deprecated) syntax for expressions, prepending to 2171 // constant lists wasn't added until after the deprecation, so it was never 2172 // valid in the first place. 2173 assert_module_error!( 2174 " 2175const wibble = [2, 3] 2176const wobble = [1 ..wibble] 2177" 2178 ); 2179} 2180 2181#[test] 2182fn prepend_to_const_list_with_multiple_spreads() { 2183 assert_module_error!( 2184 " 2185const wibble = [2, 3] 2186const wobble = [0, 1] 2187const wubble = [..wobble, ..wibble] 2188" 2189 ); 2190} 2191 2192#[test] 2193fn prepend_to_const_list_with_no_tail() { 2194 assert_module_error!( 2195 " 2196const wibble = [1, 2, ..] 2197" 2198 ); 2199} 2200 2201#[test] 2202fn prepend_no_elements_to_const_list() { 2203 assert_module_error!( 2204 " 2205const wibble = [2, 3] 2206const wobble = [..wibble] 2207" 2208 ); 2209} 2210 2211#[test] 2212fn append_to_const_list() { 2213 assert_module_error!( 2214 " 2215const wibble = [2, 3] 2216const wobble = [..wibble, 4, 5] 2217" 2218 ); 2219} 2220 2221#[test] 2222fn parse_error_for_greater_sign_after_invalid_qualified_type() { 2223 assert_module_error!("pub fn main() -> wibble.<a> { todo }"); 2224} 2225 2226#[test] 2227fn parse_error_for_type_list_after_invalid_qualified_type_1() { 2228 assert_module_error!("pub fn main() -> wibble.() { todo }"); 2229} 2230 2231#[test] 2232fn parse_error_for_type_list_after_invalid_qualified_type_2() { 2233 assert_module_error!("pub fn main() -> wibble.(a, b) { todo }"); 2234} 2235 2236#[test] 2237fn unicode_left_double_quotation_mark() { 2238 assert_module_error!("pub fn main() { \u{201C}hello\u{201D} }"); 2239} 2240 2241#[test] 2242fn unicode_right_double_quotation_mark() { 2243 assert_module_error!("pub fn main() { \u{201D}hello }"); 2244} 2245 2246#[test] 2247fn unicode_left_single_quotation_mark() { 2248 assert_module_error!("pub fn main() { \u{2018}hello\u{2019} }"); 2249} 2250 2251#[test] 2252fn unicode_right_single_quotation_mark() { 2253 assert_module_error!("pub fn main() { \u{2019}hello }"); 2254} 2255 2256#[test] 2257fn unicode_en_dash() { 2258 assert_module_error!("pub fn main() { 1\u{2013}2 }"); 2259} 2260 2261#[test] 2262fn unicode_em_dash() { 2263 assert_module_error!("pub fn main() { 1\u{2014}2 }"); 2264} 2265 2266#[test] 2267fn unicode_asterisk_operator() { 2268 assert_module_error!("pub fn main() { 1\u{2217}2 }"); 2269} 2270 2271#[test] 2272fn unicode_division_slash() { 2273 assert_module_error!("pub fn main() { 1\u{2215}2 }"); 2274} 2275 2276#[test] 2277fn unicode_non_breaking_space() { 2278 assert_module_error!("pub fn main() { let\u{00A0}x = 1\n x }"); 2279} 2280 2281#[test] 2282fn unicode_zero_width_space() { 2283 assert_module_error!("pub fn main() { let\u{200B}x = 1\n x }"); 2284} 2285 2286#[test] 2287fn unicode_cyrillic_a() { 2288 assert_module_error!("pub fn main() { \u{0430} }"); 2289} 2290 2291#[test] 2292fn unicode_cyrillic_e() { 2293 assert_module_error!("pub fn main() { \u{0435} }"); 2294} 2295 2296#[test] 2297fn unicode_cyrillic_o() { 2298 assert_module_error!("pub fn main() { \u{043E} }"); 2299} 2300 2301#[test] 2302fn unicode_cyrillic_p() { 2303 assert_module_error!("pub fn main() { \u{0440} }"); 2304} 2305 2306#[test] 2307fn unicode_modifier_letter_capital_i() { 2308 assert_module_error!("pub fn main() { \u{1D35} }"); 2309} 2310 2311#[test] 2312fn unicode_low_single_comma_quotation_mark() { 2313 assert_module_error!("pub fn main() { #(1\u{201A} 2) }"); 2314} 2315 2316#[test] 2317fn unicode_fullwidth_comma() { 2318 assert_module_error!("pub fn main() { #(1\u{FF0C} 2) }"); 2319} 2320 2321#[test] 2322fn unicode_ideographic_comma() { 2323 assert_module_error!("pub fn main() { #(1\u{3001} 2) }"); 2324} 2325 2326#[test] 2327fn unicode_fullwidth_exclamation_mark() { 2328 assert_module_error!("pub fn main() { \u{FF01}= True }"); 2329} 2330 2331#[test] 2332fn unicode_fullwidth_left_square_bracket() { 2333 assert_module_error!("pub fn main() { \u{FF3B}1, 2] }"); 2334} 2335 2336#[test] 2337fn unicode_fullwidth_right_square_bracket() { 2338 assert_module_error!("pub fn main() { [1, 2\u{FF3D} }"); 2339} 2340 2341#[test] 2342fn unicode_fullwidth_left_parenthesis() { 2343 assert_module_error!("pub fn main\u{FF08}) { Nil }"); 2344} 2345 2346#[test] 2347fn unicode_fullwidth_right_parenthesis() { 2348 assert_module_error!("pub fn main(\u{FF09} { Nil }"); 2349} 2350 2351#[test] 2352fn unicode_fullwidth_full_stop() { 2353 assert_module_error!("pub fn main() { io\u{FF0E}println(\"hi\") }"); 2354} 2355 2356#[test] 2357fn unicode_ideographic_full_stop() { 2358 assert_module_error!("pub fn main() { io\u{3002}println(\"hi\") }"); 2359} 2360 2361#[test] 2362fn unicode_fullwidth_less_than_sign() { 2363 assert_module_error!("pub fn main() { 1 \u{FF1C} 2 }"); 2364} 2365 2366#[test] 2367fn unicode_fullwidth_greater_than_sign() { 2368 assert_module_error!("pub fn main() { 1 \u{FF1E} 2 }"); 2369} 2370 2371#[test] 2372fn unicode_fullwidth_vertical_line() { 2373 assert_module_error!("pub fn main() { True \u{FF5C}\u{FF5C} False }"); 2374} 2375 2376#[test] 2377fn unicode_fullwidth_at_sign() { 2378 assert_module_error!("\u{FF20}deprecated(\"old\")\npub fn main() { Nil }"); 2379} 2380 2381#[test] 2382fn unicode_fullwidth_caret() { 2383 assert_module_error!("pub fn main() { 0b1 \u{FF3E} 0b1 }"); 2384} 2385 2386#[test] 2387fn unicode_fullwidth_colon() { 2388 assert_module_error!("pub fn wibble(x\u{FF1A} Int) { x }"); 2389} 2390 2391#[test] 2392fn missing_todo_constant_message() { 2393 assert_module_error!("pub const wibble = todo as"); 2394} 2395 2396#[test] 2397fn missing_todo_constant_message_2() { 2398 assert_module_error!( 2399 "pub const wibble = todo as 2400pub fn wibble() {}" 2401 ); 2402} 2403 2404// https://github.com/gleam-lang/gleam/issues/5711 2405#[test] 2406fn parsing_bit_array_constant_with_non_integer_size() { 2407 assert_module_error!("pub const wibble = <<1:size(something)>>"); 2408} 2409 2410#[test] 2411fn parsing_bit_array_constant_with_non_integer_unit() { 2412 assert_module_error!("pub const wibble = <<1:unit(something)>>"); 2413} 2414 2415#[test] 2416fn parsing_bit_array_expression_with_invalid_size() { 2417 assert_module_error!( 2418 "pub fn wibble() { 2419 let assert <<1:size(Upname)>> = todo 2420}" 2421 ); 2422} 2423 2424#[test] 2425fn parsing_bit_array_expression_with_invalid_unit() { 2426 assert_module_error!( 2427 "pub fn wibble() { 2428 <<1:unit(Upname)>> 2429}" 2430 ); 2431} 2432 2433#[test] 2434fn parsing_bit_array_pattern_with_invalid_unit() { 2435 assert_module_error!( 2436 "pub fn wibble() { 2437 let assert <<1:unit(Upname)>> = todo 2438}" 2439 ); 2440} 2441 2442#[test] 2443fn parsing_bit_array_constant_with_invalid_segment_type() { 2444 assert_module_error!("pub const wibble = <<1:Upname>>"); 2445} 2446 2447#[test] 2448fn parsing_invalid_external_module_name() { 2449 assert_module_error!( 2450 " 2451@external(erlang, Upname, []) 2452fn wibble() -> Nil 2453" 2454 ); 2455} 2456 2457#[test] 2458fn parsing_invalid_external_function_name() { 2459 assert_module_error!( 2460 r#" 2461@external(erlang, "module", Upname) 2462fn wibble() -> Nil 2463"# 2464 ); 2465} 2466 2467#[test] 2468fn parsing_invalid_deprecation_message() { 2469 assert_module_error!( 2470 r#" 2471@deprecated(Upname) 2472fn wibble() -> Nil 2473"# 2474 ); 2475} 2476 2477#[test] 2478fn error_message_when_using_discard_pattern_for_function_name() { 2479 assert_module_error!("fn _wibble() -> Nil"); 2480} 2481 2482#[test] 2483fn error_message_when_using_discard_pattern_for_constant_name() { 2484 assert_module_error!("const _wibble = 1"); 2485} 2486 2487#[test] 2488fn error_message_when_using_discard_pattern_for_module_name() { 2489 assert_module_error!("import _wibble"); 2490} 2491 2492#[test] 2493fn error_message_when_using_discard_pattern_for_as_pattern() { 2494 assert_module_error!( 2495 "pub fn main() { 2496 case todo { 2497 wibble as _wobble -> todo 2498 } 2499}" 2500 ); 2501}