2022-12-31 13:32:35 +01:00
|
|
|
use std::error;
|
2022-09-06 22:56:40 +02:00
|
|
|
use std::io;
|
2022-09-13 20:11:07 +02:00
|
|
|
use std::path::PathBuf;
|
2022-10-04 17:27:49 +02:00
|
|
|
use std::rc::Rc;
|
2022-10-09 21:44:53 +02:00
|
|
|
use std::str::Utf8Error;
|
2023-01-15 12:52:37 +01:00
|
|
|
use std::string::FromUtf8Error;
|
2022-10-06 16:22:17 +02:00
|
|
|
use std::sync::Arc;
|
2022-10-22 16:20:11 +02:00
|
|
|
use std::{fmt::Debug, fmt::Display, num::ParseIntError};
|
2022-08-04 15:43:51 +02:00
|
|
|
|
2022-10-06 16:22:17 +02:00
|
|
|
use codemap::{File, Span};
|
2022-10-03 12:21:28 +02:00
|
|
|
use codemap_diagnostic::{ColorConfig, Diagnostic, Emitter, Level, SpanLabel, SpanStyle};
|
2022-09-28 13:57:53 +02:00
|
|
|
use smol_str::SmolStr;
|
2023-01-15 12:52:37 +01:00
|
|
|
use xml::writer::Error as XmlError;
|
2022-09-12 15:12:43 +02:00
|
|
|
|
2023-01-16 11:05:45 +01:00
|
|
|
use crate::spans::ToSpan;
|
|
|
|
use crate::value::{CoercionKind, NixString};
|
2022-10-04 16:05:34 +02:00
|
|
|
use crate::{SourceCode, Value};
|
2022-09-12 15:12:43 +02:00
|
|
|
|
2022-08-22 22:48:47 +02:00
|
|
|
#[derive(Clone, Debug)]
|
2022-08-22 22:20:50 +02:00
|
|
|
pub enum ErrorKind {
|
2022-09-12 15:12:43 +02:00
|
|
|
/// These are user-generated errors through builtins.
|
|
|
|
Throw(String),
|
|
|
|
Abort(String),
|
|
|
|
AssertionFailed,
|
|
|
|
|
2022-11-10 00:47:07 +01:00
|
|
|
DivisionByZero,
|
|
|
|
|
2022-08-09 17:56:21 +02:00
|
|
|
DuplicateAttrsKey {
|
|
|
|
key: String,
|
|
|
|
},
|
|
|
|
|
2022-09-05 00:35:43 +02:00
|
|
|
/// Attempted to specify an invalid key type (e.g. integer) in a
|
|
|
|
/// dynamic attribute name.
|
2022-09-12 15:12:43 +02:00
|
|
|
InvalidAttributeName(Value),
|
2022-09-05 00:35:43 +02:00
|
|
|
|
2022-08-11 14:29:11 +02:00
|
|
|
AttributeNotFound {
|
|
|
|
name: String,
|
|
|
|
},
|
|
|
|
|
2022-09-06 20:58:28 +02:00
|
|
|
/// Attempted to index into a list beyond its boundaries.
|
2022-09-05 20:41:50 +02:00
|
|
|
IndexOutOfBounds {
|
2022-09-06 20:58:28 +02:00
|
|
|
index: i64,
|
2022-09-05 20:41:50 +02:00
|
|
|
},
|
|
|
|
|
2022-09-05 20:42:53 +02:00
|
|
|
/// Attempted to call `builtins.tail` on an empty list.
|
|
|
|
TailEmptyList,
|
|
|
|
|
2022-08-08 01:16:02 +02:00
|
|
|
TypeError {
|
|
|
|
expected: &'static str,
|
|
|
|
actual: &'static str,
|
|
|
|
},
|
2022-08-11 10:37:04 +02:00
|
|
|
|
|
|
|
Incomparable {
|
|
|
|
lhs: &'static str,
|
|
|
|
rhs: &'static str,
|
|
|
|
},
|
2022-08-12 17:12:28 +02:00
|
|
|
|
2022-10-21 00:52:36 +02:00
|
|
|
/// Resolving a user-supplied angle brackets path literal failed in some way.
|
|
|
|
NixPathResolution(String),
|
|
|
|
|
|
|
|
/// Resolving a user-supplied relative or home-relative path literal failed in some way.
|
|
|
|
RelativePathResolution(String),
|
2022-08-13 18:42:50 +02:00
|
|
|
|
2022-09-23 16:11:34 +02:00
|
|
|
/// Dynamic keys are not allowed in some scopes.
|
|
|
|
DynamicKeyInScope(&'static str),
|
2022-08-13 19:17:25 +02:00
|
|
|
|
2022-09-05 00:30:58 +02:00
|
|
|
/// Unknown variable in statically known scope.
|
2022-08-22 22:48:47 +02:00
|
|
|
UnknownStaticVariable,
|
2022-08-15 00:13:17 +02:00
|
|
|
|
2022-09-05 00:30:58 +02:00
|
|
|
/// Unknown variable in dynamic scope (with, rec, ...).
|
2022-08-15 00:13:17 +02:00
|
|
|
UnknownDynamicVariable(String),
|
2022-08-15 00:47:30 +02:00
|
|
|
|
2022-09-05 00:30:58 +02:00
|
|
|
/// User is defining the same variable twice at the same depth.
|
2022-09-12 15:12:43 +02:00
|
|
|
VariableAlreadyDefined(Span),
|
2022-08-27 16:16:46 +02:00
|
|
|
|
2022-09-05 00:30:58 +02:00
|
|
|
/// Attempt to call something that is not callable.
|
2022-09-21 00:10:22 +02:00
|
|
|
NotCallable(&'static str),
|
2022-08-24 01:26:58 +02:00
|
|
|
|
2022-09-05 00:30:58 +02:00
|
|
|
/// Infinite recursion encountered while forcing thunks.
|
2022-08-29 17:33:02 +02:00
|
|
|
InfiniteRecursion,
|
|
|
|
|
2022-08-15 00:47:30 +02:00
|
|
|
ParseErrors(Vec<rnix::parser::ParseError>),
|
2022-08-16 14:53:35 +02:00
|
|
|
|
2022-09-05 00:30:58 +02:00
|
|
|
/// An error occured while forcing a thunk, and needs to be
|
|
|
|
/// chained up.
|
2022-09-01 23:13:30 +02:00
|
|
|
ThunkForce(Box<Error>),
|
2022-09-11 22:12:02 +02:00
|
|
|
|
2022-09-13 15:37:19 +02:00
|
|
|
/// Given type can't be coerced to a string in the respective context
|
|
|
|
NotCoercibleToString {
|
|
|
|
from: &'static str,
|
|
|
|
kind: CoercionKind,
|
|
|
|
},
|
|
|
|
|
2022-09-13 20:11:07 +02:00
|
|
|
/// The given string doesn't represent an absolute path
|
|
|
|
NotAnAbsolutePath(PathBuf),
|
|
|
|
|
2022-09-18 18:06:11 +02:00
|
|
|
/// An error occurred when parsing an integer
|
|
|
|
ParseIntError(ParseIntError),
|
|
|
|
|
2022-09-06 23:33:10 +02:00
|
|
|
/// A negative integer was used as a value representing length.
|
|
|
|
NegativeLength {
|
|
|
|
length: i64,
|
|
|
|
},
|
|
|
|
|
2022-09-28 13:57:53 +02:00
|
|
|
// Errors specific to nested attribute sets and merges thereof.
|
|
|
|
/// Nested attributes can not be merged with an inherited value.
|
|
|
|
UnmergeableInherit {
|
|
|
|
name: SmolStr,
|
|
|
|
},
|
|
|
|
|
2022-09-28 14:08:11 +02:00
|
|
|
/// Nested attributes can not be merged with values that are not
|
|
|
|
/// literal attribute sets.
|
|
|
|
UnmergeableValue,
|
|
|
|
|
2022-10-04 17:27:49 +02:00
|
|
|
/// Parse errors occured while importing a file.
|
|
|
|
ImportParseError {
|
|
|
|
path: PathBuf,
|
2022-10-06 16:22:17 +02:00
|
|
|
file: Arc<File>,
|
2022-10-04 17:27:49 +02:00
|
|
|
errors: Vec<rnix::parser::ParseError>,
|
|
|
|
},
|
|
|
|
|
|
|
|
/// Compilation errors occured while importing a file.
|
|
|
|
ImportCompilerError {
|
|
|
|
path: PathBuf,
|
|
|
|
errors: Vec<Error>,
|
|
|
|
},
|
|
|
|
|
2022-09-06 22:56:40 +02:00
|
|
|
/// I/O errors
|
|
|
|
IO {
|
|
|
|
path: Option<PathBuf>,
|
|
|
|
error: Rc<io::Error>,
|
|
|
|
},
|
|
|
|
|
2022-10-10 06:32:57 +02:00
|
|
|
/// Errors converting JSON to a value
|
|
|
|
FromJsonError(String),
|
|
|
|
|
2023-01-24 15:54:29 +01:00
|
|
|
/// Errors converting TOML to a value
|
|
|
|
FromTomlError(String),
|
|
|
|
|
2022-10-13 05:53:03 +02:00
|
|
|
/// An unexpected argument was supplied to a function that takes formal parameters
|
|
|
|
UnexpectedArgument {
|
|
|
|
arg: NixString,
|
|
|
|
formals_span: Span,
|
|
|
|
},
|
|
|
|
|
2023-01-15 12:52:37 +01:00
|
|
|
/// Errors while serialising to XML.
|
|
|
|
Xml(Rc<XmlError>),
|
|
|
|
|
2023-01-16 11:05:45 +01:00
|
|
|
/// Variant for errors that bubble up to eval from other Tvix
|
|
|
|
/// components.
|
|
|
|
TvixError(Rc<dyn error::Error>),
|
|
|
|
|
2022-10-22 16:20:11 +02:00
|
|
|
/// Variant for code paths that are known bugs in Tvix (usually
|
|
|
|
/// issues with the compiler/VM interaction).
|
|
|
|
TvixBug {
|
|
|
|
msg: &'static str,
|
|
|
|
metadata: Option<Rc<dyn Debug>>,
|
|
|
|
},
|
|
|
|
|
2022-09-11 22:12:02 +02:00
|
|
|
/// Tvix internal warning for features triggered by users that are
|
|
|
|
/// not actually implemented yet, and without which eval can not
|
|
|
|
/// proceed.
|
|
|
|
NotImplemented(&'static str),
|
2023-01-16 18:02:33 +01:00
|
|
|
|
|
|
|
/// Internal variant which should disappear during error construction.
|
|
|
|
WithContext {
|
|
|
|
context: String,
|
|
|
|
underlying: Box<ErrorKind>,
|
|
|
|
},
|
2022-08-08 01:16:02 +02:00
|
|
|
}
|
2022-08-04 15:43:51 +02:00
|
|
|
|
2022-12-31 13:32:35 +01:00
|
|
|
impl error::Error for Error {
|
|
|
|
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
|
|
|
match &self.kind {
|
|
|
|
ErrorKind::ThunkForce(err) => err.source(),
|
|
|
|
ErrorKind::ParseErrors(err) => err.first().map(|e| e as &dyn error::Error),
|
|
|
|
ErrorKind::ParseIntError(err) => Some(err),
|
|
|
|
ErrorKind::ImportParseError { errors, .. } => {
|
|
|
|
errors.first().map(|e| e as &dyn error::Error)
|
|
|
|
}
|
|
|
|
ErrorKind::ImportCompilerError { errors, .. } => {
|
|
|
|
errors.first().map(|e| e as &dyn error::Error)
|
|
|
|
}
|
|
|
|
ErrorKind::IO { error, .. } => Some(error.as_ref()),
|
2023-01-15 12:52:37 +01:00
|
|
|
ErrorKind::Xml(error) => Some(error.as_ref()),
|
2023-01-16 11:05:45 +01:00
|
|
|
ErrorKind::TvixError(error) => Some(error.as_ref()),
|
2022-12-31 13:32:35 +01:00
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-18 18:06:11 +02:00
|
|
|
impl From<ParseIntError> for ErrorKind {
|
|
|
|
fn from(e: ParseIntError) -> Self {
|
|
|
|
Self::ParseIntError(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-09 21:44:53 +02:00
|
|
|
impl From<Utf8Error> for ErrorKind {
|
|
|
|
fn from(_: Utf8Error) -> Self {
|
|
|
|
Self::NotImplemented("FromUtf8Error not handled: https://b.tvl.fyi/issues/189")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-15 12:52:37 +01:00
|
|
|
impl From<FromUtf8Error> for ErrorKind {
|
|
|
|
fn from(_: FromUtf8Error) -> Self {
|
|
|
|
Self::NotImplemented("FromUtf8Error not handled: https://b.tvl.fyi/issues/189")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<XmlError> for ErrorKind {
|
|
|
|
fn from(err: XmlError) -> Self {
|
|
|
|
Self::Xml(Rc::new(err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-20 22:47:30 +02:00
|
|
|
/// Implementation used if errors occur while forcing thunks (which
|
|
|
|
/// can potentially be threaded through a few contexts, i.e. nested
|
|
|
|
/// thunks).
|
|
|
|
impl From<Error> for ErrorKind {
|
|
|
|
fn from(e: Error) -> Self {
|
|
|
|
Self::ThunkForce(Box::new(e))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-06 22:56:40 +02:00
|
|
|
impl From<io::Error> for ErrorKind {
|
|
|
|
fn from(e: io::Error) -> Self {
|
|
|
|
ErrorKind::IO {
|
|
|
|
path: None,
|
|
|
|
error: Rc::new(e),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-10 17:54:53 +02:00
|
|
|
impl ErrorKind {
|
|
|
|
/// Returns `true` if this error can be caught by `builtins.tryEval`
|
|
|
|
pub fn is_catchable(&self) -> bool {
|
|
|
|
match self {
|
2022-10-21 00:52:36 +02:00
|
|
|
Self::Throw(_) | Self::AssertionFailed | Self::NixPathResolution(_) => true,
|
2022-10-10 17:54:53 +02:00
|
|
|
Self::ThunkForce(err) => err.kind.is_catchable(),
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-10 06:32:57 +02:00
|
|
|
impl From<serde_json::Error> for ErrorKind {
|
|
|
|
fn from(err: serde_json::Error) -> Self {
|
|
|
|
// Can't just put the `serde_json::Error` in the ErrorKind since it doesn't impl `Clone`
|
2023-01-10 12:52:59 +01:00
|
|
|
Self::FromJsonError(format!("error in JSON serialization: {err}"))
|
2022-10-10 06:32:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-24 15:54:29 +01:00
|
|
|
impl From<toml::de::Error> for ErrorKind {
|
|
|
|
fn from(err: toml::de::Error) -> Self {
|
|
|
|
Self::FromTomlError(format!("error in TOML serialization: {err}"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-22 22:48:47 +02:00
|
|
|
#[derive(Clone, Debug)]
|
2022-08-22 22:20:50 +02:00
|
|
|
pub struct Error {
|
2023-01-16 18:02:33 +01:00
|
|
|
kind: ErrorKind,
|
|
|
|
span: Span,
|
|
|
|
contexts: Vec<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Error {
|
|
|
|
pub fn new(mut kind: ErrorKind, span: Span) -> Self {
|
|
|
|
let mut contexts = vec![];
|
|
|
|
while let ErrorKind::WithContext {
|
|
|
|
context,
|
|
|
|
underlying,
|
|
|
|
} = kind
|
|
|
|
{
|
|
|
|
kind = *underlying;
|
|
|
|
contexts.push(context);
|
|
|
|
}
|
|
|
|
|
|
|
|
Error {
|
|
|
|
kind,
|
|
|
|
span,
|
|
|
|
contexts,
|
|
|
|
}
|
|
|
|
}
|
2022-08-22 22:20:50 +02:00
|
|
|
}
|
|
|
|
|
2023-01-10 00:06:57 +01:00
|
|
|
impl Display for ErrorKind {
|
2022-08-04 15:43:51 +02:00
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
2023-01-10 00:06:57 +01:00
|
|
|
match &self {
|
2022-10-08 20:28:16 +02:00
|
|
|
ErrorKind::Throw(msg) => write!(f, "error thrown: {}", msg),
|
|
|
|
ErrorKind::Abort(msg) => write!(f, "evaluation aborted: {}", msg),
|
|
|
|
ErrorKind::AssertionFailed => write!(f, "assertion failed"),
|
|
|
|
|
2022-11-10 00:47:07 +01:00
|
|
|
ErrorKind::DivisionByZero => write!(f, "division by zero"),
|
|
|
|
|
2022-10-08 20:28:16 +02:00
|
|
|
ErrorKind::DuplicateAttrsKey { key } => {
|
|
|
|
write!(f, "attribute key '{}' already defined", key)
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorKind::InvalidAttributeName(val) => write!(
|
|
|
|
f,
|
|
|
|
"found attribute name '{}' of type '{}', but attribute names must be strings",
|
|
|
|
val,
|
|
|
|
val.type_of()
|
|
|
|
),
|
|
|
|
|
|
|
|
ErrorKind::AttributeNotFound { name } => write!(
|
|
|
|
f,
|
|
|
|
"attribute with name '{}' could not be found in the set",
|
|
|
|
name
|
|
|
|
),
|
|
|
|
|
|
|
|
ErrorKind::IndexOutOfBounds { index } => {
|
|
|
|
write!(f, "list index '{}' is out of bounds", index)
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorKind::TailEmptyList => write!(f, "'tail' called on an empty list"),
|
|
|
|
|
|
|
|
ErrorKind::TypeError { expected, actual } => write!(
|
|
|
|
f,
|
|
|
|
"expected value of type '{}', but found a '{}'",
|
|
|
|
expected, actual
|
|
|
|
),
|
|
|
|
|
|
|
|
ErrorKind::Incomparable { lhs, rhs } => {
|
|
|
|
write!(f, "can not compare a {} with a {}", lhs, rhs)
|
|
|
|
}
|
|
|
|
|
2022-10-21 00:52:36 +02:00
|
|
|
ErrorKind::NixPathResolution(err) | ErrorKind::RelativePathResolution(err) => {
|
|
|
|
write!(f, "could not resolve path: {}", err)
|
|
|
|
}
|
2022-10-08 20:28:16 +02:00
|
|
|
|
|
|
|
ErrorKind::DynamicKeyInScope(scope) => {
|
|
|
|
write!(f, "dynamically evaluated keys are not allowed in {}", scope)
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorKind::UnknownStaticVariable => write!(f, "variable not found"),
|
|
|
|
|
|
|
|
ErrorKind::UnknownDynamicVariable(name) => write!(
|
|
|
|
f,
|
|
|
|
r#"variable '{}' could not be found
|
|
|
|
|
|
|
|
Note that this occured within a `with`-expression. The problem may be related
|
|
|
|
to a missing value in the attribute set(s) included via `with`."#,
|
|
|
|
name
|
|
|
|
),
|
|
|
|
|
|
|
|
ErrorKind::VariableAlreadyDefined(_) => write!(f, "variable has already been defined"),
|
|
|
|
|
|
|
|
ErrorKind::NotCallable(other_type) => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"only functions and builtins can be called, but this is a '{}'",
|
|
|
|
other_type
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorKind::InfiniteRecursion => write!(f, "infinite recursion encountered"),
|
|
|
|
|
|
|
|
// Errors themselves ignored here & handled in Self::spans instead
|
|
|
|
ErrorKind::ParseErrors(_) => write!(f, "failed to parse Nix code:"),
|
|
|
|
|
|
|
|
// TODO(tazjin): trace through the whole chain of thunk
|
|
|
|
// forcing errors with secondary spans, instead of just
|
|
|
|
// delegating to the inner error
|
|
|
|
ErrorKind::ThunkForce(err) => write!(f, "{err}"),
|
|
|
|
|
|
|
|
ErrorKind::NotCoercibleToString { kind, from } => {
|
|
|
|
let kindly = match kind {
|
2022-11-26 01:47:26 +01:00
|
|
|
CoercionKind::ThunksOnly => "thunksonly",
|
2022-10-08 20:28:16 +02:00
|
|
|
CoercionKind::Strong => "strongly",
|
|
|
|
CoercionKind::Weak => "weakly",
|
|
|
|
};
|
|
|
|
|
|
|
|
let hint = if *from == "set" {
|
|
|
|
", missing a `__toString` or `outPath` attribute"
|
|
|
|
} else {
|
|
|
|
""
|
|
|
|
};
|
|
|
|
|
|
|
|
write!(f, "cannot ({kindly}) coerce {from} to a string{hint}")
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorKind::NotAnAbsolutePath(given) => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"string '{}' does not represent an absolute path",
|
|
|
|
given.to_string_lossy()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorKind::ParseIntError(err) => {
|
|
|
|
write!(f, "invalid integer: {}", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorKind::NegativeLength { length } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"cannot use a negative integer, {}, for a value representing length",
|
|
|
|
length
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorKind::UnmergeableInherit { name } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"cannot merge a nested attribute set into the inherited entry '{}'",
|
|
|
|
name
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorKind::UnmergeableValue => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"nested attribute sets or keys can only be merged with literal attribute sets"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Errors themselves ignored here & handled in Self::spans instead
|
|
|
|
ErrorKind::ImportParseError { path, .. } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"parse errors occured while importing '{}'",
|
|
|
|
path.to_string_lossy()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-10-11 00:04:19 +02:00
|
|
|
ErrorKind::ImportCompilerError { path, .. } => {
|
|
|
|
writeln!(
|
2022-10-08 20:28:16 +02:00
|
|
|
f,
|
2022-10-11 00:04:19 +02:00
|
|
|
"compiler errors occured while importing '{}'",
|
2022-10-08 20:28:16 +02:00
|
|
|
path.to_string_lossy()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-09-06 22:56:40 +02:00
|
|
|
ErrorKind::IO { path, error } => {
|
|
|
|
write!(f, "I/O error: ")?;
|
|
|
|
if let Some(path) = path {
|
|
|
|
write!(f, "{}: ", path.display())?;
|
|
|
|
}
|
|
|
|
write!(f, "{error}")
|
|
|
|
}
|
|
|
|
|
2022-10-10 06:32:57 +02:00
|
|
|
ErrorKind::FromJsonError(msg) => {
|
|
|
|
write!(f, "Error converting JSON to a Nix value: {msg}")
|
|
|
|
}
|
|
|
|
|
2023-01-24 15:54:29 +01:00
|
|
|
ErrorKind::FromTomlError(msg) => {
|
|
|
|
write!(f, "Error converting TOML to a Nix value: {msg}")
|
|
|
|
}
|
|
|
|
|
2022-10-13 05:53:03 +02:00
|
|
|
ErrorKind::UnexpectedArgument { arg, .. } => {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"Unexpected argument `{}` supplied to function",
|
|
|
|
arg.as_str()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-01-15 12:52:37 +01:00
|
|
|
ErrorKind::Xml(error) => write!(f, "failed to serialise to XML: {error}"),
|
|
|
|
|
2023-01-16 11:05:45 +01:00
|
|
|
ErrorKind::TvixError(inner_error) => {
|
|
|
|
write!(f, "{inner_error}")
|
|
|
|
}
|
|
|
|
|
2022-10-22 16:20:11 +02:00
|
|
|
ErrorKind::TvixBug { msg, metadata } => {
|
|
|
|
write!(f, "Tvix bug: {}", msg)?;
|
|
|
|
|
|
|
|
if let Some(metadata) = metadata {
|
|
|
|
write!(f, "; metadata: {:?}", metadata)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-10-08 20:28:16 +02:00
|
|
|
ErrorKind::NotImplemented(feature) => {
|
|
|
|
write!(f, "feature not yet implemented in Tvix: {}", feature)
|
|
|
|
}
|
2023-01-16 18:02:33 +01:00
|
|
|
|
|
|
|
ErrorKind::WithContext { .. } => {
|
|
|
|
panic!("internal ErrorKind::WithContext variant leaked")
|
|
|
|
}
|
2022-10-08 20:28:16 +02:00
|
|
|
}
|
2022-08-04 15:43:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-10 00:06:57 +01:00
|
|
|
impl Display for Error {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "{}", self.kind)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-04 15:43:51 +02:00
|
|
|
pub type EvalResult<T> = Result<T, Error>;
|
2022-09-12 15:12:43 +02:00
|
|
|
|
2022-10-06 16:22:17 +02:00
|
|
|
/// Human-readable names for rnix syntaxes.
|
|
|
|
fn name_for_syntax(syntax: &rnix::SyntaxKind) -> &'static str {
|
|
|
|
match syntax {
|
|
|
|
rnix::SyntaxKind::TOKEN_COMMENT => "a comment",
|
|
|
|
rnix::SyntaxKind::TOKEN_WHITESPACE => "whitespace",
|
|
|
|
rnix::SyntaxKind::TOKEN_ASSERT => "`assert`-keyword",
|
|
|
|
rnix::SyntaxKind::TOKEN_ELSE => "`else`-keyword",
|
|
|
|
rnix::SyntaxKind::TOKEN_IN => "`in`-keyword",
|
|
|
|
rnix::SyntaxKind::TOKEN_IF => "`if`-keyword",
|
|
|
|
rnix::SyntaxKind::TOKEN_INHERIT => "`inherit`-keyword",
|
|
|
|
rnix::SyntaxKind::TOKEN_LET => "`let`-keyword",
|
|
|
|
rnix::SyntaxKind::TOKEN_OR => "`or`-keyword",
|
|
|
|
rnix::SyntaxKind::TOKEN_REC => "`rec`-keyword",
|
|
|
|
rnix::SyntaxKind::TOKEN_THEN => "`then`-keyword",
|
|
|
|
rnix::SyntaxKind::TOKEN_WITH => "`with`-keyword",
|
|
|
|
rnix::SyntaxKind::TOKEN_L_BRACE => "{",
|
|
|
|
rnix::SyntaxKind::TOKEN_R_BRACE => "}",
|
|
|
|
rnix::SyntaxKind::TOKEN_L_BRACK => "[",
|
|
|
|
rnix::SyntaxKind::TOKEN_R_BRACK => "]",
|
|
|
|
rnix::SyntaxKind::TOKEN_ASSIGN => "=",
|
|
|
|
rnix::SyntaxKind::TOKEN_AT => "@",
|
|
|
|
rnix::SyntaxKind::TOKEN_COLON => ":",
|
|
|
|
rnix::SyntaxKind::TOKEN_COMMA => "`,`",
|
|
|
|
rnix::SyntaxKind::TOKEN_DOT => ".",
|
|
|
|
rnix::SyntaxKind::TOKEN_ELLIPSIS => "...",
|
|
|
|
rnix::SyntaxKind::TOKEN_QUESTION => "?",
|
|
|
|
rnix::SyntaxKind::TOKEN_SEMICOLON => ";",
|
|
|
|
rnix::SyntaxKind::TOKEN_L_PAREN => "(",
|
|
|
|
rnix::SyntaxKind::TOKEN_R_PAREN => ")",
|
|
|
|
rnix::SyntaxKind::TOKEN_CONCAT => "++",
|
|
|
|
rnix::SyntaxKind::TOKEN_INVERT => "!",
|
|
|
|
rnix::SyntaxKind::TOKEN_UPDATE => "//",
|
|
|
|
rnix::SyntaxKind::TOKEN_ADD => "+",
|
|
|
|
rnix::SyntaxKind::TOKEN_SUB => "-",
|
|
|
|
rnix::SyntaxKind::TOKEN_MUL => "*",
|
|
|
|
rnix::SyntaxKind::TOKEN_DIV => "/",
|
|
|
|
rnix::SyntaxKind::TOKEN_AND_AND => "&&",
|
|
|
|
rnix::SyntaxKind::TOKEN_EQUAL => "==",
|
|
|
|
rnix::SyntaxKind::TOKEN_IMPLICATION => "->",
|
|
|
|
rnix::SyntaxKind::TOKEN_LESS => "<",
|
|
|
|
rnix::SyntaxKind::TOKEN_LESS_OR_EQ => "<=",
|
|
|
|
rnix::SyntaxKind::TOKEN_MORE => ">",
|
|
|
|
rnix::SyntaxKind::TOKEN_MORE_OR_EQ => ">=",
|
|
|
|
rnix::SyntaxKind::TOKEN_NOT_EQUAL => "!=",
|
|
|
|
rnix::SyntaxKind::TOKEN_OR_OR => "||",
|
|
|
|
rnix::SyntaxKind::TOKEN_FLOAT => "a float",
|
|
|
|
rnix::SyntaxKind::TOKEN_IDENT => "an identifier",
|
|
|
|
rnix::SyntaxKind::TOKEN_INTEGER => "an integer",
|
|
|
|
rnix::SyntaxKind::TOKEN_INTERPOL_END => "}",
|
|
|
|
rnix::SyntaxKind::TOKEN_INTERPOL_START => "${",
|
|
|
|
rnix::SyntaxKind::TOKEN_PATH => "a path",
|
|
|
|
rnix::SyntaxKind::TOKEN_URI => "a literal URI",
|
|
|
|
rnix::SyntaxKind::TOKEN_STRING_CONTENT => "content of a string",
|
|
|
|
rnix::SyntaxKind::TOKEN_STRING_END => "\"",
|
|
|
|
rnix::SyntaxKind::TOKEN_STRING_START => "\"",
|
|
|
|
|
|
|
|
rnix::SyntaxKind::NODE_APPLY => "a function application",
|
|
|
|
rnix::SyntaxKind::NODE_ASSERT => "an assertion",
|
|
|
|
rnix::SyntaxKind::NODE_ATTRPATH => "an attribute path",
|
|
|
|
rnix::SyntaxKind::NODE_DYNAMIC => "a dynamic identifier",
|
|
|
|
|
|
|
|
rnix::SyntaxKind::NODE_IDENT => "an identifier",
|
|
|
|
rnix::SyntaxKind::NODE_IF_ELSE => "an `if`-expression",
|
|
|
|
rnix::SyntaxKind::NODE_SELECT => "a `select`-expression",
|
|
|
|
rnix::SyntaxKind::NODE_INHERIT => "inherited values",
|
|
|
|
rnix::SyntaxKind::NODE_INHERIT_FROM => "inherited values",
|
|
|
|
rnix::SyntaxKind::NODE_STRING => "a string",
|
|
|
|
rnix::SyntaxKind::NODE_INTERPOL => "an interpolation",
|
|
|
|
rnix::SyntaxKind::NODE_LAMBDA => "a function",
|
|
|
|
rnix::SyntaxKind::NODE_IDENT_PARAM => "a function parameter",
|
|
|
|
rnix::SyntaxKind::NODE_LEGACY_LET => "a legacy `let`-expression",
|
|
|
|
rnix::SyntaxKind::NODE_LET_IN => "a `let`-expression",
|
|
|
|
rnix::SyntaxKind::NODE_LIST => "a list",
|
|
|
|
rnix::SyntaxKind::NODE_BIN_OP => "a binary operator",
|
|
|
|
rnix::SyntaxKind::NODE_PAREN => "a parenthesised expression",
|
|
|
|
rnix::SyntaxKind::NODE_PATTERN => "a function argument pattern",
|
|
|
|
rnix::SyntaxKind::NODE_PAT_BIND => "an argument pattern binding",
|
|
|
|
rnix::SyntaxKind::NODE_PAT_ENTRY => "an argument pattern entry",
|
|
|
|
rnix::SyntaxKind::NODE_ROOT => "a Nix expression",
|
|
|
|
rnix::SyntaxKind::NODE_ATTR_SET => "an attribute set",
|
|
|
|
rnix::SyntaxKind::NODE_ATTRPATH_VALUE => "an attribute set entry",
|
|
|
|
rnix::SyntaxKind::NODE_UNARY_OP => "a unary operator",
|
|
|
|
rnix::SyntaxKind::NODE_LITERAL => "a literal value",
|
|
|
|
rnix::SyntaxKind::NODE_WITH => "a `with`-expression",
|
|
|
|
rnix::SyntaxKind::NODE_PATH => "a path",
|
|
|
|
rnix::SyntaxKind::NODE_HAS_ATTR => "`?`-operator",
|
|
|
|
|
|
|
|
// TODO(tazjin): unsure what these variants are, lets crash!
|
|
|
|
rnix::SyntaxKind::NODE_ERROR => todo!("NODE_ERROR found, tell tazjin!"),
|
|
|
|
rnix::SyntaxKind::TOKEN_ERROR => todo!("TOKEN_ERROR found, tell tazjin!"),
|
|
|
|
_ => todo!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Construct the string representation for a list of expected parser tokens.
|
|
|
|
fn expected_syntax(one_of: &[rnix::SyntaxKind]) -> String {
|
|
|
|
match one_of.len() {
|
|
|
|
0 => "nothing".into(),
|
|
|
|
1 => format!("'{}'", name_for_syntax(&one_of[0])),
|
|
|
|
_ => {
|
|
|
|
let mut out: String = "one of: ".into();
|
|
|
|
let end = one_of.len() - 1;
|
|
|
|
|
|
|
|
for (idx, item) in one_of.iter().enumerate() {
|
|
|
|
if idx != 0 {
|
|
|
|
out.push_str(", ");
|
|
|
|
} else if idx == end {
|
|
|
|
out.push_str(", or ");
|
|
|
|
};
|
|
|
|
|
|
|
|
out.push_str(name_for_syntax(item));
|
|
|
|
}
|
|
|
|
|
|
|
|
out
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Process a list of parse errors into a set of span labels, annotating parse
|
|
|
|
/// errors.
|
|
|
|
fn spans_for_parse_errors(file: &File, errors: &[rnix::parser::ParseError]) -> Vec<SpanLabel> {
|
|
|
|
// rnix has a tendency to emit some identical errors more than once, but
|
|
|
|
// they do not enhance the user experience necessarily, so we filter them
|
|
|
|
// out
|
|
|
|
let mut had_eof = false;
|
|
|
|
|
|
|
|
errors
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.filter_map(|(idx, err)| {
|
|
|
|
let (span, label): (Span, String) = match err {
|
|
|
|
rnix::parser::ParseError::Unexpected(range) => (
|
|
|
|
range.span_for(file),
|
|
|
|
"found an unexpected syntax element here".into(),
|
|
|
|
),
|
|
|
|
|
|
|
|
rnix::parser::ParseError::UnexpectedExtra(range) => (
|
|
|
|
range.span_for(file),
|
|
|
|
"found unexpected extra elements at the root of the expression".into(),
|
|
|
|
),
|
|
|
|
|
|
|
|
rnix::parser::ParseError::UnexpectedWanted(found, range, wanted) => {
|
|
|
|
let span = range.span_for(file);
|
|
|
|
(
|
|
|
|
span,
|
|
|
|
format!(
|
|
|
|
"found '{}', but expected {}",
|
2022-12-20 15:22:56 +01:00
|
|
|
name_for_syntax(found),
|
|
|
|
expected_syntax(wanted),
|
2022-10-06 16:22:17 +02:00
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
rnix::parser::ParseError::UnexpectedEOF => {
|
|
|
|
if had_eof {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
had_eof = true;
|
|
|
|
|
|
|
|
(
|
|
|
|
file.span,
|
|
|
|
"code ended unexpectedly while the parser still expected more".into(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
rnix::parser::ParseError::UnexpectedEOFWanted(wanted) => {
|
|
|
|
had_eof = true;
|
|
|
|
|
|
|
|
(
|
|
|
|
file.span,
|
|
|
|
format!(
|
|
|
|
"code ended unexpectedly, but wanted {}",
|
2022-12-20 15:22:56 +01:00
|
|
|
expected_syntax(wanted)
|
2022-10-06 16:22:17 +02:00
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
rnix::parser::ParseError::DuplicatedArgs(range, name) => (
|
|
|
|
range.span_for(file),
|
|
|
|
format!(
|
|
|
|
"the function argument pattern '{}' was bound more than once",
|
|
|
|
name
|
|
|
|
),
|
|
|
|
),
|
|
|
|
|
|
|
|
rnix::parser::ParseError::RecursionLimitExceeded => (
|
|
|
|
file.span,
|
2022-12-20 15:22:56 +01:00
|
|
|
"this code exceeds the parser's recursion limit, please report a Tvix bug"
|
|
|
|
.to_string(),
|
2022-10-06 16:22:17 +02:00
|
|
|
),
|
|
|
|
|
|
|
|
// TODO: can rnix even still throw this? it's semantic!
|
|
|
|
rnix::parser::ParseError::UnexpectedDoubleBind(range) => (
|
|
|
|
range.span_for(file),
|
|
|
|
"this pattern was bound more than once".into(),
|
|
|
|
),
|
|
|
|
|
|
|
|
// The error enum is marked as `#[non_exhaustive]` in rnix,
|
|
|
|
// which disables the compiler error for missing a variant. This
|
|
|
|
// feature makes it possible for users to miss critical updates
|
|
|
|
// of enum variants for a more exciting runtime experience.
|
|
|
|
new => todo!("new parse error variant: {}", new),
|
|
|
|
};
|
|
|
|
|
|
|
|
Some(SpanLabel {
|
|
|
|
span,
|
|
|
|
label: Some(label),
|
|
|
|
style: if idx == 0 {
|
|
|
|
SpanStyle::Primary
|
|
|
|
} else {
|
|
|
|
SpanStyle::Secondary
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
|
2022-09-12 15:12:43 +02:00
|
|
|
impl Error {
|
2022-10-04 16:05:34 +02:00
|
|
|
pub fn fancy_format_str(&self, source: &SourceCode) -> String {
|
2022-09-12 15:12:43 +02:00
|
|
|
let mut out = vec![];
|
2022-10-11 00:04:19 +02:00
|
|
|
Emitter::vec(&mut out, Some(&*source.codemap())).emit(&self.diagnostics(source));
|
2022-09-12 15:12:43 +02:00
|
|
|
String::from_utf8_lossy(&out).to_string()
|
|
|
|
}
|
|
|
|
|
2022-10-03 12:21:28 +02:00
|
|
|
/// Render a fancy, human-readable output of this error and print
|
|
|
|
/// it to stderr.
|
2022-10-04 16:05:34 +02:00
|
|
|
pub fn fancy_format_stderr(&self, source: &SourceCode) {
|
2022-10-06 16:22:17 +02:00
|
|
|
Emitter::stderr(ColorConfig::Auto, Some(&*source.codemap()))
|
2022-10-11 00:04:19 +02:00
|
|
|
.emit(&self.diagnostics(source));
|
2022-10-03 12:21:28 +02:00
|
|
|
}
|
|
|
|
|
2022-09-12 15:12:43 +02:00
|
|
|
/// Create the optional span label displayed as an annotation on
|
|
|
|
/// the underlined span of the error.
|
|
|
|
fn span_label(&self) -> Option<String> {
|
2022-10-06 16:35:15 +02:00
|
|
|
let label = match &self.kind {
|
|
|
|
ErrorKind::DuplicateAttrsKey { .. } => "in this attribute set",
|
|
|
|
ErrorKind::InvalidAttributeName(_) => "in this attribute set",
|
2022-10-21 00:52:36 +02:00
|
|
|
ErrorKind::NixPathResolution(_) | ErrorKind::RelativePathResolution(_) => {
|
|
|
|
"in this path literal"
|
|
|
|
}
|
2022-10-13 05:53:03 +02:00
|
|
|
ErrorKind::UnexpectedArgument { .. } => "in this function call",
|
2022-10-06 16:35:15 +02:00
|
|
|
|
|
|
|
// The spans for some errors don't have any more descriptive stuff
|
|
|
|
// in them, or we don't utilise it yet.
|
|
|
|
ErrorKind::Throw(_)
|
|
|
|
| ErrorKind::Abort(_)
|
|
|
|
| ErrorKind::AssertionFailed
|
|
|
|
| ErrorKind::AttributeNotFound { .. }
|
|
|
|
| ErrorKind::IndexOutOfBounds { .. }
|
|
|
|
| ErrorKind::TailEmptyList
|
|
|
|
| ErrorKind::TypeError { .. }
|
|
|
|
| ErrorKind::Incomparable { .. }
|
2022-11-10 00:47:07 +01:00
|
|
|
| ErrorKind::DivisionByZero
|
2022-10-06 16:35:15 +02:00
|
|
|
| ErrorKind::DynamicKeyInScope(_)
|
|
|
|
| ErrorKind::UnknownStaticVariable
|
|
|
|
| ErrorKind::UnknownDynamicVariable(_)
|
|
|
|
| ErrorKind::VariableAlreadyDefined(_)
|
|
|
|
| ErrorKind::NotCallable(_)
|
|
|
|
| ErrorKind::InfiniteRecursion
|
|
|
|
| ErrorKind::ParseErrors(_)
|
|
|
|
| ErrorKind::ThunkForce(_)
|
|
|
|
| ErrorKind::NotCoercibleToString { .. }
|
|
|
|
| ErrorKind::NotAnAbsolutePath(_)
|
|
|
|
| ErrorKind::ParseIntError(_)
|
|
|
|
| ErrorKind::NegativeLength { .. }
|
|
|
|
| ErrorKind::UnmergeableInherit { .. }
|
|
|
|
| ErrorKind::UnmergeableValue
|
|
|
|
| ErrorKind::ImportParseError { .. }
|
|
|
|
| ErrorKind::ImportCompilerError { .. }
|
2022-09-06 22:56:40 +02:00
|
|
|
| ErrorKind::IO { .. }
|
2022-10-10 06:32:57 +02:00
|
|
|
| ErrorKind::FromJsonError(_)
|
2023-01-24 15:54:29 +01:00
|
|
|
| ErrorKind::FromTomlError(_)
|
2023-01-15 12:52:37 +01:00
|
|
|
| ErrorKind::Xml(_)
|
2023-01-16 11:05:45 +01:00
|
|
|
| ErrorKind::TvixError(_)
|
2022-10-22 16:20:11 +02:00
|
|
|
| ErrorKind::TvixBug { .. }
|
2023-01-16 18:02:33 +01:00
|
|
|
| ErrorKind::NotImplemented(_)
|
|
|
|
| ErrorKind::WithContext { .. } => return None,
|
2022-10-06 16:35:15 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
Some(label.into())
|
2022-09-12 15:12:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Return the unique error code for this variant which can be
|
|
|
|
/// used to refer users to documentation.
|
|
|
|
fn code(&self) -> &'static str {
|
|
|
|
match self.kind {
|
|
|
|
ErrorKind::Throw(_) => "E001",
|
|
|
|
ErrorKind::Abort(_) => "E002",
|
|
|
|
ErrorKind::AssertionFailed => "E003",
|
|
|
|
ErrorKind::InvalidAttributeName { .. } => "E004",
|
|
|
|
ErrorKind::AttributeNotFound { .. } => "E005",
|
|
|
|
ErrorKind::TypeError { .. } => "E006",
|
|
|
|
ErrorKind::Incomparable { .. } => "E007",
|
2022-10-21 00:52:36 +02:00
|
|
|
ErrorKind::NixPathResolution(_) => "E008",
|
2022-09-23 16:11:34 +02:00
|
|
|
ErrorKind::DynamicKeyInScope(_) => "E009",
|
2022-09-12 15:12:43 +02:00
|
|
|
ErrorKind::UnknownStaticVariable => "E010",
|
|
|
|
ErrorKind::UnknownDynamicVariable(_) => "E011",
|
|
|
|
ErrorKind::VariableAlreadyDefined(_) => "E012",
|
2022-09-21 00:10:22 +02:00
|
|
|
ErrorKind::NotCallable(_) => "E013",
|
2022-09-12 15:12:43 +02:00
|
|
|
ErrorKind::InfiniteRecursion => "E014",
|
|
|
|
ErrorKind::ParseErrors(_) => "E015",
|
|
|
|
ErrorKind::DuplicateAttrsKey { .. } => "E016",
|
2022-09-13 15:37:19 +02:00
|
|
|
ErrorKind::NotCoercibleToString { .. } => "E018",
|
2022-09-05 20:41:50 +02:00
|
|
|
ErrorKind::IndexOutOfBounds { .. } => "E019",
|
2022-09-13 20:11:07 +02:00
|
|
|
ErrorKind::NotAnAbsolutePath(_) => "E020",
|
2022-09-18 18:06:11 +02:00
|
|
|
ErrorKind::ParseIntError(_) => "E021",
|
2022-09-06 23:33:10 +02:00
|
|
|
ErrorKind::NegativeLength { .. } => "E022",
|
2022-09-05 20:42:53 +02:00
|
|
|
ErrorKind::TailEmptyList { .. } => "E023",
|
2022-09-28 13:57:53 +02:00
|
|
|
ErrorKind::UnmergeableInherit { .. } => "E024",
|
2022-09-28 14:08:11 +02:00
|
|
|
ErrorKind::UnmergeableValue => "E025",
|
2022-10-04 17:27:49 +02:00
|
|
|
ErrorKind::ImportParseError { .. } => "E027",
|
|
|
|
ErrorKind::ImportCompilerError { .. } => "E028",
|
2022-09-06 22:56:40 +02:00
|
|
|
ErrorKind::IO { .. } => "E029",
|
2022-10-10 06:32:57 +02:00
|
|
|
ErrorKind::FromJsonError { .. } => "E030",
|
2022-10-13 05:53:03 +02:00
|
|
|
ErrorKind::UnexpectedArgument { .. } => "E031",
|
2022-10-21 00:52:36 +02:00
|
|
|
ErrorKind::RelativePathResolution(_) => "E032",
|
2022-11-10 00:47:07 +01:00
|
|
|
ErrorKind::DivisionByZero => "E033",
|
2023-01-15 12:52:37 +01:00
|
|
|
ErrorKind::Xml(_) => "E034",
|
2023-01-24 15:54:29 +01:00
|
|
|
ErrorKind::FromTomlError(_) => "E035",
|
2022-10-04 17:27:49 +02:00
|
|
|
|
2023-01-16 11:05:45 +01:00
|
|
|
// Special error code for errors from other Tvix
|
|
|
|
// components. We may want to introduce a code namespacing
|
|
|
|
// system to have these errors pass codes through.
|
|
|
|
ErrorKind::TvixError(_) => "E997",
|
|
|
|
|
2022-10-22 16:20:11 +02:00
|
|
|
// Special error code that is not part of the normal
|
|
|
|
// ordering.
|
|
|
|
ErrorKind::TvixBug { .. } => "E998",
|
|
|
|
|
2022-10-04 17:27:49 +02:00
|
|
|
// Placeholder error while Tvix is under construction.
|
2022-09-12 15:12:43 +02:00
|
|
|
ErrorKind::NotImplemented(_) => "E999",
|
2022-10-04 11:29:02 +02:00
|
|
|
|
|
|
|
// TODO: thunk force errors should yield a chained
|
|
|
|
// diagnostic, but until then we just forward the error
|
|
|
|
// code from the inner error.
|
|
|
|
//
|
|
|
|
// The error code for thunk forces is E017.
|
|
|
|
ErrorKind::ThunkForce(ref err) => err.code(),
|
2023-01-16 18:02:33 +01:00
|
|
|
|
|
|
|
ErrorKind::WithContext { .. } => {
|
|
|
|
panic!("internal ErrorKind::WithContext variant leaked")
|
|
|
|
}
|
2022-09-12 15:12:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-06 16:22:17 +02:00
|
|
|
fn spans(&self, source: &SourceCode) -> Vec<SpanLabel> {
|
2023-01-16 18:02:33 +01:00
|
|
|
let mut spans = match &self.kind {
|
2022-10-06 16:22:17 +02:00
|
|
|
ErrorKind::ImportParseError { errors, file, .. } => {
|
2022-12-20 15:22:56 +01:00
|
|
|
spans_for_parse_errors(file, errors)
|
2022-10-06 16:22:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ErrorKind::ParseErrors(errors) => {
|
|
|
|
let file = source.get_file(self.span);
|
|
|
|
spans_for_parse_errors(&file, errors)
|
|
|
|
}
|
|
|
|
|
2022-10-06 16:26:24 +02:00
|
|
|
// Unwrap thunk errors to the innermost one
|
|
|
|
// TODO: limit the number of intermediates!
|
|
|
|
ErrorKind::ThunkForce(err) => {
|
|
|
|
let mut labels = err.spans(source);
|
|
|
|
|
|
|
|
// Only add this thunk to the "cause chain" if it span isn't
|
|
|
|
// exactly identical to the next-higher level, which is very
|
|
|
|
// common for the last thunk in a chain.
|
|
|
|
if let Some(label) = labels.last() {
|
|
|
|
if label.span != self.span {
|
|
|
|
labels.push(SpanLabel {
|
|
|
|
label: Some("while evaluating this".into()),
|
|
|
|
span: self.span,
|
|
|
|
style: SpanStyle::Secondary,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
labels
|
|
|
|
}
|
|
|
|
|
2022-10-13 05:53:03 +02:00
|
|
|
ErrorKind::UnexpectedArgument { formals_span, .. } => {
|
|
|
|
vec![
|
|
|
|
SpanLabel {
|
|
|
|
label: self.span_label(),
|
|
|
|
span: self.span,
|
|
|
|
style: SpanStyle::Primary,
|
|
|
|
},
|
|
|
|
SpanLabel {
|
|
|
|
label: Some("the accepted arguments".into()),
|
|
|
|
span: *formals_span,
|
|
|
|
style: SpanStyle::Secondary,
|
|
|
|
},
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
2022-10-06 16:22:17 +02:00
|
|
|
// All other errors pretty much have the same shape.
|
|
|
|
_ => {
|
|
|
|
vec![SpanLabel {
|
|
|
|
label: self.span_label(),
|
|
|
|
span: self.span,
|
|
|
|
style: SpanStyle::Primary,
|
|
|
|
}]
|
|
|
|
}
|
2023-01-16 18:02:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
for ctx in &self.contexts {
|
|
|
|
spans.push(SpanLabel {
|
|
|
|
label: Some(format!("while {}", ctx)),
|
|
|
|
span: self.span,
|
|
|
|
style: SpanStyle::Secondary,
|
|
|
|
});
|
2022-10-06 16:22:17 +02:00
|
|
|
}
|
2023-01-16 18:02:33 +01:00
|
|
|
|
|
|
|
spans
|
2022-10-06 16:22:17 +02:00
|
|
|
}
|
2022-09-12 15:12:43 +02:00
|
|
|
|
2022-10-11 00:04:19 +02:00
|
|
|
/// Create the primary diagnostic for a given error.
|
2022-10-06 16:22:17 +02:00
|
|
|
fn diagnostic(&self, source: &SourceCode) -> Diagnostic {
|
2022-09-12 15:12:43 +02:00
|
|
|
Diagnostic {
|
|
|
|
level: Level::Error,
|
2022-10-08 20:28:16 +02:00
|
|
|
message: self.to_string(),
|
2022-10-06 16:22:17 +02:00
|
|
|
spans: self.spans(source),
|
2022-09-12 15:12:43 +02:00
|
|
|
code: Some(self.code().into()),
|
|
|
|
}
|
|
|
|
}
|
2022-10-11 00:04:19 +02:00
|
|
|
|
|
|
|
/// Return the primary diagnostic and all further associated diagnostics (if
|
|
|
|
/// any) of an error.
|
|
|
|
fn diagnostics(&self, source: &SourceCode) -> Vec<Diagnostic> {
|
|
|
|
match &self.kind {
|
|
|
|
ErrorKind::ThunkForce(err) => err.diagnostics(source),
|
|
|
|
|
|
|
|
ErrorKind::ImportCompilerError { errors, .. } => {
|
|
|
|
let mut out = vec![self.diagnostic(source)];
|
|
|
|
out.extend(errors.iter().map(|e| e.diagnostic(source)));
|
|
|
|
out
|
|
|
|
}
|
|
|
|
|
|
|
|
_ => vec![self.diagnostic(source)],
|
|
|
|
}
|
|
|
|
}
|
2022-09-12 15:12:43 +02:00
|
|
|
}
|
2023-01-16 18:02:33 +01:00
|
|
|
|
|
|
|
// Convenience methods to add context on other types.
|
|
|
|
pub trait AddContext {
|
|
|
|
/// Add context to the error-carrying type.
|
|
|
|
fn context<S: Into<String>>(self, ctx: S) -> Self;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AddContext for ErrorKind {
|
|
|
|
fn context<S: Into<String>>(self, ctx: S) -> Self {
|
|
|
|
ErrorKind::WithContext {
|
|
|
|
context: ctx.into(),
|
|
|
|
underlying: Box::new(self),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> AddContext for Result<T, ErrorKind> {
|
|
|
|
fn context<S: Into<String>>(self, ctx: S) -> Self {
|
|
|
|
self.map_err(|kind| kind.context(ctx))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> AddContext for Result<T, Error> {
|
|
|
|
fn context<S: Into<String>>(self, ctx: S) -> Self {
|
|
|
|
self.map_err(|err| Error {
|
|
|
|
kind: err.kind.context(ctx),
|
|
|
|
..err
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|