code-moniker-workspace 0.7.1

Workspace model, ports, snapshots, linkage, and change analysis for code-moniker.
Documentation
use code_moniker_core::core::moniker::query::bare_callable_name;
use code_moniker_core::core::moniker::{Moniker, MonikerBuilder};
use code_moniker_core::lang::{build_manifest::Manifest, kinds};
use rustc_hash::FxHashSet;

use crate::linkage::catalog::LinkageCandidate;
use crate::linkage::catalog::LinkageQuery;
use crate::linkage::language::generic_matches;
use crate::snapshot::{RecordTable, ReferenceId, ReferenceRecord};
use crate::source::CodeIndexMaterial;

pub(super) fn matches(query: &LinkageQuery<'_>, candidate: &LinkageCandidate<'_>) -> bool {
	generic_matches(query, candidate) || external_package_symbol_match(query, candidate)
}

fn external_package_symbol_match(
	query: &LinkageQuery<'_>,
	candidate: &LinkageCandidate<'_>,
) -> bool {
	if query
		.target_first
		.is_none_or(|segment| segment.kind != kinds::EXTERNAL_PKG)
	{
		return false;
	}
	let Some(query_name) = query.call_name.map(str::as_bytes).or_else(|| {
		query
			.target_last
			.map(|segment| bare_callable_name(segment.name))
	}) else {
		return false;
	};
	let candidate_name = candidate.call_name.or_else(|| {
		candidate
			.last_segment
			.map(|segment| bare_callable_name(segment.name))
	});
	candidate_name == Some(query_name)
}

pub(super) fn package_prefix(target: &Moniker) -> Option<String> {
	let head = target.as_view().segments().next()?;
	if head.kind != kinds::EXTERNAL_PKG {
		return None;
	}
	std::str::from_utf8(head.name).ok().map(str::to_string)
}

pub(super) fn source_declares_external_package(
	manifest: Manifest,
	deps: &FxHashSet<String>,
	package_prefix: &str,
	_query_confidence: Option<&str>,
	_workspace_declares_package: impl Fn(&str) -> bool,
) -> bool {
	if manifest != Manifest::PackageJson {
		return false;
	}
	deps.contains(&format!("{}\0{package_prefix}", manifest.tag()))
}

pub(super) fn refine_external_reexports(
	material: &CodeIndexMaterial,
	decisions: &mut [crate::linkage::binding::ReferenceLinkageDecision],
	references: &RecordTable<ReferenceRecord>,
	changed_references: Option<&FxHashSet<ReferenceId>>,
	decision_indices: &[usize],
) {
	type ExternalBinding = (crate::linkage::binding::ExternalOrigin, Moniker);
	let mut aliases = rustc_hash::FxHashMap::<(Moniker, Vec<u8>), ExternalBinding>::default();
	for &decision_idx in decision_indices {
		let decision = &decisions[decision_idx];
		let reference = &references[decision.reference_idx()];
		if reference.kind != "reexports" {
			continue;
		}
		let crate::linkage::binding::ReferenceLinkageDecision::External { origin, target, .. } =
			decision
		else {
			continue;
		};
		let Some(target) = target
			.clone()
			.or_else(|| material.reference_target(&reference.id).cloned())
		else {
			continue;
		};
		if let Some(key) = exported_binding_key(material, reference) {
			aliases.insert(key, (*origin, target));
		}
	}

	loop {
		let mut changed = false;
		for &decision_idx in decision_indices {
			let decision = &decisions[decision_idx];
			let reference = &references[decision.reference_idx()];
			if reference.kind != "reexports" {
				continue;
			}
			let Some(requested) = material.reference_target(&reference.id) else {
				continue;
			};
			let Some(target) = binding_key(requested)
				.and_then(|key| aliases.get(&key))
				.cloned()
			else {
				continue;
			};
			let Some(key) = exported_binding_key(material, reference) else {
				continue;
			};
			changed |= aliases.insert(key, target.clone()).as_ref() != Some(&target);
		}
		if !changed {
			break;
		}
	}

	for &decision_idx in decision_indices {
		let decision = &mut decisions[decision_idx];
		if changed_references.is_some_and(|changed| !changed.contains(decision.reference())) {
			continue;
		}
		let reference_idx = decision.reference_idx();
		let reference = &references[reference_idx];
		let Some(requested) = material.reference_target(&reference.id) else {
			continue;
		};
		let Some((origin, target)) = binding_key(requested).and_then(|key| aliases.get(&key))
		else {
			continue;
		};
		*decision = crate::linkage::binding::ReferenceLinkageDecision::external_target(
			*origin,
			reference_idx,
			reference.id,
			reexport_target(target, requested),
		);
	}
}

fn exported_binding_key(
	material: &CodeIndexMaterial,
	reference: &ReferenceRecord,
) -> Option<(Moniker, Vec<u8>)> {
	let owner = material.symbol_moniker(&reference.source_symbol)?.clone();
	let name = reference
		.alias
		.as_deref()
		.filter(|alias| !alias.is_empty())
		.map(|alias| alias.as_bytes().to_vec())
		.or_else(|| {
			material
				.reference_target(&reference.id)?
				.as_view()
				.segments()
				.last()
				.map(|segment| bare_callable_name(segment.name).to_vec())
		})?;
	Some((owner, name))
}

fn binding_key(target: &Moniker) -> Option<(Moniker, Vec<u8>)> {
	let segment = target.as_view().segments().last()?;
	Some((target.parent()?, bare_callable_name(segment.name).to_vec()))
}

fn reexport_target(alias: &Moniker, requested: &Moniker) -> Moniker {
	let Some(alias_last) = alias.as_view().segments().last() else {
		return alias.clone();
	};
	let Some(requested_last) = requested.as_view().segments().last() else {
		return alias.clone();
	};
	if bare_callable_name(alias_last.name) != bare_callable_name(requested_last.name) {
		return alias.clone();
	}
	let Some(owner) = alias.parent() else {
		return alias.clone();
	};
	MonikerBuilder::from_view(owner.as_view())
		.segment(requested_last.kind, requested_last.name)
		.build()
}