#![feature(lint_reasons)]
#![warn(
missing_docs,
unused_crate_dependencies,
unused_macro_rules,
variant_size_differences,
clippy::allow_attributes,
clippy::allow_attributes_without_reason,
clippy::expect_used,
clippy::indexing_slicing,
clippy::missing_docs_in_private_items,
clippy::multiple_inherent_impl,
clippy::panic,
clippy::pedantic,
clippy::str_to_string,
clippy::unreachable,
clippy::unwrap_used,
clippy::use_debug
)]
use std::fmt::Display;
use proc_macro2::Span;
#[macro_export]
macro_rules! repo {
() => {
"https://github.com/Vanille-N/chandeliers"
};
}
#[macro_export]
macro_rules! abort {
($($err:tt)*) => {{
std::panic!("
Chandeliers panicked: \x1b[1;31m{}.\x1b[0m
This error occured in \x1b[1;35m{}:{}:{}\x1b[0m
If you are not a developper of Chandeliers and you see this message then this is a bug.
I'd be grateful if you could report this error at \x1b[33m{}\x1b[0m
with the code that produced it and the version of Chandeliers you are using.
",
format!($($err)*),
file!(),
line!(),
column!(),
::chandeliers_err::repo!(),
);
}};
}
#[macro_export]
macro_rules! malformed {
() => {{
::chandeliers_err::abort!("Entered unreachable code");
}};
}
#[macro_export]
macro_rules! consistency {
($cond:expr, $($msg:tt)*) => {{
if !$cond {
::chandeliers_err::abort!($($msg)*);
}
}};
}
pub type Error = Vec<(String, Option<Span>)>;
pub trait IntoError {
fn into_err(self) -> Error;
}
pub type Result<T> = std::result::Result<T, Error>;
pub trait TrySpan {
fn try_span(&self) -> Option<Span> {
None
}
}
impl TrySpan for Span {
fn try_span(&self) -> Option<Span> {
Some(*self)
}
}
impl<T: TrySpan> TrySpan for &T {
fn try_span(&self) -> Option<Span> {
(*self).try_span()
}
}
impl<T: TrySpan> TrySpan for Option<T> {
fn try_span(&self) -> Option<Span> {
self.as_ref().and_then(TrySpan::try_span)
}
}
pub struct Basic {
pub msg: String,
pub span: Span,
}
impl IntoError for Basic {
fn into_err(self) -> Error {
vec![(self.msg, Some(self.span))]
}
}
pub struct TypeMismatch<Source, Left, Right, Msg> {
pub source: Source,
pub left: Left,
pub right: Right,
pub msg: Msg,
}
impl<Source, Left, Right, Msg> IntoError for TypeMismatch<Source, Left, Right, Msg>
where
Source: TrySpan,
Msg: Display,
Left: Display + TrySpan,
Right: Display + TrySpan,
{
fn into_err(self) -> Error {
vec![
(
format!(
"Type mismatch between the left and right sides: {}",
self.msg
),
self.source.try_span(),
),
(
format!("This element has type {}", self.left),
self.left.try_span(),
),
(
format!("While this element has type {}", self.right),
self.right.try_span(),
),
]
}
}
pub struct VarNotFound<Var, Suggest1, Suggest2> {
pub var: Var,
pub suggest1: Suggest1,
pub suggest2: Suggest2,
}
impl<Var, Suggest1, S1, Suggest2, S2> IntoError for VarNotFound<Var, Suggest1, Suggest2>
where
Var: Display + TrySpan,
Suggest1: IntoIterator<Item = S1>,
Suggest2: IntoIterator<Item = S2>,
S1: Display,
S2: Display,
{
fn into_err(self) -> Vec<(String, Option<Span>)> {
let mut suggest1 = self
.suggest1
.into_iter()
.map(|v| format!("{v}"))
.collect::<Vec<_>>();
let mut suggest2 = self
.suggest2
.into_iter()
.map(|v| format!("{v}"))
.collect::<Vec<_>>();
suggest1.sort();
suggest2.sort();
let suggest1 = if suggest1.is_empty() {
String::from("(none declared)")
} else {
suggest1.join(", ")
};
let suggest2 = if suggest2.is_empty() {
String::from("(none declared)")
} else {
suggest2.join(", ")
};
vec![
(
format!("Variable {} not found in the context.", self.var),
self.var.try_span(),
),
(
format!("Perhaps you meant one of the local variables: {suggest1}"),
None,
),
(format!("or one of the global variables: {suggest2}"), None),
]
}
}
pub struct TyVarNotFound<Var, Suggest> {
pub var: Var,
pub suggest: Suggest,
}
impl<Var, Suggest, S> IntoError for TyVarNotFound<Var, Suggest>
where
Var: Display + TrySpan,
Suggest: IntoIterator<Item = S>,
S: Display,
{
fn into_err(self) -> Vec<(String, Option<Span>)> {
let mut suggest = self
.suggest
.into_iter()
.map(|v| format!("{v}"))
.collect::<Vec<_>>();
suggest.sort();
let suggest = if suggest.is_empty() {
String::from("(none declared)")
} else {
suggest.join(", ")
};
vec![
(
format!(
"Variable {} is not available yet at this point of the typechecking.",
self.var
),
self.var.try_span(),
),
(
"During this incremental typechecking, you cannot access global
variables and you may only use local variables that have been
declared strictly beforehand, in the order of inputs then outputs
then locals."
.to_owned(),
None,
),
(
format!("These are the variables that are already useable: {suggest}"),
None,
),
]
}
}
pub struct NotConst<Item, Site> {
pub what: Item,
pub site: Site,
}
impl<Item, Site> IntoError for NotConst<Item, Site>
where
Item: Display,
Site: TrySpan,
{
fn into_err(self) -> Error {
vec![
(
format!("{} not valid in const contexts", self.what),
self.site.try_span(),
),
(
String::from("You must put this definition inside a node"),
None,
),
]
}
}
pub struct BinopMismatch<Oper, Site, Expect, Left, Right> {
pub oper: Oper,
pub site: Site,
pub expect: Expect,
pub left: Left,
pub right: Right,
}
impl<Oper, Site, Expect, Left, Right> IntoError for BinopMismatch<Oper, Site, Expect, Left, Right>
where
Oper: Display,
Site: TrySpan,
Expect: Display,
Left: Display + TrySpan,
Right: Display + TrySpan,
{
fn into_err(self) -> Vec<(String, Option<Span>)> {
vec![
(
format!(
"Binary operator `{}` expects arguments of {}",
self.oper, self.expect
),
self.site.try_span(),
),
(
format!("The left-hand-side is found to be of type {}", self.left),
self.left.try_span(),
),
(
format!("The right-hand-side is found to be of type {}", self.right),
self.right.try_span(),
),
]
}
}
pub struct UnopMismatch<Oper, Expect, Site, Inner> {
pub oper: Oper,
pub expect: Expect,
pub site: Site,
pub inner: Inner,
}
impl<Oper, Expect, Site, Inner> IntoError for UnopMismatch<Oper, Expect, Site, Inner>
where
Oper: Display,
Expect: Display,
Site: TrySpan,
Inner: Display + TrySpan,
{
fn into_err(self) -> Error {
vec![
(
format!(
"Unary operator `{}` expects an argument of {}",
self.oper, self.expect
),
self.site.try_span(),
),
(
format!("The inner value is found to be of type {}", self.inner),
self.inner.try_span(),
),
]
}
}
pub struct BoolRequired<Type, Site, Inner> {
pub actual: Type,
pub site: Site,
pub inner: Inner,
}
impl<Type, Site, Inner> IntoError for BoolRequired<Type, Site, Inner>
where
Type: Display,
Site: TrySpan,
Inner: Display + TrySpan,
{
fn into_err(self) -> Error {
vec![
(
format!("{} should be of type bool", self.actual),
self.site.try_span(),
),
(
format!("The argument is found to be of type {}", self.inner),
self.inner.try_span(),
),
]
}
}
pub struct Cycle<Items> {
pub items: Items,
}
impl<Items, Item> IntoError for Cycle<Items>
where
Items: IntoIterator<Item = (Item, Option<Span>)>,
Item: Display,
{
fn into_err(self) -> Error {
let mut v = vec![];
for (i, (it, sp)) in self.items.into_iter().enumerate() {
v.push((
if i == 0 {
format!("{it} was found to be part of a dependency cycle")
} else {
format!("The cycle also goes through {it}")
},
sp,
));
}
v
}
}
pub struct GraphUnitDeclTwice<Unit, NewSite, Prior, PriorSite> {
pub unit: Unit,
pub new_site: NewSite,
pub prior: Prior,
pub prior_site: PriorSite,
}
impl<Unit, NewSite, Prior, PriorSite> IntoError
for GraphUnitDeclTwice<Unit, NewSite, Prior, PriorSite>
where
Unit: Display,
Prior: Display,
NewSite: TrySpan,
PriorSite: TrySpan,
{
fn into_err(self) -> Error {
vec![
(
format!(
"Attempt to redefine {}, when {} already defines it",
self.unit, self.prior
),
self.new_site.try_span(),
),
(
String::from("Already defined here"),
self.prior_site.try_span(),
),
]
}
}
pub struct GraphUnitUndeclared<Unit> {
pub unit: Unit,
}
impl<Unit> IntoError for GraphUnitUndeclared<Unit>
where
Unit: Display + TrySpan,
{
fn into_err(self) -> Error {
vec![(
format!("No definition provided for {} which is required", self.unit),
self.unit.try_span(),
)]
}
}
pub struct GraphUnitDependsOnItself<Unit, Site1, Site2> {
pub unit: Unit,
pub def_site: Site1,
pub usage: Site2,
}
impl<Unit, Site1, Site2> IntoError for GraphUnitDependsOnItself<Unit, Site1, Site2>
where
Unit: Display,
Site1: TrySpan,
Site2: TrySpan,
{
fn into_err(self) -> Error {
vec![
(
format!("{} depends on itself", self.unit),
self.def_site.try_span(),
),
(
String::from("used here within its own definition"),
self.usage.try_span(),
),
]
}
}
pub struct NotPositive<Var, Site> {
pub var: Var,
pub site: Site,
pub available_depth: usize,
pub attempted_depth: usize,
}
impl<Var, Site> IntoError for NotPositive<Var, Site>
where
Var: Display,
Site: TrySpan,
{
fn into_err(self) -> Error {
vec![
(
format!("Variable {} is not positive at this depth", self.var),
self.site.try_span(),
),
(
format!(
"tried to reach {} steps into the past, with only {} available",
self.attempted_depth, self.available_depth
),
None,
),
(
String::from("Maybe add a `->` in front of the expression to increase the depth ?"),
None,
),
]
}
}
pub struct UnhandledLitType<Site> {
pub site: Site,
}
impl<Site> IntoError for UnhandledLitType<Site>
where
Site: TrySpan,
{
fn into_err(self) -> Error {
vec![(
String::from("Lustre only accepts literals of type int, float, or bool"),
self.site.try_span(),
)]
}
}
pub struct CmpNotAssociative<First, Oper1, Second, Oper2, Third, Site> {
pub oper1: Oper1,
pub first: First,
pub site: Site,
pub second: Second,
pub third: Third,
pub oper2: Oper2,
}
impl<First, Oper1, Second, Oper2, Third, Site> IntoError
for CmpNotAssociative<First, Oper1, Second, Oper2, Third, Site>
where
Oper1: Display,
Oper2: Display,
First: Display,
Second: Display,
Third: Display,
Site: TrySpan,
{
fn into_err(self) -> Error {
let Self {
first,
oper1,
second,
oper2,
third,
site,
} = &self;
vec![(
format!("Comparison operator {oper1} is not associative"),
site.try_span(),
),(
format!("Maybe replace `{first} {oper1} {second} {oper2} {third}` with `{first} {oper1} {second} and {second} {oper2} {third}` ?"),
None,
)]
}
}