difftreelog
feat(evaluator) readable error messages
in: master
6 files changed
crates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -37,6 +37,8 @@
base64 = "0.12.3"
rustc-hash = "1.1.0"
+thiserror = "1.0.20"
+
# Serialized stdlib
[dependencies.serde]
version = "1.0.115"
crates/jrsonnet-evaluator/src/builtin/format.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/format.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/format.rs
@@ -2,16 +2,23 @@
#![allow(clippy::too_many_arguments)]
use crate::{error::Error::*, throw, LocError, ObjValue, Result, Val, ValType};
+use thiserror::Error;
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Error)]
pub enum FormatError {
+ #[error("truncated format code")]
TruncatedFormatCode,
+ #[error("unrecognized conversion type: {0}")]
UnrecognizedConversionType(char),
+ #[error("not enough values")]
NotEnoughValues,
+ #[error("cannot use * width with object")]
CannotUseStarWidthWithObject,
+ #[error("mapping keys required")]
MappingKeysRequired,
+ #[error("no such format field: {0}")]
NoSuchFormatField(Rc<str>),
}
crates/jrsonnet-evaluator/src/builtin/sort.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/sort.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/sort.rs
@@ -4,9 +4,11 @@
};
use std::rc::Rc;
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, thiserror::Error)]
pub enum SortError {
+ #[error("sort key should be string or number")]
SortKeyShouldBeStringOrNumber,
+ #[error("sort elements should have equal types")]
SortElementsShouldHaveEqualType,
}
crates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth1use crate::{2 error::Error::*, future_wrapper, map::LayeredHashMap, rc_fn_helper, resolved_lazy_val,3 LazyBinding, LazyVal, ObjValue, Result, Val,4};5use rustc_hash::FxHashMap;6use std::hash::BuildHasherDefault;7use std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc};89rc_fn_helper!(10 ContextCreator,11 context_creator,12 dyn Fn(Option<ObjValue>, Option<ObjValue>) -> Result<Context>13);1415future_wrapper!(Context, FutureContext);1617struct ContextInternals {18 dollar: Option<ObjValue>,19 this: Option<ObjValue>,20 super_obj: Option<ObjValue>,21 bindings: LayeredHashMap<Rc<str>, LazyVal>,22}23impl Debug for ContextInternals {24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {25 f.debug_struct("Context")26 .field("this", &self.this.as_ref().map(|e| Rc::as_ptr(&e.0)))27 .field("bindings", &self.bindings)28 .finish()29 }30}3132#[derive(Debug, Clone)]33pub struct Context(Rc<ContextInternals>);34impl Context {35 pub fn new_future() -> FutureContext {36 FutureContext(Rc::new(RefCell::new(None)))37 }3839 pub fn dollar(&self) -> &Option<ObjValue> {40 &self.0.dollar41 }4243 pub fn this(&self) -> &Option<ObjValue> {44 &self.0.this45 }4647 pub fn super_obj(&self) -> &Option<ObjValue> {48 &self.0.super_obj49 }5051 pub fn new() -> Self {52 Self(Rc::new(ContextInternals {53 dollar: None,54 this: None,55 super_obj: None,56 bindings: LayeredHashMap::default(),57 }))58 }5960 pub fn binding(&self, name: Rc<str>) -> Result<LazyVal> {61 Ok(self62 .063 .bindings64 .get(&name)65 .cloned()66 .ok_or_else(|| UnknownVariable(name))?)67 }68 pub fn into_future(self, ctx: FutureContext) -> Self {69 {70 ctx.0.borrow_mut().replace(self);71 }72 ctx.unwrap()73 }7475 pub fn with_var(self, name: Rc<str>, value: Val) -> Self {76 let mut new_bindings =77 FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());78 new_bindings.insert(name, resolved_lazy_val!(value));79 self.extend(new_bindings, None, None, None)80 }8182 pub fn extend(83 self,84 new_bindings: FxHashMap<Rc<str>, LazyVal>,85 new_dollar: Option<ObjValue>,86 new_this: Option<ObjValue>,87 new_super_obj: Option<ObjValue>,88 ) -> Self {89 match Rc::try_unwrap(self.0) {90 Ok(mut ctx) => {91 // Extended context aren't used by anything else, we can freely mutate it without cloning92 if let Some(dollar) = new_dollar {93 ctx.dollar = Some(dollar);94 }95 if let Some(this) = new_this {96 ctx.this = Some(this);97 }98 if let Some(super_obj) = new_super_obj {99 ctx.super_obj = Some(super_obj);100 }101 if !new_bindings.is_empty() {102 ctx.bindings = ctx.bindings.extend(new_bindings);103 }104 Self(Rc::new(ctx))105 }106 Err(ctx) => {107 let dollar = new_dollar.or_else(|| ctx.dollar.clone());108 let this = new_this.or_else(|| ctx.this.clone());109 let super_obj = new_super_obj.or_else(|| ctx.super_obj.clone());110 let bindings = if new_bindings.is_empty() {111 ctx.bindings.clone()112 } else {113 ctx.bindings.clone().extend(new_bindings)114 };115 Self(Rc::new(ContextInternals {116 dollar,117 this,118 super_obj,119 bindings,120 }))121 }122 }123 }124 pub fn extend_unbound(125 self,126 new_bindings: HashMap<Rc<str>, LazyBinding>,127 new_dollar: Option<ObjValue>,128 new_this: Option<ObjValue>,129 new_super_obj: Option<ObjValue>,130 ) -> Result<Self> {131 let this = new_this.or_else(|| self.0.this.clone());132 let super_obj = new_super_obj.or_else(|| self.0.super_obj.clone());133 let mut new =134 FxHashMap::with_capacity_and_hasher(new_bindings.len(), BuildHasherDefault::default());135 for (k, v) in new_bindings.into_iter() {136 new.insert(k, v.evaluate(this.clone(), super_obj.clone())?);137 }138 Ok(self.extend(new, new_dollar, this, super_obj))139 }140 #[cfg(feature = "unstable")]141 pub fn into_weak(self) -> WeakContext {142 WeakContext(Rc::downgrade(&self.0))143 }144}145146impl Default for Context {147 fn default() -> Self {148 Self::new()149 }150}151152impl PartialEq for Context {153 fn eq(&self, other: &Self) -> bool {154 Rc::ptr_eq(&self.0, &other.0)155 }156}157158#[cfg(feature = "unstable")]159#[derive(Debug, Clone)]160pub struct WeakContext(std::rc::Weak<ContextInternals>);161#[cfg(feature = "unstable")]162impl WeakContext {163 pub fn upgrade(&self) -> Context {164 Context(self.0.upgrade().expect("context is removed"))165 }166}167#[cfg(feature = "unstable")]168impl PartialEq for WeakContext {169 fn eq(&self, other: &Self) -> bool {170 self.0.ptr_eq(&other.0)171 }172}crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -4,75 +4,117 @@
};
use jrsonnet_parser::{BinaryOpType, ExprLocation, UnaryOpType};
use std::{path::PathBuf, rc::Rc};
+use thiserror::Error;
-#[derive(Debug, Clone)]
+#[derive(Error, Debug, Clone)]
pub enum Error {
+ #[error("intrinsic not found: {0}.{1}")]
IntrinsicNotFound(Rc<str>, Rc<str>),
+ #[error("argument reordering in intrisics not supported yet")]
IntrinsicArgumentReorderingIsNotSupportedYet,
+ #[error("operator {0} does not operate on type {1}")]
UnaryOperatorDoesNotOperateOnType(UnaryOpType, ValType),
+ #[error("binary operation {1} {0} {2} is not implemented")]
BinaryOperatorDoesNotOperateOnValues(BinaryOpType, ValType, ValType),
+ #[error("no top level object in this context")]
NoTopLevelObjectFound,
+ #[error("self is only usable inside objects")]
CantUseSelfOutsideOfObject,
+ #[error("super is only usable inside objects")]
CantUseSuperOutsideOfObject,
+ #[error("for loop can only iterate over arrays")]
InComprehensionCanOnlyIterateOverArray,
+ #[error("array out of bounds: {0} is not within [0,{1})")]
ArrayBoundsError(usize, usize),
+ #[error("assert failed: {0}")]
AssertionFailed(Rc<str>),
- VariableIsNotDefined(String),
+ #[error("variable is not defined: {0}")]
+ VariableIsNotDefined(Rc<str>),
+ #[error("type mismatch: expected {2}, got {1:?} {0}")]
TypeMismatch(&'static str, Vec<ValType>, ValType),
+ #[error("no such field: {0}")]
NoSuchField(Rc<str>),
- UnknownVariable(Rc<str>),
-
+ #[error("only functions can be called, got {0}")]
OnlyFunctionsCanBeCalledGot(ValType),
+ #[error("parameter {0} is not defined")]
UnknownFunctionParameter(String),
+ #[error("argument {0} is already bound")]
BindingParameterASecondTime(Rc<str>),
+ #[error("too many args, function has {0}")]
TooManyArgsFunctionHas(usize),
+ #[error("founction argument is not passed: {0}")]
FunctionParameterNotBoundInCall(Rc<str>),
+ #[error("external variable is not defined: {0}")]
UndefinedExternalVariable(Rc<str>),
+ #[error("native is not defined: {0}")]
UndefinedExternalFunction(Rc<str>),
+ #[error("field name should be string, got {0}")]
FieldMustBeStringGot(ValType),
+ #[error("attempted to index array with string {0}")]
AttemptedIndexAnArrayWithString(Rc<str>),
+ #[error("{0} index type should be {1}, got {2}")]
ValueIndexMustBeTypeGot(ValType, ValType, ValType),
+ #[error("cant index into {0}")]
CantIndexInto(ValType),
+ #[error("super can't be used standalone")]
StandaloneSuper,
+ #[error("can't resolve {1} from {0}")]
ImportFileNotFound(PathBuf, PathBuf),
+ #[error("resolved file not found: {0}")]
ResolvedFileNotFound(PathBuf),
+ #[error("imported file is not valid utf-8: {0:?}")]
ImportBadFileUtf8(PathBuf),
+ #[error("tried to import {1} from {0}, but imports is not supported")]
ImportNotSupported(PathBuf, PathBuf),
+ #[error("syntax error")]
ImportSyntaxError {
path: Rc<PathBuf>,
source_code: Rc<str>,
error: Box<jrsonnet_parser::ParseError>,
},
+ #[error("runtime error: {0}")]
RuntimeError(Rc<str>),
+ #[error("stack overflow, try to reduce recursion, or set --max-stack to bigger value")]
StackOverflow,
+ #[error("tried to index by fractional value")]
FractionalIndex,
+ #[error("attempted to divide by zero")]
DivisionByZero,
+ #[error("string manifest output is not an string")]
StringManifestOutputIsNotAString,
+ #[error("stream manifest output is not an array")]
StreamManifestOutputIsNotAArray,
+ #[error("multi manifest output is not an object")]
MultiManifestOutputIsNotAObject,
+ #[error("cant recurse stream manifest")]
StreamManifestOutputCannotBeRecursed,
+ #[error("stream manifest output cannot consist of raw strings")]
StreamManifestCannotNestString,
+ #[error("{0}")]
ImportCallbackError(String),
+ #[error("invalid unicode codepoint: {0}")]
InvalidUnicodeCodepointGot(u32),
- Format(FormatError),
- Sort(SortError),
+ #[error("format error: {0}")]
+ Format(#[from] FormatError),
+ #[error("sort error: {0}")]
+ Sort(#[from] SortError),
}
impl From<Error> for LocError {
fn from(e: Error) -> Self {
crates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/trace/mod.rs
+++ b/crates/jrsonnet-evaluator/src/trace/mod.rs
@@ -86,7 +86,7 @@
evaluation_state: &EvaluationState,
error: &LocError,
) -> Result<(), std::fmt::Error> {
- writeln!(out, "{:?}", error.error())?;
+ writeln!(out, "{}", error.error())?;
let file_names = error
.trace()
.0
@@ -132,7 +132,7 @@
evaluation_state: &EvaluationState,
error: &LocError,
) -> Result<(), std::fmt::Error> {
- writeln!(out, "{:?}", error.error())?;
+ writeln!(out, "{}", error.error())?;
for (i, item) in error.trace().0.iter().enumerate() {
if i != 0 {
writeln!(out)?;
@@ -171,7 +171,7 @@
display_list::{DisplayList, FormatOptions},
snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},
};
- writeln!(out, "{:?}", error.error())?;
+ writeln!(out, "{}", error.error())?;
let trace = &error.trace();
for item in trace.0.iter() {
let desc = &item.desc;