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::{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}