Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

gleam / compiler-core / src / reference.rs
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}