1use std::fmt::Display;
9
10use crate::Span;
11
12pub type Error = Vec<(String, Option<Span>)>;
14
15#[expect(
17 clippy::module_name_repetitions,
18 reason = "Of course the trait contains the word 'Error'"
19)]
20pub trait IntoError {
21 fn into_err(self) -> Error;
23}
24
25pub trait TrySpan {
27 fn try_span(&self) -> Option<Span> {
30 None
31 }
32}
33
34impl TrySpan for Span {
36 fn try_span(&self) -> Option<Span> {
37 Some(*self)
38 }
39}
40
41impl<T: TrySpan> TrySpan for &T {
43 fn try_span(&self) -> Option<Span> {
44 (*self).try_span()
45 }
46}
47
48impl<T: TrySpan> TrySpan for Option<T> {
50 fn try_span(&self) -> Option<Span> {
51 self.as_ref().and_then(TrySpan::try_span)
52 }
53}
54
55impl<T: TrySpan, E> TrySpan for Result<T, E> {
57 fn try_span(&self) -> Option<Span> {
58 self.as_ref().ok().and_then(TrySpan::try_span)
59 }
60}
61
62pub trait TryDefSite {
64 fn try_def_site(&self) -> Option<Span> {
67 None
68 }
69}
70
71impl TryDefSite for Span {}
74
75impl<T: TryDefSite> TryDefSite for &T {
77 fn try_def_site(&self) -> Option<Span> {
78 (*self).try_def_site()
79 }
80}
81
82impl<T: TryDefSite> TryDefSite for Option<T> {
84 fn try_def_site(&self) -> Option<Span> {
85 self.as_ref().and_then(TryDefSite::try_def_site)
86 }
87}
88
89impl<T: TryDefSite, E> TryDefSite for Result<T, E> {
91 fn try_def_site(&self) -> Option<Span> {
92 self.as_ref().ok().and_then(TryDefSite::try_def_site)
93 }
94}
95
96pub struct DisplayTrySpan<T> {
99 pub display: T,
101 pub try_span: Option<Span>,
103}
104
105impl<T: Display> Display for DisplayTrySpan<T> {
106 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 self.display.fmt(f)
108 }
109}
110
111impl<T> TrySpan for DisplayTrySpan<T> {
112 fn try_span(&self) -> Option<Span> {
113 self.try_span
114 }
115}
116
117#[deprecated = "This is a nonspecific error constructor that should eventually become a prebuilt message."]
121pub struct Basic {
122 pub msg: String,
124 pub span: Span,
126}
127
128#[allow(clippy::allow_attributes, reason = "Needs regular (un)commenting out")]
129#[allow(deprecated, reason = "Removing impl is breaking despite deprecation")]
130impl IntoError for Basic {
131 fn into_err(self) -> Error {
132 vec![(self.msg, Some(self.span))]
133 }
134}
135
136pub struct Raw {
141 pub es: Vec<(String, Option<Span>)>,
143}
144
145#[allow(clippy::allow_attributes, reason = "Needs regular (un)commenting out")]
146#[allow(deprecated, reason = "Removing impl is breaking despite deprecation")]
147impl IntoError for Raw {
148 fn into_err(self) -> Error {
149 self.es
150 }
151}
152
153trait Condition {
155 fn truth(&self) -> bool;
157}
158
159impl Condition for bool {
160 fn truth(&self) -> bool {
161 *self
162 }
163}
164
165macro_rules! error_message_aux_push {
172 ( $constructed:ident, ) => {}; ( $constructed:ident, $fmt:tt @ $site:ident ; $($rest:tt)* ) => {
174 $constructed.push((format!($fmt), $site.try_span()));
177 error_message_aux_push!($constructed, $($rest)*);
178 };
179 ( $constructed:ident, $fmt:tt @* if $site:ident ; $($rest:tt)* ) => {
180 if let Some(site) = $site.try_def_site() {
182 $constructed.push((format!($fmt), Some(site)));
183 }
184 error_message_aux_push!($constructed, $($rest)*);
185 };
186 ( $constructed:ident, for $iterator:ident => $fmt:tt @ $site:expr ; $($rest:tt)* ) => {
187 for $iterator in $iterator {
189 $constructed.push((format!($fmt), $site.try_span()));
190 }
191 error_message_aux_push!($constructed, $($rest)*);
192 };
193 ( $constructed:ident, if $cond:ident => $fmt:tt @ $site:expr ; $($rest:tt)* ) => {
194 if $cond.truth() {
196 $constructed.push((format!($fmt), $site.try_span()));
197 }
198 error_message_aux_push!($constructed, $($rest)*);
199 };
200 ( $constructed:ident, $fmt:tt @ if $site:expr ; $($rest:tt)* ) => {
201 if let Some(site) = $site.try_span() {
203 $constructed.push((format!($fmt), Some(site)));
204 }
205 error_message_aux_push!($constructed, $($rest)*);
206 };
207 ( $constructed:ident, for $iterator:ident => $fmt:tt ; $($rest:tt)* ) => {
208 for $iterator in $iterator {
210 $constructed.push((format!($fmt), None));
211 }
212 error_message_aux_push!($constructed, $($rest)*);
213 };
214 ( $constructed:ident, $fmt:tt ; $($rest:tt)* ) => {
215 $constructed.push((format!($fmt), None));
217 error_message_aux_push!($constructed, $($rest)*);
218 };
219}
220
221macro_rules! error_message {
271 (
272 $( [ $predoc:expr ] )* struct $name:tt $( <$($explicit_generics:ident),*> )? where {
274 $( [ $doc:expr ]
276 $field:ident : $( [$item:ident .. $($iterbounds:tt)* ] )? $( { $($bounds:tt)+ } )?,
277 )*
278 } impl { $( $message:tt )*
280 }
281 ) => {
282 #[expect(non_camel_case_types, reason = "Generic type has same name as field")]
283 $( #[doc = $predoc] )*
284 pub struct $name <$($field),*> {
285 $(
286 #[doc = $doc]
287 pub $field : $field ,
288 )*
289 }
290
291 #[expect(non_camel_case_types, reason = "Generic type has same name as field")]
292 impl <$($($explicit_generics),*,)? $($field),*> IntoError for $name<$($field),*>
293 where $(
294 $field: $( IntoIterator<Item = $item>, $item: $($iterbounds)* )?
295 $( $($bounds)* , )? )*
296 {
297 fn into_err(self) -> Error {
298 let Self { $($field),* } = self;
299 let mut constructed = Vec::new();
300 error_message_aux_push!(constructed,
301 $($message)* );
303 constructed
304 }
305 }
306 };
307}
308
309error_message! {
310 ["Generate an error for incompatible types between a \"left\" and a \"right\" values."]
311 struct TypeMismatch where {
312 ["Span of the entire error"] source: {TrySpan},
313 ["Left expression"] left: {Display + TrySpan},
314 ["Right expression"] right: {Display + TrySpan},
315 ["Further details on mismatch"] msg: {Display},
316 } impl {
317 "Type mismatch between the left and right sides: {msg}" @ source;
318 "This element has type {left}" @ left;
319 "While this element has type {right}" @ right;
320 }
321}
322
323pub struct Suggest<Its> {
325 pub available: Its,
327}
328
329impl<Its, It> Display for Suggest<Its>
330where
331 Its: IntoIterator<Item = It> + Clone,
332 It: Display,
333{
334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
335 let mut suggest = self
336 .available
337 .clone()
338 .into_iter()
339 .map(|v| format!("{v}"))
340 .collect::<Vec<_>>();
341 suggest.sort();
342 if suggest.is_empty() {
343 write!(f, "(none declared)")
344 } else {
345 write!(f, "{}", suggest.join(", "))
346 }
347 }
348}
349
350error_message! {
351 ["Generate an error for a variable that was not declared yet."]
352 struct VarNotFound where {
353 ["What is missing"] var: {Display + TrySpan},
354 ["Local variable suggestions."] suggest1: {Display},
355 ["Global variable suggestions."] suggest2: {Display},
356 } impl {
357 "Variable {var} not found in the context." @ var;
358 "Perhaps you meant one of the local variables: {suggest1}";
359 "or one of the global variables: {suggest2}";
360 }
361}
362
363error_message! {
364 ["Generate an error for an undeclared function."]
365 struct FunNotFound where {
366 ["Invocation of missing function"] fun: {Display + TrySpan},
367 } impl {
368 "{fun} is not a known function in this scope." @ fun;
369 }
370}
371
372error_message! {
373 ["Generate an error for a type variable that was not declared yet,"]
374 ["which has consequences on what we should say is and isn't available."]
375 struct TyVarNotFound where {
376 ["What is missing."] var: {Display + TrySpan},
377 ["Local variable suggestions."] suggest: {Display},
378 } impl {
379 "Variable {var} is not available yet at this point of the typechecking." @ var;
380 "During this incremental typechecking, you cannot access global
381variables and you may only use local variables that have been
382declared strictly beforehand, in the order of inputs then outputs
383then locals.";
384 "These are the variables that are already useable: {suggest}";
385 }
386}
387
388error_message! {
389 ["Generate an erorr for an expression that is noot valid in a `const` declaration."]
390 struct NotConst where {
391 ["Description of the invalid expression constructor."] what: {Display},
392 ["Location of the error."] site: {TrySpan},
393 } impl {
394 "{what} not valid in const contexts" @ site;
395 "You must put this definition inside a node";
396 }
397}
398
399error_message! {
400 ["Generate an error for a binary operator that expected arguments of a specific type."]
401 struct BinopMismatch where {
402 ["Description of the operator."] oper: {Display},
403 ["Location of the error."] site: {TrySpan},
404 ["What we expected in place of the arguments."] expect: {Display},
405 ["Left hand side and span."] left: {Display + TrySpan},
406 ["Right hand side and span."] right: {Display + TrySpan},
407 } impl {
408 "Binary operator `{oper}` expects arguments of {expect}" @ site;
409 "The left-hand-side is found to be of type {left}" @ left;
410 "The right-hand-side is found to be of type {right}" @ right;
411 }
412}
413
414error_message! {
415 ["Generate an error for a unary operator that expected an argument of a specific type."]
416 struct UnopMismatch where {
417 ["Description of the operator."] oper: {Display},
418 ["What the operator expects."] expect: {Display},
419 ["Location of the error."] site: {TrySpan},
420 ["Invalid expression and span."] inner: {Display + TrySpan},
421 } impl {
422 "Unary operator `{oper}` expects an argument of {expect}" @ site;
423 "The inner value is found to be of type {inner}" @ inner;
424 }
425}
426
427error_message! {
428 ["Generate an error for something that should have been a bool but isn't,"]
429 ["e.g. `if 1 then 0 else 1`."]
430 struct BoolRequired where {
431 ["Explanation of what this item is (e.g. \"the condition of if\")"] what: {Display},
432 ["Location of the error."] site: {TrySpan},
433 ["Location of the inner contents."] inner: {Display + TrySpan},
434 } impl {
435 "{what} should be of type bool" @ site;
436 "The argument is found to be of type {inner}" @ inner;
437 }
438}
439
440error_message! {
441 ["Generate an error for a cyclic definition."]
442 struct Cycle<Its> where {
443 ["Beginning of the cycle"] head: {Display + TrySpan},
444 ["Rest of the cycle (not necessarily ordered)."] items: [Its .. TrySpan + Display],
445 } impl {
446 "{head} was found to be part of a dependency cycle" @ head;
447 for items => "The cycle also goes through {items}" @ items;
448 }
449}
450
451error_message! {
452 ["Error message for an object that was defined twice when only one"]
453 ["declaration should exist."]
454 struct GraphUnitDeclTwice where {
455 ["Display of the redefined object."] unit: {Display},
456 ["Location of the superfluous definition."] new_site: {TrySpan},
457 ["Item that defined the object previously."] prior: {Display},
458 ["Location of the first definition."] prior_site: {TrySpan},
459 } impl {
460 "Attempt to redefine {unit}, when {prior} already defines it" @ new_site;
461 "Already defined here" @ prior_site;
462 }
463}
464
465error_message! {
466 ["Error for an object that should have been declared but was not."]
467 struct GraphUnitUndeclared where {
468 ["Missing object and site where usage was attempted."] unit: {Display + TrySpan},
469 } impl {
470 "No definition provided for {unit} which is required" @ unit;
471 }
472}
473
474error_message! {
475 ["Special case of [Cycle]: custom message for an object that depends"]
476 ["specifically on itself directly."]
477 struct GraphUnitDependsOnItself where {
478 ["Object that loops."] unit: {Display},
479 ["Where it is defined."] def_site: {TrySpan},
480 ["Where it is used (usually a subspan of `def_site`)."] usage: {TrySpan},
481 } impl {
482 "{unit} depends on itself" @ def_site;
483 "used here within its own definition" @ usage;
484 }
485}
486
487error_message! {
488 ["Error for when one tried to access too far into the past."]
489 struct NotPositive where {
490 ["Variable that is not deep enough."] var: {Display},
491 ["Location of the error"] site: {TrySpan},
492 ["How deep we could have gone"] available_depth: {Display},
493 ["How deep we actually tried to go"] attempted_depth: {Display},
494 } impl {
495 "Variable {var} is not positive at this depth" @ site;
496 "tried to reach {attempted_depth} steps into the past, with only {available_depth} available";
497 "Maybe add a `->` in front of the expression to increase the depth ?";
498 }
499}
500
501error_message! {
502 ["Attempted to use a `pre` in register mode without any depth available"]
503 struct ShallowPre where {
504 ["Expression to compute"] expr: {TrySpan},
505 } impl {
506 "Cannot compute the `pre` of this expression because the context does not provide an initialization value" @ expr;
507 "Maybe add a `->` in front of the expression to increase the depth ?";
508 }
509}
510
511error_message! {
512 ["Error for a literal that is not supported."]
513 ["Lustre only has `float`, `int`, and `bool` literals, so e.g. a `&str` will trigger this error."]
514 struct UnhandledLitType where {
515 ["Location of the literal."] site: {TrySpan},
516 } impl {
517 "Lustre only accepts literals of type int, float, or bool" @ site;
518 }
519}
520
521error_message! {
522 ["Error for when a comparison operator is used with associativity."]
523 ["Since `a = b = c` is ambiguous (does it mean `(a = b) = c` or `a = (b = c)`"]
524 ["or `(a = b) and (b = c)`, we choose to reject all interpretations and"]
525 ["ask for explicit parentheses around comparison operators."]
526 struct CmpNotAssociative where {
527 ["The `<` of `a < b > c`"] oper1: {Display},
528 ["The `a` of `a < b > c`"] first: {Display},
529 ["The whole location of `a < b > c`"] site: {TrySpan},
530 ["The `b` of `a < b > c`"] second: {Display},
531 ["The `c` of `a < b > c`"] third: {Display},
532 ["The `>` of `a < b > c`"] oper2: {Display},
533 } impl {
534 "Comparison operator {oper1} is not associative" @ site;
535 "Maybe replace `{first} {oper1} {second} {oper2} {third}` with `{first} {oper1} {second} and {second} {oper2} {third}` ?";
536 }
537}
538
539error_message! {
540 ["Generate an error when due to `implicit` that has the implicit clock,"]
541 ["`slow` was expected to also have the implicit clock but doesn't."]
542 struct ClkTooSlowExpectImplicit<It> where {
543 ["Clocked by something else, should have been `'self`."] slow: {Display + TrySpan + TryDefSite},
544 ["Clocked by `'self`"] implicit: {TrySpan},
545 ["Extra help messages, optionally."] extra: [It .. Display],
546 } impl {
547 "This expression is too slow: expected the implicit clock 'self, found {slow}" @ slow;
548 "Found {slow} here" @* if slow;
549 "Expected because this expression moves at the implicit pace" @ if implicit;
550 for extra => "{extra}";
551 }
552}
553
554error_message! {
555 ["When an expression is not a valid clock (anything but a local variable)."]
556 struct NotAClock where {
557 ["The faulty expression."] expr: {Display + TrySpan},
558 } impl {
559 "The expression `{expr}` cannot be interpreted as a clock because it is not a local boolean variable" @ expr;
560 }
561}
562
563error_message! {
564 ["When two clocks are both non-implicit but different."]
565 struct ClkNotComparable where {
566 ["First clock."] first: {Display + TrySpan + TryDefSite},
567 ["Second clock."] second: {Display + TrySpan + TryDefSite},
568 ["Span of the whole expression that contains both."] whole: {TrySpan},
569 } impl {
570 "Two subexpressions have incomparable clocks: {first} and {second} are incompatible" @ whole;
571 "This is clocked by {first}" @ first;
572 "defined here" @* if first;
573 "This is clocked by {second}" @ second;
574 "defined here" @* if second;
575 }
576}
577
578error_message! {
579 ["When two clocks should have been identical."]
580 struct ClkNotIdentical where {
581 ["First clock."] first: {Display + TrySpan + TryDefSite},
582 ["Second clock."] second: {Display + TrySpan + TryDefSite},
583 ["Span of the whole expression that contains both."] whole: {TrySpan},
584 } impl {
585 "Two subexpressions have different clocks: {first} and {second} are incompatible" @ whole;
586 "This is clocked by {first}" @ first;
587 "defined here" @* if first;
588 "This is clocked by {second}" @ second;
589 "defined here" @* if second;
590 "some operations (notably assignment) have stricter requirements than others on clock equality";
591 "you may need to insert a `when` operator";
592 }
593}
594
595error_message! {
596 ["When a generic type variable is unused and thus not inferrable"]
597 struct UnusedGeneric where {
598 ["Type variable that is absent from the inputs declaration."] unused: {Display + TrySpan},
599 ["Declaration of said inputs."] inputs: {TrySpan},
600 } impl {
601 "Type variable {unused} cannot be inferred from the inputs of this node" @ unused;
602 "None of these arguments are of type {unused}" @ inputs;
603 }
604}
605
606error_message! {
607 ["Impossible to satisfy generic constraints introduced by bounds."]
608 struct UnsatGenericConstraint where {
609 ["Type variable."] variable: {Display},
610 ["Already equal to."] previous: {Display + TrySpan},
611 ["Now additionally required to be equal to."] new: {Display + TrySpan},
612 ["Bound introduced by this node declaration."] context: {TrySpan},
613 } impl {
614 "Cannot satisfy constraint {variable} = {new} introduced here..." @ new;
615 "...because a previous bound already enforces {variable} = {previous}" @ previous;
616 "Unable to satisfy the generic bounds on {variable}" @ context;
617 }
618}
619
620error_message! {
621 ["When a generic type variable is unused and thus not inferrable."]
622 struct UndeclaredGeneric where {
623 ["Type variable that is absent from the generics declaration."] undeclared: {Display + TrySpan},
624 } impl {
625 "Type variable {undeclared} was not declared" @ undeclared;
626 "Maybe add a `#[generic[{undeclared}]]` annotation to the node ?";
627 }
628}
629
630error_message! {
631 ["Node is declared as executable, but has nonempty inputs or outputs."]
632 struct ExecutableNodeSig where {
633 ["Attribute that marks it as executable."] reason: {Display},
634 ["Whether there are any inputs."] inputs_nonempty: {Condition},
635 ["Where are the inputs."] inputs: {TrySpan},
636 ["Whether there are any outputs."] outputs_nonempty: {Condition},
637 ["Where are the outputs."] outputs: {TrySpan},
638 ["Entire call site."] site: {TrySpan},
639 } impl {
640 "Node has an incompatible signature to be marked as executable (required due to {reason})" @ site;
641 if inputs_nonempty => "Inputs should be ()" @ inputs;
642 if outputs_nonempty => "Outputs should be ()" @ outputs;
643 }
644}
645
646error_message! {
647 ["Two types cannot be equal because one is a tuple and the other a scalar"]
648 struct ScalarNotTuple where {
649 ["Tuple type found"] typ: {Display + TrySpan},
650 } impl {
651 "Expected a scalar type, found a tuple {typ}" @ typ;
652 }
653}
654
655error_message! {
656 ["Attribute cannot apply to this language construct."]
657 struct InapplicableAttribute where {
658 ["Description"] attr: {Display},
659 ["Language item in question."] construct: {Display},
660 ["Location"] site: {TrySpan},
661 } impl {
662 "Attribute {attr} is not applicable to {construct}" @ site;
663 }
664}