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#[derive(thiserror::Error, Clone, Debug)]
40pub enum CatchableErrorKind {
41 #[error("error thrown: {0}")]
42 Throw(NixString),
43
44 #[error("assertion failed")]
45 AssertionFailed,
46
47 #[error("feature {0} is not implemented yet")]
48 UnimplementedFeature(Box<str>),
49
50 #[error("Nix path entry could not be resolved: {0}")]
52 NixPathResolution(Box<str>),
53}
54
55#[derive(thiserror::Error, Clone, Debug)]
56pub enum ErrorKind {
57 #[error("evaluation aborted: {0}")]
59 Abort(String),
60
61 #[error("division by zero")]
62 DivisionByZero,
63
64 #[error("attribute key '{key}' already defined")]
65 DuplicateAttrsKey { key: String },
66
67 #[error(
70 "found attribute name '{}' of type '{}', but attribute names must be strings",
71 .0,
72 .0.type_of()
73 )]
74 InvalidAttributeName(Value),
75
76 #[error("attribute with name '{name}' could not be found in the set")]
77 AttributeNotFound { name: String },
78
79 #[error("list index '{index}' is out of bounds")]
81 IndexOutOfBounds { index: i64 },
82
83 #[error("'tail' called on an empty list")]
85 TailEmptyList,
86
87 #[error("expected value of type '{expected}', but found a '{actual}'")]
88 TypeError {
89 expected: &'static str,
90 actual: &'static str,
91 },
92
93 #[error("can not compare a {lhs} with a {rhs}")]
94 Incomparable {
95 lhs: &'static str,
96 rhs: &'static str,
97 },
98
99 #[error("could not resolve path: {0}")]
101 RelativePathResolution(String),
102
103 #[error("dynamically evaluated keys are not allowed in {0}")]
105 DynamicKeyInScope(&'static str),
106
107 #[error("variable not found")]
109 UnknownStaticVariable,
110
111 #[error(
113 r#"variable '{0}' could not be found
114
115Note that this occured within a `with`-expression. The problem may be related
116to a missing value in the attribute set(s) included via `with`."#
117 )]
118 UnknownDynamicVariable(String),
119
120 #[error("variable has already been defined")]
122 VariableAlreadyDefined(Option<Span>),
123
124 #[error("only functions and builtins can be called, but this is a '{0}'")]
126 NotCallable(&'static str),
127
128 #[error("infinite recursion encountered")]
130 InfiniteRecursion {
131 first_force: Span,
132 suspended_at: Option<Span>,
133 content_span: Option<Span>,
134 },
135
136 #[error("failed to parse Nix code:")]
138 ParseErrors(Vec<rnix::parser::ParseError>),
139
140 #[error("while evaluating this as native code ({gen_type})")]
143 NativeError {
144 gen_type: &'static str,
145 err: Box<Error>,
146 },
147
148 #[error("while evaluating this Nix code")]
151 BytecodeError(Box<Error>),
152
153 #[error("cannot ({}) coerce {from} to a string{}",
155 (if .kind.strong { "strongly" } else { "weakly" }),
156 (if *.from == "set" {
157 ", missing a `__toString` or `outPath` attribute"
158 } else {
159 ""
160 })
161 )]
162 NotCoercibleToString {
163 from: &'static str,
164 kind: CoercionKind,
165 },
166
167 #[error("string '{}' does not represent an absolute path", .0.to_string_lossy())]
169 NotAnAbsolutePath(PathBuf),
170
171 #[error("invalid integer: {0}")]
173 ParseIntError(ParseIntError),
174
175 #[error("cannot merge a nested attribute set into the inherited entry '{name}'")]
178 UnmergeableInherit { name: SmolStr },
179
180 #[error("nested attribute sets or keys can only be merged with literal attribute sets")]
183 UnmergeableValue,
184
185 #[error("parse errors occured while importing '{}'", .path.to_string_lossy())]
188 ImportParseError {
189 path: PathBuf,
190 file: Arc<File>,
191 errors: Vec<rnix::parser::ParseError>,
192 },
193
194 #[error("compiler errors occured while importing '{}'", .path.to_string_lossy())]
196 ImportCompilerError { path: PathBuf, errors: Vec<Error> },
197
198 #[error("I/O error: {}",
200 ({
201 let mut msg = String::new();
202
203 if let Some(path) = .path {
204 msg.push_str(&format!("{}: ", path.display()));
205 }
206
207 msg.push_str(&.error.to_string());
208
209 msg
210 })
211 )]
212 IO {
213 path: Option<PathBuf>,
214 error: Rc<io::Error>,
215 },
216
217 #[error("Error converting JSON to a Nix value or back: {0}")]
219 JsonError(String),
220
221 #[error("a {0} cannot be converted to JSON")]
223 NotSerialisableToJson(&'static str),
224
225 #[error("Error converting TOML to a Nix value: {0}")]
227 FromTomlError(String),
228
229 #[error("Unexpected agrument `{0}` passed to builtin")]
231 UnexpectedArgumentBuiltin(NixString),
232
233 #[error("Unexpected argument `{arg}` supplied to function")]
235 UnexpectedArgumentFormals { arg: NixString, formals_span: Span },
236
237 #[error("Invalid UTF-8 in string")]
239 Utf8,
240
241 #[error("Invalid hash: {0}")]
242 InvalidHash(String),
243
244 #[error("{0}")]
247 SnixError(Rc<dyn error::Error>),
248
249 #[error("{}",
252 ({
253 let mut disp = format!("Snix bug: {}", .msg);
254
255 if let Some(metadata) = .metadata {
256 disp.push_str(&format!("; metadata: {:?}", metadata));
257 }
258
259 disp
260 })
261 )]
262 SnixBug {
263 msg: &'static str,
264 metadata: Option<Rc<dyn Debug>>,
265 },
266
267 #[error("feature not yet implemented in Snix: {0}")]
271 NotImplemented(&'static str),
272
273 #[error("internal ErrorKind::WithContext variant leaked")]
275 WithContext {
276 context: String,
277 underlying: Box<ErrorKind>,
278 },
279
280 #[error("unexpected context string")]
282 UnexpectedContext,
283
284 #[error("{0}")]
291 CatchableError(CatchableErrorKind),
292
293 #[error("unknown hash type '{0}'")]
296 UnknownHashType(String),
297}
298
299impl error::Error for Error {
300 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
301 match &self.kind {
302 ErrorKind::NativeError { err, .. } | ErrorKind::BytecodeError(err) => err.source(),
303 ErrorKind::ParseErrors(err) => err.first().map(|e| e as &dyn error::Error),
304 ErrorKind::ParseIntError(err) => Some(err),
305 ErrorKind::ImportParseError { errors, .. } => {
306 errors.first().map(|e| e as &dyn error::Error)
307 }
308 ErrorKind::ImportCompilerError { errors, .. } => {
309 errors.first().map(|e| e as &dyn error::Error)
310 }
311 ErrorKind::IO { error, .. } => Some(error.as_ref()),
312 ErrorKind::SnixError(error) => Some(error.as_ref()),
313 _ => None,
314 }
315 }
316}
317
318impl From<ParseIntError> for ErrorKind {
319 fn from(e: ParseIntError) -> Self {
320 Self::ParseIntError(e)
321 }
322}
323
324impl From<Utf8Error> for ErrorKind {
325 fn from(_: Utf8Error) -> Self {
326 Self::NotImplemented("FromUtf8Error not handled: https://b.tvl.fyi/issues/189")
327 }
328}
329
330impl From<FromUtf8Error> for ErrorKind {
331 fn from(_: FromUtf8Error) -> Self {
332 Self::NotImplemented("FromUtf8Error not handled: https://b.tvl.fyi/issues/189")
333 }
334}
335
336impl From<bstr::Utf8Error> for ErrorKind {
337 fn from(_: bstr::Utf8Error) -> Self {
338 Self::Utf8
339 }
340}
341
342impl From<bstr::FromUtf8Error> for ErrorKind {
343 fn from(_value: bstr::FromUtf8Error) -> Self {
344 Self::Utf8
345 }
346}
347
348impl From<io::Error> for ErrorKind {
349 fn from(e: io::Error) -> Self {
350 ErrorKind::IO {
351 path: None,
352 error: Rc::new(e),
353 }
354 }
355}
356
357impl From<serde_json::Error> for ErrorKind {
358 fn from(err: serde_json::Error) -> Self {
359 Self::JsonError(err.to_string())
361 }
362}
363
364impl From<toml::de::Error> for ErrorKind {
365 fn from(err: toml::de::Error) -> Self {
366 Self::FromTomlError(format!("error in TOML serialization: {err}"))
367 }
368}
369
370#[derive(Clone, Debug)]
371pub struct Error {
372 pub kind: ErrorKind,
373 pub span: Span,
374 pub contexts: Vec<String>,
375 pub source: SourceCode,
376}
377
378impl Error {
379 pub fn new(mut kind: ErrorKind, span: Span, source: SourceCode) -> Self {
380 let mut contexts = vec![];
381 while let ErrorKind::WithContext {
382 context,
383 underlying,
384 } = kind
385 {
386 kind = *underlying;
387 contexts.push(context);
388 }
389
390 Error {
391 kind,
392 span,
393 contexts,
394 source,
395 }
396 }
397}
398
399impl Display for Error {
400 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
401 write!(f, "{}", self.kind)
402 }
403}
404
405pub type EvalResult<T> = Result<T, Error>;
406
407fn name_for_syntax(syntax: &rnix::SyntaxKind) -> &'static str {
409 match syntax {
410 rnix::SyntaxKind::TOKEN_COMMENT => "a comment",
411 rnix::SyntaxKind::TOKEN_WHITESPACE => "whitespace",
412 rnix::SyntaxKind::TOKEN_ASSERT => "`assert`-keyword",
413 rnix::SyntaxKind::TOKEN_ELSE => "`else`-keyword",
414 rnix::SyntaxKind::TOKEN_IN => "`in`-keyword",
415 rnix::SyntaxKind::TOKEN_IF => "`if`-keyword",
416 rnix::SyntaxKind::TOKEN_INHERIT => "`inherit`-keyword",
417 rnix::SyntaxKind::TOKEN_LET => "`let`-keyword",
418 rnix::SyntaxKind::TOKEN_OR => "`or`-keyword",
419 rnix::SyntaxKind::TOKEN_REC => "`rec`-keyword",
420 rnix::SyntaxKind::TOKEN_THEN => "`then`-keyword",
421 rnix::SyntaxKind::TOKEN_WITH => "`with`-keyword",
422 rnix::SyntaxKind::TOKEN_L_BRACE => "{",
423 rnix::SyntaxKind::TOKEN_R_BRACE => "}",
424 rnix::SyntaxKind::TOKEN_L_BRACK => "[",
425 rnix::SyntaxKind::TOKEN_R_BRACK => "]",
426 rnix::SyntaxKind::TOKEN_ASSIGN => "=",
427 rnix::SyntaxKind::TOKEN_AT => "@",
428 rnix::SyntaxKind::TOKEN_COLON => ":",
429 rnix::SyntaxKind::TOKEN_COMMA => "`,`",
430 rnix::SyntaxKind::TOKEN_DOT => ".",
431 rnix::SyntaxKind::TOKEN_ELLIPSIS => "...",
432 rnix::SyntaxKind::TOKEN_QUESTION => "?",
433 rnix::SyntaxKind::TOKEN_SEMICOLON => ";",
434 rnix::SyntaxKind::TOKEN_L_PAREN => "(",
435 rnix::SyntaxKind::TOKEN_R_PAREN => ")",
436 rnix::SyntaxKind::TOKEN_CONCAT => "++",
437 rnix::SyntaxKind::TOKEN_INVERT => "!",
438 rnix::SyntaxKind::TOKEN_UPDATE => "//",
439 rnix::SyntaxKind::TOKEN_ADD => "+",
440 rnix::SyntaxKind::TOKEN_SUB => "-",
441 rnix::SyntaxKind::TOKEN_MUL => "*",
442 rnix::SyntaxKind::TOKEN_DIV => "/",
443 rnix::SyntaxKind::TOKEN_AND_AND => "&&",
444 rnix::SyntaxKind::TOKEN_EQUAL => "==",
445 rnix::SyntaxKind::TOKEN_IMPLICATION => "->",
446 rnix::SyntaxKind::TOKEN_LESS => "<",
447 rnix::SyntaxKind::TOKEN_LESS_OR_EQ => "<=",
448 rnix::SyntaxKind::TOKEN_MORE => ">",
449 rnix::SyntaxKind::TOKEN_MORE_OR_EQ => ">=",
450 rnix::SyntaxKind::TOKEN_NOT_EQUAL => "!=",
451 rnix::SyntaxKind::TOKEN_OR_OR => "||",
452 rnix::SyntaxKind::TOKEN_FLOAT => "a float",
453 rnix::SyntaxKind::TOKEN_IDENT => "an identifier",
454 rnix::SyntaxKind::TOKEN_INTEGER => "an integer",
455 rnix::SyntaxKind::TOKEN_INTERPOL_END => "}",
456 rnix::SyntaxKind::TOKEN_INTERPOL_START => "${",
457 rnix::SyntaxKind::TOKEN_PATH => "a path",
458 rnix::SyntaxKind::TOKEN_URI => "a literal URI",
459 rnix::SyntaxKind::TOKEN_STRING_CONTENT => "content of a string",
460 rnix::SyntaxKind::TOKEN_STRING_END => "\"",
461 rnix::SyntaxKind::TOKEN_STRING_START => "\"",
462
463 rnix::SyntaxKind::NODE_APPLY => "a function application",
464 rnix::SyntaxKind::NODE_ASSERT => "an assertion",
465 rnix::SyntaxKind::NODE_ATTRPATH => "an attribute path",
466 rnix::SyntaxKind::NODE_DYNAMIC => "a dynamic identifier",
467
468 rnix::SyntaxKind::NODE_IDENT => "an identifier",
469 rnix::SyntaxKind::NODE_IF_ELSE => "an `if`-expression",
470 rnix::SyntaxKind::NODE_SELECT => "a `select`-expression",
471 rnix::SyntaxKind::NODE_INHERIT => "inherited values",
472 rnix::SyntaxKind::NODE_INHERIT_FROM => "inherited values",
473 rnix::SyntaxKind::NODE_STRING => "a string",
474 rnix::SyntaxKind::NODE_INTERPOL => "an interpolation",
475 rnix::SyntaxKind::NODE_LAMBDA => "a function",
476 rnix::SyntaxKind::NODE_IDENT_PARAM => "a function parameter",
477 rnix::SyntaxKind::NODE_LEGACY_LET => "a legacy `let`-expression",
478 rnix::SyntaxKind::NODE_LET_IN => "a `let`-expression",
479 rnix::SyntaxKind::NODE_LIST => "a list",
480 rnix::SyntaxKind::NODE_BIN_OP => "a binary operator",
481 rnix::SyntaxKind::NODE_PAREN => "a parenthesised expression",
482 rnix::SyntaxKind::NODE_PATTERN => "a function argument pattern",
483 rnix::SyntaxKind::NODE_PAT_BIND => "an argument pattern binding",
484 rnix::SyntaxKind::NODE_PAT_ENTRY => "an argument pattern entry",
485 rnix::SyntaxKind::NODE_ROOT => "a Nix expression",
486 rnix::SyntaxKind::NODE_ATTR_SET => "an attribute set",
487 rnix::SyntaxKind::NODE_ATTRPATH_VALUE => "an attribute set entry",
488 rnix::SyntaxKind::NODE_UNARY_OP => "a unary operator",
489 rnix::SyntaxKind::NODE_LITERAL => "a literal value",
490 rnix::SyntaxKind::NODE_WITH => "a `with`-expression",
491 rnix::SyntaxKind::NODE_PATH => "a path",
492 rnix::SyntaxKind::NODE_HAS_ATTR => "`?`-operator",
493
494 rnix::SyntaxKind::NODE_ERROR => todo!("NODE_ERROR found, tell tazjin!"),
496 rnix::SyntaxKind::TOKEN_ERROR => todo!("TOKEN_ERROR found, tell tazjin!"),
497 _ => todo!(),
498 }
499}
500
501fn expected_syntax(one_of: &[rnix::SyntaxKind]) -> String {
503 match one_of.len() {
504 0 => "nothing".into(),
505 1 => format!("'{}'", name_for_syntax(&one_of[0])),
506 _ => {
507 let mut out: String = "one of: ".into();
508 let end = one_of.len() - 1;
509
510 for (idx, item) in one_of.iter().enumerate() {
511 if idx != 0 {
512 out.push_str(", ");
513 } else if idx == end {
514 out.push_str(", or ");
515 };
516
517 out.push_str(name_for_syntax(item));
518 }
519
520 out
521 }
522 }
523}
524
525fn spans_for_parse_errors(file: &File, errors: &[rnix::parser::ParseError]) -> Vec<SpanLabel> {
528 let mut had_eof = false;
532
533 errors
534 .iter()
535 .enumerate()
536 .filter_map(|(idx, err)| {
537 let (span, label): (Span, String) = match err {
538 rnix::parser::ParseError::Unexpected(range) => (
539 range.span_for(file),
540 "found an unexpected syntax element here".into(),
541 ),
542
543 rnix::parser::ParseError::UnexpectedExtra(range) => (
544 range.span_for(file),
545 "found unexpected extra elements at the root of the expression".into(),
546 ),
547
548 rnix::parser::ParseError::UnexpectedWanted(found, range, wanted) => {
549 let span = range.span_for(file);
550 (
551 span,
552 format!(
553 "found '{}', but expected {}",
554 name_for_syntax(found),
555 expected_syntax(wanted),
556 ),
557 )
558 }
559
560 rnix::parser::ParseError::UnexpectedEOF => {
561 if had_eof {
562 return None;
563 }
564
565 had_eof = true;
566
567 (
568 file.span,
569 "code ended unexpectedly while the parser still expected more".into(),
570 )
571 }
572
573 rnix::parser::ParseError::UnexpectedEOFWanted(wanted) => {
574 had_eof = true;
575
576 (
577 file.span,
578 format!(
579 "code ended unexpectedly, but wanted {}",
580 expected_syntax(wanted)
581 ),
582 )
583 }
584
585 rnix::parser::ParseError::DuplicatedArgs(range, name) => (
586 range.span_for(file),
587 format!(
588 "the function argument pattern '{}' was bound more than once",
589 name
590 ),
591 ),
592
593 rnix::parser::ParseError::RecursionLimitExceeded => (
594 file.span,
595 "this code exceeds the parser's recursion limit, please report a Snix bug"
596 .to_string(),
597 ),
598
599 rnix::parser::ParseError::UnexpectedDoubleBind(range) => (
601 range.span_for(file),
602 "this pattern was bound more than once".into(),
603 ),
604
605 new => todo!("new parse error variant: {}", new),
610 };
611
612 Some(SpanLabel {
613 span,
614 label: Some(label),
615 style: if idx == 0 {
616 SpanStyle::Primary
617 } else {
618 SpanStyle::Secondary
619 },
620 })
621 })
622 .collect()
623}
624
625impl Error {
626 pub fn fancy_format_str(&self) -> String {
627 let mut out = vec![];
628 Emitter::vec(&mut out, Some(&*self.source.codemap())).emit(&self.diagnostics());
629 String::from_utf8_lossy(&out).to_string()
630 }
631
632 pub fn fancy_format_stderr(&self) {
635 Emitter::stderr(ColorConfig::Auto, Some(&*self.source.codemap())).emit(&self.diagnostics());
636 }
637
638 pub fn fancy_format_write<E: Write + Send>(&self, stderr: &mut E) {
641 Emitter::new(Box::new(stderr), Some(&*self.source.codemap())).emit(&self.diagnostics());
642 }
643
644 fn span_label(&self) -> Option<String> {
647 let label = match &self.kind {
648 ErrorKind::DuplicateAttrsKey { .. } => "in this attribute set",
649 ErrorKind::InvalidAttributeName(_) => "in this attribute set",
650 ErrorKind::RelativePathResolution(_) => "in this path literal",
651 ErrorKind::UnexpectedArgumentBuiltin { .. } => "while calling this builtin",
652 ErrorKind::UnexpectedArgumentFormals { .. } => "in this function call",
653 ErrorKind::UnexpectedContext => "in this string",
654
655 ErrorKind::Abort(_)
658 | ErrorKind::AttributeNotFound { .. }
659 | ErrorKind::IndexOutOfBounds { .. }
660 | ErrorKind::TailEmptyList
661 | ErrorKind::TypeError { .. }
662 | ErrorKind::Incomparable { .. }
663 | ErrorKind::DivisionByZero
664 | ErrorKind::DynamicKeyInScope(_)
665 | ErrorKind::UnknownStaticVariable
666 | ErrorKind::UnknownDynamicVariable(_)
667 | ErrorKind::VariableAlreadyDefined(_)
668 | ErrorKind::NotCallable(_)
669 | ErrorKind::InfiniteRecursion { .. }
670 | ErrorKind::ParseErrors(_)
671 | ErrorKind::NativeError { .. }
672 | ErrorKind::BytecodeError(_)
673 | ErrorKind::NotCoercibleToString { .. }
674 | ErrorKind::NotAnAbsolutePath(_)
675 | ErrorKind::ParseIntError(_)
676 | ErrorKind::UnmergeableInherit { .. }
677 | ErrorKind::UnmergeableValue
678 | ErrorKind::ImportParseError { .. }
679 | ErrorKind::ImportCompilerError { .. }
680 | ErrorKind::IO { .. }
681 | ErrorKind::JsonError(_)
682 | ErrorKind::NotSerialisableToJson(_)
683 | ErrorKind::FromTomlError(_)
684 | ErrorKind::Utf8
685 | ErrorKind::SnixError(_)
686 | ErrorKind::SnixBug { .. }
687 | ErrorKind::NotImplemented(_)
688 | ErrorKind::WithContext { .. }
689 | ErrorKind::UnknownHashType(_)
690 | ErrorKind::InvalidHash(_)
691 | ErrorKind::CatchableError(_) => return None,
692 };
693
694 Some(label.into())
695 }
696
697 fn code(&self) -> &'static str {
700 match self.kind {
701 ErrorKind::CatchableError(CatchableErrorKind::Throw(_)) => "E001",
702 ErrorKind::Abort(_) => "E002",
703 ErrorKind::CatchableError(CatchableErrorKind::AssertionFailed) => "E003",
704 ErrorKind::InvalidAttributeName { .. } => "E004",
705 ErrorKind::AttributeNotFound { .. } => "E005",
706 ErrorKind::TypeError { .. } => "E006",
707 ErrorKind::Incomparable { .. } => "E007",
708 ErrorKind::CatchableError(CatchableErrorKind::NixPathResolution(_)) => "E008",
709 ErrorKind::DynamicKeyInScope(_) => "E009",
710 ErrorKind::UnknownStaticVariable => "E010",
711 ErrorKind::UnknownDynamicVariable(_) => "E011",
712 ErrorKind::VariableAlreadyDefined(_) => "E012",
713 ErrorKind::NotCallable(_) => "E013",
714 ErrorKind::InfiniteRecursion { .. } => "E014",
715 ErrorKind::ParseErrors(_) => "E015",
716 ErrorKind::DuplicateAttrsKey { .. } => "E016",
717 ErrorKind::NotCoercibleToString { .. } => "E018",
718 ErrorKind::IndexOutOfBounds { .. } => "E019",
719 ErrorKind::NotAnAbsolutePath(_) => "E020",
720 ErrorKind::ParseIntError(_) => "E021",
721 ErrorKind::TailEmptyList => "E023",
722 ErrorKind::UnmergeableInherit { .. } => "E024",
723 ErrorKind::UnmergeableValue => "E025",
724 ErrorKind::ImportParseError { .. } => "E027",
725 ErrorKind::ImportCompilerError { .. } => "E028",
726 ErrorKind::IO { .. } => "E029",
727 ErrorKind::JsonError { .. } => "E030",
728 ErrorKind::UnexpectedArgumentFormals { .. } => "E031",
729 ErrorKind::RelativePathResolution(_) => "E032",
730 ErrorKind::DivisionByZero => "E033",
731 ErrorKind::FromTomlError(_) => "E035",
732 ErrorKind::NotSerialisableToJson(_) => "E036",
733 ErrorKind::UnexpectedContext => "E037",
734 ErrorKind::Utf8 => "E038",
735 ErrorKind::UnknownHashType(_) => "E039",
736 ErrorKind::UnexpectedArgumentBuiltin { .. } => "E040",
737 ErrorKind::InvalidHash(_) => "E041",
738
739 ErrorKind::SnixError(_) => "E997",
743
744 ErrorKind::SnixBug { .. } => "E998",
747
748 ErrorKind::CatchableError(CatchableErrorKind::UnimplementedFeature(_))
750 | ErrorKind::NotImplemented(_) => "E999",
751
752 ErrorKind::NativeError { ref err, .. } | ErrorKind::BytecodeError(ref err) => {
755 err.code()
756 }
757
758 ErrorKind::WithContext { .. } => {
759 panic!("internal ErrorKind::WithContext variant leaked")
760 }
761 }
762 }
763
764 fn spans(&self) -> Vec<SpanLabel> {
765 let mut spans = match &self.kind {
766 ErrorKind::ImportParseError { errors, file, .. } => {
767 spans_for_parse_errors(file, errors)
768 }
769
770 ErrorKind::ParseErrors(errors) => {
771 let file = self.source.get_file(self.span);
772 spans_for_parse_errors(&file, errors)
773 }
774
775 ErrorKind::UnexpectedArgumentFormals { formals_span, .. } => {
776 vec![
777 SpanLabel {
778 label: self.span_label(),
779 span: self.span,
780 style: SpanStyle::Primary,
781 },
782 SpanLabel {
783 label: Some("the accepted arguments".into()),
784 span: *formals_span,
785 style: SpanStyle::Secondary,
786 },
787 ]
788 }
789
790 ErrorKind::InfiniteRecursion {
791 first_force,
792 suspended_at,
793 content_span,
794 } => {
795 let mut spans = vec![];
796
797 if let Some(content_span) = content_span {
798 spans.push(SpanLabel {
799 label: Some("this lazily-evaluated code".into()),
800 span: *content_span,
801 style: SpanStyle::Secondary,
802 })
803 }
804
805 if let Some(suspended_at) = suspended_at {
806 spans.push(SpanLabel {
807 label: Some("which was instantiated here".into()),
808 span: *suspended_at,
809 style: SpanStyle::Secondary,
810 })
811 }
812
813 spans.push(SpanLabel {
814 label: Some("was first requested to be evaluated here".into()),
815 span: *first_force,
816 style: SpanStyle::Secondary,
817 });
818
819 spans.push(SpanLabel {
820 label: Some("but then requested again here during its own evaluation".into()),
821 span: self.span,
822 style: SpanStyle::Primary,
823 });
824
825 spans
826 }
827
828 _ => {
830 vec![SpanLabel {
831 label: self.span_label(),
832 span: self.span,
833 style: SpanStyle::Primary,
834 }]
835 }
836 };
837
838 for ctx in &self.contexts {
839 spans.push(SpanLabel {
840 label: Some(format!("while {}", ctx)),
841 span: self.span,
842 style: SpanStyle::Secondary,
843 });
844 }
845
846 spans
847 }
848
849 fn diagnostic(&self) -> Diagnostic {
851 Diagnostic {
852 level: Level::Error,
853 message: self.to_string(),
854 spans: self.spans(),
855 code: Some(self.code().into()),
856 }
857 }
858
859 fn diagnostics(&self) -> Vec<Diagnostic> {
862 match &self.kind {
863 ErrorKind::ImportCompilerError { errors, .. } => {
864 let mut out = vec![self.diagnostic()];
865 out.extend(errors.iter().map(|e| e.diagnostic()));
866 out
867 }
868
869 ErrorKind::NativeError { err: next, .. } | ErrorKind::BytecodeError(next) => {
878 let mut diagnostics: Vec<Diagnostic> = vec![];
880
881 let mut next = *next.clone();
884
885 let mut this_message = self.to_string();
887
888 let mut this_span = self.span;
890
891 let mut this_spans = self.spans();
893
894 loop {
895 if is_new_span(
896 this_span,
897 diagnostics.last().and_then(|last| last.spans.last()),
898 ) {
899 diagnostics.push(Diagnostic {
900 level: Level::Note,
901 message: this_message,
902 spans: this_spans,
903 code: None, });
905 }
906
907 this_message = next.to_string();
908 this_span = next.span;
909 this_spans = next.spans();
910
911 match next.kind {
912 ErrorKind::NativeError { err: inner, .. }
913 | ErrorKind::BytecodeError(inner) => {
914 next = *inner;
915 continue;
916 }
917 _ => {
918 diagnostics.extend(next.diagnostics());
919 break;
920 }
921 }
922 }
923
924 diagnostics
925 }
926
927 _ => vec![self.diagnostic()],
928 }
929 }
930}
931
932fn is_new_span(this_span: Span, parent: Option<&SpanLabel>) -> bool {
934 match parent {
935 None => true,
936 Some(parent) => parent.span != this_span,
937 }
938}
939
940pub trait AddContext {
942 fn context<S: Into<String>>(self, ctx: S) -> Self;
944}
945
946impl AddContext for ErrorKind {
947 fn context<S: Into<String>>(self, ctx: S) -> Self {
948 ErrorKind::WithContext {
949 context: ctx.into(),
950 underlying: Box::new(self),
951 }
952 }
953}
954
955impl<T> AddContext for Result<T, ErrorKind> {
956 fn context<S: Into<String>>(self, ctx: S) -> Self {
957 self.map_err(|kind| kind.context(ctx))
958 }
959}
960
961impl<T> AddContext for Result<T, Error> {
962 fn context<S: Into<String>>(self, ctx: S) -> Self {
963 self.map_err(|err| Error {
964 kind: err.kind.context(ctx),
965 ..err
966 })
967 }
968}