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