2022-09-13 15:37:19 +02:00
|
|
|
use crate::value::CoercionKind;
|
2022-09-13 20:11:07 +02:00
|
|
|
use std::path::PathBuf;
|
2022-09-18 18:06:11 +02:00
|
|
|
use std::{fmt::Display, num::ParseIntError};
|
2022-08-04 15:43:51 +02:00
|
|
|
|
2022-09-12 15:12:43 +02:00
|
|
|
use codemap::{CodeMap, 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;
|
2022-09-12 15:12:43 +02:00
|
|
|
|
|
|
|
use crate::Value;
|
|
|
|
|
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-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-09-05 00:30:58 +02:00
|
|
|
/// Resolving a user-supplied path literal failed in some way.
|
2022-08-12 17:12:28 +02:00
|
|
|
PathResolution(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-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),
|
2022-08-08 01:16:02 +02:00
|
|
|
}
|
2022-08-04 15:43:51 +02:00
|
|
|
|
2022-09-18 18:06:11 +02:00
|
|
|
impl From<ParseIntError> for ErrorKind {
|
|
|
|
fn from(e: ParseIntError) -> Self {
|
|
|
|
Self::ParseIntError(e)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-08-22 22:48:47 +02:00
|
|
|
#[derive(Clone, Debug)]
|
2022-08-22 22:20:50 +02:00
|
|
|
pub struct Error {
|
|
|
|
pub kind: ErrorKind,
|
2022-09-12 15:12:43 +02:00
|
|
|
pub span: Span,
|
2022-08-22 22:20:50 +02:00
|
|
|
}
|
|
|
|
|
2022-08-04 15:43:51 +02:00
|
|
|
impl Display for Error {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
2022-08-22 22:20:50 +02:00
|
|
|
writeln!(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
|
|
|
|
|
|
|
impl Error {
|
|
|
|
pub fn fancy_format_str(&self, codemap: &CodeMap) -> String {
|
|
|
|
let mut out = vec![];
|
|
|
|
Emitter::vec(&mut out, Some(codemap)).emit(&[self.diagnostic(codemap)]);
|
|
|
|
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.
|
|
|
|
pub fn fancy_format_stderr(&self, codemap: &CodeMap) {
|
|
|
|
Emitter::stderr(ColorConfig::Auto, Some(codemap)).emit(&[self.diagnostic(codemap)]);
|
|
|
|
}
|
|
|
|
|
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> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create the primary error message displayed to users.
|
|
|
|
fn message(&self, codemap: &CodeMap) -> String {
|
|
|
|
match &self.kind {
|
|
|
|
ErrorKind::Throw(msg) => format!("error thrown: {}", msg),
|
|
|
|
ErrorKind::Abort(msg) => format!("evaluation aborted: {}", msg),
|
|
|
|
ErrorKind::AssertionFailed => "assertion failed".to_string(),
|
|
|
|
|
|
|
|
ErrorKind::DuplicateAttrsKey { key } => {
|
|
|
|
format!("attribute key '{}' already defined", key)
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorKind::InvalidAttributeName(val) => format!(
|
|
|
|
"found attribute name '{}' of type '{}', but attribute names must be strings",
|
|
|
|
val,
|
|
|
|
val.type_of()
|
|
|
|
),
|
|
|
|
|
|
|
|
ErrorKind::AttributeNotFound { name } => format!(
|
|
|
|
"attribute with name '{}' could not be found in the set",
|
|
|
|
name
|
|
|
|
),
|
|
|
|
|
2022-09-05 20:41:50 +02:00
|
|
|
ErrorKind::IndexOutOfBounds { index } => {
|
|
|
|
format!("list index '{}' is out of bounds", index)
|
|
|
|
}
|
|
|
|
|
2022-09-24 15:38:16 +02:00
|
|
|
ErrorKind::TailEmptyList => "'tail' called on an empty list".to_string(),
|
2022-09-05 20:42:53 +02:00
|
|
|
|
2022-09-12 15:12:43 +02:00
|
|
|
ErrorKind::TypeError { expected, actual } => format!(
|
|
|
|
"expected value of type '{}', but found a '{}'",
|
|
|
|
expected, actual
|
|
|
|
),
|
|
|
|
|
|
|
|
ErrorKind::Incomparable { lhs, rhs } => {
|
|
|
|
format!("can not compare a {} with a {}", lhs, rhs)
|
|
|
|
}
|
|
|
|
|
|
|
|
ErrorKind::PathResolution(err) => format!("could not resolve path: {}", err),
|
|
|
|
|
2022-09-23 16:11:34 +02:00
|
|
|
ErrorKind::DynamicKeyInScope(scope) => {
|
|
|
|
format!("dynamically evaluated keys are not allowed in {}", scope)
|
2022-09-12 15:12:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ErrorKind::UnknownStaticVariable => "variable not found".to_string(),
|
|
|
|
|
|
|
|
ErrorKind::UnknownDynamicVariable(name) => format!(
|
|
|
|
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(_) => "variable has already been defined".to_string(),
|
|
|
|
|
2022-09-21 00:10:22 +02:00
|
|
|
ErrorKind::NotCallable(other_type) => {
|
|
|
|
format!(
|
|
|
|
"only functions and builtins can be called, but this is a '{}'",
|
|
|
|
other_type
|
|
|
|
)
|
2022-09-12 15:12:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ErrorKind::InfiniteRecursion => "infinite recursion encountered".to_string(),
|
|
|
|
|
|
|
|
// TODO(tazjin): these errors should actually end up with
|
|
|
|
// individual spans etc.
|
|
|
|
ErrorKind::ParseErrors(errors) => format!("failed to parse Nix code: {:?}", errors),
|
|
|
|
|
|
|
|
// 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) => err.message(codemap),
|
|
|
|
|
2022-09-13 15:37:19 +02:00
|
|
|
ErrorKind::NotCoercibleToString { kind, from } => {
|
|
|
|
let kindly = match kind {
|
|
|
|
CoercionKind::Strong => "strongly",
|
|
|
|
CoercionKind::Weak => "weakly",
|
|
|
|
};
|
|
|
|
|
|
|
|
let hint = if *from == "set" {
|
|
|
|
", missing a `__toString` or `outPath` attribute"
|
|
|
|
} else {
|
|
|
|
""
|
|
|
|
};
|
|
|
|
|
|
|
|
format!("cannot ({kindly}) coerce {from} to a string{hint}")
|
|
|
|
}
|
|
|
|
|
2022-09-13 20:11:07 +02:00
|
|
|
ErrorKind::NotAnAbsolutePath(given) => {
|
|
|
|
format!(
|
|
|
|
"string {} doesn't represent an absolute path",
|
|
|
|
given.to_string_lossy()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-09-18 18:06:11 +02:00
|
|
|
ErrorKind::ParseIntError(err) => {
|
|
|
|
format!("invalid integer: {}", err)
|
|
|
|
}
|
|
|
|
|
2022-09-06 23:33:10 +02:00
|
|
|
ErrorKind::NegativeLength { length } => {
|
|
|
|
format!(
|
|
|
|
"cannot use a negative integer, {}, for a value representing length",
|
|
|
|
length
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-09-28 13:57:53 +02:00
|
|
|
ErrorKind::UnmergeableInherit { name } => {
|
|
|
|
format!(
|
|
|
|
"cannot merge a nested attribute set into the inherited entry '{}'",
|
|
|
|
name
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-09-28 14:08:11 +02:00
|
|
|
ErrorKind::UnmergeableValue => {
|
|
|
|
"nested attribute sets or keys can only be merged with literal attribute sets"
|
|
|
|
.into()
|
|
|
|
}
|
|
|
|
|
2022-09-12 15:12:43 +02:00
|
|
|
ErrorKind::NotImplemented(feature) => {
|
|
|
|
format!("feature not yet implemented in Tvix: {}", feature)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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",
|
|
|
|
ErrorKind::PathResolution(_) => "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-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(),
|
2022-09-12 15:12:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn diagnostic(&self, codemap: &CodeMap) -> Diagnostic {
|
|
|
|
let span_label = SpanLabel {
|
|
|
|
label: self.span_label(),
|
|
|
|
span: self.span,
|
|
|
|
style: SpanStyle::Primary,
|
|
|
|
};
|
|
|
|
|
|
|
|
Diagnostic {
|
|
|
|
level: Level::Error,
|
|
|
|
message: self.message(codemap),
|
|
|
|
spans: vec![span_label],
|
|
|
|
code: Some(self.code().into()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|