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