use {
crate::{
core::{Ident, SymbolPath, Value},
partitions::SymbolEntry,
},
std::{hash::Hash, sync::Arc},
};
pub type IdentResolver<I> = Arc<dyn Fn(&I) -> String + Send + Sync>;
#[derive(Debug)]
pub enum Resolution<V, I, P>
where
V: Value<I>,
I: Ident + Hash,
P: SymbolPath,
{
Resolved(SymbolEntry<V, I, P>),
Deferred,
Phantom(PhantomSymbol<V, I, P>),
WrongKind {
found: SymbolEntry<V, I, P>,
expected_kinds: Vec<String>,
},
Failed(String),
}
impl<V, I, P> Clone for Resolution<V, I, P>
where
V: Value<I>,
I: Ident + Hash,
P: SymbolPath,
{
fn clone(&self) -> Self {
match self {
| Resolution::Resolved(id) => Resolution::Resolved(*id),
| Resolution::Deferred => Resolution::Deferred,
| Resolution::Phantom(p) => Resolution::Phantom(p.clone()),
| Resolution::WrongKind {
found,
expected_kinds,
} => Resolution::WrongKind {
found: *found,
expected_kinds: expected_kinds.clone(),
},
| Resolution::Failed(msg) => Resolution::Failed(msg.clone()),
}
}
}
impl<V, I, P> PartialEq for Resolution<V, I, P>
where
V: Value<I>,
I: Ident + Hash,
P: SymbolPath,
{
fn eq(&self, other: &Self) -> bool {
match (self, other) {
| (Resolution::Resolved(a), Resolution::Resolved(b)) => a == b,
| (Resolution::Deferred, Resolution::Deferred) => true,
| (Resolution::Phantom(a), Resolution::Phantom(b)) => a == b,
| (
Resolution::WrongKind {
found: f1,
expected_kinds: e1,
},
Resolution::WrongKind {
found: f2,
expected_kinds: e2,
},
) => f1 == f2 && e1 == e2,
| (Resolution::Failed(a), Resolution::Failed(b)) => a == b,
| _ => false,
}
}
}
impl<V, I, P> Resolution<V, I, P>
where
V: Value<I>,
I: Ident + Hash,
P: SymbolPath,
{
pub fn is_resolved(&self) -> bool {
matches!(self, Resolution::Resolved(_))
}
pub fn is_failed(&self) -> bool {
matches!(self, Resolution::Failed(_))
}
pub fn is_deferred(&self) -> bool {
matches!(self, Resolution::Deferred)
}
pub fn is_phantom(&self) -> bool {
matches!(self, Resolution::Phantom(_))
}
pub fn is_wrong_kind(&self) -> bool {
matches!(self, Resolution::WrongKind { .. })
}
pub fn qualified_symbol_id(&self) -> Option<&SymbolEntry<V, I, P>> {
match self {
| Resolution::Resolved(qid) => Some(qid),
| Resolution::WrongKind { found, .. } => Some(found),
| _ => None,
}
}
pub fn symbol_id(&self) -> Option<&SymbolEntry<V, I, P>> {
self.qualified_symbol_id()
}
pub fn description(&self, resolver: &IdentResolver<I>) -> String {
match self {
| Resolution::Resolved(qid) => {
format!("Resolved to symbol {:?}", qid)
},
| Resolution::Deferred => "Deferred to next pass".to_string(),
| Resolution::Phantom(phantom) => {
format!(
"Expected {} '{}': {}",
phantom.expected_kind,
(resolver)(&phantom.name),
phantom.reason
)
},
| Resolution::WrongKind { expected_kinds, .. } => {
format!("Wrong type, expected: {}", expected_kinds.join(" or "))
},
| Resolution::Failed(msg) => format!("Failed: {}", msg),
}
}
pub fn into_result(
self,
resolver: &IdentResolver<I>,
) -> Result<SymbolEntry<V, I, P>, String> {
match self {
| Resolution::Resolved(qid) => Ok(qid),
| other => Err(other.description(resolver)),
}
}
}
#[derive(Debug)]
pub struct PhantomSymbol<V, I, P>
where
V: Value<I>,
I: Ident,
P: SymbolPath,
{
pub expected_kind: String,
pub name: I,
pub reason: String,
pub expected_in_scope: SymbolEntry<V, I, P>,
pub hints: Vec<String>,
}
impl<V, I, P> Clone for PhantomSymbol<V, I, P>
where
V: Value<I>,
I: Ident,
P: SymbolPath,
{
fn clone(&self) -> Self {
Self {
expected_kind: self.expected_kind.clone(),
name: self.name.clone(),
reason: self.reason.clone(),
expected_in_scope: self.expected_in_scope,
hints: self.hints.clone(),
}
}
}
impl<V, I, P> PartialEq for PhantomSymbol<V, I, P>
where
V: Value<I>,
I: Ident,
P: SymbolPath,
{
fn eq(&self, other: &Self) -> bool {
self.expected_kind == other.expected_kind
&& self.name == other.name
&& self.reason == other.reason
&& self.expected_in_scope == other.expected_in_scope
&& self.hints == other.hints
}
}
impl<V, I, P> PhantomSymbol<V, I, P>
where
V: Value<I>,
I: Ident,
P: SymbolPath,
{
pub fn new(
expected_kind: String,
name: I,
reason: String,
expected_in_scope: SymbolEntry<V, I, P>,
) -> Self {
Self {
expected_kind,
name,
reason,
expected_in_scope,
hints: Vec::new(),
}
}
pub fn with_hint(mut self, hint: String) -> Self {
self.hints.push(hint);
self
}
pub fn with_hints(mut self, hints: Vec<String>) -> Self {
self.hints.extend(hints);
self
}
pub fn has_hints(&self) -> bool {
!self.hints.is_empty()
}
pub fn display_name(&self, resolver: &IdentResolver<I>) -> String {
resolver(&self.name)
}
}
#[derive(Debug)]
pub struct ResolutionStep<V, I, P>
where
V: Value<I>,
I: Ident + Hash,
P: SymbolPath,
{
pub segment: I,
pub span: laburnum::Span,
pub resolution: Resolution<V, I, P>,
pub path_so_far: Vec<I>,
}
impl<V, I, P> Clone for ResolutionStep<V, I, P>
where
V: Value<I>,
I: Ident + Hash,
P: SymbolPath,
{
fn clone(&self) -> Self {
Self {
segment: self.segment.clone(),
span: self.span,
resolution: self.resolution.clone(),
path_so_far: self.path_so_far.clone(),
}
}
}
impl<V, I, P> ResolutionStep<V, I, P>
where
V: Value<I>,
I: Ident + Hash,
P: SymbolPath,
{
pub fn new(
segment: I,
span: laburnum::Span,
resolution: Resolution<V, I, P>,
path_so_far: Vec<I>,
) -> Self {
Self {
segment,
span,
resolution,
path_so_far,
}
}
pub fn is_successful(&self) -> bool {
self.resolution.is_resolved()
}
pub fn description(&self, resolver: &IdentResolver<I>) -> String {
format!(
"Resolving '{}': {}",
resolver(&self.segment),
self.resolution.description(resolver)
)
}
}
#[derive(Debug)]
pub struct ResolutionResult<V, I, P>
where
V: Value<I>,
I: Ident + Hash,
P: SymbolPath,
{
pub steps: Vec<ResolutionStep<V, I, P>>,
pub final_resolution: Resolution<V, I, P>,
}
impl<V, I, P> Clone for ResolutionResult<V, I, P>
where
V: Value<I>,
I: Ident + Hash,
P: SymbolPath,
{
fn clone(&self) -> Self {
Self {
steps: self.steps.clone(),
final_resolution: self.final_resolution.clone(),
}
}
}
impl<V, I, P> ResolutionResult<V, I, P>
where
V: Value<I>,
I: Ident + Hash,
P: SymbolPath,
{
pub fn new(
steps: Vec<ResolutionStep<V, I, P>>,
final_resolution: Resolution<V, I, P>,
) -> Self {
Self {
steps,
final_resolution,
}
}
pub fn simple(resolution: Resolution<V, I, P>) -> Self {
Self {
steps: Vec::new(),
final_resolution: resolution,
}
}
pub fn is_successful(&self) -> bool {
self.final_resolution.is_resolved()
}
pub fn step_count(&self) -> usize {
self.steps.len()
}
pub fn is_qualified_path(&self) -> bool {
self.step_count() > 1
}
pub fn symbol_id(&self) -> Option<&SymbolEntry<V, I, P>> {
self.final_resolution.symbol_id()
}
pub fn report(&self, resolver: &IdentResolver<I>) -> String {
if self.steps.is_empty() {
format!(
"Simple resolution: {}",
self.final_resolution.description(resolver)
)
} else {
let mut report =
format!("Multi-step resolution ({} steps):\n", self.steps.len());
for (i, step) in self.steps.iter().enumerate() {
report.push_str(&format!(
" {}: {}\n",
i + 1,
step.description(resolver)
));
}
report.push_str(&format!(
"Final: {}",
self.final_resolution.description(resolver)
));
report
}
}
}
pub trait SymboliqueResolver<
V: Value<I>,
I: Ident + Hash,
P: SymbolPath = String,
>
{
fn resolve(
&self,
path: &P,
from_scope: SymbolEntry<V, I, P>,
) -> Resolution<V, I, P>;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct DefaultResolver;
impl<V: Value<I>, I: Ident + Hash, P: SymbolPath> SymboliqueResolver<V, I, P>
for DefaultResolver
{
fn resolve(
&self,
path: &P,
_from_scope: SymbolEntry<V, I, P>,
) -> Resolution<V, I, P> {
Resolution::Failed(format!("Cannot resolve path: {:?}", path))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_helpers::*;
#[test]
fn resolved_is_resolved() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let resolution = Resolution::<DV, SI, TP>::Resolved(entry);
assert!(resolution.is_resolved());
}
#[test]
fn failed_is_failed() {
let resolution = Resolution::<DV, SI, TP>::Failed("msg".into());
assert!(resolution.is_failed());
}
#[test]
fn deferred_is_deferred() {
let resolution = Resolution::<DV, SI, TP>::Deferred;
assert!(resolution.is_deferred());
}
#[test]
fn phantom_is_phantom() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let phantom = PhantomSymbol::<DV, SI, TP>::new(
"function".to_string(),
SI::new("foo"),
"not found".to_string(),
entry,
);
let resolution = Resolution::<DV, SI, TP>::Phantom(phantom);
assert!(resolution.is_phantom());
}
#[test]
fn wrong_kind_is_wrong_kind() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let resolution = Resolution::<DV, SI, TP>::WrongKind {
found: entry,
expected_kinds: vec!["function".to_string()],
};
assert!(resolution.is_wrong_kind());
}
#[test]
fn resolved_symbol_id() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let resolution = Resolution::<DV, SI, TP>::Resolved(entry);
assert!(resolution.symbol_id().is_some());
}
#[test]
fn failed_symbol_id_none() {
let resolution = Resolution::<DV, SI, TP>::Failed("msg".into());
assert!(resolution.symbol_id().is_none());
}
#[test]
fn deferred_symbol_id_none() {
let resolution = Resolution::<DV, SI, TP>::Deferred;
assert!(resolution.symbol_id().is_none());
}
#[test]
fn wrong_kind_symbol_id() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let resolution = Resolution::<DV, SI, TP>::WrongKind {
found: entry,
expected_kinds: vec!["function".to_string()],
};
assert!(resolution.symbol_id().is_some());
}
#[test]
fn resolved_into_result_ok() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let resolution = Resolution::<DV, SI, TP>::Resolved(entry);
let resolver = test_ident_resolver();
assert!(resolution.into_result(&resolver).is_ok());
}
#[test]
fn failed_into_result_err() {
let resolution = Resolution::<DV, SI, TP>::Failed("msg".into());
let resolver = test_ident_resolver();
assert!(resolution.into_result(&resolver).is_err());
}
#[test]
fn resolved_description() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let resolution = Resolution::<DV, SI, TP>::Resolved(entry);
let resolver = test_ident_resolver();
let desc = resolution.description(&resolver);
assert!(desc.contains("Resolved"));
}
#[test]
fn failed_description() {
let resolution = Resolution::<DV, SI, TP>::Failed("something broke".into());
let resolver = test_ident_resolver();
let desc = resolution.description(&resolver);
assert!(desc.contains("Failed"));
}
#[test]
fn deferred_description() {
let resolution = Resolution::<DV, SI, TP>::Deferred;
let resolver = test_ident_resolver();
let desc = resolution.description(&resolver);
assert!(desc.contains("Deferred"));
}
#[test]
fn phantom_new() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let phantom = PhantomSymbol::<DV, SI, TP>::new(
"function".to_string(),
SI::new("foo"),
"not found".to_string(),
entry,
);
assert_eq!(phantom.expected_kind, "function");
assert_eq!(phantom.reason, "not found");
assert!(phantom.hints.is_empty());
}
#[test]
fn phantom_with_hint() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let phantom = PhantomSymbol::<DV, SI, TP>::new(
"function".to_string(),
SI::new("foo"),
"not found".to_string(),
entry,
)
.with_hint("did you mean bar?".to_string());
assert_eq!(phantom.hints.len(), 1);
}
#[test]
fn phantom_with_hints() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let phantom = PhantomSymbol::<DV, SI, TP>::new(
"function".to_string(),
SI::new("foo"),
"not found".to_string(),
entry,
)
.with_hints(vec!["hint1".to_string(), "hint2".to_string()]);
assert_eq!(phantom.hints.len(), 2);
}
#[test]
fn phantom_has_hints() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let phantom_no_hints = PhantomSymbol::<DV, SI, TP>::new(
"function".to_string(),
SI::new("foo"),
"not found".to_string(),
entry,
);
assert!(!phantom_no_hints.has_hints());
let phantom_with = phantom_no_hints.with_hint("a hint".to_string());
assert!(phantom_with.has_hints());
}
#[test]
fn phantom_display_name() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let phantom = PhantomSymbol::<DV, SI, TP>::new(
"function".to_string(),
SI::new("my_func"),
"not found".to_string(),
entry,
);
let resolver = test_ident_resolver();
assert_eq!(phantom.display_name(&resolver), "my_func");
}
#[test]
fn step_new() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let span = test_span(&mut cache, 1);
let segment = SI::new("foo");
let step = ResolutionStep::<DV, SI, TP>::new(
segment.clone(),
span,
Resolution::Resolved(entry),
vec![segment],
);
assert_eq!(step.span, span);
assert!(step.resolution.is_resolved());
}
#[test]
fn step_is_successful_when_resolved() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let span = test_span(&mut cache, 1);
let step = ResolutionStep::<DV, SI, TP>::new(
SI::new("foo"),
span,
Resolution::Resolved(entry),
vec![SI::new("foo")],
);
assert!(step.is_successful());
}
#[test]
fn step_is_successful_when_failed() {
let mut cache = test_span_cache();
let span = test_span(&mut cache, 1);
let step = ResolutionStep::<DV, SI, TP>::new(
SI::new("foo"),
span,
Resolution::Failed("not found".into()),
vec![SI::new("foo")],
);
assert!(!step.is_successful());
}
#[test]
fn step_description() {
let mut cache = test_span_cache();
let span = test_span(&mut cache, 1);
let step = ResolutionStep::<DV, SI, TP>::new(
SI::new("my_segment"),
span,
Resolution::Failed("not found".into()),
vec![SI::new("my_segment")],
);
let resolver = test_ident_resolver();
let desc = step.description(&resolver);
assert!(desc.contains("my_segment"));
}
#[test]
fn result_simple_resolved() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let result = ResolutionResult::<DV, SI, TP>::simple(Resolution::Resolved(entry));
assert!(result.is_successful());
}
#[test]
fn result_simple_failed() {
let result =
ResolutionResult::<DV, SI, TP>::simple(Resolution::Failed("err".into()));
assert!(!result.is_successful());
}
#[test]
fn result_step_count_zero_for_simple() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let result = ResolutionResult::<DV, SI, TP>::simple(Resolution::Resolved(entry));
assert_eq!(result.step_count(), 0);
}
#[test]
fn result_is_qualified_path_false_for_simple() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let result = ResolutionResult::<DV, SI, TP>::simple(Resolution::Resolved(entry));
assert!(!result.is_qualified_path());
}
#[test]
fn result_symbol_id() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let result = ResolutionResult::<DV, SI, TP>::simple(Resolution::Resolved(entry));
assert!(result.symbol_id().is_some());
}
#[test]
fn result_report_simple() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let result = ResolutionResult::<DV, SI, TP>::simple(Resolution::Resolved(entry));
let resolver = test_ident_resolver();
let report = result.report(&resolver);
assert!(report.contains("Simple resolution"));
}
#[test]
fn result_new_with_steps() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let span = test_span(&mut cache, 1);
let step = ResolutionStep::<DV, SI, TP>::new(
SI::new("a"),
span,
Resolution::Resolved(entry),
vec![SI::new("a")],
);
let result = ResolutionResult::<DV, SI, TP>::new(
vec![step],
Resolution::Resolved(entry),
);
assert_eq!(result.step_count(), 1);
}
#[test]
fn result_is_qualified_path_with_multiple_steps() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let span = test_span(&mut cache, 1);
let step_a = ResolutionStep::<DV, SI, TP>::new(
SI::new("a"),
span,
Resolution::Resolved(entry),
vec![SI::new("a")],
);
let step_b = ResolutionStep::<DV, SI, TP>::new(
SI::new("b"),
span,
Resolution::Resolved(entry),
vec![SI::new("a"), SI::new("b")],
);
let result = ResolutionResult::<DV, SI, TP>::new(
vec![step_a, step_b],
Resolution::Resolved(entry),
);
assert!(result.is_qualified_path());
}
#[test]
fn result_report_multi_step() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let span = test_span(&mut cache, 1);
let step_a = ResolutionStep::<DV, SI, TP>::new(
SI::new("a"),
span,
Resolution::Resolved(entry),
vec![SI::new("a")],
);
let step_b = ResolutionStep::<DV, SI, TP>::new(
SI::new("b"),
span,
Resolution::Resolved(entry),
vec![SI::new("a"), SI::new("b")],
);
let result = ResolutionResult::<DV, SI, TP>::new(
vec![step_a, step_b],
Resolution::Resolved(entry),
);
let resolver = test_ident_resolver();
let report = result.report(&resolver);
assert!(report.contains("Multi-step resolution"));
}
struct TestResolver {
known: std::collections::HashMap<String, SymbolEntry<DV, SI, TP>>,
}
impl SymboliqueResolver<DV, SI, TP> for TestResolver {
fn resolve(
&self,
path: &TP,
_from_scope: SymbolEntry<DV, SI, TP>,
) -> Resolution<DV, SI, TP> {
match self.known.get(path) {
Some(entry) => Resolution::Resolved(*entry),
None => Resolution::Failed(format!("Unknown path: {}", path)),
}
}
}
#[test]
fn default_resolver_returns_failed() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let resolver = DefaultResolver;
let path = "some.path".to_string();
let result = resolver.resolve(&path, entry);
assert!(result.is_failed());
}
#[test]
fn test_resolver_resolves_known_path() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let mut known = std::collections::HashMap::new();
known.insert("foo.bar".to_string(), entry);
let resolver = TestResolver { known };
let scope = dummy_symbol_entry(&mut cache);
let path = "foo.bar".to_string();
let result = resolver.resolve(&path, scope);
assert!(result.is_resolved());
}
#[test]
fn test_resolver_fails_unknown_path() {
let mut cache = test_span_cache();
let entry = dummy_symbol_entry(&mut cache);
let mut known = std::collections::HashMap::new();
known.insert("foo.bar".to_string(), entry);
let resolver = TestResolver { known };
let scope = dummy_symbol_entry(&mut cache);
let path = "baz.qux".to_string();
let result = resolver.resolve(&path, scope);
assert!(result.is_failed());
}
}