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 / reference.rs
36 kB 1061 lines
1// SPDX-License-Identifier: Apache-2.0 2// SPDX-FileCopyrightText: 2019 The Gleam contributors 3 4use std::collections::{HashMap, HashSet}; 5 6use ecow::EcoString; 7use lsp_types::Location; 8 9use gleam_core::{ 10 analyse, 11 ast::{ 12 self, ArgNames, AssignName, BitArraySize, ClauseGuard, CustomType, Function, 13 ModuleConstant, Pattern, RecordConstructor, SrcSpan, TypedExpr, TypedModule, visit::Visit, 14 }, 15 build::{Located, UnqualifiedImport}, 16 reference::RecordLabel, 17 type_::{ 18 ModuleInterface, ModuleValueConstructor, Type, ValueConstructor, ValueConstructorVariant, 19 error::{Named, VariableOrigin}, 20 }, 21}; 22 23use super::{ 24 compiler::ModuleSourceInformation, rename::RenameTarget, src_span_to_lsp_range, url_from_path, 25}; 26 27#[derive(Debug)] 28pub enum Referenced { 29 LocalVariable { 30 definition_location: SrcSpan, 31 location: SrcSpan, 32 origin: Option<VariableOrigin>, 33 name: EcoString, 34 }, 35 ModuleName { 36 module_name: EcoString, 37 module_alias: EcoString, 38 location: SrcSpan, 39 }, 40 ModuleValue { 41 module: EcoString, 42 name: EcoString, 43 location: SrcSpan, 44 /// The starting position of the name. 45 /// 46 /// For example, when location covers an aliased import statement, e.g. 47 /// `wibble as wobble`, this will point to the start of `wobble`. 48 /// In all other cases, this is the same as `location.start`. 49 name_start: u32, 50 name_kind: Named, 51 target_kind: RenameTarget, 52 }, 53 ModuleType { 54 module: EcoString, 55 name: EcoString, 56 location: SrcSpan, 57 /// The starting position of the name. 58 /// 59 /// For example, when location covers an aliased import statement, e.g. 60 /// `type Wibble as Wobble`, this will point to the start of `Wobble`. 61 /// When location covers a non-aliased import statement, e.g. `type 62 /// Wibble`, this will point to the start of `Wibble`. 63 /// In all other cases, this is the same as `location.start`. 64 name_start: u32, 65 target_kind: RenameTarget, 66 }, 67 TypeVariable { 68 location: SrcSpan, 69 name: EcoString, 70 }, 71 Label { 72 type_module: EcoString, 73 type_name: EcoString, 74 label: EcoString, 75 location: SrcSpan, 76 }, 77} 78 79pub fn reference_for_ast_node( 80 found: Located<'_>, 81 current_module: &EcoString, 82) -> Option<Referenced> { 83 match found { 84 Located::Expression { 85 expression: 86 TypedExpr::Var { 87 constructor: 88 ValueConstructor { 89 variant: 90 ValueConstructorVariant::LocalVariable { 91 location: definition_location, 92 origin, 93 }, 94 .. 95 }, 96 location, 97 name, 98 }, 99 .. 100 } => Some(Referenced::LocalVariable { 101 definition_location: *definition_location, 102 location: *location, 103 origin: Some(origin.clone()), 104 name: name.clone(), 105 }), 106 Located::Pattern(Pattern::Variable { 107 location, 108 origin, 109 name, 110 .. 111 }) => Some(Referenced::LocalVariable { 112 definition_location: *location, 113 location: *location, 114 origin: Some(origin.clone()), 115 name: name.clone(), 116 }), 117 Located::Pattern(Pattern::BitArraySize(BitArraySize::Variable { 118 constructor, 119 location, 120 name, 121 .. 122 })) => constructor 123 .as_ref() 124 .and_then(|constructor| match &constructor.variant { 125 ValueConstructorVariant::LocalVariable { 126 location: definition_location, 127 origin, 128 } => Some(Referenced::LocalVariable { 129 definition_location: *definition_location, 130 location: *location, 131 origin: Some(origin.clone()), 132 name: name.clone(), 133 }), 134 ValueConstructorVariant::ModuleConstant { .. } 135 | ValueConstructorVariant::ModuleFn { .. } 136 | ValueConstructorVariant::Record { .. } => None, 137 }), 138 Located::Pattern(Pattern::Assign { location, name, .. }) => { 139 Some(Referenced::LocalVariable { 140 definition_location: *location, 141 location: *location, 142 origin: None, 143 name: name.clone(), 144 }) 145 } 146 Located::Arg(arg) => match &arg.names { 147 ArgNames::Named { location, name } 148 | ArgNames::NamedLabelled { 149 name_location: location, 150 name, 151 .. 152 } => Some(Referenced::LocalVariable { 153 definition_location: *location, 154 location: *location, 155 origin: None, 156 name: name.clone(), 157 }), 158 ArgNames::Discard { .. } | ArgNames::LabelledDiscard { .. } => None, 159 }, 160 Located::Expression { 161 expression: 162 TypedExpr::Var { 163 constructor: 164 ValueConstructor { 165 variant: 166 ValueConstructorVariant::ModuleConstant { module, name, .. } 167 | ValueConstructorVariant::ModuleFn { module, name, .. }, 168 .. 169 }, 170 location, 171 .. 172 }, 173 .. 174 } => Some(Referenced::ModuleValue { 175 module: module.clone(), 176 name: name.clone(), 177 location: *location, 178 name_start: location.start, 179 name_kind: Named::Function, 180 target_kind: RenameTarget::Unqualified, 181 }), 182 183 Located::Expression { 184 expression: 185 TypedExpr::ModuleSelect { 186 module_name, 187 label, 188 constructor: 189 ModuleValueConstructor::Fn { .. } | ModuleValueConstructor::Constant { .. }, 190 location, 191 field_start, 192 .. 193 }, 194 .. 195 } => Some(Referenced::ModuleValue { 196 module: module_name.clone(), 197 name: label.clone(), 198 location: SrcSpan::new(*field_start, location.end), 199 name_start: *field_start, 200 name_kind: Named::Function, 201 target_kind: RenameTarget::Qualified, 202 }), 203 204 Located::ModuleFunction(Function { 205 name: Some((location, name)), 206 .. 207 }) 208 | Located::ModuleConstant(ModuleConstant { 209 name, 210 name_location: location, 211 .. 212 }) => Some(Referenced::ModuleValue { 213 module: current_module.clone(), 214 name: name.clone(), 215 location: *location, 216 name_start: location.start, 217 name_kind: Named::Function, 218 target_kind: RenameTarget::Definition, 219 }), 220 Located::Expression { 221 expression: 222 TypedExpr::Var { 223 constructor: 224 ValueConstructor { 225 variant: ValueConstructorVariant::Record { module, name, .. }, 226 .. 227 }, 228 location, 229 .. 230 }, 231 .. 232 } => Some(Referenced::ModuleValue { 233 module: module.clone(), 234 name: name.clone(), 235 location: *location, 236 name_start: location.start, 237 name_kind: Named::CustomTypeVariant, 238 target_kind: RenameTarget::Unqualified, 239 }), 240 Located::Expression { 241 expression: 242 TypedExpr::ModuleSelect { 243 module_name, 244 label, 245 constructor: ModuleValueConstructor::Record { .. }, 246 location, 247 field_start, 248 .. 249 }, 250 .. 251 } => Some(Referenced::ModuleValue { 252 module: module_name.clone(), 253 name: label.clone(), 254 location: SrcSpan::new(*field_start, location.end), 255 name_start: *field_start, 256 name_kind: Named::CustomTypeVariant, 257 target_kind: RenameTarget::Qualified, 258 }), 259 Located::VariantConstructorDefinition(RecordConstructor { 260 name, 261 name_location, 262 .. 263 }) => Some(Referenced::ModuleValue { 264 module: current_module.clone(), 265 name: name.clone(), 266 location: *name_location, 267 name_start: name_location.start, 268 name_kind: Named::CustomTypeVariant, 269 target_kind: RenameTarget::Definition, 270 }), 271 Located::Pattern(Pattern::Constructor { 272 constructor: analyse::Inferred::Known(constructor), 273 module: module_select, 274 name_location: location, 275 .. 276 }) => Some(Referenced::ModuleValue { 277 module: constructor.module.clone(), 278 name: constructor.name.clone(), 279 location: *location, 280 name_start: location.start, 281 name_kind: Named::CustomTypeVariant, 282 target_kind: if module_select.is_some() { 283 RenameTarget::Qualified 284 } else { 285 RenameTarget::Unqualified 286 }, 287 }), 288 Located::StringPrefixPatternVariable { location, name, .. } => { 289 Some(Referenced::LocalVariable { 290 definition_location: location, 291 location, 292 origin: None, 293 name: name.clone(), 294 }) 295 } 296 Located::Annotation { ast, type_ } => match ast { 297 ast::TypeAst::Constructor(constructor) 298 if let Some((module, name)) = type_.named_type_name() => 299 { 300 let target_kind = if constructor.name.is_qualified() { 301 RenameTarget::Qualified 302 } else { 303 RenameTarget::Unqualified 304 }; 305 let location = constructor.name.name_location()?; 306 307 Some(Referenced::ModuleType { 308 module, 309 name, 310 location, 311 name_start: location.start, 312 target_kind, 313 }) 314 } 315 316 ast::TypeAst::Var(variable) => Some(Referenced::TypeVariable { 317 location: variable.location, 318 name: variable.name.clone(), 319 }), 320 321 ast::TypeAst::Constructor(_) 322 | ast::TypeAst::Fn(_) 323 | ast::TypeAst::Tuple(_) 324 | ast::TypeAst::Hole(_) => None, 325 }, 326 Located::TypeVariable { name, location } => { 327 Some(Referenced::TypeVariable { location, name }) 328 } 329 Located::ModuleCustomType(CustomType { 330 name, 331 name_location, 332 .. 333 }) => Some(Referenced::ModuleType { 334 module: current_module.clone(), 335 name: name.clone(), 336 location: *name_location, 337 name_start: name_location.start, 338 target_kind: RenameTarget::Definition, 339 }), 340 Located::ModuleName { 341 location, 342 module_name, 343 module_alias, 344 .. 345 } => Some(Referenced::ModuleName { 346 module_name, 347 module_alias, 348 location, 349 }), 350 Located::ModuleImport(import) => { 351 let module_name = match &import.as_name { 352 Some(( 353 AssignName::Variable(module_alias) | AssignName::Discard(module_alias), 354 alias_location, 355 )) => Referenced::ModuleName { 356 module_name: import.module.clone(), 357 module_alias: module_alias.clone(), 358 location: SrcSpan { 359 start: alias_location.end - (module_alias.len() as u32), 360 end: alias_location.end, 361 }, 362 }, 363 None => Referenced::ModuleName { 364 module_name: import.module.clone(), 365 module_alias: import 366 .module 367 .split('/') 368 .next_back() 369 .map(EcoString::from) 370 .unwrap_or_else(|| import.module.clone()), 371 location: import.module_location, 372 }, 373 }; 374 375 Some(module_name) 376 } 377 378 Located::ClauseGuard(ClauseGuard::Var { 379 location, 380 type_: _, 381 name, 382 definition_location, 383 origin, 384 }) => Some(Referenced::LocalVariable { 385 definition_location: *definition_location, 386 location: *location, 387 origin: Some(origin.clone()), 388 name: name.clone(), 389 }), 390 391 Located::ClauseGuard(ClauseGuard::ModuleSelect { 392 location, 393 field_start, 394 label, 395 module_name, 396 .. 397 }) => Some(Referenced::ModuleValue { 398 module: module_name.clone(), 399 name: label.clone(), 400 location: SrcSpan::new(*field_start, location.end), 401 name_start: *field_start, 402 name_kind: Named::Function, 403 target_kind: RenameTarget::Qualified, 404 }), 405 406 Located::UnqualifiedImport( 407 import @ UnqualifiedImport { 408 name, 409 module, 410 location, 411 name_position: _, 412 as_name: _, 413 }, 414 ) => { 415 if import.is_type() { 416 Some(Referenced::ModuleType { 417 module: module.clone(), 418 name: name.clone(), 419 location: *location, 420 name_start: import.used_name_position(), 421 target_kind: RenameTarget::Unqualified, 422 }) 423 } else { 424 Some(Referenced::ModuleValue { 425 module: module.clone(), 426 name: name.clone(), 427 location: *location, 428 name_start: import.used_name_position(), 429 name_kind: import.name_kind(), 430 target_kind: RenameTarget::Unqualified, 431 }) 432 } 433 } 434 435 Located::RecordLabelUsage { 436 record_type, 437 label, 438 location, 439 .. 440 } 441 | Located::RecordAccessLabel { 442 record_type, 443 label, 444 location, 445 .. 446 } => record_type 447 .named_type_name() 448 .map(|(type_module, type_name)| Referenced::Label { 449 type_module, 450 type_name, 451 label: label.clone(), 452 location, 453 }), 454 455 // A label at its definition in a custom type, which lives in the 456 // current module. 457 Located::RecordLabelDefinition { 458 type_name, 459 label, 460 location, 461 .. 462 } => Some(Referenced::Label { 463 type_module: current_module.clone(), 464 type_name, 465 label, 466 location, 467 }), 468 469 Located::Pattern(_) 470 | Located::ClauseGuard(_) 471 | Located::PatternSpread { .. } 472 | Located::Statement(_) 473 | Located::Expression { .. } 474 | Located::FunctionBody(_) 475 | Located::Label { .. } 476 | Located::Constant(_) 477 | Located::ModuleFunction(_) 478 | Located::ModuleTypeAlias(_) => None, 479 } 480} 481 482pub fn find_module_references( 483 module_name: EcoString, 484 name: EcoString, 485 modules: &im::HashMap<EcoString, ModuleInterface>, 486 sources: &HashMap<EcoString, ModuleSourceInformation>, 487 layer: ast::Layer, 488) -> Vec<Location> { 489 let mut reference_locations = Vec::new(); 490 491 for module in modules.values() { 492 if module.name == module_name || module.references.imported_modules.contains(&module_name) { 493 let Some(source_information) = sources.get(&module.name) else { 494 continue; 495 }; 496 497 find_references_in_module( 498 &module_name, 499 &name, 500 module, 501 source_information, 502 &mut reference_locations, 503 layer, 504 ); 505 } 506 } 507 508 reference_locations 509} 510 511pub fn find_module_references_in_module( 512 module_name: EcoString, 513 name: EcoString, 514 module: &ModuleInterface, 515 source_information: &ModuleSourceInformation, 516 layer: ast::Layer, 517) -> Vec<Location> { 518 let mut reference_locations = Vec::new(); 519 520 find_references_in_module( 521 &module_name, 522 &name, 523 module, 524 source_information, 525 &mut reference_locations, 526 layer, 527 ); 528 529 reference_locations 530} 531 532pub fn find_label_references( 533 type_module: EcoString, 534 type_name: EcoString, 535 label: EcoString, 536 modules: &im::HashMap<EcoString, ModuleInterface>, 537 sources: &HashMap<EcoString, ModuleSourceInformation>, 538) -> Vec<Location> { 539 let mut reference_locations = Vec::new(); 540 541 // Unlike values and types, a label can be referenced in a module that 542 // doesn't import the type's defining module: a record value can be 543 // obtained transitively through another module and have its fields 544 // accessed. So every module has to be searched. 545 for module in modules.values() { 546 let Some(source_information) = sources.get(&module.name) else { 547 continue; 548 }; 549 reference_locations.extend(find_label_references_in_module( 550 type_module.clone(), 551 type_name.clone(), 552 label.clone(), 553 module, 554 source_information, 555 )); 556 } 557 558 reference_locations 559} 560 561pub fn find_label_references_in_module( 562 type_module: EcoString, 563 type_name: EcoString, 564 label: EcoString, 565 module: &ModuleInterface, 566 source_information: &ModuleSourceInformation, 567) -> Vec<Location> { 568 let mut reference_locations = Vec::new(); 569 570 let Some(uri) = url_from_path(source_information.path.as_str()) else { 571 return reference_locations; 572 }; 573 574 let key = RecordLabel { 575 type_module, 576 type_name, 577 label, 578 }; 579 let definitions = module.references.label_definitions.get(&key); 580 let references = module.references.label_references.get(&key); 581 let locations = definitions 582 .into_iter() 583 .flatten() 584 .map(|definition| definition.location) 585 .chain( 586 references 587 .into_iter() 588 .flatten() 589 .map(|reference| reference.location), 590 ); 591 592 for location in locations { 593 reference_locations.push(Location { 594 uri: uri.clone(), 595 range: src_span_to_lsp_range(location, &source_information.line_numbers), 596 }); 597 } 598 599 reference_locations 600} 601 602fn find_references_in_module( 603 module_name: &EcoString, 604 name: &EcoString, 605 module: &ModuleInterface, 606 source_information: &ModuleSourceInformation, 607 reference_locations: &mut Vec<Location>, 608 layer: ast::Layer, 609) { 610 let reference_map = match layer { 611 ast::Layer::Value => &module.references.value_references, 612 ast::Layer::Type => &module.references.type_references, 613 }; 614 615 let Some(references) = reference_map.get(&(module_name.clone(), name.clone())) else { 616 return; 617 }; 618 619 let Some(uri) = url_from_path(source_information.path.as_str()) else { 620 return; 621 }; 622 623 for reference in references { 624 reference_locations.push(Location { 625 uri: uri.clone(), 626 range: src_span_to_lsp_range(reference.location, &source_information.line_numbers), 627 }); 628 } 629} 630 631#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 632pub struct VariableReference { 633 pub location: SrcSpan, 634 pub kind: VariableReferenceKind, 635} 636 637#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 638pub enum VariableReferenceKind { 639 Variable, 640 LabelShorthand, 641} 642 643/// How to treat variables defined in alternative patterns 644enum AlternativeVariable { 645 Track, 646 Ignore, 647} 648 649pub struct FindVariableReferences { 650 // Due to the structure of some AST nodes (for example, record updates), 651 // when we traverse the AST it is possible to accidentally duplicate references. 652 // To avoid this, we use a `HashSet` instead of a `Vec` here. 653 // See: https://github.com/gleam-lang/gleam/issues/4859 and the linked PR. 654 references: HashSet<VariableReference>, 655 definition_location: DefinitionLocation, 656 alternative_variable: AlternativeVariable, 657 name: EcoString, 658} 659 660/// Where the variable we're finding references for is defined. 661/// 662enum DefinitionLocation { 663 /// This is the location where the variable is defined, nothing special is 664 /// going on here. For example: 665 /// 666 /// ```gleam 667 /// let wibble = 1 668 /// // ^^^^^^ Definition location for `wibble` 669 /// wibble + 1 670 /// ^^^^^^ 671 /// // `wibble` used here, defined earlier 672 /// ``` 673 /// 674 Regular { location: SrcSpan }, 675 676 /// When dealing with alternative patterns and aliases we need special care: 677 /// each usage wil always reference the first alternative where a variable 678 /// is defined and not the following ones. For example: 679 /// 680 /// ```gleam 681 /// case wibble { 682 /// [] as var | [_] as var -> var 683 /// // ^^^ ^^^ If we look where `var` thinks it's defined 684 /// // It will say it's defined here! 685 /// } 686 /// ``` 687 /// 688 /// This poses a problem if we start the renaming from the second 689 /// alternative pattern: 690 /// 691 /// ```gleam 692 /// case wibble { 693 /// [] as var | [_] as var -> var 694 /// // ^^^ Since `var` uses the first alternative as its 695 /// // definition location, this would not be considered 696 /// // a reference to that same var. 697 /// } 698 /// ``` 699 /// 700 /// So we keep track of the location of this definition, but we also need 701 /// to store the location of the first definition in the alternative case 702 /// (that's `first_alternative_location`), so that when we look for 703 /// references we can check against this one that is canonically used by 704 /// expressions in the AST 705 /// 706 Alternative { 707 location: SrcSpan, 708 first_alternative_location: SrcSpan, 709 }, 710} 711 712impl FindVariableReferences { 713 pub fn new(variable_definition_location: SrcSpan, variable_name: EcoString) -> Self { 714 Self { 715 references: HashSet::new(), 716 definition_location: DefinitionLocation::Regular { 717 location: variable_definition_location, 718 }, 719 alternative_variable: AlternativeVariable::Ignore, 720 name: variable_name, 721 } 722 } 723 724 /// Where the definition for which we're accumulating references is 725 /// originally defined. In case of alternative patterns this will point to 726 /// the first occurrence of that name! Look at the docs for 727 /// `DefinitionLocation` to learn more on why this is needed. 728 /// 729 fn definition_origin_location(&self) -> SrcSpan { 730 match self.definition_location { 731 DefinitionLocation::Regular { location } 732 | DefinitionLocation::Alternative { 733 first_alternative_location: location, 734 .. 735 } => location, 736 } 737 } 738 739 /// This is the location of the definition for which we're accumulating 740 /// references. In most cases you'll want to use `definition_origin_location`. 741 /// The difference between the two is explained in greater detail in the docs 742 /// for `DefinitionLocation`. 743 /// 744 fn definition_location(&self) -> SrcSpan { 745 match self.definition_location { 746 DefinitionLocation::Regular { location } 747 | DefinitionLocation::Alternative { location, .. } => location, 748 } 749 } 750 751 fn update_alternative_origin(&mut self, alternative_location: SrcSpan) { 752 match self.definition_location { 753 // We've found the location of the origin of an alternative pattern. 754 DefinitionLocation::Regular { location } if alternative_location < location => { 755 self.definition_location = DefinitionLocation::Alternative { 756 location, 757 first_alternative_location: alternative_location, 758 }; 759 } 760 761 // Since the new alternative location we've found is smaller, that 762 // is the actual first one for the alternative pattern! 763 DefinitionLocation::Alternative { 764 location, 765 first_alternative_location, 766 } if alternative_location < first_alternative_location => { 767 self.definition_location = DefinitionLocation::Alternative { 768 location, 769 first_alternative_location: alternative_location, 770 }; 771 } 772 773 DefinitionLocation::Regular { .. } | DefinitionLocation::Alternative { .. } => (), 774 } 775 } 776 777 pub fn find_in_module(mut self, module: &TypedModule) -> HashSet<VariableReference> { 778 self.visit_typed_module(module); 779 self.references 780 } 781 782 pub fn find(mut self, expression: &TypedExpr) -> HashSet<VariableReference> { 783 self.visit_typed_expr(expression); 784 self.references 785 } 786 787 fn register_alternative_definition(&mut self, name: &EcoString, location: &SrcSpan) { 788 match self.alternative_variable { 789 // If we are inside the same alternative pattern as the target 790 // variable and the name is the same, this is an alternative definition 791 // of the same variable. We don't register the reference if this is 792 // the exact variable though, as that would result in a duplicated 793 // reference. 794 AlternativeVariable::Track 795 if *name == self.name && *location != self.definition_location() => 796 { 797 self.update_alternative_origin(*location); 798 799 _ = self.references.insert(VariableReference { 800 location: *location, 801 kind: VariableReferenceKind::Variable, 802 }); 803 } 804 AlternativeVariable::Track | AlternativeVariable::Ignore => {} 805 } 806 } 807} 808 809impl<'ast> Visit<'ast> for FindVariableReferences { 810 fn visit_typed_function(&mut self, fun: &'ast ast::TypedFunction) { 811 if fun 812 .full_location() 813 .contains(self.definition_origin_location().start) 814 { 815 ast::visit::visit_typed_function(self, fun); 816 } 817 } 818 819 fn visit_typed_expr_var( 820 &mut self, 821 location: &'ast SrcSpan, 822 constructor: &'ast ValueConstructor, 823 _name: &'ast EcoString, 824 ) { 825 match constructor.variant { 826 ValueConstructorVariant::LocalVariable { 827 location: definition_location, 828 .. 829 } if definition_location == self.definition_origin_location() => { 830 _ = self.references.insert(VariableReference { 831 location: *location, 832 kind: VariableReferenceKind::Variable, 833 }); 834 } 835 ValueConstructorVariant::LocalVariable { .. } 836 | ValueConstructorVariant::ModuleConstant { .. } 837 | ValueConstructorVariant::ModuleFn { .. } 838 | ValueConstructorVariant::Record { .. } => {} 839 } 840 } 841 842 fn visit_typed_clause_guard_var( 843 &mut self, 844 location: &'ast SrcSpan, 845 _name: &'ast EcoString, 846 _type_: &'ast std::sync::Arc<Type>, 847 definition_location: &'ast SrcSpan, 848 _origin: &'ast VariableOrigin, 849 ) { 850 if *definition_location == self.definition_origin_location() { 851 _ = self.references.insert(VariableReference { 852 location: *location, 853 kind: VariableReferenceKind::Variable, 854 }); 855 } 856 } 857 858 fn visit_typed_clause(&mut self, clause: &'ast ast::TypedClause) { 859 // If this alternative pattern contains the variable we are finding 860 // references for, we track that so we can find alternative definitions 861 // of the target variable. 862 if clause 863 .pattern_location() 864 .contains(self.definition_origin_location().start) 865 { 866 self.alternative_variable = AlternativeVariable::Track; 867 } 868 869 for pattern in clause.pattern.iter() { 870 self.visit_typed_pattern(pattern); 871 } 872 for patterns in clause.alternative_patterns.iter() { 873 for pattern in patterns { 874 self.visit_typed_pattern(pattern); 875 } 876 } 877 878 self.alternative_variable = AlternativeVariable::Ignore; 879 880 if let Some(guard) = &clause.guard { 881 self.visit_typed_clause_guard(guard); 882 } 883 self.visit_typed_expr(&clause.then); 884 } 885 886 fn visit_typed_pattern_variable( 887 &mut self, 888 location: &'ast SrcSpan, 889 name: &'ast EcoString, 890 _type_: &'ast std::sync::Arc<Type>, 891 _origin: &'ast VariableOrigin, 892 ) { 893 self.register_alternative_definition(name, location); 894 } 895 896 fn visit_typed_pattern_assign( 897 &mut self, 898 location: &'ast SrcSpan, 899 name: &'ast EcoString, 900 pattern: &'ast ast::TypedPattern, 901 ) { 902 self.register_alternative_definition(name, location); 903 904 ast::visit::visit_typed_pattern_assign(self, location, name, pattern); 905 } 906 907 fn visit_typed_bit_array_size_variable( 908 &mut self, 909 location: &'ast SrcSpan, 910 _name: &'ast EcoString, 911 constructor: &'ast Option<Box<ValueConstructor>>, 912 _type_: &'ast std::sync::Arc<Type>, 913 ) { 914 let variant = match constructor { 915 Some(constructor) => &constructor.variant, 916 None => return, 917 }; 918 match variant { 919 ValueConstructorVariant::LocalVariable { 920 location: definition_location, 921 .. 922 } if *definition_location == self.definition_origin_location() => { 923 _ = self.references.insert(VariableReference { 924 location: *location, 925 kind: VariableReferenceKind::Variable, 926 }); 927 } 928 ValueConstructorVariant::LocalVariable { .. } 929 | ValueConstructorVariant::ModuleConstant { .. } 930 | ValueConstructorVariant::ModuleFn { .. } 931 | ValueConstructorVariant::Record { .. } => {} 932 } 933 } 934 935 fn visit_typed_call_arg(&mut self, arg: &'ast gleam_core::type_::TypedCallArg) { 936 if let TypedExpr::Var { 937 location, 938 constructor, 939 .. 940 } = &arg.value 941 { 942 match &constructor.variant { 943 ValueConstructorVariant::LocalVariable { 944 location: definition_location, 945 .. 946 } if arg.uses_label_shorthand() 947 && *definition_location == self.definition_origin_location() => 948 { 949 _ = self.references.insert(VariableReference { 950 location: *location, 951 kind: VariableReferenceKind::LabelShorthand, 952 }); 953 return; 954 } 955 ValueConstructorVariant::LocalVariable { .. } 956 | ValueConstructorVariant::ModuleConstant { .. } 957 | ValueConstructorVariant::ModuleFn { .. } 958 | ValueConstructorVariant::Record { .. } => {} 959 } 960 } 961 962 ast::visit::visit_typed_call_arg(self, arg); 963 } 964 965 fn visit_typed_pattern_string_prefix( 966 &mut self, 967 location: &'ast SrcSpan, 968 left_location: &'ast SrcSpan, 969 left_side_assignment: &'ast Option<(EcoString, SrcSpan)>, 970 right_location: &'ast SrcSpan, 971 left_side_string: &'ast EcoString, 972 right_side_assignment: &'ast AssignName, 973 ) { 974 // Handle the prefix alias in alternative pattern: "prefix" as name | "other_prefix" as name 975 if let Some((name, left_side_assignment_location)) = left_side_assignment { 976 self.register_alternative_definition(name, left_side_assignment_location); 977 } 978 979 // Handle the suffix in alternative pattern: "prefix" <> name | "other_prefix" <> name 980 match right_side_assignment { 981 AssignName::Variable(name) => { 982 self.register_alternative_definition(name, right_location); 983 } 984 AssignName::Discard(_) => {} 985 } 986 987 ast::visit::visit_typed_pattern_string_prefix( 988 self, 989 location, 990 left_location, 991 left_side_assignment, 992 right_location, 993 left_side_string, 994 right_side_assignment, 995 ); 996 } 997} 998 999pub struct FindTypeVariableReferences<'a> { 1000 pub references: Vec<SrcSpan>, 1001 pub name: &'a EcoString, 1002 pub location: SrcSpan, 1003} 1004 1005impl<'a> FindTypeVariableReferences<'a> { 1006 pub fn find_in_module( 1007 module: &TypedModule, 1008 type_variable_location: SrcSpan, 1009 type_variable_name: &'a EcoString, 1010 ) -> Vec<SrcSpan> { 1011 let mut finder = Self { 1012 references: Vec::new(), 1013 name: type_variable_name, 1014 location: type_variable_location, 1015 }; 1016 finder.visit_typed_module(module); 1017 finder.references 1018 } 1019} 1020 1021impl<'ast> Visit<'ast> for FindTypeVariableReferences<'_> { 1022 fn visit_typed_function(&mut self, fun: &'ast ast::TypedFunction) { 1023 if fun.full_location().contains_span(self.location) { 1024 ast::visit::visit_typed_function(self, fun); 1025 } 1026 } 1027 1028 fn visit_typed_custom_type(&mut self, custom_type: &'ast ast::TypedCustomType) { 1029 if custom_type.full_location().contains_span(self.location) { 1030 for (location, name) in custom_type.parameters.iter() { 1031 if name == self.name { 1032 self.references.push(*location); 1033 } 1034 } 1035 ast::visit::visit_typed_custom_type(self, custom_type); 1036 } 1037 } 1038 1039 fn visit_typed_module_constant(&mut self, constant: &'ast ast::TypedModuleConstant) { 1040 if constant.location.contains_span(self.location) { 1041 ast::visit::visit_typed_module_constant(self, constant); 1042 } 1043 } 1044 1045 fn visit_typed_type_alias(&mut self, type_alias: &'ast ast::TypedTypeAlias) { 1046 if type_alias.location.contains_span(self.location) { 1047 for (location, name) in type_alias.parameters.iter() { 1048 if name == self.name { 1049 self.references.push(*location); 1050 } 1051 } 1052 ast::visit::visit_typed_type_alias(self, type_alias); 1053 } 1054 } 1055 1056 fn visit_type_ast_var(&mut self, location: &'ast SrcSpan, name: &'ast EcoString) { 1057 if name == self.name { 1058 self.references.push(*location); 1059 } 1060 } 1061}