use std::sync::Arc;
use pedant_types::{
CandidateInput, DefinitionHandle, ResolutionCertainty, ResolutionGap, ResolutionReportBuilder,
};
use crate::resolution::go::limits::GoResolutionLimits;
use crate::resolution::identity::position;
use super::denotation::Denotation;
use super::error::GoResolutionError;
use super::index::{Index, Slot};
pub(super) fn write(
builder: &mut ResolutionReportBuilder,
index: &Index,
stated: (&[Denotation], usize),
limits: GoResolutionLimits,
) -> Result<(), GoResolutionError> {
let (denotations, unit) = stated;
for denotation in denotations {
write_one(builder, index, (denotation, unit), limits)?;
}
Ok(())
}
fn write_one(
builder: &mut ResolutionReportBuilder,
index: &Index,
stated: (&Denotation, usize),
limits: GoResolutionLimits,
) -> Result<(), GoResolutionError> {
let (denotation, unit) = stated;
check_candidate_capacity(denotation, limits)?;
let named = named_definitions(index, denotation)?;
let conditional = is_conditional(&named, denotation);
let certainty = certainty_of(denotation, conditional);
let handle = builder.add_reference(
&index.unit_handle(unit)?,
denotation.kind,
Arc::clone(&denotation.text),
denotation.span.clone(),
enclosing(index, denotation)?.as_ref(),
)?;
let retained = retained_candidates(&named, certainty);
builder.set_resolution(&handle, retained, gaps(denotation, conditional))?;
Ok(())
}
fn named_definitions<'index>(
index: &'index Index,
denotation: &Denotation,
) -> Result<Box<[&'index Slot]>, GoResolutionError> {
denotation
.candidates
.iter()
.map(|slot| recorded(index, *slot))
.collect()
}
fn recorded(index: &Index, slot: usize) -> Result<&Slot, GoResolutionError> {
index
.slot(slot)
.ok_or_else(|| GoResolutionError::DefinitionMapping {
slot: position(slot),
reason: Box::from("no definition was recorded at this position"),
})
}
fn check_candidate_capacity(
denotation: &Denotation,
limits: GoResolutionLimits,
) -> Result<(), GoResolutionError> {
let ceiling = limits.max_candidates_per_reference;
let held = u64::try_from(denotation.candidates.len()).unwrap_or(u64::MAX);
match held > u64::from(ceiling) {
true => Err(GoResolutionError::CandidateLimitExceeded { limit: ceiling }),
false => Ok(()),
}
}
fn retained_candidates(named: &[&Slot], certainty: ResolutionCertainty) -> Box<[CandidateInput]> {
named
.iter()
.map(|slot| CandidateInput::new(slot.handle.clone(), certainty))
.collect()
}
fn is_conditional(named: &[&Slot], denotation: &Denotation) -> bool {
denotation.conditional || named.iter().any(|found| found.conditional)
}
fn certainty_of(denotation: &Denotation, conditional: bool) -> ResolutionCertainty {
match (
denotation.candidates.len(),
conditional,
denotation.possible_only,
) {
(1, false, false) => ResolutionCertainty::Resolved,
_ => ResolutionCertainty::Possible,
}
}
fn gaps(denotation: &Denotation, conditional: bool) -> Box<[ResolutionGap]> {
let fanned =
denotation.candidates.len() > 1 && denotation.gap != Some(ResolutionGap::Ambiguous);
let ambiguous = fanned.then_some(ResolutionGap::Ambiguous);
let stated = conditional.then_some(ResolutionGap::ConditionalCompilation);
denotation
.gap
.into_iter()
.chain(ambiguous)
.chain(stated)
.collect()
}
fn enclosing(
index: &Index,
denotation: &Denotation,
) -> Result<Option<DefinitionHandle>, GoResolutionError> {
denotation
.enclosing
.map(|slot| recorded(index, slot).map(|found| found.handle.clone()))
.transpose()
}