1use std::error;
2use std::io;
3use std::io::Write;
4use std::path::PathBuf;
5use std::rc::Rc;
6use std::str::Utf8Error;
7use std::string::FromUtf8Error;
8use std::sync::Arc;
9use std::{fmt::Debug, fmt::Display, num::ParseIntError};
10
11use codemap::{File, Span};
12use codemap_diagnostic::{ColorConfig, Diagnostic, Emitter, Level, SpanLabel, SpanStyle};
13use smol_str::SmolStr;
14
15use crate::spans::ToSpan;
16use crate::value::{CoercionKind, NixString};
17use crate::{SourceCode, Value};
18
19#[macro_export]
58macro_rules! try_cek {
59 ($result:expr) => {
60 match $result {
61 Ok(s) => s,
62 Err(cek) => {
63 let cek: $crate::CatchableErrorKind = cek;
65 return Ok(Err(cek));
66 }
67 }
68 };
69}
70
71#[macro_export]
81macro_rules! try_cek_to_value {
82 ($result:expr) => {
83 match $result {
84 Ok(s) => s,
85 Err(cek) => {
86 let cek: $crate::CatchableErrorKind = cek;
88 return Ok(Value::Catchable(Box::new(cek)));
89 }
90 }
91 };
92}
93
94#[derive(thiserror::Error, Clone, Debug)]
115pub enum CatchableErrorKind {
116 #[error("error thrown: {0}")]
117 Throw(NixString),
118
119 #[error("assertion failed")]
120 AssertionFailed,
121
122 #[error("feature {0} is not implemented yet")]
123 UnimplementedFeature(Box<str>),
124
125 #[error("Nix path entry could not be resolved: {0}")]
127 NixPathResolution(Box<str>),
128}
129
130#[derive(thiserror::Error, Clone, Debug)]
131pub enum ErrorKind {
132 #[error("evaluation aborted: {0}")]
134 Abort(String),
135
136 #[error("division by zero")]
137 DivisionByZero,
138
139 #[error("attribute key '{key}' already defined")]
140 DuplicateAttrsKey { key: String },
141
142 #[error(
145 "found attribute name '{}' of type '{}', but attribute names must be strings",
146 .0,
147 .0.type_of()
148 )]
149 InvalidAttributeName(Value),
150
151 #[error("attribute with name '{name}' could not be found in the set")]
152 AttributeNotFound { name: String },
153
154 #[error("list index '{index}' is out of bounds")]
156 IndexOutOfBounds { index: i64 },
157
158 #[error("'tail' called on an empty list")]
160 TailEmptyList,
161
162 #[error("expected value of type '{expected}', but found a '{actual}'")]
163 TypeError {
164 expected: &'static str,
165 actual: &'static str,
166 },
167
168 #[error("can not compare a {lhs} with a {rhs}")]
169 Incomparable {
170 lhs: &'static str,
171 rhs: &'static str,
172 },
173
174 #[error("could not resolve path: {0}")]
176 RelativePathResolution(String),
177
178 #[error("dynamically evaluated keys are not allowed in {0}")]
180 DynamicKeyInScope(&'static str),
181
182 #[error("variable not found")]
184 UnknownStaticVariable,
185
186 #[error(
188 r#"variable '{0}' could not be found
189
190Note that this occured within a `with`-expression. The problem may be related
191to a missing value in the attribute set(s) included via `with`."#
192 )]
193 UnknownDynamicVariable(String),
194
195 #[error("variable has already been defined")]
197 VariableAlreadyDefined(Option<Span>),
198
199 #[error("only functions and builtins can be called, but this is a '{0}'")]
201 NotCallable(&'static str),
202
203 #[error("infinite recursion encountered")]
205 InfiniteRecursion {
206 first_force: Span,
207 suspended_at: Option<Span>,
208 content_span: Option<Span>,
209 },
210
211 #[error("failed to parse Nix code:")]
213 ParseErrors(Vec<rnix::parser::ParseError>),
214
215 #[error("while evaluating this as native code ({gen_type})")]
218 NativeError {
219 gen_type: &'static str,
220 err: Box<Error>,
221 },
222
223 #[error("while evaluating this Nix code")]
226 BytecodeError(Box<Error>),
227
228 #[error("cannot ({}) coerce {from} to a string{}",
230 (if .kind.strong { "strongly" } else { "weakly" }),
231 (if *.from == "set" {
232 ", missing a `__toString` or `outPath` attribute"
233 } else {
234 ""
235 })
236 )]
237 NotCoercibleToString {
238 from: &'static str,
239 kind: CoercionKind,
240 },
241
242 #[error("string '{}' does not represent an absolute path", .0.to_string_lossy())]
244 NotAnAbsolutePath(PathBuf),
245
246 #[error("invalid integer: {0}")]
248 ParseIntError(ParseIntError),
249
250 #[error("cannot merge a nested attribute set into the inherited entry '{name}'")]
253 UnmergeableInherit { name: SmolStr },
254
255 #[error("nested attribute sets or keys can only be merged with literal attribute sets")]
258 UnmergeableValue,
259
260 #[error("parse errors occured while importing '{}'", .path.to_string_lossy())]
263 ImportParseError {
264 path: PathBuf,
265 file: Arc<File>,
266 errors: Vec<rnix::parser::ParseError>,
267 },
268
269 #[error("compiler errors occured while importing '{}'", .path.to_string_lossy())]
271 ImportCompilerError { path: PathBuf, errors: Vec<Error> },
272
273 #[error("I/O error: {}",
275 ({
276 let mut msg = String::new();
277
278 if let Some(path) = .path {
279 msg.push_str(&format!("{}: ", path.display()));
280 }
281
282 msg.push_str(&.error.to_string());
283
284 msg
285 })
286 )]
287 IO {
288 path: Option<PathBuf>,
289 error: Rc<io::Error>,
290 },
291
292 #[error("Error converting JSON to a Nix value or back: {0}")]
294 JsonError(String),
295
296 #[error("a {0} cannot be converted to JSON")]
298 NotSerialisableToJson(&'static str),
299
300 #[error("Error converting TOML to a Nix value: {0}")]
302 FromTomlError(String),
303
304 #[error("Unexpected agrument `{0}` passed to builtin")]
306 UnexpectedArgumentBuiltin(NixString),
307
308 #[error("Unexpected argument `{arg}` supplied to function")]
310 UnexpectedArgumentFormals { arg: NixString, formals_span: Span },
311
312 #[error("Invalid UTF-8 in string")]
314 Utf8,
315
316 #[error("Invalid hash: {0}")]
317 InvalidHash(String),
318
319 #[error("{0}")]
322 SnixError(Rc<dyn error::Error>),
323
324 #[error("{}",
327 ({
328 let mut disp = format!("Snix bug: {}", .msg);
329
330 if let Some(metadata) = .metadata {
331 disp.push_str(&format!("; metadata: {metadata:?}"));
332 }
333
334 disp
335 })
336 )]
337 SnixBug {
338 msg: &'static str,
339 metadata: Option<Rc<dyn Debug>>,
340 },
341
342 #[error("feature not yet implemented in Snix: {0}")]
346 NotImplemented(&'static str),
347
348 #[error("internal ErrorKind::WithContext variant leaked")]
350 WithContext {
351 context: String,
352 underlying: Box<ErrorKind>,
353 },
354
355 #[error("unexpected context string")]
357 UnexpectedContext,
358
359 #[error("{0}")]
366 CatchableError(CatchableErrorKind),
367
368 #[error("unknown hash type '{0}'")]
371 UnknownHashType(String),
372
373 #[error("invalid regular expression '{0}'")]
375 InvalidRegex(String),
376}
377
378impl error::Error for Error {
379 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
380 match &self.kind {
381 ErrorKind::NativeError { err, .. } | ErrorKind::BytecodeError(err) => err.source(),
382 ErrorKind::ParseErrors(err) => err.first().map(|e| e as &dyn error::Error),
383 ErrorKind::ParseIntError(err) => Some(err),
384 ErrorKind::ImportParseError { errors, .. } => {
385 errors.first().map(|e| e as &dyn error::Error)
386 }
387 ErrorKind::ImportCompilerError { errors, .. } => {
388 errors.first().map(|e| e as &dyn error::Error)
389 }
390 ErrorKind::IO { error, .. } => Some(error.as_ref()),
391 ErrorKind::SnixError(error) => Some(error.as_ref()),
392 _ => None,
393 }
394 }
395}
396
397impl From<ParseIntError> for ErrorKind {
398 fn from(e: ParseIntError) -> Self {
399 Self::ParseIntError(e)
400 }
401}
402
403impl From<Utf8Error> for ErrorKind {
404 fn from(_: Utf8Error) -> Self {
405 Self::NotImplemented("FromUtf8Error not handled: https://b.tvl.fyi/issues/189")
406 }
407}
408
409impl From<FromUtf8Error> for ErrorKind {
410 fn from(_: FromUtf8Error) -> Self {
411 Self::NotImplemented("FromUtf8Error not handled: https://b.tvl.fyi/issues/189")
412 }
413}
414
415impl From<bstr::Utf8Error> for ErrorKind {
416 fn from(_: bstr::Utf8Error) -> Self {
417 Self::Utf8
418 }
419}
420
421impl From<bstr::FromUtf8Error> for ErrorKind {
422 fn from(_value: bstr::FromUtf8Error) -> Self {
423 Self::Utf8
424 }
425}
426
427impl From<io::Error> for ErrorKind {
428 fn from(e: io::Error) -> Self {
429 ErrorKind::IO {
430 path: None,
431 error: Rc::new(e),
432 }
433 }
434}
435
436impl From<serde_json::Error> for ErrorKind {
437 fn from(err: serde_json::Error) -> Self {
438 Self::JsonError(err.to_string())
440 }
441}
442
443impl From<toml::de::Error> for ErrorKind {
444 fn from(err: toml::de::Error) -> Self {
445 Self::FromTomlError(format!("error in TOML serialization: {err}"))
446 }
447}
448
449#[derive(Clone, Debug)]
450pub struct Error {
451 pub kind: ErrorKind,
452 pub span: Span,
453 pub contexts: Vec<String>,
454 pub source: SourceCode,
455}
456
457impl Error {
458 pub fn new(mut kind: ErrorKind, span: Span, source: SourceCode) -> Self {
459 let mut contexts = vec![];
460 while let ErrorKind::WithContext {
461 context,
462 underlying,
463 } = kind
464 {
465 kind = *underlying;
466 contexts.push(context);
467 }
468
469 Error {
470 kind,
471 span,
472 contexts,
473 source,
474 }
475 }
476}
477
478impl Display for Error {
479 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
480 write!(f, "{}", self.kind)
481 }
482}
483
484pub type EvalResult<T> = Result<T, Error>;
485
486fn name_for_syntax(syntax: &rnix::SyntaxKind) -> &'static str {
488 match syntax {
489 rnix::SyntaxKind::TOKEN_COMMENT => "a comment",
490 rnix::SyntaxKind::TOKEN_WHITESPACE => "whitespace",
491 rnix::SyntaxKind::TOKEN_ASSERT => "`assert`-keyword",
492 rnix::SyntaxKind::TOKEN_ELSE => "`else`-keyword",
493 rnix::SyntaxKind::TOKEN_IN => "`in`-keyword",
494 rnix::SyntaxKind::TOKEN_IF => "`if`-keyword",
495 rnix::SyntaxKind::TOKEN_INHERIT => "`inherit`-keyword",
496 rnix::SyntaxKind::TOKEN_LET => "`let`-keyword",
497 rnix::SyntaxKind::TOKEN_OR => "`or`-keyword",
498 rnix::SyntaxKind::TOKEN_REC => "`rec`-keyword",
499 rnix::SyntaxKind::TOKEN_THEN => "`then`-keyword",
500 rnix::SyntaxKind::TOKEN_WITH => "`with`-keyword",
501 rnix::SyntaxKind::TOKEN_L_BRACE => "{",
502 rnix::SyntaxKind::TOKEN_R_BRACE => "}",
503 rnix::SyntaxKind::TOKEN_L_BRACK => "[",
504 rnix::SyntaxKind::TOKEN_R_BRACK => "]",
505 rnix::SyntaxKind::TOKEN_ASSIGN => "=",
506 rnix::SyntaxKind::TOKEN_AT => "@",
507 rnix::SyntaxKind::TOKEN_COLON => ":",
508 rnix::SyntaxKind::TOKEN_COMMA => "`,`",
509 rnix::SyntaxKind::TOKEN_DOT => ".",
510 rnix::SyntaxKind::TOKEN_ELLIPSIS => "...",
511 rnix::SyntaxKind::TOKEN_QUESTION => "?",
512 rnix::SyntaxKind::TOKEN_SEMICOLON => ";",
513 rnix::SyntaxKind::TOKEN_L_PAREN => "(",
514 rnix::SyntaxKind::TOKEN_R_PAREN => ")",
515 rnix::SyntaxKind::TOKEN_CONCAT => "++",
516 rnix::SyntaxKind::TOKEN_INVERT => "!",
517 rnix::SyntaxKind::TOKEN_UPDATE => "//",
518 rnix::SyntaxKind::TOKEN_ADD => "+",
519 rnix::SyntaxKind::TOKEN_SUB => "-",
520 rnix::SyntaxKind::TOKEN_MUL => "*",
521 rnix::SyntaxKind::TOKEN_DIV => "/",
522 rnix::SyntaxKind::TOKEN_AND_AND => "&&",
523 rnix::SyntaxKind::TOKEN_EQUAL => "==",
524 rnix::SyntaxKind::TOKEN_IMPLICATION => "->",
525 rnix::SyntaxKind::TOKEN_LESS => "<",
526 rnix::SyntaxKind::TOKEN_LESS_OR_EQ => "<=",
527 rnix::SyntaxKind::TOKEN_MORE => ">",
528 rnix::SyntaxKind::TOKEN_MORE_OR_EQ => ">=",
529 rnix::SyntaxKind::TOKEN_NOT_EQUAL => "!=",
530 rnix::SyntaxKind::TOKEN_OR_OR => "||",
531 rnix::SyntaxKind::TOKEN_FLOAT => "a float",
532 rnix::SyntaxKind::TOKEN_IDENT => "an identifier",
533 rnix::SyntaxKind::TOKEN_INTEGER => "an integer",
534 rnix::SyntaxKind::TOKEN_INTERPOL_END => "}",
535 rnix::SyntaxKind::TOKEN_INTERPOL_START => "${",
536 rnix::SyntaxKind::TOKEN_PATH => "a path",
537 rnix::SyntaxKind::TOKEN_URI => "a literal URI",
538 rnix::SyntaxKind::TOKEN_STRING_CONTENT => "content of a string",
539 rnix::SyntaxKind::TOKEN_STRING_END => "\"",
540 rnix::SyntaxKind::TOKEN_STRING_START => "\"",
541
542 rnix::SyntaxKind::NODE_APPLY => "a function application",
543 rnix::SyntaxKind::NODE_ASSERT => "an assertion",
544 rnix::SyntaxKind::NODE_ATTRPATH => "an attribute path",
545 rnix::SyntaxKind::NODE_DYNAMIC => "a dynamic identifier",
546
547 rnix::SyntaxKind::NODE_IDENT => "an identifier",
548 rnix::SyntaxKind::NODE_IF_ELSE => "an `if`-expression",
549 rnix::SyntaxKind::NODE_SELECT => "a `select`-expression",
550 rnix::SyntaxKind::NODE_INHERIT => "inherited values",
551 rnix::SyntaxKind::NODE_INHERIT_FROM => "inherited values",
552 rnix::SyntaxKind::NODE_STRING => "a string",
553 rnix::SyntaxKind::NODE_INTERPOL => "an interpolation",
554 rnix::SyntaxKind::NODE_LAMBDA => "a function",
555 rnix::SyntaxKind::NODE_IDENT_PARAM => "a function parameter",
556 rnix::SyntaxKind::NODE_LEGACY_LET => "a legacy `let`-expression",
557 rnix::SyntaxKind::NODE_LET_IN => "a `let`-expression",
558 rnix::SyntaxKind::NODE_LIST => "a list",
559 rnix::SyntaxKind::NODE_BIN_OP => "a binary operator",
560 rnix::SyntaxKind::NODE_PAREN => "a parenthesised expression",
561 rnix::SyntaxKind::NODE_PATTERN => "a function argument pattern",
562 rnix::SyntaxKind::NODE_PAT_BIND => "an argument pattern binding",
563 rnix::SyntaxKind::NODE_PAT_ENTRY => "an argument pattern entry",
564 rnix::SyntaxKind::NODE_ROOT => "a Nix expression",
565 rnix::SyntaxKind::NODE_ATTR_SET => "an attribute set",
566 rnix::SyntaxKind::NODE_ATTRPATH_VALUE => "an attribute set entry",
567 rnix::SyntaxKind::NODE_UNARY_OP => "a unary operator",
568 rnix::SyntaxKind::NODE_LITERAL => "a literal value",
569 rnix::SyntaxKind::NODE_WITH => "a `with`-expression",
570 rnix::SyntaxKind::NODE_PATH => "a path",
571 rnix::SyntaxKind::NODE_HAS_ATTR => "`?`-operator",
572
573 rnix::SyntaxKind::NODE_ERROR => todo!("NODE_ERROR found, tell tazjin!"),
575 rnix::SyntaxKind::TOKEN_ERROR => todo!("TOKEN_ERROR found, tell tazjin!"),
576 _ => todo!(),
577 }
578}
579
580fn expected_syntax(one_of: &[rnix::SyntaxKind]) -> String {
582 match one_of.len() {
583 0 => "nothing".into(),
584 1 => format!("'{}'", name_for_syntax(&one_of[0])),
585 _ => {
586 let mut out: String = "one of: ".into();
587 let end = one_of.len() - 1;
588
589 for (idx, item) in one_of.iter().enumerate() {
590 if idx != 0 {
591 out.push_str(", ");
592 } else if idx == end {
593 out.push_str(", or ");
594 };
595
596 out.push_str(name_for_syntax(item));
597 }
598
599 out
600 }
601 }
602}
603
604fn spans_for_parse_errors(file: &File, errors: &[rnix::parser::ParseError]) -> Vec<SpanLabel> {
607 let mut had_eof = false;
611
612 errors
613 .iter()
614 .enumerate()
615 .filter_map(|(idx, err)| {
616 let (span, label): (Span, String) = match err {
617 rnix::parser::ParseError::Unexpected(range) => (
618 range.span_for(file),
619 "found an unexpected syntax element here".into(),
620 ),
621
622 rnix::parser::ParseError::UnexpectedExtra(range) => (
623 range.span_for(file),
624 "found unexpected extra elements at the root of the expression".into(),
625 ),
626
627 rnix::parser::ParseError::UnexpectedWanted(found, range, wanted) => {
628 let span = range.span_for(file);
629 (
630 span,
631 format!(
632 "found '{}', but expected {}",
633 name_for_syntax(found),
634 expected_syntax(wanted),
635 ),
636 )
637 }
638
639 rnix::parser::ParseError::UnexpectedEOF => {
640 if had_eof {
641 return None;
642 }
643
644 had_eof = true;
645
646 (
647 file.span,
648 "code ended unexpectedly while the parser still expected more".into(),
649 )
650 }
651
652 rnix::parser::ParseError::UnexpectedEOFWanted(wanted) => {
653 had_eof = true;
654
655 (
656 file.span,
657 format!(
658 "code ended unexpectedly, but wanted {}",
659 expected_syntax(wanted)
660 ),
661 )
662 }
663
664 rnix::parser::ParseError::DuplicatedArgs(range, name) => (
665 range.span_for(file),
666 format!("the function argument pattern '{name}' was bound more than once"),
667 ),
668
669 rnix::parser::ParseError::RecursionLimitExceeded => (
670 file.span,
671 "this code exceeds the parser's recursion limit, please report a Snix bug"
672 .to_string(),
673 ),
674
675 rnix::parser::ParseError::UnexpectedDoubleBind(range) => (
677 range.span_for(file),
678 "this pattern was bound more than once".into(),
679 ),
680
681 new => todo!("new parse error variant: {}", new),
686 };
687
688 Some(SpanLabel {
689 span,
690 label: Some(label),
691 style: if idx == 0 {
692 SpanStyle::Primary
693 } else {
694 SpanStyle::Secondary
695 },
696 })
697 })
698 .collect()
699}
700
701impl Error {
702 pub fn fancy_format_str(&self) -> String {
703 let mut out = vec![];
704 Emitter::vec(&mut out, Some(&*self.source.codemap())).emit(&self.diagnostics());
705 String::from_utf8_lossy(&out).to_string()
706 }
707
708 pub fn fancy_format_stderr(&self) {
711 Emitter::stderr(ColorConfig::Auto, Some(&*self.source.codemap())).emit(&self.diagnostics());
712 }
713
714 pub fn fancy_format_write<E: Write + Send>(&self, stderr: &mut E) {
717 Emitter::new(Box::new(stderr), Some(&*self.source.codemap())).emit(&self.diagnostics());
718 }
719
720 fn span_label(&self) -> Option<String> {
723 let label = match &self.kind {
724 ErrorKind::DuplicateAttrsKey { .. } => "in this attribute set",
725 ErrorKind::InvalidAttributeName(_) => "in this attribute set",
726 ErrorKind::RelativePathResolution(_) => "in this path literal",
727 ErrorKind::UnexpectedArgumentBuiltin { .. } => "while calling this builtin",
728 ErrorKind::UnexpectedArgumentFormals { .. } => "in this function call",
729 ErrorKind::UnexpectedContext => "in this string",
730
731 ErrorKind::Abort(_)
734 | ErrorKind::AttributeNotFound { .. }
735 | ErrorKind::IndexOutOfBounds { .. }
736 | ErrorKind::TailEmptyList
737 | ErrorKind::TypeError { .. }
738 | ErrorKind::Incomparable { .. }
739 | ErrorKind::DivisionByZero
740 | ErrorKind::DynamicKeyInScope(_)
741 | ErrorKind::UnknownStaticVariable
742 | ErrorKind::UnknownDynamicVariable(_)
743 | ErrorKind::VariableAlreadyDefined(_)
744 | ErrorKind::NotCallable(_)
745 | ErrorKind::InfiniteRecursion { .. }
746 | ErrorKind::ParseErrors(_)
747 | ErrorKind::NativeError { .. }
748 | ErrorKind::BytecodeError(_)
749 | ErrorKind::NotCoercibleToString { .. }
750 | ErrorKind::NotAnAbsolutePath(_)
751 | ErrorKind::ParseIntError(_)
752 | ErrorKind::UnmergeableInherit { .. }
753 | ErrorKind::UnmergeableValue
754 | ErrorKind::ImportParseError { .. }
755 | ErrorKind::ImportCompilerError { .. }
756 | ErrorKind::IO { .. }
757 | ErrorKind::JsonError(_)
758 | ErrorKind::NotSerialisableToJson(_)
759 | ErrorKind::FromTomlError(_)
760 | ErrorKind::Utf8
761 | ErrorKind::SnixError(_)
762 | ErrorKind::SnixBug { .. }
763 | ErrorKind::NotImplemented(_)
764 | ErrorKind::WithContext { .. }
765 | ErrorKind::UnknownHashType(_)
766 | ErrorKind::InvalidHash(_)
767 | ErrorKind::InvalidRegex(_)
768 | ErrorKind::CatchableError(_) => return None,
769 };
770
771 Some(label.into())
772 }
773
774 fn code(&self) -> &'static str {
777 match self.kind {
778 ErrorKind::CatchableError(CatchableErrorKind::Throw(_)) => "E001",
779 ErrorKind::Abort(_) => "E002",
780 ErrorKind::CatchableError(CatchableErrorKind::AssertionFailed) => "E003",
781 ErrorKind::InvalidAttributeName { .. } => "E004",
782 ErrorKind::AttributeNotFound { .. } => "E005",
783 ErrorKind::TypeError { .. } => "E006",
784 ErrorKind::Incomparable { .. } => "E007",
785 ErrorKind::CatchableError(CatchableErrorKind::NixPathResolution(_)) => "E008",
786 ErrorKind::DynamicKeyInScope(_) => "E009",
787 ErrorKind::UnknownStaticVariable => "E010",
788 ErrorKind::UnknownDynamicVariable(_) => "E011",
789 ErrorKind::VariableAlreadyDefined(_) => "E012",
790 ErrorKind::NotCallable(_) => "E013",
791 ErrorKind::InfiniteRecursion { .. } => "E014",
792 ErrorKind::ParseErrors(_) => "E015",
793 ErrorKind::DuplicateAttrsKey { .. } => "E016",
794 ErrorKind::NotCoercibleToString { .. } => "E018",
795 ErrorKind::IndexOutOfBounds { .. } => "E019",
796 ErrorKind::NotAnAbsolutePath(_) => "E020",
797 ErrorKind::ParseIntError(_) => "E021",
798 ErrorKind::TailEmptyList => "E023",
799 ErrorKind::UnmergeableInherit { .. } => "E024",
800 ErrorKind::UnmergeableValue => "E025",
801 ErrorKind::ImportParseError { .. } => "E027",
802 ErrorKind::ImportCompilerError { .. } => "E028",
803 ErrorKind::IO { .. } => "E029",
804 ErrorKind::JsonError { .. } => "E030",
805 ErrorKind::UnexpectedArgumentFormals { .. } => "E031",
806 ErrorKind::RelativePathResolution(_) => "E032",
807 ErrorKind::DivisionByZero => "E033",
808 ErrorKind::FromTomlError(_) => "E035",
809 ErrorKind::NotSerialisableToJson(_) => "E036",
810 ErrorKind::UnexpectedContext => "E037",
811 ErrorKind::Utf8 => "E038",
812 ErrorKind::UnknownHashType(_) => "E039",
813 ErrorKind::UnexpectedArgumentBuiltin { .. } => "E040",
814 ErrorKind::InvalidHash(_) => "E041",
815 ErrorKind::InvalidRegex(_) => "E042",
816
817 ErrorKind::SnixError(_) => "E997",
821
822 ErrorKind::SnixBug { .. } => "E998",
825
826 ErrorKind::CatchableError(CatchableErrorKind::UnimplementedFeature(_))
828 | ErrorKind::NotImplemented(_) => "E999",
829
830 ErrorKind::NativeError { ref err, .. } | ErrorKind::BytecodeError(ref err) => {
833 err.code()
834 }
835
836 ErrorKind::WithContext { .. } => {
837 panic!("internal ErrorKind::WithContext variant leaked")
838 }
839 }
840 }
841
842 fn spans(&self) -> Vec<SpanLabel> {
843 let mut spans = match &self.kind {
844 ErrorKind::ImportParseError { errors, file, .. } => {
845 spans_for_parse_errors(file, errors)
846 }
847
848 ErrorKind::ParseErrors(errors) => {
849 let file = self.source.get_file(self.span);
850 spans_for_parse_errors(&file, errors)
851 }
852
853 ErrorKind::UnexpectedArgumentFormals { formals_span, .. } => {
854 vec![
855 SpanLabel {
856 label: self.span_label(),
857 span: self.span,
858 style: SpanStyle::Primary,
859 },
860 SpanLabel {
861 label: Some("the accepted arguments".into()),
862 span: *formals_span,
863 style: SpanStyle::Secondary,
864 },
865 ]
866 }
867
868 ErrorKind::InfiniteRecursion {
869 first_force,
870 suspended_at,
871 content_span,
872 } => {
873 let mut spans = vec![];
874
875 if let Some(content_span) = content_span {
876 spans.push(SpanLabel {
877 label: Some("this lazily-evaluated code".into()),
878 span: *content_span,
879 style: SpanStyle::Secondary,
880 })
881 }
882
883 if let Some(suspended_at) = suspended_at {
884 spans.push(SpanLabel {
885 label: Some("which was instantiated here".into()),
886 span: *suspended_at,
887 style: SpanStyle::Secondary,
888 })
889 }
890
891 spans.push(SpanLabel {
892 label: Some("was first requested to be evaluated here".into()),
893 span: *first_force,
894 style: SpanStyle::Secondary,
895 });
896
897 spans.push(SpanLabel {
898 label: Some("but then requested again here during its own evaluation".into()),
899 span: self.span,
900 style: SpanStyle::Primary,
901 });
902
903 spans
904 }
905
906 _ => {
908 vec![SpanLabel {
909 label: self.span_label(),
910 span: self.span,
911 style: SpanStyle::Primary,
912 }]
913 }
914 };
915
916 for ctx in &self.contexts {
917 spans.push(SpanLabel {
918 label: Some(format!("while {ctx}")),
919 span: self.span,
920 style: SpanStyle::Secondary,
921 });
922 }
923
924 spans
925 }
926
927 fn diagnostic(&self) -> Diagnostic {
929 Diagnostic {
930 level: Level::Error,
931 message: self.to_string(),
932 spans: self.spans(),
933 code: Some(self.code().into()),
934 }
935 }
936
937 fn diagnostics(&self) -> Vec<Diagnostic> {
940 match &self.kind {
941 ErrorKind::ImportCompilerError { errors, .. } => {
942 let mut out = vec![self.diagnostic()];
943 out.extend(errors.iter().map(|e| e.diagnostic()));
944 out
945 }
946
947 ErrorKind::NativeError { err: next, .. } | ErrorKind::BytecodeError(next) => {
956 let mut diagnostics: Vec<Diagnostic> = vec![];
958
959 let mut next = *next.clone();
962
963 let mut this_message = self.to_string();
965
966 let mut this_span = self.span;
968
969 let mut this_spans = self.spans();
971
972 loop {
973 if is_new_span(
974 this_span,
975 diagnostics.last().and_then(|last| last.spans.last()),
976 ) {
977 diagnostics.push(Diagnostic {
978 level: Level::Note,
979 message: this_message,
980 spans: this_spans,
981 code: None, });
983 }
984
985 this_message = next.to_string();
986 this_span = next.span;
987 this_spans = next.spans();
988
989 match next.kind {
990 ErrorKind::NativeError { err: inner, .. }
991 | ErrorKind::BytecodeError(inner) => {
992 next = *inner;
993 continue;
994 }
995 _ => {
996 diagnostics.extend(next.diagnostics());
997 break;
998 }
999 }
1000 }
1001
1002 diagnostics
1003 }
1004
1005 _ => vec![self.diagnostic()],
1006 }
1007 }
1008}
1009
1010fn is_new_span(this_span: Span, parent: Option<&SpanLabel>) -> bool {
1012 match parent {
1013 None => true,
1014 Some(parent) => parent.span != this_span,
1015 }
1016}
1017
1018pub trait AddContext {
1020 fn context<S: Into<String>>(self, ctx: S) -> Self;
1022}
1023
1024impl AddContext for ErrorKind {
1025 fn context<S: Into<String>>(self, ctx: S) -> Self {
1026 ErrorKind::WithContext {
1027 context: ctx.into(),
1028 underlying: Box::new(self),
1029 }
1030 }
1031}
1032
1033impl<T> AddContext for Result<T, ErrorKind> {
1034 fn context<S: Into<String>>(self, ctx: S) -> Self {
1035 self.map_err(|kind| kind.context(ctx))
1036 }
1037}
1038
1039impl<T> AddContext for Result<T, Error> {
1040 fn context<S: Into<String>>(self, ctx: S) -> Self {
1041 self.map_err(|err| Error {
1042 kind: err.kind.context(ctx),
1043 ..err
1044 })
1045 }
1046}