use std::collections::BTreeMap;
use std::sync::Arc;
use pedant_types::ResolutionTier;
use crate::ir::semantic::{
SemanticContext, SemanticDefinitionEdge, SemanticDefinitionTargets, SemanticSite,
};
use crate::observe::{self, Observation};
use crate::resolution::rust::snapshot::RustResolutionSnapshot;
use super::claim;
use super::error::RustResolutionError;
use super::pipeline::{self, Inventory};
use super::promotion::{Promoted, Promotion, PromotionSite};
use super::target::RustTargetResolution;
struct Draft {
candidates: Vec<usize>,
enumerated: bool,
path: Arc<str>,
}
struct Answer {
candidates: Box<[usize]>,
enumerated: bool,
path: Arc<str>,
}
struct SemanticPromotion {
answers: BTreeMap<PromotionSite, Answer>,
}
impl Promotion for SemanticPromotion {
fn promote(&self, site: PromotionSite) -> Option<Promoted> {
let answer = self.answers.get(&site)?;
observe::record(Observation::Promotion(&answer.path));
Some(Promoted {
candidates: answer.candidates.clone(),
enumerated: answer.enumerated,
})
}
}
pub(super) fn resolve(
snapshot: &RustResolutionSnapshot,
context: &SemanticContext,
) -> Result<RustTargetResolution, RustResolutionError> {
if let Some(warning) = snapshot.warnings().first() {
return Err(RustResolutionError::SemanticSharedSourceMismatch {
warning: warning.clone(),
});
}
let claim = claim::state(snapshot);
let verified = context.verify_snapshot(&claim).map_err(|mismatch| {
RustResolutionError::SemanticContextMismatch {
reason: mismatch.to_string().into_boxed_str(),
}
})?;
let targets = context.definition_targets(&verified).map_err(|mismatch| {
RustResolutionError::SemanticContextMismatch {
reason: mismatch.to_string().into_boxed_str(),
}
})?;
if targets.fingerprint != claim.fingerprint {
return Err(RustResolutionError::SemanticContextMismatch {
reason: Box::from("the definition targets answer for another snapshot"),
});
}
let inventory = pipeline::inventory(snapshot, ResolutionTier::Semantic)?;
let promotion = build(snapshot, &inventory, &targets);
pipeline::finish(inventory, snapshot, &promotion)
}
fn build(
snapshot: &RustResolutionSnapshot,
inventory: &Inventory,
targets: &SemanticDefinitionTargets,
) -> SemanticPromotion {
let definitions = definition_sites(inventory);
let mut drafts: BTreeMap<PromotionSite, Draft> = BTreeMap::new();
for edge in &targets.edges {
join(snapshot, &definitions, &mut drafts, edge);
}
SemanticPromotion {
answers: drafts
.into_iter()
.map(|(site, draft)| (site, answer(draft)))
.collect(),
}
}
fn answer(draft: Draft) -> Answer {
let mut candidates = draft.candidates;
candidates.sort_unstable();
candidates.dedup();
Answer {
candidates: candidates.into_boxed_slice(),
enumerated: draft.enumerated,
path: draft.path,
}
}
fn join(
snapshot: &RustResolutionSnapshot,
definitions: &BTreeMap<PromotionSite, usize>,
drafts: &mut BTreeMap<PromotionSite, Draft>,
edge: &SemanticDefinitionEdge,
) {
let (Some(source), Some(target)) = (
promotion_site(snapshot, &edge.source),
promotion_site(snapshot, &edge.target),
) else {
return;
};
let Some(slot) = definitions.get(&target).copied() else {
return;
};
let draft = drafts.entry(source).or_insert_with(|| Draft {
candidates: Vec::new(),
enumerated: edge.enumerated,
path: Arc::clone(&edge.source.path),
});
draft.candidates.push(slot);
draft.enumerated |= edge.enumerated;
}
fn definition_sites(inventory: &Inventory) -> BTreeMap<PromotionSite, usize> {
inventory
.index
.slots()
.iter()
.enumerate()
.map(|(slot, definition)| (definition.site, slot))
.collect()
}
fn promotion_site(snapshot: &RustResolutionSnapshot, site: &SemanticSite) -> Option<PromotionSite> {
Some(PromotionSite {
unit: site.unit,
file: file_index(snapshot, &site.path)?,
line: site.line,
column: site.column,
})
}
fn file_index(snapshot: &RustResolutionSnapshot, path: &str) -> Option<usize> {
snapshot
.sources()
.binary_search_by(|source| source.path().cmp(path))
.ok()
}