Fork of daniellemaywood.uk/gleam — Wasm codegen work
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}