use std::sync::Arc;
use pedant_syntax::go::GoDeclarationKind;
use pedant_types::{Language, ResolutionReportBuilder, SourceSpan, SymbolKind};
use crate::resolution::go::binding_fact::GoBindingRecord;
use crate::resolution::go::declaration_fact::GoDeclarationRecord;
use crate::resolution::go::facts::GoSourceFacts;
use crate::resolution::go::unit::GoResolutionUnit;
use crate::resolution::identity::{index_of, position};
use super::corpus::{Corpus, UnitSource, conditional};
use super::error::GoResolutionError;
use super::index::{self, EmbeddedType, Embedding, Index, Slot, TypeName, UnitIndex};
use super::lookup::{self, Outcome};
use super::target::unit_key;
use super::types;
#[derive(Clone, Copy)]
struct Site<'a> {
unit: usize,
held: UnitSource<'a>,
}
pub(super) fn state(
builder: &mut ResolutionReportBuilder,
corpus: &Corpus<'_>,
) -> Result<Index, GoResolutionError> {
let mut index = Index::default();
for (position, unit) in corpus.units().iter().enumerate() {
let opened = open_unit(builder, &mut index, corpus, (position, unit))?;
index.open(opened);
}
for (position, unit) in corpus.units().iter().enumerate() {
state_members(builder, &mut index, corpus, (position, unit))?;
}
for (position, unit) in corpus.units().iter().enumerate() {
state_embeddings(&mut index, corpus, (position, unit))?;
}
for (position, unit) in corpus.units().iter().enumerate() {
state_methods(builder, &mut index, corpus, (position, unit))?;
}
Ok(index)
}
fn open_unit(
builder: &mut ResolutionReportBuilder,
index: &mut Index,
corpus: &Corpus<'_>,
stated: (usize, &GoResolutionUnit),
) -> Result<UnitIndex, GoResolutionError> {
let (position, unit) = stated;
let handle = builder.add_unit(Language::Go, unit_key(unit), Arc::from(unit.import_path()))?;
let span = package_span(corpus, unit)?;
let name: Arc<str> = Arc::from(unit.package_name());
let slot = index.push(Slot {
handle: builder.add_definition(
&handle,
SymbolKind::Package,
Arc::clone(&name),
span.clone(),
None,
)?,
kind: SymbolKind::Package,
declared: None,
unit: position,
name,
site: span,
holder: None,
conditional: false,
pointer_receiver: false,
general_terms: false,
result: None,
});
Ok(UnitIndex::new(handle, slot))
}
fn package_span(
corpus: &Corpus<'_>,
unit: &GoResolutionUnit,
) -> Result<SourceSpan, GoResolutionError> {
let held = corpus.sources_of(unit)?;
let stated = held
.first()
.and_then(|source| Some((source.path, source.source.facts().package_span()?)));
let (path, span) = stated.ok_or_else(|| GoResolutionError::UnitMapping {
unit: unit.id().index(),
reason: Box::from("no source of this package context states a package clause"),
})?;
Ok(index::site(path, span))
}
fn declarations(held: UnitSource<'_>) -> impl Iterator<Item = (u32, &GoDeclarationRecord)> {
held.source
.facts()
.declarations()
.iter()
.enumerate()
.map(|(ordinal, record)| (position(ordinal), record))
}
fn state_members(
builder: &mut ResolutionReportBuilder,
index: &mut Index,
corpus: &Corpus<'_>,
stated: (usize, &GoResolutionUnit),
) -> Result<(), GoResolutionError> {
let (unit, held) = stated;
for source in corpus.sources_of(held)?.iter() {
state_source_members(
builder,
index,
Site {
unit,
held: *source,
},
)?;
}
Ok(())
}
fn state_source_members(
builder: &mut ResolutionReportBuilder,
index: &mut Index,
site: Site<'_>,
) -> Result<(), GoResolutionError> {
let members = declarations(site.held).filter(|(_, record)| !is_method(record));
for declared in members {
state_member(builder, index, site, declared)?;
}
Ok(())
}
fn is_method(record: &GoDeclarationRecord) -> bool {
record.kind() == GoDeclarationKind::Method
}
fn state_member(
builder: &mut ResolutionReportBuilder,
index: &mut Index,
site: Site<'_>,
declared: (u32, &GoDeclarationRecord),
) -> Result<(), GoResolutionError> {
let (_, record) = declared;
let parent = member_parent(index, site, record);
let slot = state_definition(builder, index, site, declared, parent)?;
record_membership(index, site, (record, slot))
}
fn member_parent(index: &Index, site: Site<'_>, record: &GoDeclarationRecord) -> Option<usize> {
let unit = index.unit(site.unit)?;
match record.parent() {
Some(parent) => unit.declared(site.held.path, parent),
None => Some(unit.package),
}
}
fn record_membership(
index: &mut Index,
site: Site<'_>,
stated: (&GoDeclarationRecord, usize),
) -> Result<(), GoResolutionError> {
let (record, slot) = stated;
let owner = holder_name(site, record);
let tables = index.tables_mut(site.unit)?;
match owner {
None => tables.declare(record.name(), slot),
Some(holder) => tables.hold(holder, slot),
}
Ok(())
}
fn holder_name<'a>(site: Site<'a>, record: &GoDeclarationRecord) -> Option<&'a str> {
record
.parent()
.and_then(|parent| facts_of(site).declarations().get(index_of(parent)))
.map(GoDeclarationRecord::name)
}
fn facts_of(site: Site<'_>) -> &GoSourceFacts {
site.held.source.facts()
}
fn state_embeddings(
index: &mut Index,
corpus: &Corpus<'_>,
stated: (usize, &GoResolutionUnit),
) -> Result<(), GoResolutionError> {
let (unit, held) = stated;
for source in corpus.sources_of(held)?.iter() {
state_source_embeddings(
index,
Site {
unit,
held: *source,
},
)?;
}
Ok(())
}
fn state_source_embeddings(index: &mut Index, site: Site<'_>) -> Result<(), GoResolutionError> {
let embeddings = declarations(site.held)
.filter(|(_, record)| record.kind() == GoDeclarationKind::EmbeddedField);
for (_, record) in embeddings {
state_embedding(index, site, record)?;
}
Ok(())
}
fn state_embedding(
index: &mut Index,
site: Site<'_>,
record: &GoDeclarationRecord,
) -> Result<(), GoResolutionError> {
let stated = holder_name(site, record).zip(embedded_type(index, site, record));
match stated {
None => Ok(()),
Some((parent, embedded)) => index.tables_mut(site.unit).map(|tables| {
tables.embed(
parent,
Embedding {
embedded,
pointer: record.embedded_pointer(),
},
)
}),
}
}
fn embedded_type(
index: &Index,
site: Site<'_>,
record: &GoDeclarationRecord,
) -> Option<EmbeddedType> {
let name = record.embedded_name()?;
let imports = site.held.imports;
let outcome = match record.embedded_qualifier() {
Some(qualifier) => imports
.named(qualifier)
.map(|target| lookup::qualified(index, target, name))
.unwrap_or(Outcome::Missing),
None => lookup::bare(index, (site.unit, imports), name),
};
let Outcome::Found(found) = outcome else {
return None;
};
types::unique_type(index, &found, |_, slot| EmbeddedType {
unit: slot.unit,
name: Arc::clone(&slot.name),
})
}
fn state_methods(
builder: &mut ResolutionReportBuilder,
index: &mut Index,
corpus: &Corpus<'_>,
stated: (usize, &GoResolutionUnit),
) -> Result<(), GoResolutionError> {
let (unit, held) = stated;
for source in corpus.sources_of(held)?.iter() {
state_source_methods(
builder,
index,
Site {
unit,
held: *source,
},
)?;
}
Ok(())
}
fn state_source_methods(
builder: &mut ResolutionReportBuilder,
index: &mut Index,
site: Site<'_>,
) -> Result<(), GoResolutionError> {
let methods = declarations(site.held).filter(|(_, record)| is_method(record));
for declared in methods {
state_method(builder, index, site, declared)?;
}
Ok(())
}
fn state_method(
builder: &mut ResolutionReportBuilder,
index: &mut Index,
site: Site<'_>,
declared: (u32, &GoDeclarationRecord),
) -> Result<(), GoResolutionError> {
let (_, record) = declared;
let receiver = receiver_type(index, site, (facts_of(site), record));
let slot = state_definition(
builder,
index,
site,
declared,
receiver.map(|(held, _)| held),
)?;
hold_method(index, site, receiver, (record.name(), slot))
}
fn receiver_type<'a>(
index: &Index,
site: Site<'_>,
declared: (&'a GoSourceFacts, &GoDeclarationRecord),
) -> Option<(usize, &'a str)> {
let (facts, record) = declared;
let bound = facts.bindings().get(index_of(record.receiver()?))?;
let name = bound.type_name()?;
let unit = index.unit(site.unit)?;
unit.named(name)
.iter()
.find(|slot| index.slot(**slot).is_some_and(Slot::is_type))
.map(|slot| (*slot, name))
}
fn hold_method(
index: &mut Index,
site: Site<'_>,
receiver: Option<(usize, &str)>,
stated: (&str, usize),
) -> Result<(), GoResolutionError> {
let (_, slot) = stated;
match receiver {
None => Ok(()),
Some((_, owner)) => index
.tables_mut(site.unit)
.map(|tables| tables.hold(owner, slot)),
}
}
fn state_definition(
builder: &mut ResolutionReportBuilder,
index: &mut Index,
site: Site<'_>,
declared: (u32, &GoDeclarationRecord),
parent: Option<usize>,
) -> Result<usize, GoResolutionError> {
let (ordinal, record) = declared;
let held = parent
.and_then(|slot| index.slot(slot))
.map(|slot| slot.handle.clone());
let name: Arc<str> = Arc::from(record.name());
let handle = builder.add_definition(
&index.unit_handle(site.unit)?,
symbol_kind(record.kind()),
Arc::clone(&name),
index::site(site.held.path, record.span()),
held.as_ref(),
)?;
let slot = index.push(Slot {
handle,
kind: symbol_kind(record.kind()),
declared: Some(record.kind()),
unit: site.unit,
name,
site: index::site(site.held.path, record.name_span()),
holder: parent,
conditional: conditional(site.held.source),
pointer_receiver: receives_by_pointer(facts_of(site), record),
general_terms: record.states_general_terms(),
result: result_type(record),
});
index
.tables_mut(site.unit)?
.state(site.held.path, ordinal, slot);
Ok(slot)
}
fn result_type(record: &GoDeclarationRecord) -> Option<TypeName> {
record.result_name().map(|name| TypeName {
qualifier: record.result_qualifier().map(Box::from),
name: Box::from(name),
})
}
fn receives_by_pointer(facts: &GoSourceFacts, record: &GoDeclarationRecord) -> bool {
record
.receiver()
.and_then(|binding| facts.bindings().get(index_of(binding)))
.is_some_and(GoBindingRecord::pointer)
}
fn symbol_kind(kind: GoDeclarationKind) -> SymbolKind {
match kind {
GoDeclarationKind::Function => SymbolKind::Function,
GoDeclarationKind::Method | GoDeclarationKind::InterfaceMethod => SymbolKind::Method,
GoDeclarationKind::Struct => SymbolKind::Struct,
GoDeclarationKind::Interface => SymbolKind::Interface,
GoDeclarationKind::DefinedType => SymbolKind::DefinedType,
GoDeclarationKind::TypeAlias => SymbolKind::TypeAlias,
GoDeclarationKind::Constant => SymbolKind::Constant,
GoDeclarationKind::Variable => SymbolKind::Variable,
GoDeclarationKind::Field | GoDeclarationKind::EmbeddedField => SymbolKind::Field,
}
}