code-moniker-workspace 0.6.0

Workspace model, ports, snapshots, linkage, and change analysis for code-moniker.
Documentation
use code_moniker_core::core::code_graph::RefRecord;
use code_moniker_core::core::kinds::{REF_CALLS, REF_INSTANTIATES, REF_METHOD_CALL, REF_READS};
use code_moniker_core::core::moniker::query::bare_callable_name;
use code_moniker_core::core::moniker::{Moniker, MonikerBuilder};
use code_moniker_core::lang::kinds;
use rayon::prelude::*;
use rustc_hash::{FxHashMap, FxHashSet};

use crate::linkage::binding::{
	ExternalOrigin, ReferenceLinkageDecision, ResolutionDecision, ResolutionScope, UnknownReason,
};
use crate::linkage::catalog::CandidateCatalog;
use crate::linkage::catalog::LinkageQuery;
use crate::linkage::catalog::ReferenceLocations;
use crate::linkage::catalog::{SymbolOrdinal, SymbolSet};
use crate::linkage::language;
use crate::linkage::language::{
	CIncludeVisibility, PythonBindings, classify_c_preprocessor_tokens,
	classify_c_unindexed_external_dependencies, refine_c_include_visibility,
};
use crate::linkage::resolve::ManifestPolicy;
use crate::linkage::resolve::WorkspacePackageIndex;
use crate::linkage::resolve::refine_python_bindings;
use crate::linkage::source_groups::{LinkPermission, SourceGroupPolicy};
use crate::snapshot::{RecordTable, ReferenceId, ReferenceRecord, ResolutionEvidence};
use crate::source::CodeIndexMaterial;

mod receivers;

pub(in crate::linkage) use receivers::{
	MethodCallReference, MethodTable, ReceiverFieldTables, resolve_method_through_supers,
};
use receivers::{
	build_receiver_field_tables, pending_receiver_chains, refine_receiver_chains,
	refine_receiver_fields, refine_structural_receivers,
};

pub(in crate::linkage) struct LinkageRefiner<'a> {
	pub(in crate::linkage) material: &'a CodeIndexMaterial,
	methods: &'a MethodTable,
	pub(in crate::linkage) candidates: &'a CandidateCatalog,
	pub(in crate::linkage) locations: &'a ReferenceLocations,
	source_groups: &'a SourceGroupPolicy,
	packages: &'a WorkspacePackageIndex,
	manifests: &'a ManifestPolicy,
}

pub(in crate::linkage) struct RefinementPolicies<'a> {
	source_groups: &'a SourceGroupPolicy,
	packages: &'a WorkspacePackageIndex,
	manifests: &'a ManifestPolicy,
}

#[derive(Clone, Copy)]
pub(in crate::linkage) struct DecisionSelection<'a> {
	decision_indices: &'a [usize],
	changed_references: Option<&'a FxHashSet<ReferenceId>>,
}

impl<'a> DecisionSelection<'a> {
	fn new(
		decision_indices: &'a [usize],
		changed_references: Option<&'a FxHashSet<ReferenceId>>,
	) -> Self {
		Self {
			decision_indices,
			changed_references,
		}
	}

	pub(in crate::linkage) fn indices(self) -> &'a [usize] {
		self.decision_indices
	}

	pub(in crate::linkage) fn includes(self, reference: &ReferenceId) -> bool {
		self.changed_references
			.is_none_or(|changed| changed.contains(reference))
	}
}

impl<'a> RefinementPolicies<'a> {
	pub(in crate::linkage) fn new(
		source_groups: &'a SourceGroupPolicy,
		packages: &'a WorkspacePackageIndex,
		manifests: &'a ManifestPolicy,
	) -> Self {
		Self {
			source_groups,
			packages,
			manifests,
		}
	}
}

impl<'a> LinkageRefiner<'a> {
	pub(in crate::linkage) fn new(
		material: &'a CodeIndexMaterial,
		methods: &'a MethodTable,
		candidates: &'a CandidateCatalog,
		locations: &'a ReferenceLocations,
		policies: RefinementPolicies<'a>,
	) -> Self {
		Self {
			material,
			methods,
			candidates,
			locations,
			source_groups: policies.source_groups,
			packages: policies.packages,
			manifests: policies.manifests,
		}
	}

	pub(in crate::linkage) fn refine(
		&self,
		decisions: &mut [ReferenceLinkageDecision],
		references: &RecordTable<ReferenceRecord>,
	) {
		refine_decisions(self, decisions, references, None);
	}

	pub(in crate::linkage) fn refine_changed(
		&self,
		decisions: &mut [ReferenceLinkageDecision],
		references: &RecordTable<ReferenceRecord>,
		changed_references: &FxHashSet<ReferenceId>,
	) {
		refine_decisions(self, decisions, references, Some(changed_references));
	}
}

fn refine_decisions(
	linkage: &LinkageRefiner<'_>,
	decisions: &mut [ReferenceLinkageDecision],
	references: &RecordTable<ReferenceRecord>,
	changed_references: Option<&FxHashSet<ReferenceId>>,
) {
	let (c_decisions, python_decisions, typescript_decisions) =
		partition_language_decisions(linkage, decisions);
	let c_selection = DecisionSelection::new(&c_decisions, changed_references);
	let python_selection = DecisionSelection::new(&python_decisions, changed_references);
	language::classify_runtime_imports(
		linkage.material,
		decisions,
		references,
		changed_references,
		&python_decisions,
	);
	language::refine_external_reexports(
		linkage.material,
		decisions,
		references,
		changed_references,
		&typescript_decisions,
	);
	let c_includes = (!c_decisions.is_empty()).then(|| CIncludeVisibility::build(linkage.material));
	if let Some(c_includes) = &c_includes {
		refine_c_include_visibility(linkage, c_includes, decisions, references, c_selection);
		classify_c_preprocessor_tokens(linkage, c_includes, decisions, references, c_selection);
	}
	let bindings = if python_decisions.is_empty() {
		None
	} else {
		let bootstrap = build_receiver_field_tables(linkage, decisions, references);
		let bindings = PythonBindings::build(
			linkage.material,
			linkage.candidates,
			decisions,
			references,
			&python_decisions,
		);
		refine_python_bindings(
			&bindings,
			linkage,
			&bootstrap,
			decisions,
			references,
			python_selection,
		);
		Some(bindings)
	};
	let tables = build_receiver_field_tables(linkage, decisions, references);
	refine_receiver_fields(linkage, &tables, decisions, references, changed_references);
	if let Some(bindings) = &bindings {
		refine_python_bindings(
			bindings,
			linkage,
			&tables,
			decisions,
			references,
			python_selection,
		);
	}
	let pending = pending_receiver_chains(decisions, references, changed_references);
	refine_receiver_chains(linkage, &tables, decisions, references, pending);
	refine_structural_receivers(linkage, decisions, references, changed_references);
	language::classify_open_references(linkage.material, decisions, references, changed_references);
	if let Some(c_includes) = &c_includes {
		classify_c_unindexed_external_dependencies(
			linkage,
			c_includes,
			decisions,
			references,
			c_selection,
		);
	}
}

fn partition_language_decisions(
	linkage: &LinkageRefiner<'_>,
	decisions: &[ReferenceLinkageDecision],
) -> (Vec<usize>, Vec<usize>, Vec<usize>) {
	let mut c = Vec::new();
	let mut python = Vec::new();
	let mut typescript = Vec::new();
	for (decision_idx, decision) in decisions.iter().enumerate() {
		let Some(location) = linkage.locations.get(decision.reference_idx()) else {
			continue;
		};
		let Some(file) = linkage.material.files.get(location.source_file) else {
			continue;
		};
		match file.lang {
			code_moniker_core::lang::Lang::C => c.push(decision_idx),
			code_moniker_core::lang::Lang::Python => python.push(decision_idx),
			code_moniker_core::lang::Lang::Ts => typescript.push(decision_idx),
			_ => {}
		}
	}
	(c, python, typescript)
}