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