#![allow(unused_assignments)]
use miette::{Diagnostic, SourceSpan};
use thiserror::Error;
use crate::lower::{LowerErrorsWithSource, ir};
use crate::resolve::grant::Scope;
use microcad_lang_base::{DiagError, SrcRef, SrcReferrer};
fn capitalize_first(s: &str) -> String {
let mut c = s.chars();
match c.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + c.as_str(),
}
}
#[derive(Debug, Error, Diagnostic)]
pub enum ResolveError {
#[error("Lower Error: {0}")]
#[diagnostic(transparent)]
LowerError(#[from] LowerErrorsWithSource),
#[error("Could not find a file with hash {0}")]
UnknownHash(u64),
#[error("Hash is zero")]
NulHash,
#[error("Could not find a file with path {0}")]
FileNotFound(std::path::PathBuf),
#[error("Symbol {0} not found while resolving.")]
SymbolNotFound(ir::QualifiedName),
#[error("Symbol {0} must be loaded from {1}")]
SymbolMustBeLoaded(ir::QualifiedName, std::path::PathBuf),
#[error("Symbol {0} is not a value")]
NotAValue(ir::QualifiedName),
#[error("Ambiguous external module files found {0:?}")]
AmbiguousExternals(Vec<std::path::PathBuf>),
#[error("Symbol {0} already defined")]
SymbolAlreadyDefined(ir::QualifiedName),
#[error("Ambiguous symbol found: {0}")]
AmbiguousSymbol(ir::QualifiedName, ir::QualifiedNames),
#[error("Ambiguous identifier '{ambiguous}'")]
#[allow(missing_docs)]
AmbiguousId {
#[label(primary, "First usage of '{first}'")]
first: ir::Identifier,
#[label("Ambiguous usage of '{ambiguous}'")]
ambiguous: ir::Identifier,
},
#[error("{0}")]
ScanDirError(#[from] scan_dir::Error),
#[error("Invalid path: {0:?}")]
InvalidPath(std::path::PathBuf),
#[error("Diagnostic error: {0}")]
DiagError(#[from] DiagError),
#[error(transparent)]
#[diagnostic(transparent)]
StatementNotSupported(#[from] StatementNotSupportedError),
#[error("Resolve failed: {0}")]
ResolveCheckFailed(SrcRef),
#[error("Symbol {0} is private")]
SymbolIsPrivate(ir::QualifiedName),
#[error("{0}")]
IoError(#[from] std::io::Error),
#[error(
"Source of module '{0}' could not be found in {1:?} (expecting a file '{0}.µcad' or '{0}/mod.µcad')"
)]
SourceFileNotFound(
#[label("module not found")] ir::Identifier,
std::path::PathBuf,
),
#[error("Wrong lookup target")]
WrongTarget,
#[error("Statement not allowed within workbenches")]
IllegalWorkbenchStatement,
#[error("Code between initializers is not allowed")]
#[allow(missing_docs)]
CodeBetweenInitializers {
#[label("Between these initializers")]
initializers: SrcRef,
#[label(primary, "This statement is not allowed")]
statement: SrcRef,
#[label("Inside this {scope}")]
workbench: SrcRef,
scope: &'static str,
},
#[error("Statement not allowed prior initializers")]
#[allow(missing_docs)]
StatementNotAllowedPriorInitializers {
#[label("Before this initializer")]
initializer: SrcRef,
#[label(primary, "This statement is not allowed")]
statement: SrcRef,
#[label("Inside this {scope}")]
workbench: SrcRef,
scope: &'static str,
},
}
#[derive(Debug, Error, Diagnostic)]
#[error("{} is not allowed {} {}", capitalize_first(inner), self.placement(), self.outer())]
#[diagnostic(help("{inner} is only allowed within {}", self.allowed_parents()))]
pub struct StatementNotSupportedError {
inner: &'static str,
#[label(primary, "This {inner} is not allowed{}", self.maybe_here())]
inner_span: SrcRef,
outer: &'static str,
#[label("Within this {outer}")]
outer_span: Option<SourceSpan>,
allowed_parents: Vec<&'static str>,
}
impl StatementNotSupportedError {
pub(super) fn new(node: &Scope, parent: &Scope) -> Self {
StatementNotSupportedError {
inner: node.to_str(),
inner_span: node.src_ref(),
outer: parent.to_str(),
outer_span: parent.src_ref().as_miette_span(),
allowed_parents: node
.ty()
.allowed_parents()
.iter()
.map(|scope| scope.to_str())
.collect(),
}
}
fn allowed_parents(&self) -> impl std::fmt::Display {
struct AllowedParents(Vec<&'static str>);
impl std::fmt::Display for AllowedParents {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut items = self.0.iter();
if let Some(first) = items.next() {
write!(f, "{first}")?;
}
let last = items.next_back();
for item in items {
write!(f, ", {item}")?;
}
if let Some(last) = last {
write!(f, " or {last}")?;
}
Ok(())
}
}
AllowedParents(self.allowed_parents.clone())
}
fn parent_is_root(&self) -> bool {
self.outer == "source file"
}
fn placement(&self) -> &'static str {
if self.parent_is_root() {
"at"
} else {
"within"
}
}
fn outer(&self) -> &'static str {
if self.parent_is_root() {
"source root"
} else {
self.outer
}
}
fn maybe_here(&self) -> &'static str {
if self.parent_is_root() { " here" } else { "" }
}
}
impl SrcReferrer for ResolveError {
fn src_ref(&self) -> SrcRef {
match self {
ResolveError::SourceFileNotFound(identifier, _) => identifier.src_ref(),
ResolveError::LowerError(parse_error) => parse_error.src_ref(),
ResolveError::ResolveCheckFailed(src_ref) => src_ref.clone(),
_ => SrcRef::none(),
}
}
}
pub type ResolveResult<T> = std::result::Result<T, ResolveError>;