1use crate::SourceLocation;
4use perl_semantic_facts::{
5 AnchorId, Confidence, ExportSet, ExportTag, FileId, ImportKind, ImportSpec, ImportSymbols,
6 Provenance, ScopeId, VisibleSymbol, VisibleSymbolContext, VisibleSymbolSource,
7};
8use std::collections::BTreeMap;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
12#[non_exhaustive]
13pub struct HirId {
14 index: u32,
15}
16
17impl HirId {
18 #[inline]
20 pub const fn from_index(index: u32) -> Self {
21 Self { index }
22 }
23
24 #[inline]
26 pub const fn index(self) -> u32 {
27 self.index
28 }
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
33#[non_exhaustive]
34pub struct HirScopeId {
35 index: u32,
36}
37
38impl HirScopeId {
39 #[inline]
41 pub const fn from_index(index: u32) -> Self {
42 Self { index }
43 }
44
45 #[inline]
47 pub const fn index(self) -> u32 {
48 self.index
49 }
50}
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
54#[non_exhaustive]
55pub struct HirBindingId {
56 index: u32,
57}
58
59impl HirBindingId {
60 #[inline]
62 pub const fn from_index(index: u32) -> Self {
63 Self { index }
64 }
65
66 #[inline]
68 pub const fn index(self) -> u32 {
69 self.index
70 }
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
75#[non_exhaustive]
76pub struct AstAnchor {
77 pub node_kind: &'static str,
79 pub range: SourceLocation,
81 pub name_range: Option<SourceLocation>,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
87#[non_exhaustive]
88pub enum RecoveryConfidence {
89 Parsed,
91 Recovered,
93 Partial,
95 Unknown,
97}
98
99#[derive(Debug, Clone, PartialEq, Eq, Default)]
101#[non_exhaustive]
102pub struct HirFile {
103 pub items: Vec<HirItem>,
105 pub scope_graph: ScopeGraph,
107 pub stash_graph: StashGraph,
109 pub compile_environment: CompileEnvironment,
111 pub prototype_table: PrototypeTable,
113 pub bareword_table: BarewordTable,
115}
116
117impl HirFile {
118 #[inline]
120 pub fn is_empty(&self) -> bool {
121 self.items.is_empty()
122 }
123
124 #[must_use]
130 pub fn compile_effects(&self) -> Vec<CompileEffect> {
131 self.compile_effects_with_source_hash(None)
132 }
133
134 #[must_use]
140 pub fn compile_effects_with_source_hash(
141 &self,
142 source_hash: Option<String>,
143 ) -> Vec<CompileEffect> {
144 compile_effects_from_file(self, source_hash)
145 }
146
147 #[must_use]
152 pub fn framework_facts(&self) -> FrameworkFactGraph {
153 FrameworkAdapterRegistry::default().project_file(self)
154 }
155}
156
157#[derive(Debug, Clone, PartialEq, Eq, Default)]
159#[non_exhaustive]
160pub struct PrototypeTable {
161 pub facts: Vec<PrototypeFact>,
163}
164
165#[derive(Debug, Clone, PartialEq, Eq)]
167#[non_exhaustive]
168pub struct PrototypeFact {
169 pub sub_name: String,
171 pub package_context: Option<String>,
173 pub content: String,
175 pub range: SourceLocation,
177 pub declaration_range: SourceLocation,
179 pub declaration_item: HirId,
181 pub scope_id: Option<HirScopeId>,
183 pub anchor_id: AnchorId,
185 pub provenance: CompileProvenance,
187 pub confidence: CompileConfidence,
189}
190
191#[derive(Debug, Clone, PartialEq, Eq, Default)]
193#[non_exhaustive]
194pub struct BarewordTable {
195 pub facts: Vec<BarewordFact>,
197}
198
199#[derive(Debug, Clone, PartialEq, Eq)]
201#[non_exhaustive]
202pub struct BarewordFact {
203 pub name: String,
205 pub role: BarewordRole,
207 pub package_context: Option<String>,
209 pub range: SourceLocation,
211 pub source_item: HirId,
213 pub scope_id: Option<HirScopeId>,
215 pub anchor_id: AnchorId,
217 pub provenance: CompileProvenance,
219 pub confidence: CompileConfidence,
221}
222
223#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
225#[non_exhaustive]
226pub enum BarewordRole {
227 Expression,
229 QualifiedName,
231 ModuleRequest,
233 MethodReceiver,
235 IndirectObject,
237 HashKey,
239}
240
241#[derive(Debug, Clone, PartialEq, Eq)]
243#[non_exhaustive]
244pub struct HirItem {
245 pub id: HirId,
247 pub kind: HirKind,
249 pub range: SourceLocation,
251 pub anchor: AstAnchor,
253 pub recovery_confidence: RecoveryConfidence,
255 pub package_context: Option<String>,
257 pub scope_context: Option<HirScopeId>,
259}
260
261pub const COMPILE_EFFECT_MODEL_VERSION: u32 = 1;
263
264#[derive(Debug, Clone, PartialEq, Eq)]
270#[non_exhaustive]
271pub struct CompileEffect {
272 pub ordinal: u32,
274 pub kind: CompileEffectKind,
276 pub source_kind: CompileEffectSourceKind,
278 pub fact_kind: CompileEffectFactKind,
280 pub fact_name: Option<String>,
282 pub range: SourceLocation,
284 pub source_item: Option<HirId>,
286 pub scope_id: Option<HirScopeId>,
288 pub package_context: Option<String>,
290 pub fact_anchor_id: Option<AnchorId>,
292 pub dynamic_reason: Option<String>,
294 pub source_hash: Option<String>,
296 pub model_version: u32,
298 pub provenance: CompileProvenance,
300 pub confidence: CompileConfidence,
302}
303
304#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
306#[non_exhaustive]
307pub enum CompileEffectKind {
308 DeclarePackage,
310 DeclareSub,
312 DeclareMethod,
314 DeclareBinding,
316 SetPragmaState,
318 AddIncludePath,
320 RemoveIncludePath,
322 RequestModule,
324 ImportSymbols,
326 AssignInheritance,
328 AssignGlobAlias,
330 DefineConstant,
332 RegisterPrototype,
334 EmitDynamicBoundary,
336}
337
338#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
340#[non_exhaustive]
341pub enum CompileEffectSourceKind {
342 PackageDecl,
344 SubDecl,
346 MethodDecl,
348 VariableDecl,
350 UseDirective,
352 NoDirective,
354 RequireDirective,
356 PhaseBlock,
358 SymbolicReferenceDeref,
360 Assignment,
362 TypeglobAssignment,
364 ScopeGraph,
366 StashGraph,
368 CompileEnvironment,
370}
371
372#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
374#[non_exhaustive]
375pub enum CompileEffectFactKind {
376 Package,
378 Sub,
380 Method,
382 Binding,
384 PragmaState,
386 IncludeRoot,
388 ModuleRequest,
390 ImportSpec,
392 InheritanceEdge,
394 GlobSlot,
396 Constant,
398 Prototype,
400 DynamicBoundary,
402}
403
404#[derive(Debug)]
405struct CompileEffectEntry {
406 source_order: u32,
407 effect: CompileEffect,
408}
409
410fn compile_effects_from_file(file: &HirFile, source_hash: Option<String>) -> Vec<CompileEffect> {
411 let mut entries = Vec::new();
412 let mut next_order = 0;
413
414 for item in &file.items {
415 push_item_effects(item, &source_hash, &mut entries, &mut next_order);
416 }
417 for fact in &file.prototype_table.facts {
418 push_compile_effect(
419 &mut entries,
420 &mut next_order,
421 CompileEffectSeed {
422 kind: CompileEffectKind::RegisterPrototype,
423 source_kind: CompileEffectSourceKind::SubDecl,
424 fact_kind: CompileEffectFactKind::Prototype,
425 fact_name: Some(fact.sub_name.clone()),
426 range: fact.range,
427 source_item: Some(fact.declaration_item),
428 scope_id: fact.scope_id,
429 package_context: fact.package_context.clone(),
430 fact_anchor_id: Some(fact.anchor_id),
431 dynamic_reason: None,
432 source_hash: source_hash.clone(),
433 provenance: fact.provenance,
434 confidence: fact.confidence,
435 },
436 );
437 }
438 for binding in &file.scope_graph.bindings {
439 push_compile_effect(
440 &mut entries,
441 &mut next_order,
442 CompileEffectSeed {
443 kind: CompileEffectKind::DeclareBinding,
444 source_kind: CompileEffectSourceKind::ScopeGraph,
445 fact_kind: CompileEffectFactKind::Binding,
446 fact_name: Some(format!("{}{}", binding.sigil, binding.name)),
447 range: binding.range,
448 source_item: binding.declaration_item,
449 scope_id: Some(binding.scope_id),
450 package_context: binding.package_context.clone(),
451 fact_anchor_id: Some(AnchorId(binding.range.start as u64)),
452 dynamic_reason: None,
453 source_hash: source_hash.clone(),
454 provenance: CompileProvenance::ExactAst,
455 confidence: CompileConfidence::High,
456 },
457 );
458 }
459 for fact in &file.compile_environment.pragma_state_facts {
460 push_compile_effect(
461 &mut entries,
462 &mut next_order,
463 CompileEffectSeed {
464 kind: CompileEffectKind::SetPragmaState,
465 source_kind: CompileEffectSourceKind::CompileEnvironment,
466 fact_kind: CompileEffectFactKind::PragmaState,
467 fact_name: Some("strict/warnings/feature".to_string()),
468 range: fact.range,
469 source_item: fact.directive_item,
470 scope_id: fact.scope_id,
471 package_context: fact.package_context.clone(),
472 fact_anchor_id: Some(fact.anchor_id),
473 dynamic_reason: None,
474 source_hash: source_hash.clone(),
475 provenance: fact.provenance,
476 confidence: fact.confidence,
477 },
478 );
479 }
480 for root in &file.compile_environment.inc_roots {
481 let kind = match root.action {
482 IncRootAction::Add => CompileEffectKind::AddIncludePath,
483 IncRootAction::Remove => CompileEffectKind::RemoveIncludePath,
484 };
485 push_compile_effect(
486 &mut entries,
487 &mut next_order,
488 CompileEffectSeed {
489 kind,
490 source_kind: match root.action {
491 IncRootAction::Add => CompileEffectSourceKind::UseDirective,
492 IncRootAction::Remove => CompileEffectSourceKind::NoDirective,
493 },
494 fact_kind: CompileEffectFactKind::IncludeRoot,
495 fact_name: Some(root.path.clone()),
496 range: root.range,
497 source_item: root.directive_item,
498 scope_id: root.scope_id,
499 package_context: root.package_context.clone(),
500 fact_anchor_id: Some(AnchorId(root.range.start as u64)),
501 dynamic_reason: None,
502 source_hash: source_hash.clone(),
503 provenance: root.provenance,
504 confidence: root.confidence,
505 },
506 );
507 }
508 for request in &file.compile_environment.module_requests {
509 push_compile_effect(
510 &mut entries,
511 &mut next_order,
512 CompileEffectSeed {
513 kind: CompileEffectKind::RequestModule,
514 source_kind: module_request_source_kind(request.kind),
515 fact_kind: CompileEffectFactKind::ModuleRequest,
516 fact_name: request.target.clone().or_else(|| Some("<dynamic>".to_string())),
517 range: request.range,
518 source_item: request.directive_item,
519 scope_id: request.scope_id,
520 package_context: request.package_context.clone(),
521 fact_anchor_id: Some(AnchorId(request.range.start as u64)),
522 dynamic_reason: None,
523 source_hash: source_hash.clone(),
524 provenance: request.provenance,
525 confidence: request.confidence,
526 },
527 );
528 }
529 for spec in file.compile_environment.import_specs(FileId(0)) {
530 push_compile_effect(
531 &mut entries,
532 &mut next_order,
533 CompileEffectSeed {
534 kind: CompileEffectKind::ImportSymbols,
535 source_kind: import_spec_source_kind(&spec),
536 fact_kind: CompileEffectFactKind::ImportSpec,
537 fact_name: Some(spec.module.clone()),
538 range: SourceLocation::new(
539 spec.span_start_byte.unwrap_or_default() as usize,
540 spec.span_start_byte.unwrap_or_default() as usize,
541 ),
542 source_item: None,
543 scope_id: spec.scope_id.map(|scope| HirScopeId::from_index(scope.0 as u32)),
544 package_context: None,
545 fact_anchor_id: spec.anchor_id,
546 dynamic_reason: None,
547 source_hash: source_hash.clone(),
548 provenance: fact_provenance_to_compile(spec.provenance),
549 confidence: fact_confidence_to_compile(spec.confidence),
550 },
551 );
552 }
553 for edge in &file.stash_graph.inheritance_edges {
554 push_compile_effect(
555 &mut entries,
556 &mut next_order,
557 CompileEffectSeed {
558 kind: CompileEffectKind::AssignInheritance,
559 source_kind: CompileEffectSourceKind::StashGraph,
560 fact_kind: CompileEffectFactKind::InheritanceEdge,
561 fact_name: Some(format!("{}->{}", edge.from_package, edge.to_package)),
562 range: edge.range,
563 source_item: edge.declaration_item,
564 scope_id: None,
565 package_context: Some(edge.from_package.clone()),
566 fact_anchor_id: Some(AnchorId(edge.range.start as u64)),
567 dynamic_reason: None,
568 source_hash: source_hash.clone(),
569 provenance: stash_provenance_to_compile(edge.provenance),
570 confidence: stash_confidence_to_compile(edge.confidence),
571 },
572 );
573 }
574 for package in &file.stash_graph.packages {
575 for slot in &package.slots {
576 push_slot_effects(package, slot, &source_hash, &mut entries, &mut next_order);
577 }
578 }
579 for boundary in &file.compile_environment.dynamic_boundaries {
580 push_compile_effect(
581 &mut entries,
582 &mut next_order,
583 CompileEffectSeed {
584 kind: CompileEffectKind::EmitDynamicBoundary,
585 source_kind: compile_boundary_source_kind(boundary.kind),
586 fact_kind: CompileEffectFactKind::DynamicBoundary,
587 fact_name: Some(format!("{:?}", boundary.kind)),
588 range: boundary.range,
589 source_item: boundary.boundary_item,
590 scope_id: boundary.scope_id,
591 package_context: boundary.package_context.clone(),
592 fact_anchor_id: Some(AnchorId(boundary.range.start as u64)),
593 dynamic_reason: Some(boundary.reason.clone()),
594 source_hash: source_hash.clone(),
595 provenance: boundary.provenance,
596 confidence: boundary.confidence,
597 },
598 );
599 }
600 for boundary in &file.stash_graph.dynamic_boundaries {
601 push_compile_effect(
602 &mut entries,
603 &mut next_order,
604 CompileEffectSeed {
605 kind: CompileEffectKind::EmitDynamicBoundary,
606 source_kind: CompileEffectSourceKind::StashGraph,
607 fact_kind: CompileEffectFactKind::DynamicBoundary,
608 fact_name: boundary.symbol.clone(),
609 range: boundary.range,
610 source_item: boundary.boundary_item,
611 scope_id: None,
612 package_context: boundary.package.clone(),
613 fact_anchor_id: Some(AnchorId(boundary.range.start as u64)),
614 dynamic_reason: Some(boundary.reason.clone()),
615 source_hash: source_hash.clone(),
616 provenance: stash_provenance_to_compile(boundary.provenance),
617 confidence: stash_confidence_to_compile(boundary.confidence),
618 },
619 );
620 }
621
622 entries.sort_by_key(|entry| (entry.effect.range.start, entry.source_order));
623 entries
624 .into_iter()
625 .enumerate()
626 .map(|(ordinal, mut entry)| {
627 entry.effect.ordinal = ordinal as u32;
628 entry.effect
629 })
630 .collect()
631}
632
633fn push_item_effects(
634 item: &HirItem,
635 source_hash: &Option<String>,
636 entries: &mut Vec<CompileEffectEntry>,
637 next_order: &mut u32,
638) {
639 match &item.kind {
640 HirKind::PackageDecl(decl) => {
641 push_compile_effect(
642 entries,
643 next_order,
644 CompileEffectSeed {
645 kind: CompileEffectKind::DeclarePackage,
646 source_kind: CompileEffectSourceKind::PackageDecl,
647 fact_kind: CompileEffectFactKind::Package,
648 fact_name: Some(decl.name.clone()),
649 range: item.range,
650 source_item: Some(item.id),
651 scope_id: item.scope_context,
652 package_context: Some(decl.name.clone()),
653 fact_anchor_id: Some(AnchorId(item.range.start as u64)),
654 dynamic_reason: None,
655 source_hash: source_hash.clone(),
656 provenance: CompileProvenance::ExactAst,
657 confidence: CompileConfidence::High,
658 },
659 );
660 }
661 HirKind::SubDecl(decl) => {
662 let Some(name) = &decl.name else {
663 return;
664 };
665 push_compile_effect(
666 entries,
667 next_order,
668 CompileEffectSeed {
669 kind: CompileEffectKind::DeclareSub,
670 source_kind: CompileEffectSourceKind::SubDecl,
671 fact_kind: CompileEffectFactKind::Sub,
672 fact_name: Some(name.clone()),
673 range: item.range,
674 source_item: Some(item.id),
675 scope_id: item.scope_context,
676 package_context: item.package_context.clone(),
677 fact_anchor_id: Some(AnchorId(item.range.start as u64)),
678 dynamic_reason: None,
679 source_hash: source_hash.clone(),
680 provenance: CompileProvenance::ExactAst,
681 confidence: CompileConfidence::High,
682 },
683 );
684 }
685 HirKind::MethodDecl(decl) => {
686 push_compile_effect(
687 entries,
688 next_order,
689 CompileEffectSeed {
690 kind: CompileEffectKind::DeclareMethod,
691 source_kind: CompileEffectSourceKind::MethodDecl,
692 fact_kind: CompileEffectFactKind::Method,
693 fact_name: Some(decl.name.clone()),
694 range: item.range,
695 source_item: Some(item.id),
696 scope_id: item.scope_context,
697 package_context: item.package_context.clone(),
698 fact_anchor_id: Some(AnchorId(item.range.start as u64)),
699 dynamic_reason: None,
700 source_hash: source_hash.clone(),
701 provenance: CompileProvenance::ExactAst,
702 confidence: CompileConfidence::High,
703 },
704 );
705 }
706 _ => {}
707 }
708}
709
710fn push_slot_effects(
711 package: &PackageStash,
712 slot: &GlobSlot,
713 source_hash: &Option<String>,
714 entries: &mut Vec<CompileEffectEntry>,
715 next_order: &mut u32,
716) {
717 let (kind, fact_kind) = match slot.source {
718 GlobSlotSource::TypeglobAlias => {
719 (CompileEffectKind::AssignGlobAlias, CompileEffectFactKind::GlobSlot)
720 }
721 GlobSlotSource::ConstantDeclaration => {
722 (CompileEffectKind::DefineConstant, CompileEffectFactKind::Constant)
723 }
724 _ => return,
725 };
726
727 push_compile_effect(
728 entries,
729 next_order,
730 CompileEffectSeed {
731 kind,
732 source_kind: match slot.source {
733 GlobSlotSource::TypeglobAlias => CompileEffectSourceKind::TypeglobAssignment,
734 _ => CompileEffectSourceKind::StashGraph,
735 },
736 fact_kind,
737 fact_name: Some(format!("{}::{}", package.package, slot.name)),
738 range: slot.range,
739 source_item: slot.declaration_item,
740 scope_id: None,
741 package_context: Some(package.package.clone()),
742 fact_anchor_id: Some(AnchorId(slot.range.start as u64)),
743 dynamic_reason: None,
744 source_hash: source_hash.clone(),
745 provenance: stash_provenance_to_compile(slot.provenance),
746 confidence: stash_confidence_to_compile(slot.confidence),
747 },
748 );
749}
750
751#[derive(Debug)]
752struct CompileEffectSeed {
753 kind: CompileEffectKind,
754 source_kind: CompileEffectSourceKind,
755 fact_kind: CompileEffectFactKind,
756 fact_name: Option<String>,
757 range: SourceLocation,
758 source_item: Option<HirId>,
759 scope_id: Option<HirScopeId>,
760 package_context: Option<String>,
761 fact_anchor_id: Option<AnchorId>,
762 dynamic_reason: Option<String>,
763 source_hash: Option<String>,
764 provenance: CompileProvenance,
765 confidence: CompileConfidence,
766}
767
768fn push_compile_effect(
769 entries: &mut Vec<CompileEffectEntry>,
770 next_order: &mut u32,
771 seed: CompileEffectSeed,
772) {
773 let order = *next_order;
774 *next_order += 1;
775 entries.push(CompileEffectEntry {
776 source_order: order,
777 effect: CompileEffect {
778 ordinal: 0,
779 kind: seed.kind,
780 source_kind: seed.source_kind,
781 fact_kind: seed.fact_kind,
782 fact_name: seed.fact_name,
783 range: seed.range,
784 source_item: seed.source_item,
785 scope_id: seed.scope_id,
786 package_context: seed.package_context,
787 fact_anchor_id: seed.fact_anchor_id,
788 dynamic_reason: seed.dynamic_reason,
789 source_hash: seed.source_hash,
790 model_version: COMPILE_EFFECT_MODEL_VERSION,
791 provenance: seed.provenance,
792 confidence: seed.confidence,
793 },
794 });
795}
796
797fn module_request_source_kind(kind: ModuleRequestKind) -> CompileEffectSourceKind {
798 match kind {
799 ModuleRequestKind::Require => CompileEffectSourceKind::RequireDirective,
800 _ => CompileEffectSourceKind::UseDirective,
801 }
802}
803
804fn import_spec_source_kind(spec: &ImportSpec) -> CompileEffectSourceKind {
805 match spec.kind {
806 ImportKind::Require | ImportKind::DynamicRequire => {
807 CompileEffectSourceKind::RequireDirective
808 }
809 _ => CompileEffectSourceKind::UseDirective,
810 }
811}
812
813fn compile_boundary_source_kind(kind: CompileEnvironmentBoundaryKind) -> CompileEffectSourceKind {
814 match kind {
815 CompileEnvironmentBoundaryKind::DynamicRequire => CompileEffectSourceKind::RequireDirective,
816 CompileEnvironmentBoundaryKind::DynamicPragmaArgs
817 | CompileEnvironmentBoundaryKind::DynamicIncRoot => CompileEffectSourceKind::UseDirective,
818 CompileEnvironmentBoundaryKind::PhaseBlockExecution => CompileEffectSourceKind::PhaseBlock,
819 CompileEnvironmentBoundaryKind::SymbolicReferenceDeref => {
820 CompileEffectSourceKind::SymbolicReferenceDeref
821 }
822 }
823}
824
825fn stash_provenance_to_compile(provenance: StashProvenance) -> CompileProvenance {
826 match provenance {
827 StashProvenance::ExactAst => CompileProvenance::ExactAst,
828 StashProvenance::DesugaredAst => CompileProvenance::DesugaredAst,
829 StashProvenance::DynamicBoundary => CompileProvenance::DynamicBoundary,
830 }
831}
832
833fn stash_confidence_to_compile(confidence: StashConfidence) -> CompileConfidence {
834 match confidence {
835 StashConfidence::High => CompileConfidence::High,
836 StashConfidence::Medium => CompileConfidence::Medium,
837 StashConfidence::Low => CompileConfidence::Low,
838 }
839}
840
841fn fact_provenance_to_compile(provenance: Provenance) -> CompileProvenance {
842 match provenance {
843 Provenance::ExactAst | Provenance::LiteralRequireImport => CompileProvenance::ExactAst,
845 Provenance::DesugaredAst
846 | Provenance::SemanticAnalyzer
847 | Provenance::FrameworkSynthesis
848 | Provenance::ImportExportInference
849 | Provenance::PragmaInference => CompileProvenance::DesugaredAst,
850 Provenance::NameHeuristic | Provenance::SearchFallback | Provenance::DynamicBoundary => {
851 CompileProvenance::DynamicBoundary
852 }
853 }
854}
855
856fn fact_confidence_to_compile(confidence: Confidence) -> CompileConfidence {
857 match confidence {
858 Confidence::High => CompileConfidence::High,
859 Confidence::Medium => CompileConfidence::Medium,
860 Confidence::Low => CompileConfidence::Low,
861 }
862}
863
864#[derive(Debug, Clone, PartialEq, Eq, Default)]
870#[non_exhaustive]
871pub struct ScopeGraph {
872 pub scopes: Vec<ScopeFrame>,
874 pub bindings: Vec<Binding>,
876 pub references: Vec<BindingReference>,
878}
879
880impl ScopeGraph {
881 #[inline]
883 pub fn root_scope(&self) -> Option<&ScopeFrame> {
884 self.scopes.first()
885 }
886}
887
888#[derive(Debug, Clone, PartialEq, Eq)]
890#[non_exhaustive]
891pub struct ScopeFrame {
892 pub id: HirScopeId,
894 pub parent: Option<HirScopeId>,
896 pub kind: ScopeKind,
898 pub range: SourceLocation,
900 pub package_context: Option<String>,
902}
903
904#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
906#[non_exhaustive]
907pub enum ScopeKind {
908 File,
910 Package,
912 Block,
914 Subroutine,
916 Method,
918 Signature,
920 Format,
922 EvalString,
924 PhaseBlock,
926}
927
928#[derive(Debug, Clone, PartialEq, Eq)]
930#[non_exhaustive]
931pub struct Binding {
932 pub id: HirBindingId,
934 pub scope_id: HirScopeId,
936 pub sigil: String,
938 pub name: String,
940 pub range: SourceLocation,
942 pub storage: StorageClass,
944 pub package_context: Option<String>,
946 pub declaration_item: Option<HirId>,
948 pub shadows: Option<HirBindingId>,
950}
951
952#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
954#[non_exhaustive]
955pub enum StorageClass {
956 LexicalMy,
958 LexicalState,
960 PackageOur,
962 LocalizedPackage,
964 Parameter,
966 MethodInvocant,
968 Implicit,
970 PackageGlobal,
972}
973
974#[derive(Debug, Clone, PartialEq, Eq)]
976#[non_exhaustive]
977pub struct BindingReference {
978 pub scope_id: HirScopeId,
980 pub sigil: String,
982 pub name: String,
984 pub range: SourceLocation,
986 pub resolved_binding: Option<HirBindingId>,
988}
989
990#[derive(Debug, Clone, PartialEq, Eq, Default)]
995#[non_exhaustive]
996pub struct StashGraph {
997 pub packages: Vec<PackageStash>,
999 pub inheritance_edges: Vec<PackageInheritanceEdge>,
1001 pub export_declarations: Vec<ExportDeclaration>,
1003 pub dynamic_boundaries: Vec<StashDynamicBoundary>,
1005}
1006
1007impl StashGraph {
1008 #[must_use]
1013 pub fn export_sets(&self) -> Vec<ExportSet> {
1014 let mut builders = BTreeMap::<String, ExportSetBuilder>::new();
1015
1016 for declaration in &self.export_declarations {
1017 let builder = builders.entry(declaration.package.clone()).or_insert_with(|| {
1018 ExportSetBuilder::new(
1019 declaration.package.clone(),
1020 declaration.range,
1021 stash_provenance_to_fact(declaration.provenance),
1022 stash_confidence_to_fact(declaration.confidence),
1023 )
1024 });
1025 builder.absorb(declaration);
1026 }
1027
1028 builders.into_values().map(ExportSetBuilder::into_export_set).collect()
1029 }
1030
1031 #[must_use]
1037 pub fn constant_table(&self) -> ConstantTable {
1038 let mut entries = Vec::new();
1039
1040 for package in &self.packages {
1041 for slot in &package.slots {
1042 if slot.kind != GlobSlotKind::Code
1043 || slot.source != GlobSlotSource::ConstantDeclaration
1044 {
1045 continue;
1046 }
1047
1048 entries.push(ConstantTableEntry {
1049 package: package.package.clone(),
1050 name: slot.name.clone(),
1051 canonical_name: format!("{}::{}", package.package, slot.name),
1052 range: slot.range,
1053 declaration_item: slot.declaration_item,
1054 source: slot.source,
1055 provenance: slot.provenance,
1056 confidence: slot.confidence,
1057 });
1058 }
1059 }
1060
1061 ConstantTable { entries }
1062 }
1063}
1064
1065#[derive(Debug, Clone, PartialEq, Eq, Default)]
1067#[non_exhaustive]
1068pub struct ConstantTable {
1069 pub entries: Vec<ConstantTableEntry>,
1071}
1072
1073impl ConstantTable {
1074 #[must_use]
1076 pub fn is_empty(&self) -> bool {
1077 self.entries.is_empty()
1078 }
1079}
1080
1081#[derive(Debug, Clone, PartialEq, Eq)]
1083#[non_exhaustive]
1084pub struct ConstantTableEntry {
1085 pub package: String,
1087 pub name: String,
1089 pub canonical_name: String,
1091 pub range: SourceLocation,
1093 pub declaration_item: Option<HirId>,
1095 pub source: GlobSlotSource,
1097 pub provenance: StashProvenance,
1099 pub confidence: StashConfidence,
1101}
1102
1103#[derive(Debug)]
1104struct ExportSetBuilder {
1105 module_name: String,
1106 anchor_range: SourceLocation,
1107 default_exports: Vec<String>,
1108 optional_exports: Vec<String>,
1109 tags: BTreeMap<String, Vec<String>>,
1110 provenance: Provenance,
1111 confidence: Confidence,
1112}
1113
1114impl ExportSetBuilder {
1115 fn new(
1116 module_name: String,
1117 anchor_range: SourceLocation,
1118 provenance: Provenance,
1119 confidence: Confidence,
1120 ) -> Self {
1121 Self {
1122 module_name,
1123 anchor_range,
1124 default_exports: Vec::new(),
1125 optional_exports: Vec::new(),
1126 tags: BTreeMap::new(),
1127 provenance,
1128 confidence,
1129 }
1130 }
1131
1132 fn absorb(&mut self, declaration: &ExportDeclaration) {
1133 if declaration.range.start < self.anchor_range.start {
1134 self.anchor_range = declaration.range;
1135 }
1136 self.provenance =
1137 combine_provenance(self.provenance, stash_provenance_to_fact(declaration.provenance));
1138 self.confidence =
1139 combine_confidence(self.confidence, stash_confidence_to_fact(declaration.confidence));
1140
1141 match declaration.kind {
1142 ExportDeclarationKind::Default => {
1143 self.default_exports.extend(declaration.symbols.iter().cloned());
1144 }
1145 ExportDeclarationKind::Optional => {
1146 self.optional_exports.extend(declaration.symbols.iter().cloned());
1147 }
1148 ExportDeclarationKind::Tag => {
1149 if let Some(tag_name) = &declaration.tag_name {
1150 self.tags
1151 .entry(tag_name.clone())
1152 .or_default()
1153 .extend(declaration.symbols.iter().cloned());
1154 }
1155 }
1156 }
1157 }
1158
1159 fn into_export_set(mut self) -> ExportSet {
1160 sort_dedup(&mut self.default_exports);
1161 sort_dedup(&mut self.optional_exports);
1162
1163 let tags = self
1164 .tags
1165 .into_iter()
1166 .map(|(name, mut members)| {
1167 sort_dedup(&mut members);
1168 ExportTag { name, members }
1169 })
1170 .collect();
1171
1172 ExportSet {
1173 default_exports: self.default_exports,
1174 optional_exports: self.optional_exports,
1175 tags,
1176 provenance: self.provenance,
1177 confidence: self.confidence,
1178 module_name: Some(self.module_name),
1179 anchor_id: Some(AnchorId(self.anchor_range.start as u64)),
1180 }
1181 }
1182}
1183
1184fn sort_dedup(values: &mut Vec<String>) {
1185 values.sort();
1186 values.dedup();
1187}
1188
1189fn combine_provenance(current: Provenance, next: Provenance) -> Provenance {
1190 if current == Provenance::DynamicBoundary || next == Provenance::DynamicBoundary {
1191 Provenance::DynamicBoundary
1192 } else if current == Provenance::ImportExportInference
1193 || next == Provenance::ImportExportInference
1194 {
1195 Provenance::ImportExportInference
1196 } else {
1197 current
1198 }
1199}
1200
1201fn combine_confidence(current: Confidence, next: Confidence) -> Confidence {
1202 match (current, next) {
1203 (Confidence::Low, _) | (_, Confidence::Low) => Confidence::Low,
1204 (Confidence::Medium, _) | (_, Confidence::Medium) => Confidence::Medium,
1205 (Confidence::High, Confidence::High) => Confidence::High,
1206 }
1207}
1208
1209fn stash_provenance_to_fact(provenance: StashProvenance) -> Provenance {
1210 match provenance {
1211 StashProvenance::ExactAst => Provenance::ExactAst,
1212 StashProvenance::DesugaredAst => Provenance::DesugaredAst,
1213 StashProvenance::DynamicBoundary => Provenance::DynamicBoundary,
1214 }
1215}
1216
1217fn stash_confidence_to_fact(confidence: StashConfidence) -> Confidence {
1218 match confidence {
1219 StashConfidence::High => Confidence::High,
1220 StashConfidence::Medium => Confidence::Medium,
1221 StashConfidence::Low => Confidence::Low,
1222 }
1223}
1224
1225#[derive(Debug, Clone, PartialEq, Eq)]
1227#[non_exhaustive]
1228pub struct PackageStash {
1229 pub package: String,
1231 pub range: SourceLocation,
1233 pub declaration_item: Option<HirId>,
1235 pub slots: Vec<GlobSlot>,
1237 pub provenance: StashProvenance,
1239 pub confidence: StashConfidence,
1241}
1242
1243#[derive(Debug, Clone, PartialEq, Eq)]
1245#[non_exhaustive]
1246pub struct GlobSlot {
1247 pub name: String,
1249 pub kind: GlobSlotKind,
1251 pub range: SourceLocation,
1253 pub declaration_item: Option<HirId>,
1255 pub source: GlobSlotSource,
1257 pub alias_target: Option<String>,
1259 pub provenance: StashProvenance,
1261 pub confidence: StashConfidence,
1263}
1264
1265#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1267#[non_exhaustive]
1268pub enum GlobSlotKind {
1269 Scalar,
1271 Array,
1273 Hash,
1275 Code,
1277 Io,
1279 Format,
1281}
1282
1283#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1285#[non_exhaustive]
1286pub enum GlobSlotSource {
1287 PackageDeclaration,
1289 SubDeclaration,
1291 MethodDeclaration,
1293 OurDeclaration,
1295 FormatDeclaration,
1297 ConstantDeclaration,
1299 PackageAssignment,
1301 TypeglobAlias,
1303}
1304
1305#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1307#[non_exhaustive]
1308pub enum StashProvenance {
1309 ExactAst,
1311 DesugaredAst,
1313 DynamicBoundary,
1315}
1316
1317#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1319#[non_exhaustive]
1320pub enum StashConfidence {
1321 High,
1323 Medium,
1325 Low,
1327}
1328
1329#[derive(Debug, Clone, PartialEq, Eq)]
1331#[non_exhaustive]
1332pub struct PackageInheritanceEdge {
1333 pub from_package: String,
1335 pub to_package: String,
1337 pub range: SourceLocation,
1339 pub declaration_item: Option<HirId>,
1341 pub source: InheritanceSource,
1343 pub provenance: StashProvenance,
1345 pub confidence: StashConfidence,
1347}
1348
1349#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1351#[non_exhaustive]
1352pub enum InheritanceSource {
1353 IsaAssignment,
1355 UseParent,
1357 UseBase,
1359}
1360
1361#[derive(Debug, Clone, PartialEq, Eq)]
1363#[non_exhaustive]
1364pub struct ExportDeclaration {
1365 pub package: String,
1367 pub kind: ExportDeclarationKind,
1369 pub tag_name: Option<String>,
1371 pub symbols: Vec<String>,
1373 pub range: SourceLocation,
1375 pub declaration_item: Option<HirId>,
1377 pub provenance: StashProvenance,
1379 pub confidence: StashConfidence,
1381}
1382
1383#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1385#[non_exhaustive]
1386pub enum ExportDeclarationKind {
1387 Default,
1389 Optional,
1391 Tag,
1393}
1394
1395#[derive(Debug, Clone, PartialEq, Eq)]
1397#[non_exhaustive]
1398pub struct StashDynamicBoundary {
1399 pub package: Option<String>,
1401 pub symbol: Option<String>,
1403 pub range: SourceLocation,
1405 pub boundary_item: Option<HirId>,
1407 pub kind: StashDynamicBoundaryKind,
1409 pub reason: String,
1411 pub provenance: StashProvenance,
1413 pub confidence: StashConfidence,
1415}
1416
1417#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1419#[non_exhaustive]
1420pub enum StashDynamicBoundaryKind {
1421 DynamicStashMutation,
1423 DynamicExportDeclaration,
1425 Autoload,
1427}
1428
1429#[derive(Debug, Clone, PartialEq, Eq, Default)]
1435#[non_exhaustive]
1436pub struct CompileEnvironment {
1437 pub directives: Vec<CompileDirective>,
1439 pub pragma_effects: Vec<PragmaEffect>,
1441 pub pragma_state_facts: Vec<PragmaStateFact>,
1443 pub inc_roots: Vec<IncRootFact>,
1445 pub module_requests: Vec<ModuleRequest>,
1447 pub phase_blocks: Vec<CompilePhaseBlock>,
1449 pub dynamic_boundaries: Vec<CompileEnvironmentBoundary>,
1451}
1452
1453impl CompileEnvironment {
1454 #[must_use]
1456 pub fn pragma_state_facts(&self) -> &[PragmaStateFact] {
1457 &self.pragma_state_facts
1458 }
1459
1460 #[must_use]
1462 pub fn pragma_state_at(&self, offset: usize) -> Option<&PragmaStateFact> {
1463 let idx = self.pragma_state_facts.partition_point(|fact| fact.range.start <= offset);
1464 if idx > 0 { self.pragma_state_facts.get(idx - 1) } else { None }
1465 }
1466
1467 #[must_use]
1472 pub fn import_specs(&self, file_id: FileId) -> Vec<ImportSpec> {
1473 self.directives
1474 .iter()
1475 .filter_map(|directive| import_spec_from_directive(directive, file_id))
1476 .collect()
1477 }
1478
1479 #[must_use]
1487 pub fn module_resolution_candidates(
1488 &self,
1489 supplied_roots: &[ModuleResolutionRoot],
1490 ) -> Vec<ModuleResolutionCandidate> {
1491 self.module_requests
1492 .iter()
1493 .enumerate()
1494 .filter_map(|(request_index, request)| {
1495 let target = request.target.as_ref()?;
1496 let normalized_target = normalize_module_target(target);
1497 let relative_path = module_target_to_relative_path(&normalized_target)?;
1498 let candidate_roots =
1499 self.candidate_roots_for_request(request, &relative_path, supplied_roots);
1500 let status = if candidate_roots.is_empty() {
1501 ModuleResolutionCandidateStatus::NotFound
1502 } else {
1503 ModuleResolutionCandidateStatus::CandidateBuilt
1504 };
1505
1506 Some(ModuleResolutionCandidate {
1507 request_index,
1508 directive_item: request.directive_item,
1509 request_kind: request.kind,
1510 target: normalized_target,
1511 relative_path,
1512 roots: candidate_roots,
1513 status,
1514 resolved_path: None,
1515 range: request.range,
1516 package_context: request.package_context.clone(),
1517 provenance: request.provenance,
1518 confidence: request.confidence,
1519 })
1520 })
1521 .collect()
1522 }
1523
1524 #[must_use]
1530 pub fn resolved_module_resolution_candidates(
1531 &self,
1532 supplied_roots: &[ModuleResolutionRoot],
1533 mut path_exists: impl FnMut(&str) -> bool,
1534 ) -> Vec<ModuleResolutionCandidate> {
1535 let mut candidates = self.module_resolution_candidates(supplied_roots);
1536
1537 for candidate in &mut candidates {
1538 if candidate.status != ModuleResolutionCandidateStatus::CandidateBuilt {
1539 continue;
1540 }
1541
1542 if let Some(root) =
1543 candidate.roots.iter().find(|root| path_exists(&root.candidate_path))
1544 {
1545 candidate.status = ModuleResolutionCandidateStatus::Resolved;
1546 candidate.resolved_path = Some(root.candidate_path.clone());
1547 } else {
1548 candidate.status = ModuleResolutionCandidateStatus::NotFound;
1549 }
1550 }
1551
1552 candidates
1553 }
1554
1555 fn candidate_roots_for_request(
1556 &self,
1557 request: &ModuleRequest,
1558 relative_path: &str,
1559 supplied_roots: &[ModuleResolutionRoot],
1560 ) -> Vec<ModuleResolutionCandidateRoot> {
1561 let active_lexical_roots = self.active_lexical_roots_for_request(request);
1562 active_lexical_roots
1563 .iter()
1564 .map(|root| ModuleResolutionRoot {
1565 path: root.path.clone(),
1566 kind: root.kind,
1567 source: root.source.clone(),
1568 })
1569 .chain(supplied_roots.iter().cloned())
1570 .enumerate()
1571 .map(|(precedence, root)| ModuleResolutionCandidateRoot {
1572 path: root.path.clone(),
1573 kind: root.kind,
1574 source: root.source,
1575 candidate_path: join_candidate_path(&root.path, relative_path),
1576 precedence,
1577 })
1578 .collect()
1579 }
1580
1581 fn active_lexical_roots_for_request(&self, request: &ModuleRequest) -> Vec<ActiveLexicalRoot> {
1582 let mut active = Vec::new();
1583
1584 for (order, root) in self.inc_roots.iter().enumerate() {
1585 if root.range.start > request.range.start {
1586 continue;
1587 }
1588 if root.kind != IncRootKind::UseLib {
1589 continue;
1590 }
1591
1592 match root.action {
1593 IncRootAction::Add => {
1594 active.push(ActiveLexicalRoot {
1595 path: root.path.clone(),
1596 kind: root.kind,
1597 source: "use-lib-lexical".to_string(),
1598 range_start: root.range.start,
1599 order,
1600 });
1601 }
1602 IncRootAction::Remove => {
1603 active.retain(|active_root| active_root.path != root.path);
1604 }
1605 }
1606 }
1607
1608 active.sort_by(|left, right| {
1609 right.range_start.cmp(&left.range_start).then_with(|| left.order.cmp(&right.order))
1610 });
1611
1612 active
1613 }
1614}
1615
1616#[derive(Debug, Clone, PartialEq, Eq)]
1617struct ActiveLexicalRoot {
1618 path: String,
1619 kind: IncRootKind,
1620 source: String,
1621 range_start: usize,
1622 order: usize,
1623}
1624
1625#[derive(Debug, Clone, PartialEq, Eq)]
1627#[non_exhaustive]
1628pub struct CompileDirective {
1629 pub action: CompileDirectiveAction,
1631 pub module: Option<String>,
1633 pub args: Vec<String>,
1635 pub range: SourceLocation,
1637 pub item_id: Option<HirId>,
1639 pub scope_id: Option<HirScopeId>,
1641 pub package_context: Option<String>,
1643 pub kind: CompileDirectiveKind,
1645 pub provenance: CompileProvenance,
1647 pub confidence: CompileConfidence,
1649}
1650
1651#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1653#[non_exhaustive]
1654pub enum CompileDirectiveAction {
1655 Use,
1657 No,
1659 Require,
1661}
1662
1663#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1665#[non_exhaustive]
1666pub enum CompileDirectiveKind {
1667 Strict,
1669 Warnings,
1671 Feature,
1673 Lib,
1675 Inheritance,
1677 Constant,
1679 Module,
1681 Dynamic,
1683}
1684
1685#[derive(Debug, Clone, PartialEq, Eq)]
1687#[non_exhaustive]
1688pub struct PragmaEffect {
1689 pub pragma: String,
1691 pub enabled: bool,
1693 pub args: Vec<String>,
1695 pub argument_kind: PragmaArgumentKind,
1697 pub range: SourceLocation,
1699 pub directive_item: Option<HirId>,
1701 pub scope_id: Option<HirScopeId>,
1703 pub package_context: Option<String>,
1705 pub provenance: CompileProvenance,
1707 pub confidence: CompileConfidence,
1709}
1710
1711#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1713#[non_exhaustive]
1714pub enum PragmaArgumentKind {
1715 Broad,
1717 Categories,
1719}
1720
1721#[derive(Debug, Clone, PartialEq, Eq)]
1723#[non_exhaustive]
1724pub struct PragmaStateFact {
1725 pub range: SourceLocation,
1727 pub anchor_id: AnchorId,
1729 pub directive_item: Option<HirId>,
1731 pub scope_id: Option<HirScopeId>,
1733 pub package_context: Option<String>,
1735 pub strict_vars: bool,
1737 pub strict_subs: bool,
1739 pub strict_refs: bool,
1741 pub warnings: bool,
1743 pub disabled_warning_categories: Vec<String>,
1745 pub features: Vec<String>,
1747 pub provenance: CompileProvenance,
1749 pub confidence: CompileConfidence,
1751}
1752
1753impl PragmaStateFact {
1754 #[must_use]
1756 pub fn strict_enabled(&self) -> bool {
1757 self.strict_vars && self.strict_subs && self.strict_refs
1758 }
1759
1760 #[must_use]
1762 pub fn warning_active(&self, category: &str) -> bool {
1763 self.warnings && !self.disabled_warning_categories.iter().any(|name| name == category)
1764 }
1765
1766 #[must_use]
1768 pub fn has_feature(&self, feature: &str) -> bool {
1769 self.features.iter().any(|name| name == feature)
1770 }
1771}
1772
1773#[derive(Debug, Clone, PartialEq, Eq)]
1779#[non_exhaustive]
1780pub struct FrameworkAdapterRegistry {
1781 adapters: Vec<FrameworkAdapterKind>,
1782}
1783
1784impl Default for FrameworkAdapterRegistry {
1785 fn default() -> Self {
1786 Self { adapters: vec![FrameworkAdapterKind::ExporterFamily] }
1787 }
1788}
1789
1790impl FrameworkAdapterRegistry {
1791 #[must_use]
1793 pub fn new(adapters: Vec<FrameworkAdapterKind>) -> Self {
1794 Self { adapters }
1795 }
1796
1797 #[must_use]
1799 pub fn adapters(&self) -> &[FrameworkAdapterKind] {
1800 &self.adapters
1801 }
1802
1803 #[must_use]
1805 pub fn project_file(&self, file: &HirFile) -> FrameworkFactGraph {
1806 let mut graph = FrameworkFactGraph::default();
1807
1808 for adapter in &self.adapters {
1809 match adapter {
1810 FrameworkAdapterKind::ExporterFamily => {
1811 project_exporter_family_facts(file, &mut graph);
1812 }
1813 }
1814 }
1815
1816 graph
1817 }
1818}
1819
1820#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1822#[non_exhaustive]
1823pub enum FrameworkAdapterKind {
1824 ExporterFamily,
1826}
1827
1828#[derive(Debug, Clone, PartialEq, Eq, Default)]
1830#[non_exhaustive]
1831pub struct FrameworkFactGraph {
1832 pub exported_symbols: Vec<FrameworkExportedSymbolFact>,
1834 pub dynamic_boundaries: Vec<FrameworkDynamicBoundaryFact>,
1836}
1837
1838#[derive(Debug, Clone, PartialEq, Eq)]
1840#[non_exhaustive]
1841pub struct FrameworkExportedSymbolFact {
1842 pub adapter: FrameworkAdapterKind,
1844 pub package: String,
1846 pub name: String,
1848 pub kind: FrameworkExportedSymbolKind,
1850 pub tag_name: Option<String>,
1852 pub range: SourceLocation,
1854 pub declaration_item: Option<HirId>,
1856 pub visible_symbol: VisibleSymbol,
1858 pub source_provenance: Provenance,
1860 pub source_confidence: Confidence,
1862 pub provenance: Provenance,
1864 pub confidence: Confidence,
1866}
1867
1868#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1870#[non_exhaustive]
1871pub enum FrameworkExportedSymbolKind {
1872 Default,
1874 Optional,
1876 TagMember,
1878}
1879
1880#[derive(Debug, Clone, PartialEq, Eq)]
1882#[non_exhaustive]
1883pub struct FrameworkDynamicBoundaryFact {
1884 pub adapter: FrameworkAdapterKind,
1886 pub package: Option<String>,
1888 pub symbol: Option<String>,
1890 pub range: SourceLocation,
1892 pub boundary_item: Option<HirId>,
1894 pub kind: StashDynamicBoundaryKind,
1896 pub reason: String,
1898 pub provenance: Provenance,
1900 pub confidence: Confidence,
1902}
1903
1904fn project_exporter_family_facts(file: &HirFile, graph: &mut FrameworkFactGraph) {
1905 for declaration in &file.stash_graph.export_declarations {
1906 match declaration.kind {
1907 ExportDeclarationKind::Default => {
1908 project_exported_symbols_from_declaration(
1909 declaration,
1910 FrameworkExportedSymbolKind::Default,
1911 None,
1912 graph,
1913 );
1914 }
1915 ExportDeclarationKind::Optional => {
1916 project_exported_symbols_from_declaration(
1917 declaration,
1918 FrameworkExportedSymbolKind::Optional,
1919 None,
1920 graph,
1921 );
1922 }
1923 ExportDeclarationKind::Tag => {
1924 project_exported_symbols_from_declaration(
1925 declaration,
1926 FrameworkExportedSymbolKind::TagMember,
1927 declaration.tag_name.as_deref(),
1928 graph,
1929 );
1930 }
1931 }
1932 }
1933
1934 for boundary in &file.stash_graph.dynamic_boundaries {
1935 if boundary.kind != StashDynamicBoundaryKind::DynamicExportDeclaration {
1936 continue;
1937 }
1938 graph.dynamic_boundaries.push(FrameworkDynamicBoundaryFact {
1939 adapter: FrameworkAdapterKind::ExporterFamily,
1940 package: boundary.package.clone(),
1941 symbol: boundary.symbol.clone(),
1942 range: boundary.range,
1943 boundary_item: boundary.boundary_item,
1944 kind: boundary.kind,
1945 reason: boundary.reason.clone(),
1946 provenance: Provenance::DynamicBoundary,
1947 confidence: Confidence::Low,
1948 });
1949 }
1950}
1951
1952fn project_exported_symbols_from_declaration(
1953 declaration: &ExportDeclaration,
1954 kind: FrameworkExportedSymbolKind,
1955 tag_name: Option<&str>,
1956 graph: &mut FrameworkFactGraph,
1957) {
1958 for symbol in &declaration.symbols {
1959 graph.exported_symbols.push(FrameworkExportedSymbolFact {
1960 adapter: FrameworkAdapterKind::ExporterFamily,
1961 package: declaration.package.clone(),
1962 name: symbol.clone(),
1963 kind,
1964 tag_name: tag_name.map(str::to_string),
1965 range: declaration.range,
1966 declaration_item: declaration.declaration_item,
1967 visible_symbol: visible_symbol_for_export(declaration, symbol, kind),
1968 source_provenance: stash_provenance_to_fact(declaration.provenance),
1969 source_confidence: stash_confidence_to_fact(declaration.confidence),
1970 provenance: Provenance::FrameworkSynthesis,
1971 confidence: Confidence::Medium,
1972 });
1973 }
1974}
1975
1976fn visible_symbol_for_export(
1977 declaration: &ExportDeclaration,
1978 symbol: &str,
1979 kind: FrameworkExportedSymbolKind,
1980) -> VisibleSymbol {
1981 let source = match kind {
1982 FrameworkExportedSymbolKind::Default => VisibleSymbolSource::DefaultExport,
1983 FrameworkExportedSymbolKind::Optional => VisibleSymbolSource::ExplicitImport,
1984 FrameworkExportedSymbolKind::TagMember => VisibleSymbolSource::ExportTag,
1985 };
1986
1987 VisibleSymbol {
1988 name: symbol.to_string(),
1989 entity_id: None,
1990 source,
1991 confidence: Confidence::Medium,
1992 context: Some(VisibleSymbolContext::new(
1993 Some(declaration.package.clone()),
1994 None,
1995 Some(AnchorId(declaration.range.start as u64)),
1996 )),
1997 }
1998}
1999
2000#[derive(Debug, Clone, PartialEq, Eq)]
2002#[non_exhaustive]
2003pub struct IncRootFact {
2004 pub path: String,
2006 pub action: IncRootAction,
2008 pub kind: IncRootKind,
2010 pub range: SourceLocation,
2012 pub directive_item: Option<HirId>,
2014 pub scope_id: Option<HirScopeId>,
2016 pub package_context: Option<String>,
2018 pub provenance: CompileProvenance,
2020 pub confidence: CompileConfidence,
2022}
2023
2024#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2026#[non_exhaustive]
2027pub enum IncRootAction {
2028 Add,
2030 Remove,
2032}
2033
2034#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2036#[non_exhaustive]
2037pub enum IncRootKind {
2038 UseLib,
2040 Configured,
2042 Perl5Lib,
2044 SystemInc,
2046}
2047
2048#[derive(Debug, Clone, PartialEq, Eq)]
2050#[non_exhaustive]
2051pub struct ModuleResolutionRoot {
2052 pub path: String,
2054 pub kind: IncRootKind,
2056 pub source: String,
2058}
2059
2060impl ModuleResolutionRoot {
2061 #[must_use]
2063 pub fn new(path: impl Into<String>, kind: IncRootKind, source: impl Into<String>) -> Self {
2064 Self { path: path.into(), kind, source: source.into() }
2065 }
2066}
2067
2068#[derive(Debug, Clone, PartialEq, Eq)]
2070#[non_exhaustive]
2071pub struct ModuleRequest {
2072 pub target: Option<String>,
2074 pub kind: ModuleRequestKind,
2076 pub range: SourceLocation,
2078 pub directive_item: Option<HirId>,
2080 pub scope_id: Option<HirScopeId>,
2082 pub package_context: Option<String>,
2084 pub resolution: ModuleResolutionStatus,
2086 pub provenance: CompileProvenance,
2088 pub confidence: CompileConfidence,
2090}
2091
2092#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2094#[non_exhaustive]
2095pub enum ModuleRequestKind {
2096 Use,
2098 Require,
2100 Parent,
2102 Base,
2104}
2105
2106#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2108#[non_exhaustive]
2109pub enum ModuleResolutionStatus {
2110 Deferred,
2112 Dynamic,
2114}
2115
2116#[derive(Debug, Clone, PartialEq, Eq)]
2118#[non_exhaustive]
2119pub struct ModuleResolutionCandidate {
2120 pub request_index: usize,
2122 pub directive_item: Option<HirId>,
2124 pub request_kind: ModuleRequestKind,
2126 pub target: String,
2128 pub relative_path: String,
2130 pub roots: Vec<ModuleResolutionCandidateRoot>,
2132 pub status: ModuleResolutionCandidateStatus,
2134 pub resolved_path: Option<String>,
2136 pub range: SourceLocation,
2138 pub package_context: Option<String>,
2140 pub provenance: CompileProvenance,
2142 pub confidence: CompileConfidence,
2144}
2145
2146impl ModuleResolutionCandidate {
2147 #[must_use]
2155 pub fn cache_key(&self, resolver_epoch: u64) -> ModuleResolutionCacheKey {
2156 ModuleResolutionCacheKey {
2157 resolver_epoch,
2158 request_index: self.request_index,
2159 directive_item: self.directive_item,
2160 request_kind: self.request_kind,
2161 target: self.target.clone(),
2162 relative_path: self.relative_path.clone(),
2163 roots: self
2164 .roots
2165 .iter()
2166 .map(ModuleResolutionCacheRootKey::from_candidate_root)
2167 .collect(),
2168 range: self.range,
2169 package_context: self.package_context.clone(),
2170 }
2171 }
2172
2173 #[must_use]
2178 pub fn cache_invalidation(
2179 &self,
2180 resolver_epoch: u64,
2181 mut path_exists: impl FnMut(&str) -> bool,
2182 ) -> ModuleResolutionCacheInvalidation {
2183 let path_states = self
2184 .roots
2185 .iter()
2186 .map(|root| ModuleResolutionCandidatePathState {
2187 candidate_path: root.candidate_path.clone(),
2188 exists: path_exists(&root.candidate_path),
2189 })
2190 .collect();
2191
2192 ModuleResolutionCacheInvalidation { key: self.cache_key(resolver_epoch), path_states }
2193 }
2194}
2195
2196#[derive(Debug, Clone, PartialEq, Eq)]
2198#[non_exhaustive]
2199pub struct ModuleResolutionCandidateRoot {
2200 pub path: String,
2202 pub kind: IncRootKind,
2204 pub source: String,
2206 pub candidate_path: String,
2208 pub precedence: usize,
2210}
2211
2212#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2214#[non_exhaustive]
2215pub struct ModuleResolutionCacheKey {
2216 pub resolver_epoch: u64,
2218 pub request_index: usize,
2220 pub directive_item: Option<HirId>,
2222 pub request_kind: ModuleRequestKind,
2224 pub target: String,
2226 pub relative_path: String,
2228 pub roots: Vec<ModuleResolutionCacheRootKey>,
2230 pub range: SourceLocation,
2232 pub package_context: Option<String>,
2234}
2235
2236#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2238#[non_exhaustive]
2239pub struct ModuleResolutionCacheRootKey {
2240 pub path: String,
2242 pub kind: IncRootKind,
2244 pub source: String,
2246 pub candidate_path: String,
2248 pub precedence: usize,
2250}
2251
2252impl ModuleResolutionCacheRootKey {
2253 fn from_candidate_root(root: &ModuleResolutionCandidateRoot) -> Self {
2254 Self {
2255 path: root.path.clone(),
2256 kind: root.kind,
2257 source: root.source.clone(),
2258 candidate_path: root.candidate_path.clone(),
2259 precedence: root.precedence,
2260 }
2261 }
2262}
2263
2264#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2266#[non_exhaustive]
2267pub struct ModuleResolutionCandidatePathState {
2268 pub candidate_path: String,
2270 pub exists: bool,
2272}
2273
2274#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2276#[non_exhaustive]
2277pub struct ModuleResolutionCacheInvalidation {
2278 pub key: ModuleResolutionCacheKey,
2280 pub path_states: Vec<ModuleResolutionCandidatePathState>,
2282}
2283
2284#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2286#[non_exhaustive]
2287pub enum ModuleResolutionCandidateStatus {
2288 CandidateBuilt,
2290 Dynamic,
2292 NotFound,
2294 Resolved,
2296 TimedOut,
2298}
2299
2300#[derive(Debug, Clone, PartialEq, Eq)]
2302#[non_exhaustive]
2303pub struct CompilePhaseBlock {
2304 pub phase: CompilePhase,
2306 pub range: SourceLocation,
2308 pub scope_id: Option<HirScopeId>,
2310 pub package_context: Option<String>,
2312 pub provenance: CompileProvenance,
2314 pub confidence: CompileConfidence,
2316}
2317
2318#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2320#[non_exhaustive]
2321pub enum CompilePhase {
2322 Begin,
2324 UnitCheck,
2326 Check,
2328 Init,
2330 End,
2332 Unknown,
2334}
2335
2336#[derive(Debug, Clone, PartialEq, Eq)]
2338#[non_exhaustive]
2339pub struct CompileEnvironmentBoundary {
2340 pub kind: CompileEnvironmentBoundaryKind,
2342 pub range: SourceLocation,
2344 pub boundary_item: Option<HirId>,
2346 pub scope_id: Option<HirScopeId>,
2348 pub package_context: Option<String>,
2350 pub reason: String,
2352 pub provenance: CompileProvenance,
2354 pub confidence: CompileConfidence,
2356}
2357
2358#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2360#[non_exhaustive]
2361pub enum CompileEnvironmentBoundaryKind {
2362 DynamicRequire,
2364 DynamicPragmaArgs,
2366 DynamicIncRoot,
2368 PhaseBlockExecution,
2370 SymbolicReferenceDeref,
2372}
2373
2374#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2376#[non_exhaustive]
2377pub enum CompileProvenance {
2378 ExactAst,
2380 DesugaredAst,
2382 DynamicBoundary,
2384}
2385
2386#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2388#[non_exhaustive]
2389pub enum CompileConfidence {
2390 High,
2392 Medium,
2394 Low,
2396}
2397
2398fn import_spec_from_directive(directive: &CompileDirective, file_id: FileId) -> Option<ImportSpec> {
2399 match directive.action {
2400 CompileDirectiveAction::Use => {
2401 let module = directive.module.as_deref()?;
2402 if is_version_pragma(module) {
2403 return None;
2404 }
2405 if module == "constant" {
2406 return Some(classify_constant_import(directive, file_id));
2407 }
2408
2409 let (kind, symbols, provenance, confidence) =
2410 classify_import_args(&directive.args, module, directive.range);
2411 Some(import_spec(
2412 module.to_string(),
2413 kind,
2414 symbols,
2415 provenance,
2416 confidence,
2417 directive,
2418 file_id,
2419 ))
2420 }
2421 CompileDirectiveAction::Require => {
2422 let (module, kind, symbols, provenance, confidence) =
2423 if let Some(module) = directive.module.as_ref() {
2424 (
2425 module.clone(),
2426 ImportKind::Require,
2427 ImportSymbols::Default,
2428 Provenance::ExactAst,
2429 Confidence::High,
2430 )
2431 } else {
2432 (
2433 String::new(),
2434 ImportKind::DynamicRequire,
2435 ImportSymbols::Dynamic,
2436 Provenance::DynamicBoundary,
2437 Confidence::Low,
2438 )
2439 };
2440 Some(import_spec(module, kind, symbols, provenance, confidence, directive, file_id))
2441 }
2442 CompileDirectiveAction::No => None,
2443 }
2444}
2445
2446fn import_spec(
2447 module: String,
2448 kind: ImportKind,
2449 symbols: ImportSymbols,
2450 provenance: Provenance,
2451 confidence: Confidence,
2452 directive: &CompileDirective,
2453 file_id: FileId,
2454) -> ImportSpec {
2455 ImportSpec {
2456 module,
2457 kind,
2458 symbols,
2459 provenance,
2460 confidence,
2461 file_id: Some(file_id),
2462 anchor_id: Some(AnchorId(directive.range.start as u64)),
2463 scope_id: directive.scope_id.map(|id| ScopeId(id.index() as u64)),
2464 span_start_byte: Some(directive.range.start as u32),
2465 }
2466}
2467
2468fn classify_import_args(
2469 args: &[String],
2470 module: &str,
2471 range: SourceLocation,
2472) -> (ImportKind, ImportSymbols, Provenance, Confidence) {
2473 if args.is_empty() {
2474 let bare_len = "use ".len() + module.len() + 1;
2475 let span_len = range.end.saturating_sub(range.start);
2476 if span_len > bare_len {
2477 return (
2478 ImportKind::UseEmpty,
2479 ImportSymbols::None,
2480 Provenance::ExactAst,
2481 Confidence::High,
2482 );
2483 }
2484 return (ImportKind::Use, ImportSymbols::Default, Provenance::ExactAst, Confidence::High);
2485 }
2486
2487 let mut explicit_names = Vec::new();
2488 let mut tags = Vec::new();
2489 let mut has_dynamic_arg = false;
2490
2491 for arg in args {
2492 let trimmed = arg.trim();
2493 if trimmed == "=>" || trimmed == "," || trimmed == "\\" {
2494 continue;
2495 }
2496
2497 if let Some(inner) = parse_qw_content(trimmed) {
2498 collect_qw_import_words(inner, &mut explicit_names, &mut tags);
2499 continue;
2500 }
2501
2502 let was_quoted = is_quoted(trimmed);
2503 let unquoted = unquote(trimmed);
2504 if !was_quoted && looks_like_dynamic_import_arg(unquoted) {
2505 has_dynamic_arg = true;
2506 continue;
2507 }
2508
2509 if let Some(tag) = unquoted.strip_prefix(':') {
2510 tags.push(tag.to_string());
2511 continue;
2512 }
2513
2514 if looks_like_symbol_name(unquoted) {
2515 explicit_names.push(unquoted.to_string());
2516 }
2517 }
2518
2519 if has_dynamic_arg {
2520 return (
2521 ImportKind::UseExplicitList,
2522 ImportSymbols::Dynamic,
2523 Provenance::DynamicBoundary,
2524 Confidence::Low,
2525 );
2526 }
2527
2528 if !tags.is_empty() && explicit_names.is_empty() {
2529 return (
2530 ImportKind::UseTag,
2531 ImportSymbols::Tags(tags),
2532 Provenance::ExactAst,
2533 Confidence::High,
2534 );
2535 }
2536
2537 if !tags.is_empty() && !explicit_names.is_empty() {
2538 return (
2539 ImportKind::UseExplicitList,
2540 ImportSymbols::Mixed { tags, names: explicit_names },
2541 Provenance::ExactAst,
2542 Confidence::High,
2543 );
2544 }
2545
2546 if !explicit_names.is_empty() {
2547 return (
2548 ImportKind::UseExplicitList,
2549 ImportSymbols::Explicit(explicit_names),
2550 Provenance::ExactAst,
2551 Confidence::High,
2552 );
2553 }
2554
2555 (ImportKind::UseEmpty, ImportSymbols::None, Provenance::ExactAst, Confidence::High)
2556}
2557
2558fn classify_constant_import(directive: &CompileDirective, file_id: FileId) -> ImportSpec {
2559 let mut constant_names = Vec::new();
2560 let args = &directive.args;
2561
2562 if args.first().map(String::as_str) == Some("{") {
2563 let mut index = 1;
2564 while index < args.len() {
2565 let token = args[index].trim();
2566 if token == "}" || token == "=>" || token == "," {
2567 index += 1;
2568 continue;
2569 }
2570 if index + 1 < args.len() && args[index + 1].trim() == "=>" {
2571 constant_names.push(unquote(token).to_string());
2572 index += 3;
2573 } else {
2574 index += 1;
2575 }
2576 }
2577 } else if let Some(inner) = args.first().and_then(|arg| parse_qw_content(arg.trim())) {
2578 constant_names.extend(inner.split_whitespace().map(str::to_string));
2579 } else if let Some(name) = args.first() {
2580 let trimmed = unquote(name.trim());
2581 if looks_like_constant_name(trimmed) {
2582 constant_names.push(trimmed.to_string());
2583 }
2584 }
2585
2586 let mut seen = std::collections::HashSet::new();
2587 constant_names.retain(|name| seen.insert(name.clone()));
2588
2589 let symbols = if constant_names.is_empty() {
2590 ImportSymbols::None
2591 } else {
2592 ImportSymbols::Explicit(constant_names)
2593 };
2594
2595 import_spec(
2596 "constant".to_string(),
2597 ImportKind::UseConstant,
2598 symbols,
2599 Provenance::ExactAst,
2600 Confidence::High,
2601 directive,
2602 file_id,
2603 )
2604}
2605
2606fn collect_qw_import_words(inner: &str, explicit_names: &mut Vec<String>, tags: &mut Vec<String>) {
2607 for word in inner.split_whitespace() {
2608 if let Some(tag) = word.strip_prefix(':') {
2609 tags.push(tag.to_string());
2610 } else {
2611 explicit_names.push(word.to_string());
2612 }
2613 }
2614}
2615
2616fn is_version_pragma(module: &str) -> bool {
2617 if module.chars().next().is_some_and(|character| character.is_ascii_digit()) {
2618 return true;
2619 }
2620 module.starts_with('v')
2621 && module.len() > 1
2622 && module[1..].chars().all(|character| character.is_ascii_digit() || character == '.')
2623}
2624
2625fn parse_qw_content(value: &str) -> Option<&str> {
2626 let rest = value.strip_prefix("qw")?.trim_start();
2627 let mut chars = rest.chars();
2628 let open = chars.next()?;
2629 let close = match open {
2630 '(' => ')',
2631 '[' => ']',
2632 '{' => '}',
2633 '<' => '>',
2634 other => other,
2635 };
2636 let inner_start = open.len_utf8();
2637 let inner_end = rest.len().checked_sub(close.len_utf8())?;
2638 if inner_start > inner_end || !rest.ends_with(close) {
2639 return None;
2640 }
2641 Some(&rest[inner_start..inner_end])
2642}
2643
2644fn is_quoted(value: &str) -> bool {
2645 (value.starts_with('\'') && value.ends_with('\''))
2646 || (value.starts_with('"') && value.ends_with('"'))
2647}
2648
2649fn unquote(value: &str) -> &str {
2650 if is_quoted(value) && value.len() >= 2 { &value[1..value.len() - 1] } else { value }
2651}
2652
2653fn looks_like_dynamic_import_arg(value: &str) -> bool {
2654 value.starts_with('$')
2655 || value.starts_with('@')
2656 || value.starts_with('%')
2657 || value.starts_with('&')
2658 || value.starts_with('*')
2659}
2660
2661fn looks_like_symbol_name(value: &str) -> bool {
2662 let value = unquote(value);
2663 if value.is_empty() {
2664 return false;
2665 }
2666 if value.starts_with(':') {
2667 return true;
2668 }
2669 value
2670 .chars()
2671 .next()
2672 .is_some_and(|character| character.is_ascii_alphabetic() || character == '_')
2673}
2674
2675fn looks_like_constant_name(value: &str) -> bool {
2676 if value.is_empty() {
2677 return false;
2678 }
2679 value
2680 .chars()
2681 .next()
2682 .is_some_and(|character| character.is_ascii_alphabetic() || character == '_')
2683}
2684
2685fn module_target_to_relative_path(target: &str) -> Option<String> {
2686 let relative_path =
2687 if target.ends_with(".pm") || target.ends_with(".pl") || target.contains(['/', '\\']) {
2688 target.replace('\\', "/")
2689 } else {
2690 let canonical = target.replace('\'', "::");
2691 format!("{}.pm", canonical.replace("::", "/"))
2692 };
2693
2694 is_safe_relative_module_path(&relative_path).then_some(relative_path)
2695}
2696
2697fn normalize_module_target(target: &str) -> String {
2698 target.trim().trim_matches('"').trim_matches('\'').to_string()
2699}
2700
2701fn is_safe_relative_module_path(path: &str) -> bool {
2702 if path.is_empty() || path.starts_with('/') || path.contains(':') {
2703 return false;
2704 }
2705
2706 path.split('/').all(|segment| !matches!(segment, "" | "." | ".."))
2707}
2708
2709fn join_candidate_path(root: &str, relative_path: &str) -> String {
2710 let normalized_root = root.replace('\\', "/");
2711 let trimmed_root = normalized_root.trim_end_matches('/');
2712 if trimmed_root.is_empty() {
2713 relative_path.to_string()
2714 } else {
2715 format!("{trimmed_root}/{relative_path}")
2716 }
2717}
2718
2719#[derive(Debug, Clone, PartialEq, Eq)]
2721#[non_exhaustive]
2722pub enum HirKind {
2723 PackageDecl(PackageDecl),
2725 SubDecl(SubDecl),
2727 MethodDecl(MethodDecl),
2729 UseDecl(UseDecl),
2731 RequireDecl(RequireDecl),
2733 VariableDecl(VariableDecl),
2735 CallExpr(CallExpr),
2737 MethodCallExpr(MethodCallExpr),
2739 IndirectCallExpr(IndirectCallExpr),
2741 BarewordExpr(BarewordExpr),
2743 LiteralExpr(LiteralExpr),
2745 BlockShell(BlockShell),
2747 DynamicBoundary(DynamicBoundary),
2749}
2750
2751impl HirKind {
2752 pub const ALL_KIND_NAMES: &[&'static str] = &[
2757 "BarewordExpr",
2758 "BlockShell",
2759 "CallExpr",
2760 "DynamicBoundary",
2761 "IndirectCallExpr",
2762 "LiteralExpr",
2763 "MethodCallExpr",
2764 "MethodDecl",
2765 "PackageDecl",
2766 "RequireDecl",
2767 "SubDecl",
2768 "UseDecl",
2769 "VariableDecl",
2770 ];
2771}
2772
2773#[derive(Debug, Clone, PartialEq, Eq)]
2775#[non_exhaustive]
2776pub struct PackageDecl {
2777 pub name: String,
2779 pub name_range: SourceLocation,
2781 pub has_block: bool,
2783}
2784
2785#[derive(Debug, Clone, PartialEq, Eq)]
2787#[non_exhaustive]
2788pub struct SubDecl {
2789 pub name: Option<String>,
2791 pub name_range: Option<SourceLocation>,
2793 pub has_prototype: bool,
2795 pub has_signature: bool,
2797 pub attribute_count: usize,
2799}
2800
2801#[derive(Debug, Clone, PartialEq, Eq)]
2803#[non_exhaustive]
2804pub struct MethodDecl {
2805 pub name: String,
2807 pub has_signature: bool,
2809 pub attribute_count: usize,
2811}
2812
2813#[derive(Debug, Clone, PartialEq, Eq)]
2815#[non_exhaustive]
2816pub struct UseDecl {
2817 pub module: String,
2819 pub args: Vec<String>,
2821 pub has_filter_risk: bool,
2823}
2824
2825#[derive(Debug, Clone, PartialEq, Eq)]
2827#[non_exhaustive]
2828pub struct RequireDecl {
2829 pub target: Option<String>,
2831 pub arg_count: usize,
2833}
2834
2835#[derive(Debug, Clone, PartialEq, Eq)]
2837#[non_exhaustive]
2838pub struct VariableDecl {
2839 pub declarator: String,
2841 pub variables: Vec<VariableBinding>,
2843 pub attribute_count: usize,
2845 pub has_initializer: bool,
2847 pub is_list: bool,
2849}
2850
2851#[derive(Debug, Clone, PartialEq, Eq)]
2853#[non_exhaustive]
2854pub struct VariableBinding {
2855 pub sigil: String,
2857 pub name: String,
2859 pub range: SourceLocation,
2861}
2862
2863#[derive(Debug, Clone, PartialEq, Eq)]
2865#[non_exhaustive]
2866pub struct CallExpr {
2867 pub name: String,
2869 pub arg_count: usize,
2871 pub form: CallForm,
2873}
2874
2875#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2877#[non_exhaustive]
2878pub enum CallForm {
2879 NamedFunction,
2881 Coderef,
2883}
2884
2885#[derive(Debug, Clone, PartialEq, Eq)]
2887#[non_exhaustive]
2888pub struct MethodCallExpr {
2889 pub method: String,
2891 pub arg_count: usize,
2893 pub object_kind: &'static str,
2895}
2896
2897#[derive(Debug, Clone, PartialEq, Eq)]
2899#[non_exhaustive]
2900pub struct IndirectCallExpr {
2901 pub method: String,
2903 pub arg_count: usize,
2905 pub object_kind: &'static str,
2907}
2908
2909#[derive(Debug, Clone, PartialEq, Eq)]
2911#[non_exhaustive]
2912pub struct BarewordExpr {
2913 pub name: String,
2915}
2916
2917#[derive(Debug, Clone, PartialEq, Eq)]
2919#[non_exhaustive]
2920pub struct LiteralExpr {
2921 pub kind: LiteralKind,
2923 pub value: Option<String>,
2925 pub interpolated: Option<bool>,
2927 pub element_count: Option<usize>,
2929 pub pair_count: Option<usize>,
2931}
2932
2933#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2935#[non_exhaustive]
2936pub enum LiteralKind {
2937 Number,
2939 String,
2941 Undef,
2943 Array,
2945 Hash,
2947}
2948
2949#[derive(Debug, Clone, PartialEq, Eq)]
2951#[non_exhaustive]
2952pub struct BlockShell {
2953 pub statement_count: usize,
2955}
2956
2957#[derive(Debug, Clone, PartialEq, Eq)]
2959#[non_exhaustive]
2960pub struct DynamicBoundary {
2961 pub kind: DynamicBoundaryKind,
2963 pub reason: String,
2965}
2966
2967#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2969#[non_exhaustive]
2970pub enum DynamicBoundaryKind {
2971 CoderefCall,
2973 EvalExpression,
2975 DoExpression,
2977 DynamicStashMutation,
2979 Autoload,
2981 SymbolicReferenceDeref,
2983}