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::Located, 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::RecordLabelUsage { 383 record_type, 384 label, 385 location, 386 .. 387 } 388 | Located::RecordAccessLabel { 389 record_type, 390 label, 391 location, 392 .. 393 } => record_type 394 .named_type_name() 395 .map(|(type_module, type_name)| Referenced::Label { 396 type_module, 397 type_name, 398 label: label.clone(), 399 location, 400 }), 401 402 // A label at its definition in a custom type, which lives in the 403 // current module. 404 Located::RecordLabelDefinition { 405 type_name, 406 label, 407 location, 408 .. 409 } => Some(Referenced::Label { 410 type_module: current_module.clone(), 411 type_name, 412 label, 413 location, 414 }), 415 416 Located::Pattern(_) 417 | Located::ClauseGuard(_) 418 | Located::PatternSpread { .. } 419 | Located::Statement(_) 420 | Located::Expression { .. } 421 | Located::FunctionBody(_) 422 | Located::UnqualifiedImport(_) 423 | Located::Label { .. } 424 | Located::Constant(_) 425 | Located::ModuleFunction(_) 426 | Located::ModuleTypeAlias(_) => None, 427 } 428} 429 430pub fn find_module_references( 431 module_name: EcoString, 432 name: EcoString, 433 modules: &im::HashMap<EcoString, ModuleInterface>, 434 sources: &HashMap<EcoString, ModuleSourceInformation>, 435 layer: ast::Layer, 436) -> Vec<Location> { 437 let mut reference_locations = Vec::new(); 438 439 for module in modules.values() { 440 if module.name == module_name || module.references.imported_modules.contains(&module_name) { 441 let Some(source_information) = sources.get(&module.name) else { 442 continue; 443 }; 444 445 find_references_in_module( 446 &module_name, 447 &name, 448 module, 449 source_information, 450 &mut reference_locations, 451 layer, 452 ); 453 } 454 } 455 456 reference_locations 457} 458 459pub fn find_module_references_in_module( 460 module_name: EcoString, 461 name: EcoString, 462 module: &ModuleInterface, 463 source_information: &ModuleSourceInformation, 464 layer: ast::Layer, 465) -> Vec<Location> { 466 let mut reference_locations = Vec::new(); 467 468 find_references_in_module( 469 &module_name, 470 &name, 471 module, 472 source_information, 473 &mut reference_locations, 474 layer, 475 ); 476 477 reference_locations 478} 479 480pub fn find_label_references( 481 type_module: EcoString, 482 type_name: EcoString, 483 label: EcoString, 484 modules: &im::HashMap<EcoString, ModuleInterface>, 485 sources: &HashMap<EcoString, ModuleSourceInformation>, 486) -> Vec<Location> { 487 let mut reference_locations = Vec::new(); 488 489 // Unlike values and types, a label can be referenced in a module that 490 // doesn't import the type's defining module: a record value can be 491 // obtained transitively through another module and have its fields 492 // accessed. So every module has to be searched. 493 for module in modules.values() { 494 let Some(source_information) = sources.get(&module.name) else { 495 continue; 496 }; 497 reference_locations.extend(find_label_references_in_module( 498 type_module.clone(), 499 type_name.clone(), 500 label.clone(), 501 module, 502 source_information, 503 )); 504 } 505 506 reference_locations 507} 508 509pub fn find_label_references_in_module( 510 type_module: EcoString, 511 type_name: EcoString, 512 label: EcoString, 513 module: &ModuleInterface, 514 source_information: &ModuleSourceInformation, 515) -> Vec<Location> { 516 let mut reference_locations = Vec::new(); 517 518 let Some(uri) = url_from_path(source_information.path.as_str()) else { 519 return reference_locations; 520 }; 521 522 let key = RecordLabel { 523 type_module, 524 type_name, 525 label, 526 }; 527 let definitions = module.references.label_definitions.get(&key); 528 let references = module.references.label_references.get(&key); 529 let locations = definitions 530 .into_iter() 531 .flatten() 532 .map(|definition| definition.location) 533 .chain( 534 references 535 .into_iter() 536 .flatten() 537 .map(|reference| reference.location), 538 ); 539 540 for location in locations { 541 reference_locations.push(Location { 542 uri: uri.clone(), 543 range: src_span_to_lsp_range(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}