Fork of daniellemaywood.uk/gleam — Wasm codegen work
28 kB
816 lines
1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2025 The Gleam contributors
3
4use std::collections::{HashMap, HashSet};
5
6use crate::ast::{Publicity, SrcSpan};
7use bimap::{BiMap, Overwritten};
8use ecow::EcoString;
9use petgraph::{
10 Directed, Direction,
11 stable_graph::{NodeIndex, StableGraph},
12};
13
14/// Describes one of a number of situations where references can be generated.
15/// See each variant for an explanation.
16#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
17pub enum ReferenceKind {
18 /// A type or value which is referenced using the qualified syntax, along with
19 /// some information about the qualifier which is used for tracking module name
20 /// references. For example:
21 /// ```gleam
22 /// import gleam/option.{Some}
23 ///
24 /// pub fn main() -> option.Option(Int) {
25 /// // ^^^^^^ `module_location` covers this
26 /// Some(1)
27 /// }
28 /// ```
29 ///
30 /// Here, `module_alias` is `option`, as that's what's being used as a
31 /// qualifier.
32 ///
33 ///
34 /// ```gleam
35 /// import gleam/int as integer
36 ///
37 /// pub fn main() {
38 /// integer.add(1, 2)
39 /// //^^^^^^^ `module_location` covers this
40 /// }
41 /// ```
42 ///
43 /// In this case, `module_alias` is `integer`, due to the aliased import.
44 ///
45 Qualified {
46 module_alias: EcoString,
47 module_location: SrcSpan,
48 },
49 /// A type or value is being referenced using unqualified syntax. This may
50 /// be due to being imported unqualified, or because it's from the same
51 /// module. For example:
52 ///
53 /// ```gleam
54 /// import gleam/option.{None}
55 ///
56 /// pub fn main() {
57 /// none()
58 /// //^^^^ Unqualified
59 /// }
60 ///
61 /// fn none() {
62 /// None
63 /// //^^^^ Unqualified
64 /// }
65 /// ```
66 ///
67 Unqualified,
68 /// A value or type is being referenced inside an unqualified import. For
69 /// example:
70 /// ```gleam
71 /// import gleam/option.{None}
72 /// // ^^^^ Import
73 /// import gleam/dynamic/decode.{type Dynamic}
74 /// // ^^^^^^^ Import
75 /// ```
76 /// The contained span is the location of `as ...` part, if it exists,
77 /// otherwise it will just be empty. For example:
78 /// ```gleam
79 /// import gleam/option.{None as Nothing}
80 /// // ^^^^^^^^^^^ Alias span
81 /// ```
82 Import(SrcSpan),
83 /// The original definition location of a type or value. This also counts as
84 /// a reference for renaming and "find references" purposes. For example:
85 ///
86 /// ```gleam
87 /// pub type Wibble {
88 /// // ^^^^^^ Definition
89 /// Wibble(Int)
90 /// //^^^^^^ Definition
91 /// }
92 ///
93 /// pub fn extract(w: Wibble) {
94 /// // ^^^^^^^ Definition
95 /// let Wibble(x) = w
96 /// x
97 /// }
98 /// ```
99 Definition,
100 /// A value or type is being referenced using unqualified syntax, with a
101 /// name other than its original definition. This can be due to importing it
102 /// using an alias, or due to referencing it through a type alias. For example:
103 ///
104 /// ```gleam
105 /// import gleam/option.{None as Nothing, type Option as Maybe}
106 ///
107 /// pub fn nothing() -> Maybe(_) {
108 /// // ^^^^^ Alias
109 /// Nothing
110 /// //^^^^^^^ Alias
111 /// }
112 /// ```
113 ///
114 Alias,
115}
116
117#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
118pub struct Reference {
119 pub location: SrcSpan,
120 pub kind: ReferenceKind,
121}
122
123/// A reference to a module name. This is similar to a `Reference`, which covers
124/// types and values, but it is separate because we care about slightly different
125/// pieces of information when, for example, renaming modules vs. renaming types
126/// or values.
127///
128#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
129pub enum ModuleNameReference {
130 /// The location of a module name in a `ModuleSelect`, when the module name
131 /// is not aliased. For example:
132 /// ```gleam
133 /// import gleam/option
134 ///
135 /// pub fn main() -> option.Option(_) {
136 /// // ^^^^^^ ModuleSelect
137 /// option.None
138 /// //^^^^^^ ModuleSelect
139 /// }
140 /// ```
141 ///
142 ModuleSelect(SrcSpan),
143 /// The location of a module name in a `ModuleSelect`, when the module name
144 /// *is* aliased. For example:
145 /// ```gleam
146 /// import gleam/option as maybe
147 ///
148 /// pub fn main() -> maybe.Option(_) {
149 /// // ^^^^^ AliasedModuleSelect
150 /// maybe.None
151 /// //^^^^^ AliasedModuleSelect
152 /// }
153 /// ```
154 ///
155 AliasedModuleSelect(SrcSpan),
156 /// The location of a module name in an `import` statement, when the module
157 /// name is not aliased. For example:
158 /// ```gleam
159 /// import gleam/option.{None, Some}
160 /// // ^^^^^^^^^^^^ Import ^ `import_end`
161 /// ```
162 ///
163 Import {
164 module_location: SrcSpan,
165 import_end: u32,
166 },
167 /// The location of a module name in an `import` statement, when the module
168 /// name *is* aliased. Also stores the location of the alias (including the
169 /// `as` keyword), and what the alias is. For example:
170 /// ```gleam
171 /// import gleam/option.{None, Some} as maybe
172 /// // ^^^^^^^^^^^^ `module_location`
173 /// // ^^^^^^^^ `alias_location`
174 /// ```
175 /// In this example, `alias` would be `maybe`.
176 ///
177 AliasedImport {
178 module_location: SrcSpan,
179 alias_location: SrcSpan,
180 alias: EcoString,
181 },
182}
183
184pub type ReferenceMap = HashMap<(EcoString, EcoString), Vec<Reference>>;
185
186/// A use of a record field label: a labelled argument in a record constructor
187/// call or pattern, a record update argument, or a `record.field` access.
188#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
189pub struct LabelReference {
190 /// The location of the label. For a shorthand (`label:`) this spans the
191 /// whole `label:`, mirroring how variable references record label
192 /// shorthands. For everything else it is just the label itself.
193 pub location: SrcSpan,
194 pub syntax: LabelSyntax,
195}
196
197/// The syntax used to write a record field label where it is used.
198#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
199pub enum LabelSyntax {
200 /// The label is written out on its own, as in `Wibble(label: value)` or
201 /// `record.label`, so renaming it is a simple replacement.
202 Longhand,
203 /// The label shorthand syntax (`label:`), which stands for both the label
204 /// and a variable with the same name. Renaming the field then has to
205 /// expand it so the value keeps its name: `wibble:` becomes
206 /// `wobble: wibble`.
207 Shorthand,
208}
209
210/// The location at which a record field label is defined in one of the
211/// variants of a custom type.
212#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
213pub struct LabelDefinition {
214 pub location: SrcSpan,
215 /// The name of the variant this label is defined in.
216 pub variant: EcoString,
217}
218
219/// Identifies a record field label within a custom type, used to look up the
220/// references to that label.
221#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
222pub struct RecordLabel {
223 /// The module the type this label belongs to is defined in.
224 pub type_module: EcoString,
225 /// The name of the type this label belongs to.
226 pub type_name: EcoString,
227 pub label: EcoString,
228}
229
230#[derive(Debug, Clone)]
231pub struct EntityInformation {
232 pub origin: SrcSpan,
233 pub kind: EntityKind,
234}
235
236/// Information about an "Entity". This determines how we warn about an entity
237/// being unused.
238#[derive(Debug, Clone, Eq, PartialEq)]
239pub enum EntityKind {
240 Function,
241 Constant,
242 Constructor,
243 Type,
244 ImportedModule { module_name: EcoString },
245 ModuleAlias { module: EcoString },
246 ImportedConstructor { module: EcoString },
247 ImportedType { module: EcoString },
248 ImportedValue { module: EcoString },
249}
250
251/// Like `ast::Layer`, this type differentiates between different scopes. For example,
252/// there can be a `wibble` value, a `wibble` module and a `wibble` type in the same
253/// scope all at once!
254///
255#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
256enum EntityLayer {
257 /// An entity which exists in the type layer: a custom type, type variable
258 /// or type alias.
259 Type,
260 /// An entity which exists in the value layer: a constant, function or
261 /// custom type variant constructor.
262 Value,
263 /// An entity which has been shadowed. This allows us to keep track of unused
264 /// imports even if they have been shadowed by another value in the current
265 /// module.
266 /// This extra variant is needed because we used `Entity` as a key in a hashmap,
267 /// and so a duplicate key would not be able to exist.
268 /// We also would not want to get this shadowed entity when performing a lookup
269 /// of a named entity; we only want it to register it as an entity in the
270 /// `unused` function.
271 Shadowed,
272 /// The name of an imported module. Modules are separate to values!
273 Module,
274}
275
276#[derive(Debug, Clone, PartialEq, Eq, Hash)]
277pub struct Entity {
278 pub name: EcoString,
279 layer: EntityLayer,
280}
281
282#[derive(Debug, Default)]
283pub struct ReferenceTracker {
284 /// A call-graph which tracks which values are referenced by which other value,
285 /// used for dead code detection.
286 graph: StableGraph<(), (), Directed>,
287 entities: BiMap<Entity, NodeIndex>,
288 current_node: NodeIndex,
289 public_entities: HashSet<Entity>,
290 entity_information: HashMap<Entity, EntityInformation>,
291
292 /// The locations of the references to each value in this module, used for
293 /// renaming and go-to reference.
294 pub value_references: ReferenceMap,
295 /// The locations of the references to each type in this module, used for
296 /// renaming and go-to reference.
297 pub type_references: ReferenceMap,
298 /// The locations of the references to each imported module, used for
299 /// renaming and go-to reference.
300 pub module_references: HashMap<EcoString, Vec<ModuleNameReference>>,
301 /// The locations of the references to each record field label in this
302 /// module, used for renaming and go-to reference.
303 pub label_references: HashMap<RecordLabel, Vec<LabelReference>>,
304 /// The locations at which each record field label is defined, one per
305 /// variant that defines it, used for renaming and go-to definition.
306 pub label_definitions: HashMap<RecordLabel, Vec<LabelDefinition>>,
307
308 /// This map is used to access the nodes of modules that were not
309 /// aliased, given their name.
310 /// We need this to keep track of references made to imports by unqualified
311 /// values/types: when an unqualified item is used we want to add an edge
312 /// pointing to the import it comes from, so that if the item is used the
313 /// import won't be marked as unused:
314 ///
315 /// ```gleam
316 /// import wibble/wobble.{used}
317 ///
318 /// pub fn main() {
319 /// used
320 /// }
321 /// ```
322 ///
323 /// And each imported entity carries around the _name of the module_ and not
324 /// just the alias (here it would be `wibble/wobble` and not just `wobble`).
325 ///
326 module_name_to_node: HashMap<EcoString, NodeIndex>,
327}
328
329impl ReferenceTracker {
330 pub fn new() -> Self {
331 Self::default()
332 }
333
334 fn get_or_create_node(&mut self, name: EcoString, layer: EntityLayer) -> NodeIndex {
335 let entity = Entity { name, layer };
336
337 match self.entities.get_by_left(&entity) {
338 Some(index) => *index,
339 None => {
340 let index = self.graph.add_node(());
341 _ = self.entities.insert(entity, index);
342 index
343 }
344 }
345 }
346
347 fn create_node(&mut self, name: EcoString, layer: EntityLayer) -> NodeIndex {
348 let entity = Entity { name, layer };
349 let index = self.graph.add_node(());
350
351 self.create_node_and_maybe_shadow(entity, index);
352
353 index
354 }
355
356 fn create_node_and_maybe_shadow(&mut self, entity: Entity, index: NodeIndex) {
357 match self.entities.insert(entity, index) {
358 Overwritten::Neither => {}
359 Overwritten::Left(mut entity, index)
360 | Overwritten::Right(mut entity, index)
361 | Overwritten::Pair(mut entity, index)
362 | Overwritten::Both((mut entity, index), _) => {
363 // If an entity with the same name as this already exists,
364 // we still need to keep track of its usage! Thought it cannot
365 // be referenced anymore, it still might have been used before this
366 // point, or need to be marked as unused.
367 // To do this, we keep track of a "Shadowed" entity in `entity_information`.
368 if let Some(information) = self.entity_information.get(&entity) {
369 entity.layer = EntityLayer::Shadowed;
370 _ = self
371 .entity_information
372 .insert(entity.clone(), information.clone());
373 _ = self.entities.insert(entity, index);
374 }
375 }
376 }
377 }
378
379 /// This function exists because of a specific edge-case where constants
380 /// can shadow imported values. For example:
381 /// ```gleam
382 /// import math.{pi}
383 ///
384 /// pub const pi = pi
385 /// ```
386 /// Here, the new `pi` constant shadows the imported `pi` value, but it still
387 /// references it, so it should not be marked as unused.
388 /// In order for this to work, we must first set the `current_function` field
389 /// so that the `pi` value is referenced by the public `pi` constant.
390 /// However, we can't insert the `pi` constant into the name scope yet, since
391 /// then it would count as referencing itself. We first need to set `current_function`,
392 /// then once we have analysed the right-hand-side of the constant, we can
393 /// register it in the scope using `register_constant`.
394 ///
395 pub fn begin_constant(&mut self) {
396 self.current_node = self.graph.add_node(());
397 }
398
399 pub fn register_constant(&mut self, name: EcoString, location: SrcSpan, publicity: Publicity) {
400 let entity = Entity {
401 name,
402 layer: EntityLayer::Value,
403 };
404 self.create_node_and_maybe_shadow(entity.clone(), self.current_node);
405 match publicity {
406 Publicity::Public | Publicity::Internal { .. } => {
407 let _ = self.public_entities.insert(entity.clone());
408 }
409 Publicity::Private => {}
410 }
411
412 _ = self.entity_information.insert(
413 entity,
414 EntityInformation {
415 kind: EntityKind::Constant,
416 origin: location,
417 },
418 );
419 }
420
421 pub fn register_value(
422 &mut self,
423 name: EcoString,
424 kind: EntityKind,
425 location: SrcSpan,
426 publicity: Publicity,
427 ) {
428 self.current_node = self.create_node(name.clone(), EntityLayer::Value);
429 self.register_module_reference_from_imported_entity(&kind);
430
431 let entity = Entity {
432 name,
433 layer: EntityLayer::Value,
434 };
435 match publicity {
436 Publicity::Public | Publicity::Internal { .. } => {
437 let _ = self.public_entities.insert(entity.clone());
438 }
439 Publicity::Private => {}
440 }
441
442 _ = self.entity_information.insert(
443 entity,
444 EntityInformation {
445 kind,
446 origin: location,
447 },
448 );
449 }
450
451 pub fn set_current_node(&mut self, name: EcoString) {
452 self.current_node = self.get_or_create_node(name, EntityLayer::Value);
453 }
454
455 pub fn register_type(
456 &mut self,
457 name: EcoString,
458 kind: EntityKind,
459 location: SrcSpan,
460 publicity: Publicity,
461 ) {
462 self.current_node = self.create_node(name.clone(), EntityLayer::Type);
463 self.register_module_reference_from_imported_entity(&kind);
464
465 let entity = Entity {
466 name,
467 layer: EntityLayer::Type,
468 };
469 match publicity {
470 Publicity::Public | Publicity::Internal { .. } => {
471 let _ = self.public_entities.insert(entity.clone());
472 }
473 Publicity::Private => {}
474 }
475
476 _ = self.entity_information.insert(
477 entity,
478 EntityInformation {
479 kind,
480 origin: location,
481 },
482 );
483 }
484
485 pub fn register_aliased_module(
486 &mut self,
487 used_name: EcoString,
488 module_name: EcoString,
489 alias_location: SrcSpan,
490 import_location: SrcSpan,
491 module_location: SrcSpan,
492 ) {
493 // We first record a node for the module being aliased.
494 self.register_module(
495 used_name.clone(),
496 module_name.clone(),
497 import_location,
498 module_location,
499 Some(alias_location),
500 );
501
502 // Then we create a node for the alias, as the alias itself might be
503 // unused!
504 self.current_node = self.create_node(used_name.clone(), EntityLayer::Module);
505 // Also we want to register the fact that if this alias is used then the
506 // import is used: so we add a reference from the alias to the import
507 // we've just added.
508 self.register_module_reference(module_name.clone());
509
510 // Finally we can add information for this alias:
511 let entity = Entity {
512 name: used_name,
513 layer: EntityLayer::Module,
514 };
515 _ = self.entity_information.insert(
516 entity,
517 EntityInformation {
518 kind: EntityKind::ModuleAlias {
519 module: module_name,
520 },
521 origin: alias_location,
522 },
523 );
524 }
525
526 pub fn register_module(
527 &mut self,
528 used_name: EcoString,
529 module_name: EcoString,
530 location: SrcSpan,
531 module_location: SrcSpan,
532 alias_location: Option<SrcSpan>,
533 ) {
534 self.current_node = self.create_node(used_name.clone(), EntityLayer::Module);
535 let _ = self
536 .module_name_to_node
537 .insert(module_name.clone(), self.current_node);
538
539 let reference = if let Some(alias_location) = alias_location {
540 ModuleNameReference::AliasedImport {
541 module_location,
542 alias_location,
543 alias: used_name.clone(),
544 }
545 } else {
546 ModuleNameReference::Import {
547 module_location,
548 import_end: location.end,
549 }
550 };
551
552 self.register_module_name_reference(module_name.clone(), reference);
553
554 let entity = Entity {
555 name: used_name,
556 layer: EntityLayer::Module,
557 };
558
559 _ = self.entity_information.insert(
560 entity,
561 EntityInformation {
562 kind: EntityKind::ImportedModule { module_name },
563 origin: location,
564 },
565 );
566 }
567
568 fn register_module_reference_from_imported_entity(&mut self, entity_kind: &EntityKind) {
569 match entity_kind {
570 EntityKind::Function
571 | EntityKind::Constant
572 | EntityKind::Constructor
573 | EntityKind::Type
574 | EntityKind::ImportedModule { .. }
575 | EntityKind::ModuleAlias { .. } => (),
576
577 EntityKind::ImportedConstructor { module }
578 | EntityKind::ImportedType { module }
579 | EntityKind::ImportedValue { module } => {
580 self.register_module_reference(module.clone())
581 }
582 }
583 }
584
585 pub fn register_value_reference(
586 &mut self,
587 module: EcoString,
588 name: EcoString,
589 referenced_name: &EcoString,
590 location: SrcSpan,
591 kind: ReferenceKind,
592 ) {
593 match &kind {
594 ReferenceKind::Qualified {
595 module_alias,
596 module_location,
597 } => {
598 let last_module_segment = module.split('/').next_back().unwrap_or(&module);
599 let reference = if last_module_segment == module_alias {
600 ModuleNameReference::ModuleSelect(*module_location)
601 } else {
602 ModuleNameReference::AliasedModuleSelect(*module_location)
603 };
604 self.register_module_name_reference(module.clone(), reference);
605 }
606 ReferenceKind::Import(_) | ReferenceKind::Definition => {}
607 ReferenceKind::Alias | ReferenceKind::Unqualified => {
608 let target = self.get_or_create_node(referenced_name.clone(), EntityLayer::Value);
609 _ = self.graph.add_edge(self.current_node, target, ());
610 }
611 }
612
613 self.value_references
614 .entry((module, name))
615 .or_default()
616 .push(Reference { location, kind });
617 }
618
619 pub fn register_type_reference(
620 &mut self,
621 module: EcoString,
622 name: EcoString,
623 referenced_name: &EcoString,
624 location: SrcSpan,
625 kind: ReferenceKind,
626 ) {
627 match &kind {
628 ReferenceKind::Qualified {
629 module_alias,
630 module_location,
631 } => {
632 let last_module_segment = module.split('/').next_back().unwrap_or(&module);
633 let reference = if last_module_segment == module_alias {
634 ModuleNameReference::ModuleSelect(*module_location)
635 } else {
636 ModuleNameReference::AliasedModuleSelect(*module_location)
637 };
638 self.register_module_name_reference(module.clone(), reference);
639 }
640 ReferenceKind::Import(_) | ReferenceKind::Definition => {}
641 ReferenceKind::Alias | ReferenceKind::Unqualified => {
642 self.register_type_reference_in_call_graph(referenced_name.clone())
643 }
644 }
645
646 self.type_references
647 .entry((module, name))
648 .or_default()
649 .push(Reference { location, kind });
650 }
651
652 /// Register a reference to a module in the code. This is separate to
653 /// `register_module_reference`, as references to modules can be created
654 /// implicitly, for example when using unqualified imports. This only register
655 /// explicit references in the source code, when the module name or local
656 /// alias is written.
657 pub fn register_module_name_reference(
658 &mut self,
659 module: EcoString,
660 reference: ModuleNameReference,
661 ) {
662 self.module_references
663 .entry(module)
664 .or_default()
665 .push(reference);
666 }
667
668 /// Registers a use of a record field label.
669 ///
670 /// `type_` is the `(module, name)` of the type the label belongs to, as
671 /// returned by [`Type::named_type_name`].
672 pub fn register_label_reference(
673 &mut self,
674 type_: (EcoString, EcoString),
675 label: EcoString,
676 location: SrcSpan,
677 syntax: LabelSyntax,
678 ) {
679 let (type_module, type_name) = type_;
680 self.label_references
681 .entry(RecordLabel {
682 type_module,
683 type_name,
684 label,
685 })
686 .or_default()
687 .push(LabelReference { location, syntax });
688 }
689
690 /// Registers the definition of a record field label in one of the
691 /// variants of a custom type.
692 ///
693 /// `type_` is the `(module, name)` of the type being defined, as returned
694 /// by [`Type::named_type_name`].
695 pub fn register_label_definition(
696 &mut self,
697 type_: (EcoString, EcoString),
698 label: EcoString,
699 location: SrcSpan,
700 variant: EcoString,
701 ) {
702 let (type_module, type_name) = type_;
703 self.label_definitions
704 .entry(RecordLabel {
705 type_module,
706 type_name,
707 label,
708 })
709 .or_default()
710 .push(LabelDefinition { location, variant });
711 }
712
713 /// Like `register_type_reference`, but doesn't modify `self.type_references`.
714 /// This is used when we define a constructor for a custom type. The constructor
715 /// doesn't actually "reference" its type, but if the constructor is used, the
716 /// type should also be considered used. The best way to represent this relationship
717 /// is to make a connection between them in the call graph.
718 ///
719 pub fn register_type_reference_in_call_graph(&mut self, name: EcoString) {
720 let target = self.get_or_create_node(name, EntityLayer::Type);
721 _ = self.graph.add_edge(self.current_node, target, ());
722 }
723
724 pub fn register_module_reference(&mut self, name: EcoString) {
725 let target = match self.module_name_to_node.get(&name) {
726 Some(target) => *target,
727 None => self.get_or_create_node(name, EntityLayer::Module),
728 };
729 _ = self.graph.add_edge(self.current_node, target, ());
730 }
731
732 pub fn unused(&self) -> HashMap<Entity, EntityInformation> {
733 let mut unused_values = HashMap::with_capacity(self.entities.len());
734
735 for (entity, information) in self.entity_information.iter() {
736 _ = unused_values.insert(entity.clone(), information.clone());
737 }
738 for entity in self.public_entities.iter() {
739 if let Some(index) = self.entities.get_by_left(entity) {
740 self.mark_entity_as_used(&mut unused_values, entity, *index);
741 }
742 }
743
744 for (entity, _) in self.entities.iter() {
745 let Some(index) = self.entities.get_by_left(entity) else {
746 continue;
747 };
748
749 if self.public_entities.contains(entity) {
750 self.mark_entity_as_used(&mut unused_values, entity, *index);
751 } else {
752 // If the entity is not public, we still want to mark referenced
753 // imports as used.
754 self.mark_referenced_imports_as_used(&mut unused_values, entity, *index);
755 }
756 }
757
758 unused_values
759 }
760
761 fn mark_entity_as_used(
762 &self,
763 unused: &mut HashMap<Entity, EntityInformation>,
764 entity: &Entity,
765 index: NodeIndex,
766 ) {
767 if unused.remove(entity).is_some() {
768 for node in self.graph.neighbors_directed(index, Direction::Outgoing) {
769 if let Some(entity) = self.entities.get_by_right(&node) {
770 self.mark_entity_as_used(unused, entity, node);
771 }
772 }
773 }
774 }
775
776 fn mark_referenced_imports_as_used(
777 &self,
778 unused: &mut HashMap<Entity, EntityInformation>,
779 entity: &Entity,
780 index: NodeIndex,
781 ) {
782 // If the entity is a module there's no way it can reference other
783 // modules so we just ignore it.
784 // This also means that module aliases do not count as using a module!
785 if entity.layer == EntityLayer::Module {
786 return;
787 }
788
789 for node in self.graph.neighbors_directed(index, Direction::Outgoing) {
790 // We only want to mark referenced modules as used, so if the node
791 // is not a module we just skip it.
792 let Some(
793 module @ Entity {
794 layer: EntityLayer::Module,
795 ..
796 },
797 ) = self.entities.get_by_right(&node)
798 else {
799 continue;
800 };
801
802 // If the value appears in the module import list, it doesn't count
803 // as using it!
804 let is_imported_type = self
805 .type_references
806 .contains_key(&(module.name.clone(), entity.name.clone()));
807 let is_imported_value = self
808 .value_references
809 .contains_key(&(module.name.clone(), entity.name.clone()));
810 let appears_in_module_import_list = is_imported_type || is_imported_value;
811 if !(appears_in_module_import_list) {
812 self.mark_entity_as_used(unused, module, node);
813 }
814 }
815 }
816}