git.delta.rocks / jrsonnet / refs/commits / 3ea3f30e2192

difftreelog

feat enable friendly-errors by default

Yaroslav Bolyukin2023-06-14parent: #bbe146e.patch.diff
in: master

4 files changed

modifiedcrates/jrsonnet-evaluator/Cargo.tomldiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/Cargo.toml
+++ b/crates/jrsonnet-evaluator/Cargo.toml
@@ -8,14 +8,11 @@
 edition = "2021"
 
 [features]
-default = ["explaining-traces", "friendly-errors"]
+default = ["explaining-traces"]
 # Rustc-like trace visualization
 explaining-traces = ["annotate-snippets"]
 # Allows library authors to throw custom errors
 anyhow-error = ["anyhow"]
-# Provides helpful explaintations to errors, at cost of adding
-# more dependencies and slowing down error path
-friendly-errors = ["strsim"]
 # Adds ability to build import closure in async
 async-import = ["async-trait"]
 
@@ -45,12 +42,12 @@
 rustc-hash = "1.1"
 
 thiserror = "1.0"
+# Friendly errors
+strsim = { version = "0.10.0" }
 
 serde = "1.0"
 
 anyhow = { version = "1.0", optional = true }
-# Friendly errors
-strsim = { version = "0.10.0", optional = true }
 # Serialized stdlib
 bincode = { version = "1.3", optional = true }
 # Explaining traces
modifiedcrates/jrsonnet-evaluator/src/ctx.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/ctx.rs
1use std::fmt::Debug;23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::IStr;56use crate::{7	error::ErrorKind::*, gc::GcHashMap, map::LayeredHashMap, ObjValue, Pending, Result, State,8	Thunk, Val,9};1011#[derive(Trace)]12struct ContextInternals {13	state: Option<State>,14	dollar: Option<ObjValue>,15	sup: Option<ObjValue>,16	this: Option<ObjValue>,17	bindings: LayeredHashMap,18}19impl Debug for ContextInternals {20	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {21		f.debug_struct("Context").finish()22	}23}2425/// Context keeps information about current lexical code location26///27/// This information includes local variables, top-level object (`$`), current object (`this`), and super object (`super`)28#[derive(Debug, Clone, Trace)]29pub struct Context(Cc<ContextInternals>);30impl Context {31	pub fn new_future() -> Pending<Self> {32		Pending::new()33	}3435	pub fn state(&self) -> &State {36		self.037			.state38			.as_ref()39			.expect("used state from dummy context")40	}4142	pub fn dollar(&self) -> Option<&ObjValue> {43		self.0.dollar.as_ref()44	}4546	pub fn this(&self) -> Option<&ObjValue> {47		self.0.this.as_ref()48	}4950	pub fn super_obj(&self) -> Option<&ObjValue> {51		self.0.sup.as_ref()52	}5354	#[cfg(not(feature = "friendly-errors"))]55	pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {56		Ok(self57			.058			.bindings59			.get(&name)60			.cloned()61			.ok_or(VariableIsNotDefined(name, vec![]))?)62	}6364	#[cfg(feature = "friendly-errors")]65	pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {66		use std::cmp::Ordering;6768		use crate::throw;6970		if let Some(val) = self.0.bindings.get(&name).cloned() {71			return Ok(val);72		}7374		let mut heap = Vec::new();75		self.0.bindings.clone().iter_keys(|k| {76			let conf = strsim::jaro_winkler(&k as &str, &name as &str);77			if conf < 0.8 {78				return;79			}80			heap.push((conf, k));81		});82		heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));8384		throw!(VariableIsNotDefined(85			name,86			heap.into_iter().map(|(_, k)| k).collect()87		))88	}89	pub fn contains_binding(&self, name: IStr) -> bool {90		self.0.bindings.contains_key(&name)91	}92	#[must_use]93	pub fn into_future(self, ctx: Pending<Self>) -> Self {94		{95			ctx.0.borrow_mut().replace(self);96		}97		ctx.unwrap()98	}99100	#[must_use]101	pub fn with_var(self, name: IStr, value: Val) -> Self {102		let mut new_bindings = GcHashMap::with_capacity(1);103		new_bindings.insert(name, Thunk::evaluated(value));104		self.extend(new_bindings, None, None, None)105	}106107	#[must_use]108	pub fn extend(109		self,110		new_bindings: GcHashMap<IStr, Thunk<Val>>,111		new_dollar: Option<ObjValue>,112		new_sup: Option<ObjValue>,113		new_this: Option<ObjValue>,114	) -> Self {115		let ctx = &self.0;116		let dollar = new_dollar.or_else(|| ctx.dollar.clone());117		let this = new_this.or_else(|| ctx.this.clone());118		let sup = new_sup.or_else(|| ctx.sup.clone());119		let bindings = if new_bindings.is_empty() {120			ctx.bindings.clone()121		} else {122			ctx.bindings.clone().extend(new_bindings)123		};124		Self(Cc::new(ContextInternals {125			state: ctx.state.clone(),126			dollar,127			sup,128			this,129			bindings,130		}))131	}132}133134impl PartialEq for Context {135	fn eq(&self, other: &Self) -> bool {136		Cc::ptr_eq(&self.0, &other.0)137	}138}139140pub struct ContextBuilder {141	state: Option<State>,142	bindings: GcHashMap<IStr, Thunk<Val>>,143	extend: Option<Context>,144}145146impl ContextBuilder {147	/// # Panics148	/// Panics aren't directly caused by this function, but if state from resulting context is used149	pub fn dangerous_empty_state() -> Self {150		Self {151			state: None,152			bindings: GcHashMap::new(),153			extend: None,154		}155	}156	pub fn new(state: State) -> Self {157		Self::with_capacity(state, 0)158	}159	pub fn with_capacity(state: State, capacity: usize) -> Self {160		Self {161			state: Some(state),162			bindings: GcHashMap::with_capacity(capacity),163			extend: None,164		}165	}166	pub fn extend(parent: Context) -> Self {167		Self {168			state: parent.0.state.clone(),169			bindings: GcHashMap::new(),170			extend: Some(parent),171		}172	}173	/// # Panics174	/// If `name` is already bound175	pub fn bind(&mut self, name: IStr, value: Thunk<Val>) -> &mut Self {176		let old = self.bindings.insert(name, value);177		assert!(old.is_none(), "variable bound twice in single context call");178		self179	}180	pub fn build(self) -> Context {181		if let Some(parent) = self.extend {182			// TODO: replace self.extend with Result<Context, State>, and remove `state` field183			parent.extend(self.bindings, None, None, None)184		} else {185			Context(Cc::new(ContextInternals {186				state: self.state,187				bindings: LayeredHashMap::new(self.bindings),188				dollar: None,189				sup: None,190				this: None,191			}))192		}193	}194}
after · crates/jrsonnet-evaluator/src/ctx.rs
1use std::fmt::Debug;23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::IStr;56use crate::{7	error::ErrorKind::*, gc::GcHashMap, map::LayeredHashMap, ObjValue, Pending, Result, State,8	Thunk, Val,9};1011#[derive(Trace)]12struct ContextInternals {13	state: Option<State>,14	dollar: Option<ObjValue>,15	sup: Option<ObjValue>,16	this: Option<ObjValue>,17	bindings: LayeredHashMap,18}19impl Debug for ContextInternals {20	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {21		f.debug_struct("Context").finish()22	}23}2425/// Context keeps information about current lexical code location26///27/// This information includes local variables, top-level object (`$`), current object (`this`), and super object (`super`)28#[derive(Debug, Clone, Trace)]29pub struct Context(Cc<ContextInternals>);30impl Context {31	pub fn new_future() -> Pending<Self> {32		Pending::new()33	}3435	pub fn state(&self) -> &State {36		self.037			.state38			.as_ref()39			.expect("used state from dummy context")40	}4142	pub fn dollar(&self) -> Option<&ObjValue> {43		self.0.dollar.as_ref()44	}4546	pub fn this(&self) -> Option<&ObjValue> {47		self.0.this.as_ref()48	}4950	pub fn super_obj(&self) -> Option<&ObjValue> {51		self.0.sup.as_ref()52	}5354	pub fn binding(&self, name: IStr) -> Result<Thunk<Val>> {55		use std::cmp::Ordering;5657		use crate::throw;5859		if let Some(val) = self.0.bindings.get(&name).cloned() {60			return Ok(val);61		}6263		let mut heap = Vec::new();64		self.0.bindings.clone().iter_keys(|k| {65			let conf = strsim::jaro_winkler(&k as &str, &name as &str);66			if conf < 0.8 {67				return;68			}69			heap.push((conf, k));70		});71		heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));7273		throw!(VariableIsNotDefined(74			name,75			heap.into_iter().map(|(_, k)| k).collect()76		))77	}78	pub fn contains_binding(&self, name: IStr) -> bool {79		self.0.bindings.contains_key(&name)80	}81	#[must_use]82	pub fn into_future(self, ctx: Pending<Self>) -> Self {83		{84			ctx.0.borrow_mut().replace(self);85		}86		ctx.unwrap()87	}8889	#[must_use]90	pub fn with_var(self, name: IStr, value: Val) -> Self {91		let mut new_bindings = GcHashMap::with_capacity(1);92		new_bindings.insert(name, Thunk::evaluated(value));93		self.extend(new_bindings, None, None, None)94	}9596	#[must_use]97	pub fn extend(98		self,99		new_bindings: GcHashMap<IStr, Thunk<Val>>,100		new_dollar: Option<ObjValue>,101		new_sup: Option<ObjValue>,102		new_this: Option<ObjValue>,103	) -> Self {104		let ctx = &self.0;105		let dollar = new_dollar.or_else(|| ctx.dollar.clone());106		let this = new_this.or_else(|| ctx.this.clone());107		let sup = new_sup.or_else(|| ctx.sup.clone());108		let bindings = if new_bindings.is_empty() {109			ctx.bindings.clone()110		} else {111			ctx.bindings.clone().extend(new_bindings)112		};113		Self(Cc::new(ContextInternals {114			state: ctx.state.clone(),115			dollar,116			sup,117			this,118			bindings,119		}))120	}121}122123impl PartialEq for Context {124	fn eq(&self, other: &Self) -> bool {125		Cc::ptr_eq(&self.0, &other.0)126	}127}128129pub struct ContextBuilder {130	state: Option<State>,131	bindings: GcHashMap<IStr, Thunk<Val>>,132	extend: Option<Context>,133}134135impl ContextBuilder {136	/// # Panics137	/// Panics aren't directly caused by this function, but if state from resulting context is used138	pub fn dangerous_empty_state() -> Self {139		Self {140			state: None,141			bindings: GcHashMap::new(),142			extend: None,143		}144	}145	pub fn new(state: State) -> Self {146		Self::with_capacity(state, 0)147	}148	pub fn with_capacity(state: State, capacity: usize) -> Self {149		Self {150			state: Some(state),151			bindings: GcHashMap::with_capacity(capacity),152			extend: None,153		}154	}155	pub fn extend(parent: Context) -> Self {156		Self {157			state: parent.0.state.clone(),158			bindings: GcHashMap::new(),159			extend: Some(parent),160		}161	}162	/// # Panics163	/// If `name` is already bound164	pub fn bind(&mut self, name: IStr, value: Thunk<Val>) -> &mut Self {165		let old = self.bindings.insert(name, value);166		assert!(old.is_none(), "variable bound twice in single context call");167		self168	}169	pub fn build(self) -> Context {170		if let Some(parent) = self.extend {171			// TODO: replace self.extend with Result<Context, State>, and remove `state` field172			parent.extend(self.bindings, None, None, None)173		} else {174			Context(Cc::new(ContextInternals {175				state: self.state,176				bindings: LayeredHashMap::new(self.bindings),177				dollar: None,178				sup: None,179				this: None,180			}))181		}182	}183}
modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -1,6 +1,7 @@
 use std::{
+	cmp::Ordering,
 	fmt::{Debug, Display},
-	path::PathBuf, cmp::Ordering,
+	path::PathBuf,
 };
 
 use jrsonnet_gcmodule::Trace;
@@ -79,9 +80,8 @@
 		if conf < 0.8 {
 			continue;
 		}
-		if field.as_str() == key.as_str() {
-			panic!("looks like string pooling failure, please write any info regarding this crash to https://github.com/CertainLach/jrsonnet/issues/113, thanks!");
-		}
+		assert!(field.as_str() != key.as_str(), "looks like string pooling failure, please write any info regarding this crash to https://github.com/CertainLach/jrsonnet/issues/113, thanks!");
+
 		heap.push((conf, field));
 	}
 	heap.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(Ordering::Equal));
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -1,4 +1,4 @@
-use std::{cmp::Ordering, rc::Rc};
+use std::rc::Rc;
 
 use jrsonnet_gcmodule::{Cc, Trace};
 use jrsonnet_interner::IStr;
@@ -12,7 +12,7 @@
 use crate::{
 	arr::ArrValue,
 	destructure::evaluate_dest,
-	error::{ErrorKind::*, suggest_object_fields},
+	error::{suggest_object_fields, ErrorKind::*},
 	evaluate::operator::{evaluate_add_op, evaluate_binary_op_special, evaluate_unary_op},
 	function::{CallLocation, FuncDesc, FuncVal},
 	throw,