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