1use crate::SourceLocation;
4use crate::hir::body::{BodyOwner, HirBody, HirBodyId};
5use perl_semantic_facts::{
6 AnchorId, Confidence, ExportSet, ExportTag, FileId, ImportKind, ImportSpec, ImportSymbols,
7 Provenance, ScopeId, VisibleSymbol, VisibleSymbolContext, VisibleSymbolSource,
8};
9use std::collections::BTreeMap;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
13#[non_exhaustive]
14pub struct HirId {
15 index: u32,
16}
17
18impl HirId {
19 #[inline]
21 pub const fn from_index(index: u32) -> Self {
22 Self { index }
23 }
24
25 #[inline]
27 pub const fn index(self) -> u32 {
28 self.index
29 }
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
34#[non_exhaustive]
35pub struct HirScopeId {
36 index: u32,
37}
38
39impl HirScopeId {
40 #[inline]
42 pub const fn from_index(index: u32) -> Self {
43 Self { index }
44 }
45
46 #[inline]
48 pub const fn index(self) -> u32 {
49 self.index
50 }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
55#[non_exhaustive]
56pub struct HirBindingId {
57 index: u32,
58}
59
60impl HirBindingId {
61 #[inline]
63 pub const fn from_index(index: u32) -> Self {
64 Self { index }
65 }
66
67 #[inline]
69 pub const fn index(self) -> u32 {
70 self.index
71 }
72}
73
74#[derive(Debug, Clone, PartialEq, Eq)]
76#[non_exhaustive]
77pub struct AstAnchor {
78 pub node_kind: &'static str,
80 pub range: SourceLocation,
82 pub name_range: Option<SourceLocation>,
84}
85
86#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
88#[non_exhaustive]
89pub enum RecoveryConfidence {
90 Parsed,
92 Recovered,
94 Partial,
96 Unknown,
98}
99
100pub const HIR_BODY_MODEL_VERSION: u32 = 1;
104
105#[derive(Debug, Clone, PartialEq, Eq, Default)]
107#[non_exhaustive]
108pub struct HirFile {
109 pub items: Vec<HirItem>,
111 pub scope_graph: ScopeGraph,
113 pub stash_graph: StashGraph,
115 pub compile_environment: CompileEnvironment,
117 pub prototype_table: PrototypeTable,
119 pub bareword_table: BarewordTable,
121 pub bodies: Vec<HirBody>,
126 pub body_owners: BTreeMap<BodyOwner, HirBodyId>,
128 pub body_model_version: u32,
130}
131
132impl HirFile {
133 #[inline]
135 pub fn is_empty(&self) -> bool {
136 self.items.is_empty()
137 }
138
139 #[inline]
144 #[must_use]
145 pub fn root_body(&self) -> Option<&HirBody> {
146 self.bodies.first()
147 }
148
149 #[must_use]
155 pub fn compile_effects(&self) -> Vec<CompileEffect> {
156 self.compile_effects_with_source_hash(None)
157 }
158
159 #[must_use]
165 pub fn compile_effects_with_source_hash(
166 &self,
167 source_hash: Option<String>,
168 ) -> Vec<CompileEffect> {
169 compile_effects_from_file(self, source_hash)
170 }
171
172 #[must_use]
177 pub fn framework_facts(&self) -> FrameworkFactGraph {
178 FrameworkAdapterRegistry::default().project_file(self)
179 }
180}
181
182#[derive(Debug, Clone, PartialEq, Eq, Default)]
184#[non_exhaustive]
185pub struct PrototypeTable {
186 pub facts: Vec<PrototypeFact>,
188}
189
190#[derive(Debug, Clone, PartialEq, Eq)]
192#[non_exhaustive]
193pub struct PrototypeFact {
194 pub sub_name: String,
196 pub package_context: Option<String>,
198 pub content: String,
200 pub range: SourceLocation,
202 pub declaration_range: SourceLocation,
204 pub declaration_item: HirId,
206 pub scope_id: Option<HirScopeId>,
208 pub anchor_id: AnchorId,
210 pub provenance: CompileProvenance,
212 pub confidence: CompileConfidence,
214}
215
216#[derive(Debug, Clone, PartialEq, Eq, Default)]
218#[non_exhaustive]
219pub struct BarewordTable {
220 pub facts: Vec<BarewordFact>,
222}
223
224#[derive(Debug, Clone, PartialEq, Eq)]
226#[non_exhaustive]
227pub struct BarewordFact {
228 pub name: String,
230 pub role: BarewordRole,
232 pub package_context: Option<String>,
234 pub range: SourceLocation,
236 pub source_item: HirId,
238 pub scope_id: Option<HirScopeId>,
240 pub anchor_id: AnchorId,
242 pub provenance: CompileProvenance,
244 pub confidence: CompileConfidence,
246}
247
248#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
250#[non_exhaustive]
251pub enum BarewordRole {
252 Expression,
254 QualifiedName,
256 ModuleRequest,
258 MethodReceiver,
260 IndirectObject,
262 HashKey,
264}
265
266#[derive(Debug, Clone, PartialEq, Eq)]
268#[non_exhaustive]
269pub struct HirItem {
270 pub id: HirId,
272 pub kind: HirKind,
274 pub range: SourceLocation,
276 pub anchor: AstAnchor,
278 pub recovery_confidence: RecoveryConfidence,
280 pub package_context: Option<String>,
282 pub scope_context: Option<HirScopeId>,
284}
285
286pub const COMPILE_EFFECT_MODEL_VERSION: u32 = 1;
288
289#[derive(Debug, Clone, PartialEq, Eq)]
295#[non_exhaustive]
296pub struct CompileEffect {
297 pub ordinal: u32,
299 pub kind: CompileEffectKind,
301 pub source_kind: CompileEffectSourceKind,
303 pub fact_kind: CompileEffectFactKind,
305 pub fact_name: Option<String>,
307 pub range: SourceLocation,
309 pub source_item: Option<HirId>,
311 pub scope_id: Option<HirScopeId>,
313 pub package_context: Option<String>,
315 pub fact_anchor_id: Option<AnchorId>,
317 pub dynamic_reason: Option<String>,
319 pub source_hash: Option<String>,
321 pub model_version: u32,
323 pub provenance: CompileProvenance,
325 pub confidence: CompileConfidence,
327}
328
329#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
331#[non_exhaustive]
332pub enum CompileEffectKind {
333 DeclarePackage,
335 DeclareSub,
337 DeclareMethod,
339 DeclareBinding,
341 SetPragmaState,
343 AddIncludePath,
345 RemoveIncludePath,
347 RequestModule,
349 ImportSymbols,
351 AssignInheritance,
353 AssignGlobAlias,
355 DefineConstant,
357 RegisterPrototype,
359 EmitDynamicBoundary,
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
365#[non_exhaustive]
366pub enum CompileEffectSourceKind {
367 PackageDecl,
369 SubDecl,
371 MethodDecl,
373 VariableDecl,
375 UseDirective,
377 NoDirective,
379 RequireDirective,
381 PhaseBlock,
383 SymbolicReferenceDeref,
385 Assignment,
387 TypeglobAssignment,
389 ScopeGraph,
391 StashGraph,
393 CompileEnvironment,
395}
396
397#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
399#[non_exhaustive]
400pub enum CompileEffectFactKind {
401 Package,
403 Sub,
405 Method,
407 Binding,
409 PragmaState,
411 IncludeRoot,
413 ModuleRequest,
415 ImportSpec,
417 InheritanceEdge,
419 GlobSlot,
421 Constant,
423 Prototype,
425 DynamicBoundary,
427}
428
429#[derive(Debug)]
430struct CompileEffectEntry {
431 source_order: u32,
432 effect: CompileEffect,
433}
434
435fn compile_effects_from_file(file: &HirFile, source_hash: Option<String>) -> Vec<CompileEffect> {
436 let mut entries = Vec::new();
437 let mut next_order = 0;
438
439 for item in &file.items {
440 push_item_effects(item, &source_hash, &mut entries, &mut next_order);
441 }
442 for fact in &file.prototype_table.facts {
443 push_compile_effect(
444 &mut entries,
445 &mut next_order,
446 CompileEffectSeed {
447 kind: CompileEffectKind::RegisterPrototype,
448 source_kind: CompileEffectSourceKind::SubDecl,
449 fact_kind: CompileEffectFactKind::Prototype,
450 fact_name: Some(fact.sub_name.clone()),
451 range: fact.range,
452 source_item: Some(fact.declaration_item),
453 scope_id: fact.scope_id,
454 package_context: fact.package_context.clone(),
455 fact_anchor_id: Some(fact.anchor_id),
456 dynamic_reason: None,
457 source_hash: source_hash.clone(),
458 provenance: fact.provenance,
459 confidence: fact.confidence,
460 },
461 );
462 }
463 for binding in &file.scope_graph.bindings {
464 push_compile_effect(
465 &mut entries,
466 &mut next_order,
467 CompileEffectSeed {
468 kind: CompileEffectKind::DeclareBinding,
469 source_kind: CompileEffectSourceKind::ScopeGraph,
470 fact_kind: CompileEffectFactKind::Binding,
471 fact_name: Some(format!("{}{}", binding.sigil, binding.name)),
472 range: binding.range,
473 source_item: binding.declaration_item,
474 scope_id: Some(binding.scope_id),
475 package_context: binding.package_context.clone(),
476 fact_anchor_id: Some(AnchorId(binding.range.start as u64)),
477 dynamic_reason: None,
478 source_hash: source_hash.clone(),
479 provenance: CompileProvenance::ExactAst,
480 confidence: CompileConfidence::High,
481 },
482 );
483 }
484 for fact in &file.compile_environment.pragma_state_facts {
485 push_compile_effect(
486 &mut entries,
487 &mut next_order,
488 CompileEffectSeed {
489 kind: CompileEffectKind::SetPragmaState,
490 source_kind: CompileEffectSourceKind::CompileEnvironment,
491 fact_kind: CompileEffectFactKind::PragmaState,
492 fact_name: Some("strict/warnings/feature".to_string()),
493 range: fact.range,
494 source_item: fact.directive_item,
495 scope_id: fact.scope_id,
496 package_context: fact.package_context.clone(),
497 fact_anchor_id: Some(fact.anchor_id),
498 dynamic_reason: None,
499 source_hash: source_hash.clone(),
500 provenance: fact.provenance,
501 confidence: fact.confidence,
502 },
503 );
504 }
505 for root in &file.compile_environment.inc_roots {
506 let kind = match root.action {
507 IncRootAction::Add => CompileEffectKind::AddIncludePath,
508 IncRootAction::Remove => CompileEffectKind::RemoveIncludePath,
509 };
510 push_compile_effect(
511 &mut entries,
512 &mut next_order,
513 CompileEffectSeed {
514 kind,
515 source_kind: match root.action {
516 IncRootAction::Add => CompileEffectSourceKind::UseDirective,
517 IncRootAction::Remove => CompileEffectSourceKind::NoDirective,
518 },
519 fact_kind: CompileEffectFactKind::IncludeRoot,
520 fact_name: Some(root.path.clone()),
521 range: root.range,
522 source_item: root.directive_item,
523 scope_id: root.scope_id,
524 package_context: root.package_context.clone(),
525 fact_anchor_id: Some(AnchorId(root.range.start as u64)),
526 dynamic_reason: None,
527 source_hash: source_hash.clone(),
528 provenance: root.provenance,
529 confidence: root.confidence,
530 },
531 );
532 }
533 for request in &file.compile_environment.module_requests {
534 push_compile_effect(
535 &mut entries,
536 &mut next_order,
537 CompileEffectSeed {
538 kind: CompileEffectKind::RequestModule,
539 source_kind: module_request_source_kind(request.kind),
540 fact_kind: CompileEffectFactKind::ModuleRequest,
541 fact_name: request.target.clone().or_else(|| Some("<dynamic>".to_string())),
542 range: request.range,
543 source_item: request.directive_item,
544 scope_id: request.scope_id,
545 package_context: request.package_context.clone(),
546 fact_anchor_id: Some(AnchorId(request.range.start as u64)),
547 dynamic_reason: None,
548 source_hash: source_hash.clone(),
549 provenance: request.provenance,
550 confidence: request.confidence,
551 },
552 );
553 }
554 for spec in file.compile_environment.import_specs(FileId(0)) {
555 push_compile_effect(
556 &mut entries,
557 &mut next_order,
558 CompileEffectSeed {
559 kind: CompileEffectKind::ImportSymbols,
560 source_kind: import_spec_source_kind(&spec),
561 fact_kind: CompileEffectFactKind::ImportSpec,
562 fact_name: Some(spec.module.clone()),
563 range: SourceLocation::new(
564 spec.span_start_byte.unwrap_or_default() as usize,
565 spec.span_start_byte.unwrap_or_default() as usize,
566 ),
567 source_item: None,
568 scope_id: spec.scope_id.map(|scope| HirScopeId::from_index(scope.0 as u32)),
569 package_context: None,
570 fact_anchor_id: spec.anchor_id,
571 dynamic_reason: None,
572 source_hash: source_hash.clone(),
573 provenance: fact_provenance_to_compile(spec.provenance),
574 confidence: fact_confidence_to_compile(spec.confidence),
575 },
576 );
577 }
578 for edge in &file.stash_graph.inheritance_edges {
579 push_compile_effect(
580 &mut entries,
581 &mut next_order,
582 CompileEffectSeed {
583 kind: CompileEffectKind::AssignInheritance,
584 source_kind: CompileEffectSourceKind::StashGraph,
585 fact_kind: CompileEffectFactKind::InheritanceEdge,
586 fact_name: Some(format!("{}->{}", edge.from_package, edge.to_package)),
587 range: edge.range,
588 source_item: edge.declaration_item,
589 scope_id: None,
590 package_context: Some(edge.from_package.clone()),
591 fact_anchor_id: Some(AnchorId(edge.range.start as u64)),
592 dynamic_reason: None,
593 source_hash: source_hash.clone(),
594 provenance: stash_provenance_to_compile(edge.provenance),
595 confidence: stash_confidence_to_compile(edge.confidence),
596 },
597 );
598 }
599 for package in &file.stash_graph.packages {
600 for slot in &package.slots {
601 push_slot_effects(package, slot, &source_hash, &mut entries, &mut next_order);
602 }
603 }
604 for boundary in &file.compile_environment.dynamic_boundaries {
605 push_compile_effect(
606 &mut entries,
607 &mut next_order,
608 CompileEffectSeed {
609 kind: CompileEffectKind::EmitDynamicBoundary,
610 source_kind: compile_boundary_source_kind(boundary.kind),
611 fact_kind: CompileEffectFactKind::DynamicBoundary,
612 fact_name: Some(format!("{:?}", boundary.kind)),
613 range: boundary.range,
614 source_item: boundary.boundary_item,
615 scope_id: boundary.scope_id,
616 package_context: boundary.package_context.clone(),
617 fact_anchor_id: Some(AnchorId(boundary.range.start as u64)),
618 dynamic_reason: Some(boundary.reason.clone()),
619 source_hash: source_hash.clone(),
620 provenance: boundary.provenance,
621 confidence: boundary.confidence,
622 },
623 );
624 }
625 for boundary in &file.stash_graph.dynamic_boundaries {
626 push_compile_effect(
627 &mut entries,
628 &mut next_order,
629 CompileEffectSeed {
630 kind: CompileEffectKind::EmitDynamicBoundary,
631 source_kind: CompileEffectSourceKind::StashGraph,
632 fact_kind: CompileEffectFactKind::DynamicBoundary,
633 fact_name: boundary.symbol.clone(),
634 range: boundary.range,
635 source_item: boundary.boundary_item,
636 scope_id: None,
637 package_context: boundary.package.clone(),
638 fact_anchor_id: Some(AnchorId(boundary.range.start as u64)),
639 dynamic_reason: Some(boundary.reason.clone()),
640 source_hash: source_hash.clone(),
641 provenance: stash_provenance_to_compile(boundary.provenance),
642 confidence: stash_confidence_to_compile(boundary.confidence),
643 },
644 );
645 }
646
647 entries.sort_by_key(|entry| (entry.effect.range.start, entry.source_order));
648 entries
649 .into_iter()
650 .enumerate()
651 .map(|(ordinal, mut entry)| {
652 entry.effect.ordinal = ordinal as u32;
653 entry.effect
654 })
655 .collect()
656}
657
658fn push_item_effects(
659 item: &HirItem,
660 source_hash: &Option<String>,
661 entries: &mut Vec<CompileEffectEntry>,
662 next_order: &mut u32,
663) {
664 match &item.kind {
665 HirKind::PackageDecl(decl) => {
666 push_compile_effect(
667 entries,
668 next_order,
669 CompileEffectSeed {
670 kind: CompileEffectKind::DeclarePackage,
671 source_kind: CompileEffectSourceKind::PackageDecl,
672 fact_kind: CompileEffectFactKind::Package,
673 fact_name: Some(decl.name.clone()),
674 range: item.range,
675 source_item: Some(item.id),
676 scope_id: item.scope_context,
677 package_context: Some(decl.name.clone()),
678 fact_anchor_id: Some(AnchorId(item.range.start as u64)),
679 dynamic_reason: None,
680 source_hash: source_hash.clone(),
681 provenance: CompileProvenance::ExactAst,
682 confidence: CompileConfidence::High,
683 },
684 );
685 }
686 HirKind::SubDecl(decl) => {
687 let Some(name) = &decl.name else {
688 return;
689 };
690 push_compile_effect(
691 entries,
692 next_order,
693 CompileEffectSeed {
694 kind: CompileEffectKind::DeclareSub,
695 source_kind: CompileEffectSourceKind::SubDecl,
696 fact_kind: CompileEffectFactKind::Sub,
697 fact_name: Some(name.clone()),
698 range: item.range,
699 source_item: Some(item.id),
700 scope_id: item.scope_context,
701 package_context: item.package_context.clone(),
702 fact_anchor_id: Some(AnchorId(item.range.start as u64)),
703 dynamic_reason: None,
704 source_hash: source_hash.clone(),
705 provenance: CompileProvenance::ExactAst,
706 confidence: CompileConfidence::High,
707 },
708 );
709 }
710 HirKind::MethodDecl(decl) => {
711 push_compile_effect(
712 entries,
713 next_order,
714 CompileEffectSeed {
715 kind: CompileEffectKind::DeclareMethod,
716 source_kind: CompileEffectSourceKind::MethodDecl,
717 fact_kind: CompileEffectFactKind::Method,
718 fact_name: Some(decl.name.clone()),
719 range: item.range,
720 source_item: Some(item.id),
721 scope_id: item.scope_context,
722 package_context: item.package_context.clone(),
723 fact_anchor_id: Some(AnchorId(item.range.start as u64)),
724 dynamic_reason: None,
725 source_hash: source_hash.clone(),
726 provenance: CompileProvenance::ExactAst,
727 confidence: CompileConfidence::High,
728 },
729 );
730 }
731 _ => {}
732 }
733}
734
735fn push_slot_effects(
736 package: &PackageStash,
737 slot: &GlobSlot,
738 source_hash: &Option<String>,
739 entries: &mut Vec<CompileEffectEntry>,
740 next_order: &mut u32,
741) {
742 let (kind, fact_kind) = match slot.source {
743 GlobSlotSource::TypeglobAlias => {
744 (CompileEffectKind::AssignGlobAlias, CompileEffectFactKind::GlobSlot)
745 }
746 GlobSlotSource::ConstantDeclaration => {
747 (CompileEffectKind::DefineConstant, CompileEffectFactKind::Constant)
748 }
749 _ => return,
750 };
751
752 push_compile_effect(
753 entries,
754 next_order,
755 CompileEffectSeed {
756 kind,
757 source_kind: match slot.source {
758 GlobSlotSource::TypeglobAlias => CompileEffectSourceKind::TypeglobAssignment,
759 _ => CompileEffectSourceKind::StashGraph,
760 },
761 fact_kind,
762 fact_name: Some(format!("{}::{}", package.package, slot.name)),
763 range: slot.range,
764 source_item: slot.declaration_item,
765 scope_id: None,
766 package_context: Some(package.package.clone()),
767 fact_anchor_id: Some(AnchorId(slot.range.start as u64)),
768 dynamic_reason: None,
769 source_hash: source_hash.clone(),
770 provenance: stash_provenance_to_compile(slot.provenance),
771 confidence: stash_confidence_to_compile(slot.confidence),
772 },
773 );
774}
775
776#[derive(Debug)]
777struct CompileEffectSeed {
778 kind: CompileEffectKind,
779 source_kind: CompileEffectSourceKind,
780 fact_kind: CompileEffectFactKind,
781 fact_name: Option<String>,
782 range: SourceLocation,
783 source_item: Option<HirId>,
784 scope_id: Option<HirScopeId>,
785 package_context: Option<String>,
786 fact_anchor_id: Option<AnchorId>,
787 dynamic_reason: Option<String>,
788 source_hash: Option<String>,
789 provenance: CompileProvenance,
790 confidence: CompileConfidence,
791}
792
793fn push_compile_effect(
794 entries: &mut Vec<CompileEffectEntry>,
795 next_order: &mut u32,
796 seed: CompileEffectSeed,
797) {
798 let order = *next_order;
799 *next_order += 1;
800 entries.push(CompileEffectEntry {
801 source_order: order,
802 effect: CompileEffect {
803 ordinal: 0,
804 kind: seed.kind,
805 source_kind: seed.source_kind,
806 fact_kind: seed.fact_kind,
807 fact_name: seed.fact_name,
808 range: seed.range,
809 source_item: seed.source_item,
810 scope_id: seed.scope_id,
811 package_context: seed.package_context,
812 fact_anchor_id: seed.fact_anchor_id,
813 dynamic_reason: seed.dynamic_reason,
814 source_hash: seed.source_hash,
815 model_version: COMPILE_EFFECT_MODEL_VERSION,
816 provenance: seed.provenance,
817 confidence: seed.confidence,
818 },
819 });
820}
821
822fn module_request_source_kind(kind: ModuleRequestKind) -> CompileEffectSourceKind {
823 match kind {
824 ModuleRequestKind::Require => CompileEffectSourceKind::RequireDirective,
825 _ => CompileEffectSourceKind::UseDirective,
826 }
827}
828
829fn import_spec_source_kind(spec: &ImportSpec) -> CompileEffectSourceKind {
830 match spec.kind {
831 ImportKind::Require | ImportKind::DynamicRequire => {
832 CompileEffectSourceKind::RequireDirective
833 }
834 _ => CompileEffectSourceKind::UseDirective,
835 }
836}
837
838fn compile_boundary_source_kind(kind: CompileEnvironmentBoundaryKind) -> CompileEffectSourceKind {
839 match kind {
840 CompileEnvironmentBoundaryKind::DynamicRequire => CompileEffectSourceKind::RequireDirective,
841 CompileEnvironmentBoundaryKind::DynamicPragmaArgs
842 | CompileEnvironmentBoundaryKind::DynamicIncRoot => CompileEffectSourceKind::UseDirective,
843 CompileEnvironmentBoundaryKind::PhaseBlockExecution => CompileEffectSourceKind::PhaseBlock,
844 CompileEnvironmentBoundaryKind::SymbolicReferenceDeref => {
845 CompileEffectSourceKind::SymbolicReferenceDeref
846 }
847 }
848}
849
850fn stash_provenance_to_compile(provenance: StashProvenance) -> CompileProvenance {
851 match provenance {
852 StashProvenance::ExactAst => CompileProvenance::ExactAst,
853 StashProvenance::DesugaredAst => CompileProvenance::DesugaredAst,
854 StashProvenance::DynamicBoundary => CompileProvenance::DynamicBoundary,
855 }
856}
857
858fn stash_confidence_to_compile(confidence: StashConfidence) -> CompileConfidence {
859 match confidence {
860 StashConfidence::High => CompileConfidence::High,
861 StashConfidence::Medium => CompileConfidence::Medium,
862 StashConfidence::Low => CompileConfidence::Low,
863 }
864}
865
866fn fact_provenance_to_compile(provenance: Provenance) -> CompileProvenance {
867 match provenance {
868 Provenance::ExactAst | Provenance::LiteralRequireImport => CompileProvenance::ExactAst,
870 Provenance::DesugaredAst
871 | Provenance::SemanticAnalyzer
872 | Provenance::FrameworkSynthesis
873 | Provenance::ImportExportInference
874 | Provenance::PragmaInference => CompileProvenance::DesugaredAst,
875 Provenance::NameHeuristic | Provenance::SearchFallback | Provenance::DynamicBoundary => {
876 CompileProvenance::DynamicBoundary
877 }
878 }
879}
880
881fn fact_confidence_to_compile(confidence: Confidence) -> CompileConfidence {
882 match confidence {
883 Confidence::High => CompileConfidence::High,
884 Confidence::Medium => CompileConfidence::Medium,
885 Confidence::Low => CompileConfidence::Low,
886 }
887}
888
889#[derive(Debug, Clone, PartialEq, Eq, Default)]
895#[non_exhaustive]
896pub struct ScopeGraph {
897 pub scopes: Vec<ScopeFrame>,
899 pub bindings: Vec<Binding>,
901 pub references: Vec<BindingReference>,
903}
904
905impl ScopeGraph {
906 #[inline]
908 pub fn root_scope(&self) -> Option<&ScopeFrame> {
909 self.scopes.first()
910 }
911}
912
913#[derive(Debug, Clone, PartialEq, Eq)]
915#[non_exhaustive]
916pub struct ScopeFrame {
917 pub id: HirScopeId,
919 pub parent: Option<HirScopeId>,
921 pub kind: ScopeKind,
923 pub range: SourceLocation,
925 pub package_context: Option<String>,
927}
928
929#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
931#[non_exhaustive]
932pub enum ScopeKind {
933 File,
935 Package,
937 Block,
939 Subroutine,
941 Method,
943 Signature,
945 Format,
947 EvalString,
949 PhaseBlock,
951}
952
953#[derive(Debug, Clone, PartialEq, Eq)]
955#[non_exhaustive]
956pub struct Binding {
957 pub id: HirBindingId,
959 pub scope_id: HirScopeId,
961 pub sigil: String,
963 pub name: String,
965 pub range: SourceLocation,
967 pub storage: StorageClass,
969 pub package_context: Option<String>,
971 pub declaration_item: Option<HirId>,
973 pub shadows: Option<HirBindingId>,
975}
976
977#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
979#[non_exhaustive]
980pub enum StorageClass {
981 LexicalMy,
983 LexicalState,
985 PackageOur,
987 LocalizedPackage,
989 Parameter,
991 MethodInvocant,
993 Implicit,
995 PackageGlobal,
997}
998
999#[derive(Debug, Clone, PartialEq, Eq)]
1001#[non_exhaustive]
1002pub struct BindingReference {
1003 pub scope_id: HirScopeId,
1005 pub sigil: String,
1007 pub name: String,
1009 pub range: SourceLocation,
1011 pub resolved_binding: Option<HirBindingId>,
1013}
1014
1015#[derive(Debug, Clone, PartialEq, Eq, Default)]
1020#[non_exhaustive]
1021pub struct StashGraph {
1022 pub packages: Vec<PackageStash>,
1024 pub inheritance_edges: Vec<PackageInheritanceEdge>,
1026 pub export_declarations: Vec<ExportDeclaration>,
1028 pub dynamic_boundaries: Vec<StashDynamicBoundary>,
1030}
1031
1032impl StashGraph {
1033 #[must_use]
1038 pub fn export_sets(&self) -> Vec<ExportSet> {
1039 let mut builders = BTreeMap::<String, ExportSetBuilder>::new();
1040
1041 for declaration in &self.export_declarations {
1042 let builder = builders.entry(declaration.package.clone()).or_insert_with(|| {
1043 ExportSetBuilder::new(
1044 declaration.package.clone(),
1045 declaration.range,
1046 stash_provenance_to_fact(declaration.provenance),
1047 stash_confidence_to_fact(declaration.confidence),
1048 )
1049 });
1050 builder.absorb(declaration);
1051 }
1052
1053 builders.into_values().map(ExportSetBuilder::into_export_set).collect()
1054 }
1055
1056 #[must_use]
1062 pub fn constant_table(&self) -> ConstantTable {
1063 let mut entries = Vec::new();
1064
1065 for package in &self.packages {
1066 for slot in &package.slots {
1067 if slot.kind != GlobSlotKind::Code
1068 || slot.source != GlobSlotSource::ConstantDeclaration
1069 {
1070 continue;
1071 }
1072
1073 entries.push(ConstantTableEntry {
1074 package: package.package.clone(),
1075 name: slot.name.clone(),
1076 canonical_name: format!("{}::{}", package.package, slot.name),
1077 range: slot.range,
1078 declaration_item: slot.declaration_item,
1079 source: slot.source,
1080 provenance: slot.provenance,
1081 confidence: slot.confidence,
1082 });
1083 }
1084 }
1085
1086 ConstantTable { entries }
1087 }
1088}
1089
1090#[derive(Debug, Clone, PartialEq, Eq, Default)]
1092#[non_exhaustive]
1093pub struct ConstantTable {
1094 pub entries: Vec<ConstantTableEntry>,
1096}
1097
1098impl ConstantTable {
1099 #[must_use]
1101 pub fn is_empty(&self) -> bool {
1102 self.entries.is_empty()
1103 }
1104}
1105
1106#[derive(Debug, Clone, PartialEq, Eq)]
1108#[non_exhaustive]
1109pub struct ConstantTableEntry {
1110 pub package: String,
1112 pub name: String,
1114 pub canonical_name: String,
1116 pub range: SourceLocation,
1118 pub declaration_item: Option<HirId>,
1120 pub source: GlobSlotSource,
1122 pub provenance: StashProvenance,
1124 pub confidence: StashConfidence,
1126}
1127
1128#[derive(Debug)]
1129struct ExportSetBuilder {
1130 module_name: String,
1131 anchor_range: SourceLocation,
1132 default_exports: Vec<String>,
1133 optional_exports: Vec<String>,
1134 tags: BTreeMap<String, Vec<String>>,
1135 provenance: Provenance,
1136 confidence: Confidence,
1137}
1138
1139impl ExportSetBuilder {
1140 fn new(
1141 module_name: String,
1142 anchor_range: SourceLocation,
1143 provenance: Provenance,
1144 confidence: Confidence,
1145 ) -> Self {
1146 Self {
1147 module_name,
1148 anchor_range,
1149 default_exports: Vec::new(),
1150 optional_exports: Vec::new(),
1151 tags: BTreeMap::new(),
1152 provenance,
1153 confidence,
1154 }
1155 }
1156
1157 fn absorb(&mut self, declaration: &ExportDeclaration) {
1158 if declaration.range.start < self.anchor_range.start {
1159 self.anchor_range = declaration.range;
1160 }
1161 self.provenance =
1162 combine_provenance(self.provenance, stash_provenance_to_fact(declaration.provenance));
1163 self.confidence =
1164 combine_confidence(self.confidence, stash_confidence_to_fact(declaration.confidence));
1165
1166 match declaration.kind {
1167 ExportDeclarationKind::Default => {
1168 self.default_exports.extend(declaration.symbols.iter().cloned());
1169 }
1170 ExportDeclarationKind::Optional => {
1171 self.optional_exports.extend(declaration.symbols.iter().cloned());
1172 }
1173 ExportDeclarationKind::Tag => {
1174 if let Some(tag_name) = &declaration.tag_name {
1175 self.tags
1176 .entry(tag_name.clone())
1177 .or_default()
1178 .extend(declaration.symbols.iter().cloned());
1179 }
1180 }
1181 }
1182 }
1183
1184 fn into_export_set(mut self) -> ExportSet {
1185 sort_dedup(&mut self.default_exports);
1186 sort_dedup(&mut self.optional_exports);
1187
1188 let tags = self
1189 .tags
1190 .into_iter()
1191 .map(|(name, mut members)| {
1192 sort_dedup(&mut members);
1193 ExportTag { name, members }
1194 })
1195 .collect();
1196
1197 ExportSet {
1198 default_exports: self.default_exports,
1199 optional_exports: self.optional_exports,
1200 tags,
1201 provenance: self.provenance,
1202 confidence: self.confidence,
1203 module_name: Some(self.module_name),
1204 anchor_id: Some(AnchorId(self.anchor_range.start as u64)),
1205 }
1206 }
1207}
1208
1209fn sort_dedup(values: &mut Vec<String>) {
1210 values.sort();
1211 values.dedup();
1212}
1213
1214fn combine_provenance(current: Provenance, next: Provenance) -> Provenance {
1215 if current == Provenance::DynamicBoundary || next == Provenance::DynamicBoundary {
1216 Provenance::DynamicBoundary
1217 } else if current == Provenance::ImportExportInference
1218 || next == Provenance::ImportExportInference
1219 {
1220 Provenance::ImportExportInference
1221 } else {
1222 current
1223 }
1224}
1225
1226fn combine_confidence(current: Confidence, next: Confidence) -> Confidence {
1227 match (current, next) {
1228 (Confidence::Low, _) | (_, Confidence::Low) => Confidence::Low,
1229 (Confidence::Medium, _) | (_, Confidence::Medium) => Confidence::Medium,
1230 (Confidence::High, Confidence::High) => Confidence::High,
1231 }
1232}
1233
1234fn stash_provenance_to_fact(provenance: StashProvenance) -> Provenance {
1235 match provenance {
1236 StashProvenance::ExactAst => Provenance::ExactAst,
1237 StashProvenance::DesugaredAst => Provenance::DesugaredAst,
1238 StashProvenance::DynamicBoundary => Provenance::DynamicBoundary,
1239 }
1240}
1241
1242fn stash_confidence_to_fact(confidence: StashConfidence) -> Confidence {
1243 match confidence {
1244 StashConfidence::High => Confidence::High,
1245 StashConfidence::Medium => Confidence::Medium,
1246 StashConfidence::Low => Confidence::Low,
1247 }
1248}
1249
1250#[derive(Debug, Clone, PartialEq, Eq)]
1252#[non_exhaustive]
1253pub struct PackageStash {
1254 pub package: String,
1256 pub range: SourceLocation,
1258 pub declaration_item: Option<HirId>,
1260 pub slots: Vec<GlobSlot>,
1262 pub provenance: StashProvenance,
1264 pub confidence: StashConfidence,
1266}
1267
1268#[derive(Debug, Clone, PartialEq, Eq)]
1270#[non_exhaustive]
1271pub struct GlobSlot {
1272 pub name: String,
1274 pub kind: GlobSlotKind,
1276 pub range: SourceLocation,
1278 pub declaration_item: Option<HirId>,
1280 pub source: GlobSlotSource,
1282 pub alias_target: Option<String>,
1284 pub provenance: StashProvenance,
1286 pub confidence: StashConfidence,
1288}
1289
1290#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1292#[non_exhaustive]
1293pub enum GlobSlotKind {
1294 Scalar,
1296 Array,
1298 Hash,
1300 Code,
1302 Io,
1304 Format,
1306}
1307
1308#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1310#[non_exhaustive]
1311pub enum GlobSlotSource {
1312 PackageDeclaration,
1314 SubDeclaration,
1316 MethodDeclaration,
1318 OurDeclaration,
1320 FormatDeclaration,
1322 ConstantDeclaration,
1324 PackageAssignment,
1326 TypeglobAlias,
1328}
1329
1330#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1332#[non_exhaustive]
1333pub enum StashProvenance {
1334 ExactAst,
1336 DesugaredAst,
1338 DynamicBoundary,
1340}
1341
1342#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1344#[non_exhaustive]
1345pub enum StashConfidence {
1346 High,
1348 Medium,
1350 Low,
1352}
1353
1354#[derive(Debug, Clone, PartialEq, Eq)]
1356#[non_exhaustive]
1357pub struct PackageInheritanceEdge {
1358 pub from_package: String,
1360 pub to_package: String,
1362 pub range: SourceLocation,
1364 pub declaration_item: Option<HirId>,
1366 pub source: InheritanceSource,
1368 pub provenance: StashProvenance,
1370 pub confidence: StashConfidence,
1372}
1373
1374#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1376#[non_exhaustive]
1377pub enum InheritanceSource {
1378 IsaAssignment,
1380 UseParent,
1382 UseBase,
1384}
1385
1386#[derive(Debug, Clone, PartialEq, Eq)]
1388#[non_exhaustive]
1389pub struct ExportDeclaration {
1390 pub package: String,
1392 pub kind: ExportDeclarationKind,
1394 pub tag_name: Option<String>,
1396 pub symbols: Vec<String>,
1398 pub range: SourceLocation,
1400 pub declaration_item: Option<HirId>,
1402 pub provenance: StashProvenance,
1404 pub confidence: StashConfidence,
1406}
1407
1408#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1410#[non_exhaustive]
1411pub enum ExportDeclarationKind {
1412 Default,
1414 Optional,
1416 Tag,
1418}
1419
1420#[derive(Debug, Clone, PartialEq, Eq)]
1422#[non_exhaustive]
1423pub struct StashDynamicBoundary {
1424 pub package: Option<String>,
1426 pub symbol: Option<String>,
1428 pub range: SourceLocation,
1430 pub boundary_item: Option<HirId>,
1432 pub kind: StashDynamicBoundaryKind,
1434 pub reason: String,
1436 pub provenance: StashProvenance,
1438 pub confidence: StashConfidence,
1440}
1441
1442#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1444#[non_exhaustive]
1445pub enum StashDynamicBoundaryKind {
1446 DynamicStashMutation,
1448 DynamicExportDeclaration,
1450 Autoload,
1452}
1453
1454#[derive(Debug, Clone, PartialEq, Eq, Default)]
1460#[non_exhaustive]
1461pub struct CompileEnvironment {
1462 pub directives: Vec<CompileDirective>,
1464 pub pragma_effects: Vec<PragmaEffect>,
1466 pub pragma_state_facts: Vec<PragmaStateFact>,
1468 pub inc_roots: Vec<IncRootFact>,
1470 pub module_requests: Vec<ModuleRequest>,
1472 pub phase_blocks: Vec<CompilePhaseBlock>,
1474 pub dynamic_boundaries: Vec<CompileEnvironmentBoundary>,
1476}
1477
1478impl CompileEnvironment {
1479 #[must_use]
1481 pub fn pragma_state_facts(&self) -> &[PragmaStateFact] {
1482 &self.pragma_state_facts
1483 }
1484
1485 #[must_use]
1487 pub fn pragma_state_at(&self, offset: usize) -> Option<&PragmaStateFact> {
1488 let idx = self.pragma_state_facts.partition_point(|fact| fact.range.start <= offset);
1489 if idx > 0 { self.pragma_state_facts.get(idx - 1) } else { None }
1490 }
1491
1492 #[must_use]
1497 pub fn import_specs(&self, file_id: FileId) -> Vec<ImportSpec> {
1498 self.directives
1499 .iter()
1500 .filter_map(|directive| import_spec_from_directive(directive, file_id))
1501 .collect()
1502 }
1503
1504 #[must_use]
1512 pub fn module_resolution_candidates(
1513 &self,
1514 supplied_roots: &[ModuleResolutionRoot],
1515 ) -> Vec<ModuleResolutionCandidate> {
1516 self.module_requests
1517 .iter()
1518 .enumerate()
1519 .filter_map(|(request_index, request)| {
1520 let target = request.target.as_ref()?;
1521 let normalized_target = normalize_module_target(target);
1522 let relative_path = module_target_to_relative_path(&normalized_target)?;
1523 let candidate_roots =
1524 self.candidate_roots_for_request(request, &relative_path, supplied_roots);
1525 let status = if candidate_roots.is_empty() {
1526 ModuleResolutionCandidateStatus::NotFound
1527 } else {
1528 ModuleResolutionCandidateStatus::CandidateBuilt
1529 };
1530
1531 Some(ModuleResolutionCandidate {
1532 request_index,
1533 directive_item: request.directive_item,
1534 request_kind: request.kind,
1535 target: normalized_target,
1536 relative_path,
1537 roots: candidate_roots,
1538 status,
1539 resolved_path: None,
1540 range: request.range,
1541 package_context: request.package_context.clone(),
1542 provenance: request.provenance,
1543 confidence: request.confidence,
1544 })
1545 })
1546 .collect()
1547 }
1548
1549 #[must_use]
1555 pub fn resolved_module_resolution_candidates(
1556 &self,
1557 supplied_roots: &[ModuleResolutionRoot],
1558 mut path_exists: impl FnMut(&str) -> bool,
1559 ) -> Vec<ModuleResolutionCandidate> {
1560 let mut candidates = self.module_resolution_candidates(supplied_roots);
1561
1562 for candidate in &mut candidates {
1563 if candidate.status != ModuleResolutionCandidateStatus::CandidateBuilt {
1564 continue;
1565 }
1566
1567 if let Some(root) =
1568 candidate.roots.iter().find(|root| path_exists(&root.candidate_path))
1569 {
1570 candidate.status = ModuleResolutionCandidateStatus::Resolved;
1571 candidate.resolved_path = Some(root.candidate_path.clone());
1572 } else {
1573 candidate.status = ModuleResolutionCandidateStatus::NotFound;
1574 }
1575 }
1576
1577 candidates
1578 }
1579
1580 fn candidate_roots_for_request(
1581 &self,
1582 request: &ModuleRequest,
1583 relative_path: &str,
1584 supplied_roots: &[ModuleResolutionRoot],
1585 ) -> Vec<ModuleResolutionCandidateRoot> {
1586 let active_lexical_roots = self.active_lexical_roots_for_request(request);
1587 active_lexical_roots
1588 .iter()
1589 .map(|root| ModuleResolutionRoot {
1590 path: root.path.clone(),
1591 kind: root.kind,
1592 source: root.source.clone(),
1593 })
1594 .chain(supplied_roots.iter().cloned())
1595 .enumerate()
1596 .map(|(precedence, root)| ModuleResolutionCandidateRoot {
1597 path: root.path.clone(),
1598 kind: root.kind,
1599 source: root.source,
1600 candidate_path: join_candidate_path(&root.path, relative_path),
1601 precedence,
1602 })
1603 .collect()
1604 }
1605
1606 fn active_lexical_roots_for_request(&self, request: &ModuleRequest) -> Vec<ActiveLexicalRoot> {
1607 let mut active = Vec::new();
1608
1609 for (order, root) in self.inc_roots.iter().enumerate() {
1610 if root.range.start > request.range.start {
1611 continue;
1612 }
1613 if root.kind != IncRootKind::UseLib {
1614 continue;
1615 }
1616
1617 match root.action {
1618 IncRootAction::Add => {
1619 active.push(ActiveLexicalRoot {
1620 path: root.path.clone(),
1621 kind: root.kind,
1622 source: "use-lib-lexical".to_string(),
1623 range_start: root.range.start,
1624 order,
1625 });
1626 }
1627 IncRootAction::Remove => {
1628 active.retain(|active_root| active_root.path != root.path);
1629 }
1630 }
1631 }
1632
1633 active.sort_by(|left, right| {
1634 right.range_start.cmp(&left.range_start).then_with(|| left.order.cmp(&right.order))
1635 });
1636
1637 active
1638 }
1639}
1640
1641#[derive(Debug, Clone, PartialEq, Eq)]
1642struct ActiveLexicalRoot {
1643 path: String,
1644 kind: IncRootKind,
1645 source: String,
1646 range_start: usize,
1647 order: usize,
1648}
1649
1650#[derive(Debug, Clone, PartialEq, Eq)]
1652#[non_exhaustive]
1653pub struct CompileDirective {
1654 pub action: CompileDirectiveAction,
1656 pub module: Option<String>,
1658 pub args: Vec<String>,
1660 pub range: SourceLocation,
1662 pub item_id: Option<HirId>,
1664 pub scope_id: Option<HirScopeId>,
1666 pub package_context: Option<String>,
1668 pub kind: CompileDirectiveKind,
1670 pub provenance: CompileProvenance,
1672 pub confidence: CompileConfidence,
1674}
1675
1676#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1678#[non_exhaustive]
1679pub enum CompileDirectiveAction {
1680 Use,
1682 No,
1684 Require,
1686}
1687
1688#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1690#[non_exhaustive]
1691pub enum CompileDirectiveKind {
1692 Strict,
1694 Warnings,
1696 Feature,
1698 Lib,
1700 Inheritance,
1702 Constant,
1704 Module,
1706 Dynamic,
1708}
1709
1710#[derive(Debug, Clone, PartialEq, Eq)]
1712#[non_exhaustive]
1713pub struct PragmaEffect {
1714 pub pragma: String,
1716 pub enabled: bool,
1718 pub args: Vec<String>,
1720 pub argument_kind: PragmaArgumentKind,
1722 pub range: SourceLocation,
1724 pub directive_item: Option<HirId>,
1726 pub scope_id: Option<HirScopeId>,
1728 pub package_context: Option<String>,
1730 pub provenance: CompileProvenance,
1732 pub confidence: CompileConfidence,
1734}
1735
1736#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1738#[non_exhaustive]
1739pub enum PragmaArgumentKind {
1740 Broad,
1742 Categories,
1744}
1745
1746#[derive(Debug, Clone, PartialEq, Eq)]
1748#[non_exhaustive]
1749pub struct PragmaStateFact {
1750 pub range: SourceLocation,
1752 pub anchor_id: AnchorId,
1754 pub directive_item: Option<HirId>,
1756 pub scope_id: Option<HirScopeId>,
1758 pub package_context: Option<String>,
1760 pub strict_vars: bool,
1762 pub strict_subs: bool,
1764 pub strict_refs: bool,
1766 pub warnings: bool,
1768 pub disabled_warning_categories: Vec<String>,
1770 pub features: Vec<String>,
1772 pub provenance: CompileProvenance,
1774 pub confidence: CompileConfidence,
1776}
1777
1778impl PragmaStateFact {
1779 #[must_use]
1781 pub fn strict_enabled(&self) -> bool {
1782 self.strict_vars && self.strict_subs && self.strict_refs
1783 }
1784
1785 #[must_use]
1787 pub fn warning_active(&self, category: &str) -> bool {
1788 self.warnings && !self.disabled_warning_categories.iter().any(|name| name == category)
1789 }
1790
1791 #[must_use]
1793 pub fn has_feature(&self, feature: &str) -> bool {
1794 self.features.iter().any(|name| name == feature)
1795 }
1796}
1797
1798#[derive(Debug, Clone, PartialEq, Eq)]
1804#[non_exhaustive]
1805pub struct FrameworkAdapterRegistry {
1806 adapters: Vec<FrameworkAdapterKind>,
1807}
1808
1809impl Default for FrameworkAdapterRegistry {
1810 fn default() -> Self {
1811 Self { adapters: vec![FrameworkAdapterKind::ExporterFamily] }
1812 }
1813}
1814
1815impl FrameworkAdapterRegistry {
1816 #[must_use]
1818 pub fn new(adapters: Vec<FrameworkAdapterKind>) -> Self {
1819 Self { adapters }
1820 }
1821
1822 #[must_use]
1824 pub fn adapters(&self) -> &[FrameworkAdapterKind] {
1825 &self.adapters
1826 }
1827
1828 #[must_use]
1830 pub fn project_file(&self, file: &HirFile) -> FrameworkFactGraph {
1831 let mut graph = FrameworkFactGraph::default();
1832
1833 for adapter in &self.adapters {
1834 match adapter {
1835 FrameworkAdapterKind::ExporterFamily => {
1836 project_exporter_family_facts(file, &mut graph);
1837 }
1838 }
1839 }
1840
1841 graph
1842 }
1843}
1844
1845#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1847#[non_exhaustive]
1848pub enum FrameworkAdapterKind {
1849 ExporterFamily,
1851}
1852
1853#[derive(Debug, Clone, PartialEq, Eq, Default)]
1855#[non_exhaustive]
1856pub struct FrameworkFactGraph {
1857 pub exported_symbols: Vec<FrameworkExportedSymbolFact>,
1859 pub dynamic_boundaries: Vec<FrameworkDynamicBoundaryFact>,
1861}
1862
1863#[derive(Debug, Clone, PartialEq, Eq)]
1865#[non_exhaustive]
1866pub struct FrameworkExportedSymbolFact {
1867 pub adapter: FrameworkAdapterKind,
1869 pub package: String,
1871 pub name: String,
1873 pub kind: FrameworkExportedSymbolKind,
1875 pub tag_name: Option<String>,
1877 pub range: SourceLocation,
1879 pub declaration_item: Option<HirId>,
1881 pub visible_symbol: VisibleSymbol,
1883 pub source_provenance: Provenance,
1885 pub source_confidence: Confidence,
1887 pub provenance: Provenance,
1889 pub confidence: Confidence,
1891}
1892
1893#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1895#[non_exhaustive]
1896pub enum FrameworkExportedSymbolKind {
1897 Default,
1899 Optional,
1901 TagMember,
1903}
1904
1905#[derive(Debug, Clone, PartialEq, Eq)]
1907#[non_exhaustive]
1908pub struct FrameworkDynamicBoundaryFact {
1909 pub adapter: FrameworkAdapterKind,
1911 pub package: Option<String>,
1913 pub symbol: Option<String>,
1915 pub range: SourceLocation,
1917 pub boundary_item: Option<HirId>,
1919 pub kind: StashDynamicBoundaryKind,
1921 pub reason: String,
1923 pub provenance: Provenance,
1925 pub confidence: Confidence,
1927}
1928
1929fn project_exporter_family_facts(file: &HirFile, graph: &mut FrameworkFactGraph) {
1930 for declaration in &file.stash_graph.export_declarations {
1931 match declaration.kind {
1932 ExportDeclarationKind::Default => {
1933 project_exported_symbols_from_declaration(
1934 declaration,
1935 FrameworkExportedSymbolKind::Default,
1936 None,
1937 graph,
1938 );
1939 }
1940 ExportDeclarationKind::Optional => {
1941 project_exported_symbols_from_declaration(
1942 declaration,
1943 FrameworkExportedSymbolKind::Optional,
1944 None,
1945 graph,
1946 );
1947 }
1948 ExportDeclarationKind::Tag => {
1949 project_exported_symbols_from_declaration(
1950 declaration,
1951 FrameworkExportedSymbolKind::TagMember,
1952 declaration.tag_name.as_deref(),
1953 graph,
1954 );
1955 }
1956 }
1957 }
1958
1959 for boundary in &file.stash_graph.dynamic_boundaries {
1960 if boundary.kind != StashDynamicBoundaryKind::DynamicExportDeclaration {
1961 continue;
1962 }
1963 graph.dynamic_boundaries.push(FrameworkDynamicBoundaryFact {
1964 adapter: FrameworkAdapterKind::ExporterFamily,
1965 package: boundary.package.clone(),
1966 symbol: boundary.symbol.clone(),
1967 range: boundary.range,
1968 boundary_item: boundary.boundary_item,
1969 kind: boundary.kind,
1970 reason: boundary.reason.clone(),
1971 provenance: Provenance::DynamicBoundary,
1972 confidence: Confidence::Low,
1973 });
1974 }
1975}
1976
1977fn project_exported_symbols_from_declaration(
1978 declaration: &ExportDeclaration,
1979 kind: FrameworkExportedSymbolKind,
1980 tag_name: Option<&str>,
1981 graph: &mut FrameworkFactGraph,
1982) {
1983 for symbol in &declaration.symbols {
1984 graph.exported_symbols.push(FrameworkExportedSymbolFact {
1985 adapter: FrameworkAdapterKind::ExporterFamily,
1986 package: declaration.package.clone(),
1987 name: symbol.clone(),
1988 kind,
1989 tag_name: tag_name.map(str::to_string),
1990 range: declaration.range,
1991 declaration_item: declaration.declaration_item,
1992 visible_symbol: visible_symbol_for_export(declaration, symbol, kind),
1993 source_provenance: stash_provenance_to_fact(declaration.provenance),
1994 source_confidence: stash_confidence_to_fact(declaration.confidence),
1995 provenance: Provenance::FrameworkSynthesis,
1996 confidence: Confidence::Medium,
1997 });
1998 }
1999}
2000
2001fn visible_symbol_for_export(
2002 declaration: &ExportDeclaration,
2003 symbol: &str,
2004 kind: FrameworkExportedSymbolKind,
2005) -> VisibleSymbol {
2006 let source = match kind {
2007 FrameworkExportedSymbolKind::Default => VisibleSymbolSource::DefaultExport,
2008 FrameworkExportedSymbolKind::Optional => VisibleSymbolSource::ExplicitImport,
2009 FrameworkExportedSymbolKind::TagMember => VisibleSymbolSource::ExportTag,
2010 };
2011
2012 VisibleSymbol {
2013 name: symbol.to_string(),
2014 entity_id: None,
2015 source,
2016 confidence: Confidence::Medium,
2017 context: Some(VisibleSymbolContext::new(
2018 Some(declaration.package.clone()),
2019 None,
2020 Some(AnchorId(declaration.range.start as u64)),
2021 )),
2022 }
2023}
2024
2025#[derive(Debug, Clone, PartialEq, Eq)]
2027#[non_exhaustive]
2028pub struct IncRootFact {
2029 pub path: String,
2031 pub action: IncRootAction,
2033 pub kind: IncRootKind,
2035 pub range: SourceLocation,
2037 pub directive_item: Option<HirId>,
2039 pub scope_id: Option<HirScopeId>,
2041 pub package_context: Option<String>,
2043 pub provenance: CompileProvenance,
2045 pub confidence: CompileConfidence,
2047}
2048
2049#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2051#[non_exhaustive]
2052pub enum IncRootAction {
2053 Add,
2055 Remove,
2057}
2058
2059#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2061#[non_exhaustive]
2062pub enum IncRootKind {
2063 UseLib,
2065 Configured,
2067 Perl5Lib,
2069 SystemInc,
2071}
2072
2073#[derive(Debug, Clone, PartialEq, Eq)]
2075#[non_exhaustive]
2076pub struct ModuleResolutionRoot {
2077 pub path: String,
2079 pub kind: IncRootKind,
2081 pub source: String,
2083}
2084
2085impl ModuleResolutionRoot {
2086 #[must_use]
2088 pub fn new(path: impl Into<String>, kind: IncRootKind, source: impl Into<String>) -> Self {
2089 Self { path: path.into(), kind, source: source.into() }
2090 }
2091}
2092
2093#[derive(Debug, Clone, PartialEq, Eq)]
2095#[non_exhaustive]
2096pub struct ModuleRequest {
2097 pub target: Option<String>,
2099 pub kind: ModuleRequestKind,
2101 pub range: SourceLocation,
2103 pub directive_item: Option<HirId>,
2105 pub scope_id: Option<HirScopeId>,
2107 pub package_context: Option<String>,
2109 pub resolution: ModuleResolutionStatus,
2111 pub provenance: CompileProvenance,
2113 pub confidence: CompileConfidence,
2115}
2116
2117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2119#[non_exhaustive]
2120pub enum ModuleRequestKind {
2121 Use,
2123 Require,
2125 Parent,
2127 Base,
2129}
2130
2131#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2133#[non_exhaustive]
2134pub enum ModuleResolutionStatus {
2135 Deferred,
2137 Dynamic,
2139}
2140
2141#[derive(Debug, Clone, PartialEq, Eq)]
2143#[non_exhaustive]
2144pub struct ModuleResolutionCandidate {
2145 pub request_index: usize,
2147 pub directive_item: Option<HirId>,
2149 pub request_kind: ModuleRequestKind,
2151 pub target: String,
2153 pub relative_path: String,
2155 pub roots: Vec<ModuleResolutionCandidateRoot>,
2157 pub status: ModuleResolutionCandidateStatus,
2159 pub resolved_path: Option<String>,
2161 pub range: SourceLocation,
2163 pub package_context: Option<String>,
2165 pub provenance: CompileProvenance,
2167 pub confidence: CompileConfidence,
2169}
2170
2171impl ModuleResolutionCandidate {
2172 #[must_use]
2180 pub fn cache_key(&self, resolver_epoch: u64) -> ModuleResolutionCacheKey {
2181 ModuleResolutionCacheKey {
2182 resolver_epoch,
2183 request_index: self.request_index,
2184 directive_item: self.directive_item,
2185 request_kind: self.request_kind,
2186 target: self.target.clone(),
2187 relative_path: self.relative_path.clone(),
2188 roots: self
2189 .roots
2190 .iter()
2191 .map(ModuleResolutionCacheRootKey::from_candidate_root)
2192 .collect(),
2193 range: self.range,
2194 package_context: self.package_context.clone(),
2195 }
2196 }
2197
2198 #[must_use]
2203 pub fn cache_invalidation(
2204 &self,
2205 resolver_epoch: u64,
2206 mut path_exists: impl FnMut(&str) -> bool,
2207 ) -> ModuleResolutionCacheInvalidation {
2208 let path_states = self
2209 .roots
2210 .iter()
2211 .map(|root| ModuleResolutionCandidatePathState {
2212 candidate_path: root.candidate_path.clone(),
2213 exists: path_exists(&root.candidate_path),
2214 })
2215 .collect();
2216
2217 ModuleResolutionCacheInvalidation { key: self.cache_key(resolver_epoch), path_states }
2218 }
2219}
2220
2221#[derive(Debug, Clone, PartialEq, Eq)]
2223#[non_exhaustive]
2224pub struct ModuleResolutionCandidateRoot {
2225 pub path: String,
2227 pub kind: IncRootKind,
2229 pub source: String,
2231 pub candidate_path: String,
2233 pub precedence: usize,
2235}
2236
2237#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2239#[non_exhaustive]
2240pub struct ModuleResolutionCacheKey {
2241 pub resolver_epoch: u64,
2243 pub request_index: usize,
2245 pub directive_item: Option<HirId>,
2247 pub request_kind: ModuleRequestKind,
2249 pub target: String,
2251 pub relative_path: String,
2253 pub roots: Vec<ModuleResolutionCacheRootKey>,
2255 pub range: SourceLocation,
2257 pub package_context: Option<String>,
2259}
2260
2261#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2263#[non_exhaustive]
2264pub struct ModuleResolutionCacheRootKey {
2265 pub path: String,
2267 pub kind: IncRootKind,
2269 pub source: String,
2271 pub candidate_path: String,
2273 pub precedence: usize,
2275}
2276
2277impl ModuleResolutionCacheRootKey {
2278 fn from_candidate_root(root: &ModuleResolutionCandidateRoot) -> Self {
2279 Self {
2280 path: root.path.clone(),
2281 kind: root.kind,
2282 source: root.source.clone(),
2283 candidate_path: root.candidate_path.clone(),
2284 precedence: root.precedence,
2285 }
2286 }
2287}
2288
2289#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2291#[non_exhaustive]
2292pub struct ModuleResolutionCandidatePathState {
2293 pub candidate_path: String,
2295 pub exists: bool,
2297}
2298
2299#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2301#[non_exhaustive]
2302pub struct ModuleResolutionCacheInvalidation {
2303 pub key: ModuleResolutionCacheKey,
2305 pub path_states: Vec<ModuleResolutionCandidatePathState>,
2307}
2308
2309#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2311#[non_exhaustive]
2312pub enum ModuleResolutionCandidateStatus {
2313 CandidateBuilt,
2315 Dynamic,
2317 NotFound,
2319 Resolved,
2321 TimedOut,
2323}
2324
2325#[derive(Debug, Clone, PartialEq, Eq)]
2327#[non_exhaustive]
2328pub struct CompilePhaseBlock {
2329 pub phase: CompilePhase,
2331 pub range: SourceLocation,
2333 pub scope_id: Option<HirScopeId>,
2335 pub package_context: Option<String>,
2337 pub provenance: CompileProvenance,
2339 pub confidence: CompileConfidence,
2341}
2342
2343#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2345#[non_exhaustive]
2346pub enum CompilePhase {
2347 Begin,
2349 UnitCheck,
2351 Check,
2353 Init,
2355 End,
2357 Unknown,
2359}
2360
2361#[derive(Debug, Clone, PartialEq, Eq)]
2363#[non_exhaustive]
2364pub struct CompileEnvironmentBoundary {
2365 pub kind: CompileEnvironmentBoundaryKind,
2367 pub range: SourceLocation,
2369 pub boundary_item: Option<HirId>,
2371 pub scope_id: Option<HirScopeId>,
2373 pub package_context: Option<String>,
2375 pub reason: String,
2377 pub provenance: CompileProvenance,
2379 pub confidence: CompileConfidence,
2381}
2382
2383#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2385#[non_exhaustive]
2386pub enum CompileEnvironmentBoundaryKind {
2387 DynamicRequire,
2389 DynamicPragmaArgs,
2391 DynamicIncRoot,
2393 PhaseBlockExecution,
2395 SymbolicReferenceDeref,
2397}
2398
2399#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2401#[non_exhaustive]
2402pub enum CompileProvenance {
2403 ExactAst,
2405 DesugaredAst,
2407 DynamicBoundary,
2409}
2410
2411#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2413#[non_exhaustive]
2414pub enum CompileConfidence {
2415 High,
2417 Medium,
2419 Low,
2421}
2422
2423fn import_spec_from_directive(directive: &CompileDirective, file_id: FileId) -> Option<ImportSpec> {
2424 match directive.action {
2425 CompileDirectiveAction::Use => {
2426 let module = directive.module.as_deref()?;
2427 if is_version_pragma(module) {
2428 return None;
2429 }
2430 if module == "constant" {
2431 return Some(classify_constant_import(directive, file_id));
2432 }
2433
2434 let (kind, symbols, provenance, confidence) =
2435 classify_import_args(&directive.args, module, directive.range);
2436 Some(import_spec(
2437 module.to_string(),
2438 kind,
2439 symbols,
2440 provenance,
2441 confidence,
2442 directive,
2443 file_id,
2444 ))
2445 }
2446 CompileDirectiveAction::Require => {
2447 let (module, kind, symbols, provenance, confidence) =
2448 if let Some(module) = directive.module.as_ref() {
2449 (
2450 module.clone(),
2451 ImportKind::Require,
2452 ImportSymbols::Default,
2453 Provenance::ExactAst,
2454 Confidence::High,
2455 )
2456 } else {
2457 (
2458 String::new(),
2459 ImportKind::DynamicRequire,
2460 ImportSymbols::Dynamic,
2461 Provenance::DynamicBoundary,
2462 Confidence::Low,
2463 )
2464 };
2465 Some(import_spec(module, kind, symbols, provenance, confidence, directive, file_id))
2466 }
2467 CompileDirectiveAction::No => None,
2468 }
2469}
2470
2471fn import_spec(
2472 module: String,
2473 kind: ImportKind,
2474 symbols: ImportSymbols,
2475 provenance: Provenance,
2476 confidence: Confidence,
2477 directive: &CompileDirective,
2478 file_id: FileId,
2479) -> ImportSpec {
2480 ImportSpec {
2481 module,
2482 kind,
2483 symbols,
2484 provenance,
2485 confidence,
2486 file_id: Some(file_id),
2487 anchor_id: Some(AnchorId(directive.range.start as u64)),
2488 scope_id: directive.scope_id.map(|id| ScopeId(id.index() as u64)),
2489 span_start_byte: Some(directive.range.start as u32),
2490 }
2491}
2492
2493fn classify_import_args(
2494 args: &[String],
2495 module: &str,
2496 range: SourceLocation,
2497) -> (ImportKind, ImportSymbols, Provenance, Confidence) {
2498 if args.is_empty() {
2499 let bare_len = "use ".len() + module.len() + 1;
2500 let span_len = range.end.saturating_sub(range.start);
2501 if span_len > bare_len {
2502 return (
2503 ImportKind::UseEmpty,
2504 ImportSymbols::None,
2505 Provenance::ExactAst,
2506 Confidence::High,
2507 );
2508 }
2509 return (ImportKind::Use, ImportSymbols::Default, Provenance::ExactAst, Confidence::High);
2510 }
2511
2512 let mut explicit_names = Vec::new();
2513 let mut tags = Vec::new();
2514 let mut has_dynamic_arg = false;
2515
2516 for arg in args {
2517 let trimmed = arg.trim();
2518 if trimmed == "=>" || trimmed == "," || trimmed == "\\" {
2519 continue;
2520 }
2521
2522 if let Some(inner) = parse_qw_content(trimmed) {
2523 collect_qw_import_words(inner, &mut explicit_names, &mut tags);
2524 continue;
2525 }
2526
2527 let was_quoted = is_quoted(trimmed);
2528 let unquoted = unquote(trimmed);
2529 if !was_quoted && looks_like_dynamic_import_arg(unquoted) {
2530 has_dynamic_arg = true;
2531 continue;
2532 }
2533
2534 if let Some(tag) = unquoted.strip_prefix(':') {
2535 tags.push(tag.to_string());
2536 continue;
2537 }
2538
2539 if looks_like_symbol_name(unquoted) {
2540 explicit_names.push(unquoted.to_string());
2541 }
2542 }
2543
2544 if has_dynamic_arg {
2545 return (
2546 ImportKind::UseExplicitList,
2547 ImportSymbols::Dynamic,
2548 Provenance::DynamicBoundary,
2549 Confidence::Low,
2550 );
2551 }
2552
2553 if !tags.is_empty() && explicit_names.is_empty() {
2554 return (
2555 ImportKind::UseTag,
2556 ImportSymbols::Tags(tags),
2557 Provenance::ExactAst,
2558 Confidence::High,
2559 );
2560 }
2561
2562 if !tags.is_empty() && !explicit_names.is_empty() {
2563 return (
2564 ImportKind::UseExplicitList,
2565 ImportSymbols::Mixed { tags, names: explicit_names },
2566 Provenance::ExactAst,
2567 Confidence::High,
2568 );
2569 }
2570
2571 if !explicit_names.is_empty() {
2572 return (
2573 ImportKind::UseExplicitList,
2574 ImportSymbols::Explicit(explicit_names),
2575 Provenance::ExactAst,
2576 Confidence::High,
2577 );
2578 }
2579
2580 (ImportKind::UseEmpty, ImportSymbols::None, Provenance::ExactAst, Confidence::High)
2581}
2582
2583fn classify_constant_import(directive: &CompileDirective, file_id: FileId) -> ImportSpec {
2584 let mut constant_names = Vec::new();
2585 let args = &directive.args;
2586
2587 if args.first().map(String::as_str) == Some("{") {
2588 let mut index = 1;
2589 while index < args.len() {
2590 let token = args[index].trim();
2591 if token == "}" || token == "=>" || token == "," {
2592 index += 1;
2593 continue;
2594 }
2595 if index + 1 < args.len() && args[index + 1].trim() == "=>" {
2596 constant_names.push(unquote(token).to_string());
2597 index += 3;
2598 } else {
2599 index += 1;
2600 }
2601 }
2602 } else if let Some(inner) = args.first().and_then(|arg| parse_qw_content(arg.trim())) {
2603 constant_names.extend(inner.split_whitespace().map(str::to_string));
2604 } else if let Some(name) = args.first() {
2605 let trimmed = unquote(name.trim());
2606 if looks_like_constant_name(trimmed) {
2607 constant_names.push(trimmed.to_string());
2608 }
2609 }
2610
2611 let mut seen = std::collections::HashSet::new();
2612 constant_names.retain(|name| seen.insert(name.clone()));
2613
2614 let symbols = if constant_names.is_empty() {
2615 ImportSymbols::None
2616 } else {
2617 ImportSymbols::Explicit(constant_names)
2618 };
2619
2620 import_spec(
2621 "constant".to_string(),
2622 ImportKind::UseConstant,
2623 symbols,
2624 Provenance::ExactAst,
2625 Confidence::High,
2626 directive,
2627 file_id,
2628 )
2629}
2630
2631fn collect_qw_import_words(inner: &str, explicit_names: &mut Vec<String>, tags: &mut Vec<String>) {
2632 for word in inner.split_whitespace() {
2633 if let Some(tag) = word.strip_prefix(':') {
2634 tags.push(tag.to_string());
2635 } else {
2636 explicit_names.push(word.to_string());
2637 }
2638 }
2639}
2640
2641fn is_version_pragma(module: &str) -> bool {
2642 if module.chars().next().is_some_and(|character| character.is_ascii_digit()) {
2643 return true;
2644 }
2645 module.starts_with('v')
2646 && module.len() > 1
2647 && module[1..].chars().all(|character| character.is_ascii_digit() || character == '.')
2648}
2649
2650fn parse_qw_content(value: &str) -> Option<&str> {
2651 crate::syntax::quote::parse_quote_operator_content(value, "qw")
2652}
2653
2654fn is_quoted(value: &str) -> bool {
2655 (value.starts_with('\'') && value.ends_with('\''))
2656 || (value.starts_with('"') && value.ends_with('"'))
2657}
2658
2659fn unquote(value: &str) -> &str {
2660 if is_quoted(value) && value.len() >= 2 { &value[1..value.len() - 1] } else { value }
2661}
2662
2663fn looks_like_dynamic_import_arg(value: &str) -> bool {
2664 value.starts_with('$')
2665 || value.starts_with('@')
2666 || value.starts_with('%')
2667 || value.starts_with('&')
2668 || value.starts_with('*')
2669}
2670
2671fn looks_like_symbol_name(value: &str) -> bool {
2672 let value = unquote(value);
2673 if value.is_empty() {
2674 return false;
2675 }
2676 if value.starts_with(':') {
2677 return true;
2678 }
2679 value
2680 .chars()
2681 .next()
2682 .is_some_and(|character| character.is_ascii_alphabetic() || character == '_')
2683}
2684
2685fn looks_like_constant_name(value: &str) -> bool {
2686 if value.is_empty() {
2687 return false;
2688 }
2689 value
2690 .chars()
2691 .next()
2692 .is_some_and(|character| character.is_ascii_alphabetic() || character == '_')
2693}
2694
2695fn module_target_to_relative_path(target: &str) -> Option<String> {
2696 let relative_path =
2697 if target.ends_with(".pm") || target.ends_with(".pl") || target.contains(['/', '\\']) {
2698 target.replace('\\', "/")
2699 } else {
2700 let canonical = target.replace('\'', "::");
2701 format!("{}.pm", canonical.replace("::", "/"))
2702 };
2703
2704 is_safe_relative_module_path(&relative_path).then_some(relative_path)
2705}
2706
2707fn normalize_module_target(target: &str) -> String {
2708 target.trim().trim_matches('"').trim_matches('\'').to_string()
2709}
2710
2711fn is_safe_relative_module_path(path: &str) -> bool {
2712 if path.is_empty() || path.starts_with('/') || path.contains(':') {
2713 return false;
2714 }
2715
2716 path.split('/').all(|segment| !matches!(segment, "" | "." | ".."))
2717}
2718
2719fn join_candidate_path(root: &str, relative_path: &str) -> String {
2720 let normalized_root = root.replace('\\', "/");
2721 let trimmed_root = normalized_root.trim_end_matches('/');
2722 if trimmed_root.is_empty() {
2723 relative_path.to_string()
2724 } else {
2725 format!("{trimmed_root}/{relative_path}")
2726 }
2727}
2728
2729#[derive(Debug, Clone, PartialEq, Eq)]
2731#[non_exhaustive]
2732pub enum HirKind {
2733 PackageDecl(PackageDecl),
2735 SubDecl(SubDecl),
2737 MethodDecl(MethodDecl),
2739 UseDecl(UseDecl),
2741 RequireDecl(RequireDecl),
2743 VariableDecl(VariableDecl),
2745 CallExpr(CallExpr),
2747 MethodCallExpr(MethodCallExpr),
2749 IndirectCallExpr(IndirectCallExpr),
2751 BarewordExpr(BarewordExpr),
2753 LiteralExpr(LiteralExpr),
2755 BlockShell(BlockShell),
2757 BranchShell(BranchShell),
2759 LoopShell(LoopShell),
2761 ControlTransfer(ControlTransfer),
2763 StatementModifierShell(StatementModifierShell),
2765 DynamicBoundary(DynamicBoundary),
2767}
2768
2769impl HirKind {
2770 pub const ALL_KIND_NAMES: &[&'static str] = &[
2775 "BarewordExpr",
2776 "BlockShell",
2777 "BranchShell",
2778 "CallExpr",
2779 "ControlTransfer",
2780 "DynamicBoundary",
2781 "IndirectCallExpr",
2782 "LiteralExpr",
2783 "LoopShell",
2784 "MethodCallExpr",
2785 "MethodDecl",
2786 "PackageDecl",
2787 "RequireDecl",
2788 "StatementModifierShell",
2789 "SubDecl",
2790 "UseDecl",
2791 "VariableDecl",
2792 ];
2793}
2794
2795#[derive(Debug, Clone, PartialEq, Eq)]
2797#[non_exhaustive]
2798pub struct PackageDecl {
2799 pub name: String,
2801 pub name_range: SourceLocation,
2803 pub has_block: bool,
2805}
2806
2807#[derive(Debug, Clone, PartialEq, Eq)]
2809#[non_exhaustive]
2810pub struct SubDecl {
2811 pub name: Option<String>,
2813 pub name_range: Option<SourceLocation>,
2815 pub has_prototype: bool,
2817 pub has_signature: bool,
2819 pub attribute_count: usize,
2821}
2822
2823#[derive(Debug, Clone, PartialEq, Eq)]
2825#[non_exhaustive]
2826pub struct MethodDecl {
2827 pub name: String,
2829 pub has_signature: bool,
2831 pub attribute_count: usize,
2833}
2834
2835#[derive(Debug, Clone, PartialEq, Eq)]
2837#[non_exhaustive]
2838pub struct UseDecl {
2839 pub module: String,
2841 pub args: Vec<String>,
2843 pub has_filter_risk: bool,
2845}
2846
2847#[derive(Debug, Clone, PartialEq, Eq)]
2849#[non_exhaustive]
2850pub struct RequireDecl {
2851 pub target: Option<String>,
2853 pub arg_count: usize,
2855}
2856
2857#[derive(Debug, Clone, PartialEq, Eq)]
2859#[non_exhaustive]
2860pub struct VariableDecl {
2861 pub declarator: String,
2863 pub variables: Vec<VariableBinding>,
2865 pub attribute_count: usize,
2867 pub has_initializer: bool,
2869 pub is_list: bool,
2871}
2872
2873#[derive(Debug, Clone, PartialEq, Eq)]
2875#[non_exhaustive]
2876pub struct VariableBinding {
2877 pub sigil: String,
2879 pub name: String,
2881 pub range: SourceLocation,
2883}
2884
2885#[derive(Debug, Clone, PartialEq, Eq)]
2887#[non_exhaustive]
2888pub struct CallExpr {
2889 pub name: String,
2891 pub arg_count: usize,
2893 pub form: CallForm,
2895}
2896
2897#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2899#[non_exhaustive]
2900pub enum CallForm {
2901 NamedFunction,
2903 Coderef,
2905}
2906
2907#[derive(Debug, Clone, PartialEq, Eq)]
2909#[non_exhaustive]
2910pub struct MethodCallExpr {
2911 pub method: String,
2913 pub arg_count: usize,
2915 pub object_kind: &'static str,
2917}
2918
2919#[derive(Debug, Clone, PartialEq, Eq)]
2921#[non_exhaustive]
2922pub struct IndirectCallExpr {
2923 pub method: String,
2925 pub arg_count: usize,
2927 pub object_kind: &'static str,
2929}
2930
2931#[derive(Debug, Clone, PartialEq, Eq)]
2933#[non_exhaustive]
2934pub struct BarewordExpr {
2935 pub name: String,
2937}
2938
2939#[derive(Debug, Clone, PartialEq, Eq)]
2941#[non_exhaustive]
2942pub struct LiteralExpr {
2943 pub kind: LiteralKind,
2945 pub value: Option<String>,
2947 pub interpolated: Option<bool>,
2949 pub element_count: Option<usize>,
2951 pub pair_count: Option<usize>,
2953}
2954
2955#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2957#[non_exhaustive]
2958pub enum LiteralKind {
2959 Number,
2961 String,
2963 Undef,
2965 Array,
2967 Hash,
2969}
2970
2971#[derive(Debug, Clone, PartialEq, Eq)]
2973#[non_exhaustive]
2974pub struct BlockShell {
2975 pub statement_count: usize,
2977}
2978
2979#[derive(Debug, Clone, PartialEq, Eq)]
2981#[non_exhaustive]
2982pub struct DynamicBoundary {
2983 pub kind: DynamicBoundaryKind,
2985 pub reason: String,
2987}
2988
2989#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2991#[non_exhaustive]
2992pub enum DynamicBoundaryKind {
2993 CoderefCall,
2995 EvalExpression,
2997 DoExpression,
2999 DynamicStashMutation,
3001 Autoload,
3003 SymbolicReferenceDeref,
3005}
3006
3007#[derive(Debug, Clone, PartialEq, Eq)]
3013#[non_exhaustive]
3014pub struct BranchShell {
3015 pub keyword: BranchKeyword,
3017 pub condition_range: SourceLocation,
3019 pub elsif_count: usize,
3021 pub has_else: bool,
3023}
3024
3025#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3027#[non_exhaustive]
3028pub enum BranchKeyword {
3029 If,
3031 Unless,
3033 Ternary,
3035}
3036
3037#[derive(Debug, Clone, PartialEq, Eq)]
3044#[non_exhaustive]
3045pub struct LoopShell {
3046 pub kind: LoopKind,
3048 pub has_condition: bool,
3054 pub has_continue: bool,
3056 pub declares_iterator: bool,
3058 pub label: Option<String>,
3060}
3061
3062#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3064#[non_exhaustive]
3065pub enum LoopKind {
3066 While,
3068 Until,
3070 CStyleFor,
3072 Foreach,
3074}
3075
3076#[derive(Debug, Clone, PartialEq, Eq)]
3083#[non_exhaustive]
3084pub struct ControlTransfer {
3085 pub kind: ControlTransferKind,
3087 pub label: Option<String>,
3089 pub has_value: bool,
3091}
3092
3093#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3095#[non_exhaustive]
3096pub enum ControlTransferKind {
3097 Return,
3099 Next,
3101 Last,
3103 Redo,
3105 Goto,
3107}
3108
3109#[derive(Debug, Clone, PartialEq, Eq)]
3115#[non_exhaustive]
3116pub struct StatementModifierShell {
3117 pub modifier: StatementModifierKind,
3119 pub condition_range: SourceLocation,
3121 pub label: Option<String>,
3125}
3126
3127#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3129#[non_exhaustive]
3130pub enum StatementModifierKind {
3131 If,
3133 Unless,
3135 While,
3137 Until,
3139 Foreach,
3141 Other,
3143}