Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / language-server / src / tests / definition.rs
22 kB 997 lines
1use lsp_types::{DefinitionParams, Location, Position, Range, TypeDefinitionParams, Uri as Url}; 2 3use super::*; 4 5fn definition(tester: &TestProject<'_>, position: Position) -> Option<Location> { 6 tester.at(position, |engine, param, _| { 7 let params = DefinitionParams { 8 text_document_position_params: param, 9 work_done_progress_params: Default::default(), 10 partial_result_params: Default::default(), 11 }; 12 let response = engine.goto_definition(params); 13 response.result.unwrap() 14 }) 15} 16 17fn pretty_definition(project: TestProject<'_>, position_finder: PositionFinder) -> String { 18 let position = position_finder.find_position(project.src); 19 let location = definition(&project, position).expect("a location to jump to"); 20 jump_locations_to_string(project, position, vec![location]) 21} 22 23fn type_definition(tester: &TestProject<'_>, position: Position) -> Vec<Location> { 24 tester.at(position, |engine, param, _| { 25 let params = TypeDefinitionParams { 26 text_document_position_params: param, 27 work_done_progress_params: Default::default(), 28 partial_result_params: Default::default(), 29 }; 30 let response = engine.goto_type_definition(params); 31 32 response.result.unwrap() 33 }) 34} 35 36fn pretty_type_definition(project: TestProject<'_>, position_finder: PositionFinder) -> String { 37 let position = position_finder.find_position(project.src); 38 let location = type_definition(&project, position); 39 format!( 40 "Jumping to type definition\n\n{}", 41 jump_locations_to_string(project, position, location) 42 ) 43} 44 45fn jump_locations_to_string( 46 project: TestProject<'_>, 47 original_position: Position, 48 locations: Vec<Location>, 49) -> String { 50 let src = hover::show_hover( 51 project.src, 52 Range { 53 start: original_position, 54 end: original_position, 55 }, 56 original_position, 57 ); 58 59 let destinations = locations 60 .iter() 61 .map(|location| { 62 let pretty_destination = location 63 .uri 64 .path_segments() 65 .expect("a location to jump to") 66 // To make snapshots the same both on windows and unix systems we need 67 // to discard windows' `C:` path segment at the beginning of a uri. 68 .skip_while(|segment| *segment == "C:") 69 .join("/"); 70 71 let destination_code = hover::show_hover( 72 project 73 .src_from_module_url(&location.uri) 74 .expect("a module to jump to"), 75 location.range, 76 location.range.start, 77 ); 78 79 format!( 80 "----- Jumped to `{pretty_destination}` 81{destination_code}" 82 ) 83 }) 84 .join("\n\n"); 85 86 format!( 87 "----- Jumping from `src/app.gleam` 88{src} 89{destinations}", 90 ) 91} 92 93#[macro_export] 94macro_rules! assert_goto { 95 ($src:literal, $position:expr) => { 96 let project = TestProject::for_source($src); 97 assert_goto!(project, $position); 98 }; 99 ($project:expr, $position:expr) => { 100 let output = pretty_definition($project, $position); 101 insta::assert_snapshot!(insta::internals::AutoName, output); 102 }; 103} 104 105#[macro_export] 106macro_rules! assert_goto_type { 107 ($src:literal, $position:expr) => { 108 let project = TestProject::for_source($src); 109 assert_goto_type!(project, $position); 110 }; 111 ($project:expr, $position:expr) => { 112 let output = pretty_type_definition($project, $position); 113 insta::assert_snapshot!(insta::internals::AutoName, output); 114 }; 115} 116 117#[test] 118fn goto_type_definition_in_same_file() { 119 assert_goto_type!( 120 " 121pub type Wibble { 122 Wibble 123} 124 125pub fn main() { 126 let x = Wibble 127 x 128}", 129 find_position_of("x").nth_occurrence(2) 130 ); 131} 132 133#[test] 134fn goto_type_definition_in_different_file_of_same_project() { 135 let src = " 136import wibble.{type Wibble} 137 138pub fn main() { 139 use_wibble(todo) 140} 141 142pub fn use_wibble(wibble: Wibble) { todo } 143"; 144 145 assert_goto_type!( 146 TestProject::for_source(src).add_module("wibble", "pub type Wibble"), 147 find_position_of("todo") 148 ); 149} 150 151#[test] 152fn goto_type_definition_in_different_file_of_dependency() { 153 let src = " 154import wibble.{type Wibble} 155 156pub fn main() { 157 use_wibble(todo) 158} 159 160pub fn use_wibble(wibble: Wibble) { todo } 161"; 162 163 assert_goto_type!( 164 TestProject::for_source(src).add_dep_module("wibble", "pub type Wibble"), 165 find_position_of("todo") 166 ); 167} 168 169#[test] 170fn goto_type_definition_can_jump_to_multiple_types() { 171 let src = " 172import wibble.{type Wibble, Wibble} 173import box.{Box} 174 175pub fn main() { 176 let a = Box(Wibble) 177} 178"; 179 180 assert_goto_type!( 181 TestProject::for_source(src) 182 .add_dep_module("wibble", "pub type Wibble { Wibble }") 183 .add_dep_module("box", "pub type Box(a) { Box(a) }"), 184 find_position_of("let a") 185 ); 186} 187 188#[test] 189fn goto_type_definition_can_jump_to_all_types_in_a_tuple() { 190 let src = " 191import wibble.{type Wibble} 192import wobble.{type Wobble} 193import box.{type Box} 194 195pub fn main() { 196 let a: #(Box(Wibble), Wobble) = todo 197} 198"; 199 200 assert_goto_type!( 201 TestProject::for_source(src) 202 .add_dep_module("wibble", "pub type Wibble { Wibble }") 203 .add_dep_module("wobble", "pub type Wobble { Wobble }") 204 .add_dep_module("box", "pub type Box(a) { Box(a) }"), 205 find_position_of("let a") 206 ); 207} 208 209#[test] 210fn goto_type_definition_can_jump_to_all_types_in_a_function_type() { 211 let src = " 212import wibble.{type Wibble} 213import wobble.{type Wobble} 214import box.{type Box} 215 216pub fn main() { 217 let a = fn(wibble: Wibble) { box.Box(wobble.Wobble) } 218} 219"; 220 221 assert_goto_type!( 222 TestProject::for_source(src) 223 .add_dep_module("wibble", "pub type Wibble { Wibble }") 224 .add_dep_module("wobble", "pub type Wobble { Wobble }") 225 .add_dep_module("box", "pub type Box(a) { Box(a) }"), 226 find_position_of("let a") 227 ); 228} 229 230#[test] 231fn goto_definition_local_variable() { 232 assert_goto!( 233 " 234pub fn main() { 235 let x = 1 236 x 237}", 238 find_position_of("x").nth_occurrence(2) 239 ); 240} 241 242#[test] 243fn goto_definition_record_update() { 244 assert_goto!( 245 " 246pub type Wibble { Wibble(one: Int, two: Int) } 247 248pub fn main() { 249 Wibble(..todo, one: 1) 250}", 251 find_position_of("Wibble").nth_occurrence(3) 252 ); 253} 254 255#[test] 256fn goto_definition_same_module_constants() { 257 assert_goto!( 258 " 259const x = 1 260 261pub fn main() { 262 x 263}", 264 find_position_of("x").nth_occurrence(2) 265 ); 266} 267 268#[test] 269fn goto_definition_same_module_functions() { 270 assert_goto!( 271 " 272fn add_2(x) { 273 x + 2 274} 275 276pub fn main() { 277 add_2(1) 278}", 279 find_position_of("add_2(1)") 280 ); 281} 282 283#[test] 284fn goto_definition_same_module_records() { 285 assert_goto!( 286 " 287pub type Rec { 288 Var1(Int) 289 Var2(Int, Int) 290} 291 292pub fn main() { 293 let a = Var1(1) 294 let b = Var2(2, 3) 295}", 296 find_position_of("Var1(1)") 297 ); 298} 299 300#[test] 301fn goto_definition_imported_module_constants() { 302 let code = " 303import example_module 304fn main() { 305 example_module.my_num 306} 307"; 308 309 assert_goto!( 310 TestProject::for_source(code).add_module("example_module", "pub const my_num = 1"), 311 find_position_of("my_num") 312 ); 313} 314 315#[test] 316fn goto_definition_unqualified_imported_module_constants() { 317 let code = " 318import example_module.{my_num} 319fn main() { 320 my_num 321} 322"; 323 324 assert_goto!( 325 TestProject::for_source(code).add_module("example_module", "pub const my_num = 1"), 326 find_position_of("my_num").nth_occurrence(2) 327 ); 328} 329 330#[test] 331fn goto_definition_module_function_calls() { 332 let code = " 333import example_module 334fn main() { 335 example_module.my_fn 336} 337"; 338 339 assert_goto!( 340 TestProject::for_source(code).add_module("example_module", "pub fn my_fn() { Nil }"), 341 find_position_of("my_fn") 342 ); 343} 344 345#[test] 346fn goto_definition_imported_module_records() { 347 let dep_src = " 348pub type Rec { 349 Var1(Int) 350 Var2(Int, Int) 351}"; 352 353 let code = " 354import example_module 355fn main() { 356 example_module.Var1(1) 357} 358"; 359 360 assert_goto!( 361 TestProject::for_source(code).add_module("example_module", dep_src), 362 find_position_of("Var1(1)") 363 ); 364} 365 366#[test] 367fn goto_definition_unqualified_imported_module_records() { 368 let dep_src = " 369pub type Rec { 370 Var1(Int) 371 Var2(Int, Int) 372}"; 373 374 let code = " 375import example_module.{Var1} 376fn main() { 377 Var1(1) 378} 379"; 380 381 assert_goto!( 382 TestProject::for_source(code).add_module("example_module", dep_src), 383 find_position_of("Var1(1)").under_char('a') 384 ); 385} 386 387#[test] 388fn goto_definition_external_module_constants() { 389 let code = " 390import example_module 391fn main() { 392 example_module.my_num 393} 394"; 395 396 assert_goto!( 397 TestProject::for_source(code).add_hex_module("example_module", "pub const my_num = 1"), 398 find_position_of("my_num").under_char('u') 399 ); 400} 401 402#[test] 403fn goto_definition_external_module_function_calls() { 404 let code = " 405import example_module 406fn main() { 407 example_module.my_fn 408} 409"; 410 411 assert_goto!( 412 TestProject::for_source(code).add_hex_module("example_module", "pub fn my_fn() { Nil }"), 413 find_position_of("my_fn") 414 ); 415} 416 417#[test] 418fn goto_definition_external_module_function_calls_with_multiple_compiles() { 419 let dep = "pub fn my_fn() { Nil }"; 420 let code = " 421import example_module 422fn main() { 423 example_module.my_fn 424} 425"; 426 427 let (mut engine, position_param) = TestProject::for_source(code) 428 .add_hex_module("example_module", dep) 429 .positioned_with_io(Position::new(3, 20)); 430 431 let params = DefinitionParams { 432 text_document_position_params: position_param.clone(), 433 work_done_progress_params: Default::default(), 434 partial_result_params: Default::default(), 435 }; 436 let response = engine.goto_definition(params.clone()); 437 let response = response.result.unwrap(); 438 439 assert_eq!( 440 response, 441 Some(Location { 442 uri: Url::from_file_path(Utf8PathBuf::from(if cfg!(target_family = "windows") { 443 r"\\?\C:\build\packages\hex\src\example_module.gleam" 444 } else { 445 "/build/packages/hex/src/example_module.gleam" 446 })) 447 .unwrap(), 448 range: Range { 449 start: Position { 450 line: 0, 451 character: 0 452 }, 453 end: Position { 454 line: 0, 455 character: 14 456 } 457 } 458 }) 459 ); 460 461 engine.compiler.sources.clear(); 462 let response = engine.compile_please(); 463 assert!(response.result.is_ok()); 464 465 let response = engine.goto_definition(params.clone()); 466 let response = response.result.unwrap(); 467 468 assert_eq!( 469 response, 470 Some(Location { 471 uri: Url::from_file_path(Utf8PathBuf::from(if cfg!(target_family = "windows") { 472 r"\\?\C:\build\packages\hex\src\example_module.gleam" 473 } else { 474 "/build/packages/hex/src/example_module.gleam" 475 })) 476 .unwrap(), 477 range: Range { 478 start: Position { 479 line: 0, 480 character: 0 481 }, 482 end: Position { 483 line: 0, 484 character: 14 485 } 486 } 487 }) 488 ) 489} 490 491#[test] 492fn goto_definition_path_module_function_calls_with_multiple_compiles() { 493 let dep = "pub fn my_fn() { Nil }"; 494 let code = " 495import example_module 496fn main() { 497 example_module.my_fn 498} 499"; 500 501 let (mut engine, position_param) = TestProject::for_source(code) 502 .add_dep_module("example_module", dep) 503 .positioned_with_io(Position::new(3, 20)); 504 505 let params = DefinitionParams { 506 text_document_position_params: position_param.clone(), 507 work_done_progress_params: Default::default(), 508 partial_result_params: Default::default(), 509 }; 510 511 let response = engine.goto_definition(params.clone()); 512 let response = response.result.unwrap(); 513 514 assert_eq!( 515 response, 516 Some(Location { 517 uri: Url::from_file_path(Utf8PathBuf::from(if cfg!(target_family = "windows") { 518 r"\\?\C:\dep\src\example_module.gleam" 519 } else { 520 "/dep/src/example_module.gleam" 521 })) 522 .unwrap(), 523 range: Range { 524 start: Position { 525 line: 0, 526 character: 0 527 }, 528 end: Position { 529 line: 0, 530 character: 14 531 } 532 } 533 }) 534 ); 535 536 engine.compiler.sources.clear(); 537 let response = engine.compile_please(); 538 assert!(response.result.is_ok()); 539 540 let response = engine.goto_definition(params.clone()); 541 let response = response.result.unwrap(); 542 543 assert_eq!( 544 response, 545 Some(Location { 546 uri: Url::from_file_path(Utf8PathBuf::from(if cfg!(target_family = "windows") { 547 r"\\?\C:\dep\src\example_module.gleam" 548 } else { 549 "/dep/src/example_module.gleam" 550 })) 551 .unwrap(), 552 range: Range { 553 start: Position { 554 line: 0, 555 character: 0 556 }, 557 end: Position { 558 line: 0, 559 character: 14 560 } 561 } 562 }) 563 ) 564} 565 566#[test] 567fn goto_definition_external_module_records() { 568 let hex_src = " 569pub type Rec { 570 Var1(Int) 571 Var2(Int, Int) 572} 573"; 574 575 let code = " 576import example_module 577fn main() { 578 example_module.Var1(1) 579} 580"; 581 582 assert_goto!( 583 TestProject::for_source(code).add_hex_module("example_module", hex_src), 584 find_position_of("Var1(1)").under_char('r') 585 ); 586} 587 588#[test] 589fn goto_definition_path_module_function_calls() { 590 let code = " 591import example_module 592fn main() { 593 example_module.my_fn 594} 595"; 596 597 assert_goto!( 598 TestProject::for_source(code).add_dep_module("example_module", "pub fn my_fn() { Nil }"), 599 find_position_of("my_fn").under_char('y') 600 ); 601} 602 603#[test] 604fn goto_definition_type() { 605 assert_goto!( 606 " 607pub type Rec { 608 Var1(Int) 609 Var2(Int, Int) 610} 611 612pub fn make_var() -> Rec { 613 Var1(1) 614}", 615 find_position_of("Rec").nth_occurrence(2) 616 ); 617} 618 619#[test] 620fn goto_definition_type_in_module() { 621 let hex_src = " 622pub type Rec { 623 Var1(Int) 624 Var2(Int, Int) 625} 626"; 627 628 let code = " 629import example_module 630fn make_var() -> example_module.Rec { 631 example_module.Var1(1) 632} 633"; 634 635 assert_goto!( 636 TestProject::for_source(code).add_hex_module("example_module", hex_src), 637 find_position_of("Rec") 638 ); 639} 640 641#[test] 642fn goto_definition_type_in_path_dep() { 643 let dep = " 644pub type Rec { 645 Var1(Int) 646 Var2(Int, Int) 647} 648"; 649 650 let code = " 651import example_module 652fn make_var() -> example_module.Rec { 653 example_module.Var1(1) 654} 655"; 656 657 assert_goto!( 658 TestProject::for_source(code).add_dep_module("example_module", dep), 659 find_position_of("Rec") 660 ); 661} 662 663#[test] 664fn goto_definition_deep_type_in_module() { 665 let hex_src = " 666pub type Wobble { 667 Wobble(Int) 668} 669 670pub type Wibble(a) { 671 Wibble(a) 672} 673 674pub type Wabble(a) { 675 Wabble(a) 676} 677"; 678 679 let code = " 680import example_module 681fn make_var() -> example_module.Wabble(example_module.Wibble(example_module.Wobble)) { 682 example_module.Wabble(example_module.Wibble(example_module.Wobble(1))) 683} 684"; 685 686 assert_goto!( 687 TestProject::for_source(code).add_hex_module("example_module", hex_src), 688 find_position_of("Wobble").under_char('o') 689 ); 690} 691 692#[test] 693fn goto_definition_import() { 694 let code = " 695import example_module 696fn main() { 697 example_module.my_num 698} 699"; 700 701 assert_goto!( 702 TestProject::for_source(code).add_module("example_module", "pub const my_num = 1"), 703 find_position_of("example_module").under_char('p') 704 ); 705} 706 707#[test] 708fn goto_definition_import_aliased() { 709 let code = " 710import example_module as example 711fn main() { 712 example.my_num 713} 714"; 715 716 assert_goto!( 717 TestProject::for_source(code).add_module("example_module", "pub const my_num = 1"), 718 find_position_of("example") 719 .nth_occurrence(2) 720 .under_char('x') 721 ); 722} 723 724#[test] 725fn goto_definition_import_unqualified_value() { 726 let code = " 727import example_module.{my_num} 728fn main() { 729 my_num 730} 731"; 732 733 assert_goto!( 734 TestProject::for_source(code).add_module("example_module", "pub const my_num = 1"), 735 find_position_of("my_num").under_char('_') 736 ); 737} 738 739#[test] 740fn goto_definition_unqualified_function() { 741 let code = " 742import wibble.{wobble} 743fn main() { 744 wobble() 745} 746"; 747 748 assert_goto!( 749 TestProject::for_source(code).add_module("wibble", "pub fn wobble() {}"), 750 find_position_of("wobble").nth_occurrence(2).under_char('o') 751 ); 752} 753 754#[test] 755fn goto_definition_import_unqualified_type() { 756 let code = " 757import example_module.{type MyType} 758fn main() -> MyType { 759 0 760} 761"; 762 763 assert_goto!( 764 TestProject::for_source(code).add_module("example_module", "pub type MyType = Int"), 765 find_position_of("MyType").under_char('T') 766 ); 767} 768 769// https://github.com/gleam-lang/gleam/issues/3610 770#[test] 771fn goto_definition_of_external_function_in_same_module() { 772 let code = " 773@external(erlang, \"wibble\", \"wobble\") 774fn external_function() -> Nil 775 776fn main() { 777 external_function() 778} 779"; 780 781 assert_goto!( 782 TestProject::for_source(code), 783 find_position_of("external_function") 784 .nth_occurrence(2) 785 .under_char('l') 786 ); 787} 788 789// https://github.com/gleam-lang/gleam/issues/3758 790#[test] 791fn goto_definition_from_anonymous_function() { 792 let code = " 793pub type Wibble 794 795pub fn main() { 796 fn(w: Wibble) { todo } 797} 798"; 799 800 assert_goto!( 801 TestProject::for_source(code), 802 find_position_of("w: Wibble").under_char('i') 803 ); 804} 805 806#[test] 807fn goto_definition_module() { 808 let code = " 809import wibble 810 811pub fn main() { 812 wibble.wibble() 813} 814"; 815 816 assert_goto!( 817 TestProject::for_source(code).add_module("wibble", "pub fn wibble() {}"), 818 find_position_of("wibble.").under_char('i') 819 ); 820} 821 822#[test] 823fn goto_definition_constant() { 824 assert_goto!( 825 " 826const value = 25 827 828const my_constant = value 829", 830 find_position_of("= value").under_char('a') 831 ); 832} 833 834#[test] 835fn goto_definition_constant_record() { 836 assert_goto!( 837 " 838type Wibble { 839 Wibble(Int) 840} 841 842const wibble = Wibble(10) 843", 844 find_position_of("Wibble(10)").under_char('l') 845 ); 846} 847 848#[test] 849fn goto_definition_imported_constant() { 850 let src = " 851import wibble 852 853const my_constant = wibble.value 854"; 855 856 assert_goto!( 857 TestProject::for_source(src).add_hex_module("wibble", "pub const value = 10"), 858 find_position_of("value").under_char('v') 859 ); 860} 861 862#[test] 863fn goto_definition_constant_imported_record() { 864 let src = " 865import wibble 866 867const my_constant = wibble.Wibble(10) 868"; 869 870 assert_goto!( 871 TestProject::for_source(src).add_hex_module("wibble", "pub type Wibble { Wibble(Int) }"), 872 find_position_of("Wibble(10)").under_char('W') 873 ); 874} 875 876#[test] 877fn goto_definition_from_alternative_pattern() { 878 assert_goto!( 879 " 880type Wibble { 881 Wibble 882 Wobble 883} 884 885fn warble(wibble: Wibble) { 886 case wibble { 887 Wibble | Wobble -> 0 888 } 889} 890", 891 find_position_of("Wobble ->") 892 ); 893} 894 895#[test] 896fn goto_definition_of_local_variable_from_guard() { 897 assert_goto!( 898 " 899pub fn main() { 900 let wibble = True 901 let wobble = False 902 case wibble { 903 True if wobble -> !wibble 904 False if !wobble -> wibble 905 _ -> wobble 906 } 907} 908", 909 find_position_of("wobble").nth_occurrence(2).under_char('o') 910 ); 911} 912 913#[test] 914fn goto_definition_of_record_from_guard() { 915 assert_goto!( 916 " 917type Wibble { 918 Wibble 919 Wobble 920} 921 922pub fn main() { 923 let wibble = True 924 let wobble = Wibble 925 case wibble { 926 True if wobble == Wibble -> !wibble 927 False if wobble == Wobble -> wibble 928 _ -> Wibble 929 } 930} 931", 932 find_position_of("== Wibble").under_char('l') 933 ); 934} 935 936#[test] 937fn goto_definition_of_module_select_from_guard() { 938 let src = " 939import mod 940 941pub fn main() { 942 let wibble = True 943 case wibble { 944 True if mod.wibble < 5 -> !wibble 945 False if mod.wibble != 10 -> wibble 946 _ -> mod.wibble + 1 947 } 948} 949"; 950 assert_goto!( 951 TestProject::for_source(src).add_module("mod", "pub const wibble = 10"), 952 find_position_of("mod.wibble").under_char('w') 953 ); 954} 955 956#[test] 957fn goto_definition_of_record_module_select_from_guard() { 958 let src = " 959import mod 960 961pub fn main() { 962 let wibble = True 963 let wobble = mod.Wibble 964 case wibble { 965 True if wobble == mod.Wobble -> !wibble 966 False if wobble == mod.Wibble -> wibble 967 _ -> mod.Wibble 968 } 969} 970"; 971 assert_goto!( 972 TestProject::for_source(src).add_module("mod", "pub type Wobble { Wibble Wobble }"), 973 find_position_of("== mod.Wibble").under_char('i') 974 ); 975} 976 977#[test] 978fn goto_for_invalid_constant_todo_message_still_works() { 979 assert_goto!( 980 " 981const wibble = 1 982const wobble = todo as wibble 983", 984 find_position_of("wibble").nth_occurrence(2) 985 ); 986} 987 988#[test] 989fn goto_for_invalid_constant_todo_message_still_works_2() { 990 assert_goto!( 991 " 992const wibble = 1 993const wobble = todo as [wibble] 994", 995 find_position_of("wibble").nth_occurrence(2) 996 ); 997}