use std::collections::HashMap;
use super::VersioningError;
use super::lifecycle::{Availability, Site, SiteKind, availability};
use super::timeline::VersionTimeline;
use crate::error::ParserResult;
use crate::hir::{Definition, Specification, TypeDcl, is_skipped};
struct WireClaim {
decl: String,
wire: String,
from: usize,
until: usize,
}
struct ClaimWalker<'a> {
timeline: Option<&'a VersionTimeline>,
containers: HashMap<String, Vec<WireClaim>>,
}
pub(super) fn check(spec: &Specification, timeline: Option<&VersionTimeline>) -> ParserResult<()> {
let mut walker = ClaimWalker {
timeline,
containers: HashMap::new(),
};
walker.walk(&spec.0, "", Availability::full())?;
walker.report()
}
impl ClaimWalker<'_> {
fn interval(&self, site: &Site, parent: Availability) -> ParserResult<Availability> {
match self.timeline {
Some(timeline) => availability(site, parent, timeline),
None => Ok(Availability::full()),
}
}
fn report(self) -> ParserResult<()> {
for (container, claims) in &self.containers {
for (index, first) in claims.iter().enumerate() {
for second in claims.iter().skip(index + 1) {
if first.wire != second.wire {
continue;
}
let overlap_from = first.from.max(second.from);
if overlap_from >= first.until.min(second.until) {
continue;
}
let version = match self.timeline {
Some(timeline) => timeline
.iter()
.find(|(id, _)| id.index() == overlap_from)
.map(|(_, def)| def.value.clone()),
None => None,
};
return Err(VersioningError::DuplicateWireName {
container: container.clone(),
wire: first.wire.clone(),
first: first.decl.clone(),
second: second.decl.clone(),
version,
}
.into());
}
}
}
Ok(())
}
fn walk(
&mut self,
definitions: &[Definition],
path: &str,
parent: Availability,
) -> ParserResult<()> {
for definition in definitions {
match definition {
Definition::ModuleDcl(module) => {
let here = super::paths::join(path, &module.ident);
let site = Site::new(SiteKind::Module, &here, path, &module.annotations);
let inner = self.interval(&site, parent)?;
self.walk(&module.definition, &here, inner)?;
}
Definition::TypeDcl(TypeDcl::ConstrTypeDcl(constr)) => {
self.walk_type(constr, path, parent)?;
}
Definition::ExceptDcl(except) => {
let here = super::paths::join(path, &except.ident);
let site = Site::new(SiteKind::Type, &here, path, &except.annotations);
let inner = self.interval(&site, parent)?;
let members = &except.member;
let annotations = &except.annotations;
self.claim_members(&here, inner, annotations, members)?;
}
_ => {}
}
}
Ok(())
}
fn walk_type(
&mut self,
constr: &crate::hir::ConstrTypeDcl,
path: &str,
parent: Availability,
) -> ParserResult<()> {
match constr {
crate::hir::ConstrTypeDcl::StructDcl(def) => {
let here = super::paths::join(path, &def.ident);
let site = Site::new(SiteKind::Type, &here, path, &def.annotations);
let inner = self.interval(&site, parent)?;
self.claim_members(&here, inner, &def.annotations, &def.member)?;
}
crate::hir::ConstrTypeDcl::EnumDcl(def) => {
let here = super::paths::join(path, &def.ident);
let site = Site::new(SiteKind::Type, &here, path, &def.annotations);
let inner = self.interval(&site, parent)?;
for member in &def.member {
let member_site = Site::new(
SiteKind::Enumerator,
&super::paths::join(&here, &member.ident),
&here,
&member.annotations,
);
let member_avail = self.interval(&member_site, inner)?;
let wire = crate::hir::effective_wire_name(
&member.ident,
&member.annotations,
&def.annotations,
);
self.push(&here, member_site.path, wire, member_avail);
}
}
crate::hir::ConstrTypeDcl::UnionDef(def) => {
let here = super::paths::join(path, &def.ident);
let site = Site::new(SiteKind::Type, &here, path, &def.annotations);
let inner = self.interval(&site, parent)?;
for case in &def.case {
let Some(name) = declarator_ident(&case.element.value) else {
continue;
};
let member_site = Site::new(
SiteKind::Member,
&super::paths::join(&here, &name),
&here,
&case.element.annotations,
);
let member_avail = self.interval(&member_site, inner)?;
let wire = crate::hir::effective_wire_name(
&name,
&case.element.annotations,
&def.annotations,
);
self.push(&here, member_site.path, wire, member_avail);
}
}
_ => {}
}
Ok(())
}
fn claim_members(
&mut self,
container: &str,
container_avail: Availability,
container_annotations: &[crate::hir::Annotation],
members: &[crate::hir::Member],
) -> ParserResult<()> {
for member in members {
if is_skipped(&member.annotations) {
continue;
}
let site = Site::new(
SiteKind::Member,
&super::paths::join(container, &super::paths::declarators_name(&member.ident)),
container,
&member.annotations,
);
let member_avail = self.interval(&site, container_avail)?;
for decl in &member.ident {
let Some(name) = declarator_ident(decl) else {
continue;
};
let wire = crate::hir::effective_wire_name(
&name,
&member.annotations,
container_annotations,
);
self.push(
container,
super::paths::join(container, &name),
wire,
member_avail,
);
}
}
Ok(())
}
fn push(&mut self, container: &str, decl: String, wire: String, avail: Availability) {
self.containers
.entry(container.to_string())
.or_default()
.push(WireClaim {
decl,
wire,
from: avail.from,
until: avail.until,
});
}
}
fn declarator_ident(decl: &crate::hir::Declarator) -> Option<String> {
match decl {
crate::hir::Declarator::SimpleDeclarator(simple) => Some(simple.0.clone()),
crate::hir::Declarator::ArrayDeclarator(array) => Some(array.ident.clone()),
}
}