git.delta.rocks / jrsonnet / refs/commits / 5f9657289c33

difftreelog

test pass full go-jsonnet test suite

pwtnqznvYaroslav Bolyukin2026-02-08parent: #eea91c3.patch.diff
in: master

54 files changed

modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -532,17 +532,23 @@
 					)),
 
 					(Val::Str(s), Val::Num(n)) => Val::Str({
+						let n = n.get();
+						if n.fract() > f64::EPSILON {
+							bail!(FractionalIndex)
+						}
+						if n < 0.0 {
+							bail!(ArrayBoundsError(n as isize, s.into_flat().chars().count()));
+						}
 						let v: IStr = s
 							.clone()
 							.into_flat()
 							.chars()
-							.skip(n.get() as usize)
+							.skip(n as usize)
 							.take(1)
 							.collect::<String>()
 							.into();
 						if v.is_empty() {
-							let size = s.into_flat().chars().count();
-							bail!(StringBoundsError(n.get() as usize, size))
+							bail!(StringBoundsError(n as usize, s.into_flat().chars().count()))
 						}
 						StrValue::Flat(v)
 					}),
modifiedcrates/jrsonnet-stdlib/src/math.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/math.rs
+++ b/crates/jrsonnet-stdlib/src/math.rs
@@ -168,12 +168,12 @@
 
 #[builtin]
 pub fn builtin_deg2rad(x: f64) -> f64 {
-	x * f64::consts::PI / 180.0
+	x.to_radians()
 }
 
 #[builtin]
 pub fn builtin_rad2deg(x: f64) -> f64 {
-	x * 180.0 / f64::consts::PI
+	x.to_degrees()
 }
 
 #[builtin]
modifiedcrates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth
before · crates/jrsonnet-stdlib/src/sort.rs
1#![allow(non_snake_case)]23use std::cmp::Ordering;45use jrsonnet_evaluator::{6	bail,7	function::{builtin, FuncVal},8	operator::evaluate_compare_op,9	val::{equals, ArrValue},10	Result, Thunk, Val,11};12use jrsonnet_parser::BinaryOpType;1314use crate::eval_on_empty;1516#[derive(Copy, Clone)]17enum SortKeyType {18	Number,19	String,20	Unknown,21}2223fn get_sort_type<T>(values: &[T], key_getter: impl Fn(&T) -> &Val) -> Result<SortKeyType> {24	let mut sort_type = SortKeyType::Unknown;25	for i in values {26		let i = key_getter(i);27		match (i, sort_type) {28			(Val::Str(_), SortKeyType::Unknown) => sort_type = SortKeyType::String,29			(Val::Num(_), SortKeyType::Unknown) => sort_type = SortKeyType::Number,30			(Val::Str(_), SortKeyType::String) | (Val::Num(_), SortKeyType::Number) => {}31			(Val::Str(_) | Val::Num(_), _) => {32				bail!("sort elements should have the same types")33			}34			_ => {}35		}36	}37	Ok(sort_type)38}3940fn sort_identity(mut values: Vec<Val>) -> Result<Vec<Val>> {41	// Fast path, identity key getter42	let sort_type = get_sort_type(&values, |k| k)?;43	match sort_type {44		SortKeyType::Number => values.sort_unstable_by_key(|v| match v {45			Val::Num(n) => *n,46			_ => unreachable!(),47		}),48		SortKeyType::String => values.sort_unstable_by_key(|v| match v {49			Val::Str(s) => s.clone(),50			_ => unreachable!(),51		}),52		SortKeyType::Unknown => {53			let mut err = None;54			// evaluate_compare_op will never return equal on types, which are different from55			// jsonnet perspective56			values.sort_unstable_by(|a, b| match evaluate_compare_op(a, b, BinaryOpType::Lt) {57				Ok(ord) => ord,58				Err(e) if err.is_none() => {59					let _ = err.insert(e);60					Ordering::Equal61				}62				Err(_) => Ordering::Equal,63			});64			if let Some(err) = err {65				return Err(err);66			}67		}68	};69	Ok(values)70}7172fn sort_keyf(values: ArrValue, keyf: FuncVal) -> Result<Vec<Thunk<Val>>> {73	// Slow path, user provided key getter74	let mut vk = Vec::with_capacity(values.len());75	for value in values.iter_lazy() {76		vk.push((77			value.clone(),78			keyf.evaluate_simple(&(value.clone(),), false)?,79		));80	}81	let sort_type = get_sort_type(&vk, |v| &v.1)?;82	match sort_type {83		SortKeyType::Number => vk.sort_by_key(|v| match v.1 {84			Val::Num(n) => n,85			_ => unreachable!(),86		}),87		SortKeyType::String => vk.sort_by_key(|v| match &v.1 {88			Val::Str(s) => s.clone(),89			_ => unreachable!(),90		}),91		SortKeyType::Unknown => {92			let mut err = None;93			// evaluate_compare_op will never return equal on types, which are different from94			// jsonnet perspective95			vk.sort_by(96				|(_a, ak), (_b, bk)| match evaluate_compare_op(ak, bk, BinaryOpType::Lt) {97					Ok(ord) => ord,98					Err(e) if err.is_none() => {99						let _ = err.insert(e);100						Ordering::Equal101					}102					Err(_) => Ordering::Equal,103				},104			);105			if let Some(err) = err {106				return Err(err);107			}108		}109	};110	Ok(vk.into_iter().map(|v| v.0).collect())111}112113/// * `key_getter` - None, if identity sort required114pub fn sort(values: ArrValue, key_getter: FuncVal) -> Result<ArrValue> {115	if values.len() <= 1 {116		return Ok(values);117	}118	if key_getter.is_identity() {119		Ok(ArrValue::eager(sort_identity(120			values.iter().collect::<Result<Vec<Val>>>()?,121		)?))122	} else {123		Ok(ArrValue::lazy(sort_keyf(values, key_getter)?))124	}125}126127#[builtin]128pub fn builtin_sort(129	arr: ArrValue,130131	#[default(FuncVal::identity())] keyF: FuncVal,132) -> Result<ArrValue> {133	super::sort::sort(arr, keyF)134}135136fn uniq_identity(arr: Vec<Val>) -> Result<Vec<Val>> {137	let mut out = Vec::new();138	let mut last = arr[0].clone();139	out.push(last.clone());140	for next in arr.into_iter().skip(1) {141		if !equals(&last, &next)? {142			out.push(next.clone());143		}144		last = next;145	}146	Ok(out)147}148149fn uniq_keyf(arr: ArrValue, keyf: FuncVal) -> Result<Vec<Thunk<Val>>> {150	let mut out = Vec::new();151	let last_value = arr.get_lazy(0).unwrap();152	let mut last_key = keyf.evaluate_simple(&(last_value.clone(),), false)?;153	out.push(last_value);154155	for next in arr.iter_lazy().skip(1) {156		let next_key = keyf.evaluate_simple(&(next.clone(),), false)?;157		if !equals(&last_key, &next_key)? {158			out.push(next.clone());159		}160		last_key = next_key;161	}162	Ok(out)163}164165#[builtin]166#[allow(non_snake_case)]167pub fn builtin_uniq(168	arr: ArrValue,169170	#[default(FuncVal::identity())] keyF: FuncVal,171) -> Result<ArrValue> {172	if arr.len() <= 1 {173		return Ok(arr);174	}175	if keyF.is_identity() {176		Ok(ArrValue::eager(uniq_identity(177			arr.iter().collect::<Result<Vec<Val>>>()?,178		)?))179	} else {180		Ok(ArrValue::lazy(uniq_keyf(arr, keyF)?))181	}182}183184#[builtin]185#[allow(non_snake_case)]186pub fn builtin_set(187	arr: ArrValue,188189	#[default(FuncVal::identity())] keyF: FuncVal,190) -> Result<ArrValue> {191	if arr.len() <= 1 {192		return Ok(arr);193	}194	if keyF.is_identity() {195		let arr = arr.iter().collect::<Result<Vec<Val>>>()?;196		let arr = sort_identity(arr)?;197		let arr = uniq_identity(arr)?;198		Ok(ArrValue::eager(arr))199	} else {200		let arr = sort_keyf(arr, keyF.clone())?;201		let arr = uniq_keyf(ArrValue::lazy(arr), keyF)?;202		Ok(ArrValue::lazy(arr))203	}204}205206fn eval_keyf(val: Val, key_f: &Option<FuncVal>) -> Result<Val> {207	if let Some(key_f) = key_f {208		key_f.evaluate_simple(&(val,), false)209	} else {210		Ok(val)211	}212}213214fn array_top1(arr: ArrValue, key_f: Option<FuncVal>, ordering: Ordering) -> Result<Val> {215	let mut iter = arr.iter();216	let mut min = iter.next().expect("not empty")?;217	let mut min_key = eval_keyf(min.clone(), &key_f)?;218	for item in iter {219		let cur = item?;220		let cur_key = eval_keyf(cur.clone(), &key_f)?;221		if evaluate_compare_op(&cur_key, &min_key, BinaryOpType::Lt)? == ordering {222			min = cur;223			min_key = cur_key;224		}225	}226	Ok(min)227}228229#[builtin]230pub fn builtin_min_array(231	arr: ArrValue,232	keyF: Option<FuncVal>,233	onEmpty: Option<Thunk<Val>>,234) -> Result<Val> {235	if arr.is_empty() {236		return eval_on_empty(onEmpty);237	}238	array_top1(arr, keyF, Ordering::Less)239}240#[builtin]241pub fn builtin_max_array(242	arr: ArrValue,243	keyF: Option<FuncVal>,244	onEmpty: Option<Thunk<Val>>,245) -> Result<Val> {246	if arr.is_empty() {247		return eval_on_empty(onEmpty);248	}249	array_top1(arr, keyF, Ordering::Greater)250}
after · crates/jrsonnet-stdlib/src/sort.rs
1#![allow(non_snake_case)]23use std::cmp::Ordering;45use jrsonnet_evaluator::{6	bail,7	function::{builtin, FuncVal},8	operator::evaluate_compare_op,9	val::{equals, ArrValue},10	Result, Thunk, Val,11};12use jrsonnet_parser::BinaryOpType;1314use crate::eval_on_empty;1516#[derive(Copy, Clone)]17enum SortKeyType {18	Number,19	String,20	Unspecialized,21	Unknown,22}2324fn get_sort_type<T>(values: &[T], key_getter: impl Fn(&T) -> &Val) -> Result<SortKeyType> {25	let mut sort_type = SortKeyType::Unknown;26	for i in values {27		let i = key_getter(i);28		match (i, sort_type) {29			(Val::Str(_), SortKeyType::Unknown) => sort_type = SortKeyType::String,30			(Val::Num(_), SortKeyType::Unknown) => sort_type = SortKeyType::Number,31			(Val::Str(_), SortKeyType::String) | (Val::Num(_), SortKeyType::Number) => {}32			(Val::Str(_) | Val::Num(_), _) => {33				bail!("sort elements should have the same types")34			}35			(_, _) => return Ok(SortKeyType::Unspecialized),36		}37	}38	Ok(sort_type)39}4041fn sort_identity(mut values: Vec<Val>) -> Result<Vec<Val>> {42	// Fast path, identity key getter43	let sort_type = get_sort_type(&values, |k| k)?;44	match sort_type {45		SortKeyType::Number => values.sort_unstable_by_key(|v| match v {46			Val::Num(n) => *n,47			_ => unreachable!(),48		}),49		SortKeyType::String => values.sort_unstable_by_key(|v| match v {50			Val::Str(s) => s.clone(),51			_ => unreachable!(),52		}),53		SortKeyType::Unknown | SortKeyType::Unspecialized => {54			let mut err = None;55			// evaluate_compare_op will never return equal on types, which are different from56			// jsonnet perspective57			values.sort_unstable_by(|a, b| match evaluate_compare_op(a, b, BinaryOpType::Lt) {58				Ok(ord) => ord,59				Err(e) if err.is_none() => {60					let _ = err.insert(e);61					Ordering::Equal62				}63				Err(_) => Ordering::Equal,64			});65			if let Some(err) = err {66				return Err(err);67			}68		}69	};70	Ok(values)71}7273fn sort_keyf(values: ArrValue, keyf: FuncVal) -> Result<Vec<Thunk<Val>>> {74	// Slow path, user provided key getter75	let mut vk = Vec::with_capacity(values.len());76	for value in values.iter_lazy() {77		vk.push((78			value.clone(),79			keyf.evaluate_simple(&(value.clone(),), false)?,80		));81	}82	let sort_type = get_sort_type(&vk, |v| &v.1)?;83	match sort_type {84		SortKeyType::Number => vk.sort_by_key(|v| match v.1 {85			Val::Num(n) => n,86			_ => unreachable!(),87		}),88		SortKeyType::String => vk.sort_by_key(|v| match &v.1 {89			Val::Str(s) => s.clone(),90			_ => unreachable!(),91		}),92		SortKeyType::Unknown | SortKeyType::Unspecialized => {93			let mut err = None;94			// evaluate_compare_op will never return equal on types, which are different from95			// jsonnet perspective96			vk.sort_by(97				|(_a, ak), (_b, bk)| match evaluate_compare_op(ak, bk, BinaryOpType::Lt) {98					Ok(ord) => ord,99					Err(e) if err.is_none() => {100						let _ = err.insert(e);101						Ordering::Equal102					}103					Err(_) => Ordering::Equal,104				},105			);106			if let Some(err) = err {107				return Err(err);108			}109		}110	};111	Ok(vk.into_iter().map(|v| v.0).collect())112}113114/// * `key_getter` - None, if identity sort required115pub fn sort(values: ArrValue, key_getter: FuncVal) -> Result<ArrValue> {116	if values.len() <= 1 {117		return Ok(values);118	}119	if key_getter.is_identity() {120		Ok(ArrValue::eager(sort_identity(121			values.iter().collect::<Result<Vec<Val>>>()?,122		)?))123	} else {124		Ok(ArrValue::lazy(sort_keyf(values, key_getter)?))125	}126}127128#[builtin]129pub fn builtin_sort(130	arr: ArrValue,131132	#[default(FuncVal::identity())] keyF: FuncVal,133) -> Result<ArrValue> {134	super::sort::sort(arr, keyF)135}136137fn uniq_identity(arr: Vec<Val>) -> Result<Vec<Val>> {138	let mut out = Vec::new();139	let mut last = arr[0].clone();140	out.push(last.clone());141	for next in arr.into_iter().skip(1) {142		if !equals(&last, &next)? {143			out.push(next.clone());144		}145		last = next;146	}147	Ok(out)148}149150fn uniq_keyf(arr: ArrValue, keyf: FuncVal) -> Result<Vec<Thunk<Val>>> {151	let mut out = Vec::new();152	let last_value = arr.get_lazy(0).unwrap();153	let mut last_key = keyf.evaluate_simple(&(last_value.clone(),), false)?;154	out.push(last_value);155156	for next in arr.iter_lazy().skip(1) {157		let next_key = keyf.evaluate_simple(&(next.clone(),), false)?;158		if !equals(&last_key, &next_key)? {159			out.push(next.clone());160		}161		last_key = next_key;162	}163	Ok(out)164}165166#[builtin]167#[allow(non_snake_case)]168pub fn builtin_uniq(169	arr: ArrValue,170171	#[default(FuncVal::identity())] keyF: FuncVal,172) -> Result<ArrValue> {173	if arr.len() <= 1 {174		return Ok(arr);175	}176	if keyF.is_identity() {177		Ok(ArrValue::eager(uniq_identity(178			arr.iter().collect::<Result<Vec<Val>>>()?,179		)?))180	} else {181		Ok(ArrValue::lazy(uniq_keyf(arr, keyF)?))182	}183}184185#[builtin]186#[allow(non_snake_case)]187pub fn builtin_set(188	arr: ArrValue,189190	#[default(FuncVal::identity())] keyF: FuncVal,191) -> Result<ArrValue> {192	if arr.len() <= 1 {193		return Ok(arr);194	}195	if keyF.is_identity() {196		let arr = arr.iter().collect::<Result<Vec<Val>>>()?;197		let arr = sort_identity(arr)?;198		let arr = uniq_identity(arr)?;199		Ok(ArrValue::eager(arr))200	} else {201		let arr = sort_keyf(arr, keyF.clone())?;202		let arr = uniq_keyf(ArrValue::lazy(arr), keyF)?;203		Ok(ArrValue::lazy(arr))204	}205}206207fn eval_keyf(val: Val, key_f: &Option<FuncVal>) -> Result<Val> {208	if let Some(key_f) = key_f {209		key_f.evaluate_simple(&(val,), false)210	} else {211		Ok(val)212	}213}214215fn array_top1(arr: ArrValue, key_f: Option<FuncVal>, ordering: Ordering) -> Result<Val> {216	let mut iter = arr.iter();217	let mut min = iter.next().expect("not empty")?;218	let mut min_key = eval_keyf(min.clone(), &key_f)?;219	for item in iter {220		let cur = item?;221		let cur_key = eval_keyf(cur.clone(), &key_f)?;222		if evaluate_compare_op(&cur_key, &min_key, BinaryOpType::Lt)? == ordering {223			min = cur;224			min_key = cur_key;225		}226	}227	Ok(min)228}229230#[builtin]231pub fn builtin_min_array(232	arr: ArrValue,233	keyF: Option<FuncVal>,234	onEmpty: Option<Thunk<Val>>,235) -> Result<Val> {236	if arr.is_empty() {237		return eval_on_empty(onEmpty);238	}239	array_top1(arr, keyF, Ordering::Less)240}241#[builtin]242pub fn builtin_max_array(243	arr: ArrValue,244	keyF: Option<FuncVal>,245	onEmpty: Option<Thunk<Val>>,246) -> Result<Val> {247	if arr.is_empty() {248		return eval_on_empty(onEmpty);249	}250	array_top1(arr, keyF, Ordering::Greater)251}
modifiedcrates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/strings.rs
+++ b/crates/jrsonnet-stdlib/src/strings.rs
@@ -25,8 +25,11 @@
 }
 
 #[builtin]
-pub fn builtin_str_replace(str: String, from: IStr, to: IStr) -> String {
-	str.replace(&from as &str, &to as &str)
+pub fn builtin_str_replace(str: String, from: IStr, to: IStr) -> Result<String> {
+	if from.is_empty() {
+		bail!("'from' string must not be zero length");
+	}
+	Ok(str.replace(&from as &str, &to as &str))
 }
 
 #[builtin]
addedtests/go_testdata_golden_override/builtinObjectRemoveKey_super_assert.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/builtinObjectRemoveKey_super_assert.jsonnet.golden
@@ -0,0 +1,3 @@
+no such field: x
+    builtinObjectRemoveKey_super_assert.jsonnet:2:15-17: field <x> access
+    builtinObjectRemoveKey_super_assert.jsonnet:2:10-17: assertion condition
\ No newline at end of file
addedtests/go_testdata_golden_override/std.filter8.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.filter8.jsonnet.golden
@@ -0,0 +1,3 @@
+type error: expected function, got array
+    argument <func> evaluation
+    std.filter8.jsonnet:1:1-37: function <builtin_filter> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.filter_swapped_args.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.filter_swapped_args.jsonnet.golden
@@ -0,0 +1,3 @@
+type error: expected function, got array
+    argument <func> evaluation
+    std.filter_swapped_args.jsonnet:1:1-39: function <builtin_filter> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.flatmap5.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.flatmap5.jsonnet.golden
@@ -0,0 +1,5 @@
+runtime error: a
+    std.flatmap5.jsonnet:1:21-29: error statement
+    std.flatmap5.jsonnet:2:10-49: function <builtin_flatmap> call
+    argument <x> evaluation
+    std.flatmap5.jsonnet:2:1-50:  function <builtin_type> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.join7.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.join7.jsonnet.golden
@@ -0,0 +1,2 @@
+runtime error: in std.join all items should be strings
+    std.join7.jsonnet:1:1-28: function <builtin_join> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.join8.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.join8.jsonnet.golden
@@ -0,0 +1,2 @@
+runtime error: in std.join all items should be arrays
+    std.join8.jsonnet:1:1-34: function <builtin_join> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.makeArrayNamed3.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.makeArrayNamed3.jsonnet.golden
@@ -0,0 +1,2 @@
+parameter blahblah is not defined
+    std.makeArrayNamed3.jsonnet:1:1-55: function <builtin_make_array> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.makeArray_bad.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.makeArray_bad.jsonnet.golden
@@ -0,0 +1,3 @@
+type error: expected BoundedNumber<0, 2147483647>, got string
+    argument <sz> evaluation
+    std.makeArray_bad.jsonnet:1:1-37: function <builtin_make_array> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.makeArray_bad2.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.makeArray_bad2.jsonnet.golden
@@ -0,0 +1,3 @@
+type error: expected function, got string
+    argument <func> evaluation
+    std.makeArray_bad2.jsonnet:1:1-26: function <builtin_make_array> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.makeArray_noninteger.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.makeArray_noninteger.jsonnet.golden
@@ -0,0 +1,3 @@
+runtime error: cannot convert number with fractional part to i32
+    argument <sz> evaluation
+    std.makeArray_noninteger.jsonnet:1:1-35: function <builtin_make_array> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.makeArray_noninteger_big.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.makeArray_noninteger_big.jsonnet.golden
@@ -0,0 +1,3 @@
+type error: number out of bounds: 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 not in 0..2147483647
+    argument <sz> evaluation
+    std.makeArray_noninteger_big.jsonnet:1:1-37: function <builtin_make_array> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.manifestYamlDoc_error.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.manifestYamlDoc_error.jsonnet.golden
@@ -0,0 +1,5 @@
+runtime error: foo
+    std.manifestYamlDoc_error.jsonnet:1:31-43: error statement
+    field <y> evaluation
+    field <x> manifestification
+    std.manifestYamlDoc_error.jsonnet:1:1-48:  function <builtin_manifest_yaml_doc> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.mantissa2.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.mantissa2.jsonnet.golden
@@ -0,0 +1 @@
+0.6562500000000002
\ No newline at end of file
addedtests/go_testdata_golden_override/std.mantissa3.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.mantissa3.jsonnet.golden
@@ -0,0 +1 @@
+0.8399999999999999
\ No newline at end of file
addedtests/go_testdata_golden_override/std.mantissa4.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.mantissa4.jsonnet.golden
@@ -0,0 +1 @@
+0.571493695641147
\ No newline at end of file
addedtests/go_testdata_golden_override/std.mantissa5.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.mantissa5.jsonnet.golden
@@ -0,0 +1 @@
+0.6562499999999998
\ No newline at end of file
addedtests/go_testdata_golden_override/std.mantissa6.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.mantissa6.jsonnet.golden
@@ -0,0 +1 @@
+0.6562499999999998
\ No newline at end of file
addedtests/go_testdata_golden_override/std.mantissa7.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.mantissa7.jsonnet.golden
@@ -0,0 +1 @@
+-0.6562500000000002
\ No newline at end of file
addedtests/go_testdata_golden_override/std.maxArrayOnEmpty.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.maxArrayOnEmpty.jsonnet.golden
@@ -0,0 +1,2 @@
+runtime error: expected non-empty array
+    std.maxArrayOnEmpty.jsonnet:1:1-18: function <builtin_max_array> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.md5_6.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.md5_6.jsonnet.golden
@@ -0,0 +1,3 @@
+type error: expected string, got number
+    argument <s> evaluation
+    std.md5_6.jsonnet:1:1-13: function <builtin_md5> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.minArrayOnEmpty.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.minArrayOnEmpty.jsonnet.golden
@@ -0,0 +1,2 @@
+runtime error: expected non-empty array
+    std.minArrayOnEmpty.jsonnet:1:1-18: function <builtin_min_array> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.modulo2.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.modulo2.jsonnet.golden
@@ -0,0 +1,3 @@
+type error: expected number, got string
+    argument <x> evaluation
+    std.modulo2.jsonnet:1:1-23: function <builtin_modulo> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.modulo3.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.modulo3.jsonnet.golden
@@ -0,0 +1,3 @@
+type error: expected number, got string
+    argument <x> evaluation
+    std.modulo3.jsonnet:1:1-23: function <builtin_modulo> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.primitiveEquals10.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.primitiveEquals10.jsonnet.golden
@@ -0,0 +1,4 @@
+runtime error: x
+    std.primitiveEquals10.jsonnet:1:21-31: error statement
+    argument <x> evaluation
+    std.primitiveEquals10.jsonnet:1:1-36:  function <builtin_primitive_equals> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.primitiveEquals13.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.primitiveEquals13.jsonnet.golden
@@ -0,0 +1,2 @@
+runtime error: primitiveEquals operates on primitive types, got array
+    std.primitiveEquals13.jsonnet:1:1-29: function <builtin_primitive_equals> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.primitiveEquals6.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.primitiveEquals6.jsonnet.golden
@@ -0,0 +1,2 @@
+runtime error: primitiveEquals operates on primitive types, got object
+    std.primitiveEquals6.jsonnet:1:1-29: function <builtin_primitive_equals> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.primitiveEquals7.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.primitiveEquals7.jsonnet.golden
@@ -0,0 +1,2 @@
+runtime error: cannot test equality of functions
+    std.primitiveEquals7.jsonnet:1:1-51: function <builtin_primitive_equals> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.primitiveEquals9.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.primitiveEquals9.jsonnet.golden
@@ -0,0 +1,4 @@
+runtime error: x
+    std.primitiveEquals9.jsonnet:1:25-35: error statement
+    argument <y> evaluation
+    std.primitiveEquals9.jsonnet:1:1-36:  function <builtin_primitive_equals> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.sort3.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.sort3.jsonnet.golden
@@ -0,0 +1,3 @@
+runtime error: foo
+    std.sort3.jsonnet:1:16-28: error statement
+    std.sort3.jsonnet:1:1-30:  function <builtin_sort> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.sort4.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.sort4.jsonnet.golden
@@ -0,0 +1,2 @@
+binary operation array < number is not implemented
+    std.sort4.jsonnet:1:1-30: function <builtin_sort> call
\ No newline at end of file
addedtests/go_testdata_golden_override/std.thisFile.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.thisFile.jsonnet.golden
@@ -0,0 +1 @@
+"std.thisFile.jsonnet"
\ No newline at end of file
addedtests/go_testdata_golden_override/std.thisFile2.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.thisFile2.jsonnet.golden
@@ -0,0 +1 @@
+"std.thisFile.jsonnet"
\ No newline at end of file
addedtests/go_testdata_golden_override/std.toString5.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/std.toString5.jsonnet.golden
@@ -0,0 +1,4 @@
+runtime error: x
+    std.toString5.jsonnet:1:14-24: error statement
+    argument <a> evaluation
+    std.toString5.jsonnet:1:1-25:  function <builtin_to_string> call
\ No newline at end of file
addedtests/go_testdata_golden_override/stdlib_smoke_test.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/stdlib_smoke_test.jsonnet.golden
@@ -0,0 +1,279 @@
+{
+    "abs": 42,
+    "acos": 1.0471975511965979,
+    "asciiLower": "blah",
+    "asciiUpper": "BLAH",
+    "asin": 0.5235987755982989,
+    "assertEqual": true,
+    "atan": 1.373400766945016,
+    "base64": [
+        "YmxhaA==",
+        "YmxhaA=="
+    ],
+    "base64Decode": "blah\n",
+    "base64DecodeBytes": [
+        98,
+        108,
+        97,
+        104,
+        10
+    ],
+    "ceil": 5,
+    "char": "A",
+    "codepoint": 65,
+    "cos": 0.28366218546322625,
+    "count": 1,
+    "decodeUTF8": "AAA",
+    "encodeUTF8": [
+        98,
+        108,
+        97,
+        104
+    ],
+    "endsWith": true,
+    "escapeStringBash": "'test '\"'\"'test'\"'\"'test'",
+    "escapeStringDollars": "test 'test'test",
+    "escapeStringJson": "\"test 'test'test\"",
+    "escapeStringPython": "\"test 'test'test\"",
+    "exp": 148.4131591025766,
+    "exponent": 3,
+    "filter": [
+        2,
+        4
+    ],
+    "filterMap": [
+        4,
+        8
+    ],
+    "find": [
+        2,
+        4
+    ],
+    "findSubstr": [
+        0,
+        5
+    ],
+    "flatMap": [
+        2,
+        3,
+        4,
+        6,
+        6,
+        9
+    ],
+    "flattenArrays": [
+        1,
+        2,
+        3,
+        4,
+        5,
+        [
+            6,
+            7
+        ]
+    ],
+    "floor": 5,
+    "foldl": [
+        0,
+        1,
+        2,
+        3
+    ],
+    "foldr": [
+        1,
+        2,
+        3,
+        4
+    ],
+    "format": "test blah 42",
+    "get": [
+        17,
+        42,
+        18,
+        42
+    ],
+    "isArray": true,
+    "isBoolean": true,
+    "isFunction": true,
+    "isNumber": true,
+    "isObject": true,
+    "isString": true,
+    "join": "a,b,c",
+    "length": 0,
+    "lines": "a\nb\nc\n",
+    "log": 1.6094379124341003,
+    "lstripChars": "bbbbcccc",
+    "makeArray": [
+        0,
+        1,
+        2,
+        3,
+        4
+    ],
+    "manifestIni": "a = 1\nb = 2\n[s1]\nx = 1\ny = 2\n",
+    "manifestJsonEx": "{\n \"a\": {\n  \"b\": \"c\"\n }\n}",
+    "manifestJsonMinified": "{\"a\":{\"b\":\"c\"}}",
+    "manifestPython": "{\"a\": {\"b\": \"c\"}}",
+    "manifestPythonVars": "a = {\"b\": \"c\"}\n",
+    "manifestTomlEx": "[a]\n b = \"c\"",
+    "manifestXmlJsonml": "<blah a=\"42\"></blah>",
+    "manifestYamlDoc": "\"a\":\n  \"b\": \"c\"",
+    "manifestYamlStream": "---\n42\n---\n\"a\":\n  \"b\": \"c\"\n...\n",
+    "mantissa": 0.6249999999999999,
+    "map": [
+        -1,
+        -2,
+        -3
+    ],
+    "mapWithIndex": [
+        3,
+        3,
+        3
+    ],
+    "mapWithKey": {
+        "a": 42
+    },
+    "max": 3,
+    "md5": "1bc29b36f623ba82aaf6724fd3b16718",
+    "member": true,
+    "mergePatch": { },
+    "min": 2,
+    "objectFields": [ ],
+    "objectFieldsAll": [ ],
+    "objectHas": false,
+    "objectHasAll": false,
+    "objectKeysValues": [ ],
+    "objectKeysValuesAll": [ ],
+    "objectValues": [ ],
+    "objectValuesAll": [ ],
+    "parseHex": 3735928559,
+    "parseInt": 42,
+    "parseJson": {
+        "a": "b"
+    },
+    "parseOctal": 83,
+    "pow": 8,
+    "prune": {
+        "y": [
+            "42"
+        ]
+    },
+    "range": [
+        1,
+        2,
+        3,
+        4,
+        5
+    ],
+    "repeat": "foofoofoo",
+    "reverse": [
+        "a",
+        "b"
+    ],
+    "rstripChars": "aaabbbb",
+    "set": [
+        [
+            1,
+            2,
+            3
+        ],
+        [
+            3,
+            2,
+            1
+        ]
+    ],
+    "setDiff": [
+        [
+            1,
+            2
+        ],
+        [
+            1,
+            3
+        ]
+    ],
+    "setInter": [
+        [
+            3
+        ],
+        [
+            2
+        ]
+    ],
+    "setMember": [
+        false,
+        true
+    ],
+    "setUnion": [
+        [
+            1,
+            2,
+            3,
+            4,
+            5
+        ],
+        [
+            1,
+            2,
+            3,
+            4,
+            5
+        ]
+    ],
+    "sign": 1,
+    "sin": -0.9589242746631385,
+    "slice": "o",
+    "sort": [
+        [
+            1,
+            2,
+            3
+        ],
+        [
+            3,
+            2,
+            1
+        ]
+    ],
+    "split": [
+        "a",
+        "b",
+        "c"
+    ],
+    "splitLimit": [
+        "a",
+        "b,c"
+    ],
+    "splitLimitR": [
+        "a,b",
+        "c"
+    ],
+    "sqrt": 2.23606797749979,
+    "startsWith": true,
+    "strReplace": "bba",
+    "stringChars": [
+        "b",
+        "l",
+        "a",
+        "h"
+    ],
+    "stripChars": "bbbb",
+    "substr": "s",
+    "tan": -3.380515006246586,
+    "thisFile": "stdlib_smoke_test.jsonnet",
+    "toString": "42",
+    "type": "object",
+    "uniq": [
+        [
+            1,
+            2,
+            3
+        ],
+        [
+            "a",
+            "B",
+            "a"
+        ]
+    ]
+}
\ No newline at end of file
addedtests/go_testdata_golden_override/strReplace3.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/strReplace3.jsonnet.golden
@@ -0,0 +1,2 @@
+runtime error: 'from' string must not be zero length
+    strReplace3.jsonnet:1:1-36: function <builtin_str_replace> call
\ No newline at end of file
addedtests/go_testdata_golden_override/string_divided_by_number.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/string_divided_by_number.jsonnet.golden
@@ -0,0 +1 @@
+binary operation string / number is not implemented
\ No newline at end of file
addedtests/go_testdata_golden_override/string_index_negative.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/string_index_negative.jsonnet.golden
@@ -0,0 +1 @@
+array out of bounds: -1 is not within [0,4)
\ No newline at end of file
addedtests/go_testdata_golden_override/string_index_out_of_bounds.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/string_index_out_of_bounds.jsonnet.golden
@@ -0,0 +1 @@
+string out of bounds: 4 is not within [0,4)
\ No newline at end of file
addedtests/go_testdata_golden_override/string_minus_number.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/string_minus_number.jsonnet.golden
@@ -0,0 +1 @@
+binary operation string - number is not implemented
\ No newline at end of file
addedtests/go_testdata_golden_override/string_plus_function.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/string_plus_function.jsonnet.golden
@@ -0,0 +1 @@
+runtime error: tried to manifest function
\ No newline at end of file
addedtests/go_testdata_golden_override/supersugar8.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/supersugar8.jsonnet.golden
@@ -0,0 +1,2 @@
+assert failed: null
+    supersugar8.jsonnet:1:10-17: assertion failure
\ No newline at end of file
addedtests/go_testdata_golden_override/syntax_error.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/syntax_error.jsonnet.golden
@@ -0,0 +1,2 @@
+syntax error: expected one of "(", "[", "{", <identifier>, <number>, <string>, <unary op>, ['"'], ['\''], got "EOF"
+    syntax_error.jsonnet:1:5
\ No newline at end of file
addedtests/go_testdata_golden_override/tailstrict2.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/tailstrict2.jsonnet.golden
@@ -0,0 +1,3 @@
+runtime error: xxx
+    tailstrict2.jsonnet:1:13-21: error statement
+    tailstrict2.jsonnet:2:14-19: function <e> call
\ No newline at end of file
addedtests/go_testdata_golden_override/too_many_arguments.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/too_many_arguments.jsonnet.golden
@@ -0,0 +1,3 @@
+too many args, function has 3
+Function has the following signature: (x, y, z)
+    too_many_arguments.jsonnet:1:1-36: function <anonymous> call
\ No newline at end of file
addedtests/go_testdata_golden_override/type_error.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/type_error.jsonnet.golden
@@ -0,0 +1,4 @@
+runtime error: xxx
+    type_error.jsonnet:1:10-22: error statement
+    argument <x> evaluation
+    type_error.jsonnet:1:1-23:  function <builtin_type> call
\ No newline at end of file
addedtests/go_testdata_golden_override/unary_minus4.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/unary_minus4.jsonnet.golden
@@ -0,0 +1 @@
+operator - does not operate on type string
\ No newline at end of file
addedtests/go_testdata_golden_override/unary_object.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/unary_object.jsonnet.golden
@@ -0,0 +1 @@
+operator + does not operate on type object
\ No newline at end of file
addedtests/go_testdata_golden_override/unfinished_args.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/unfinished_args.jsonnet.golden
@@ -0,0 +1,2 @@
+syntax error: expected one of "(", ")", ".", "?", "[", "{", <binary op>, got "EOF"
+    unfinished_args.jsonnet:1:18
\ No newline at end of file
addedtests/go_testdata_golden_override/variable_not_visible.jsonnet.goldendiffbeforeafterboth
--- /dev/null
+++ b/tests/go_testdata_golden_override/variable_not_visible.jsonnet.golden
@@ -0,0 +1,3 @@
+local is not defined: nested
+    variable_not_visible.jsonnet:1:44-51: local <nested> access
+    variable_not_visible.jsonnet:1:52-55: local <x2> access
\ No newline at end of file
modifiedtests/tests/cpp_test_suite.rsdiffbeforeafterboth
--- a/tests/tests/cpp_test_suite.rs
+++ b/tests/tests/cpp_test_suite.rs
@@ -183,6 +183,15 @@
 	"number_leading_zero.jsonnet",
 	// Jrsonnet has this overload
 	"number_times_string.jsonnet",
+	// Jrsonnet has stricter implementations, this is a dumb thing that the filter value might not be
+	// evaluated anyway...
+	"std.filter7.jsonnet",
+	// Golang fails with max stack frames exceeded error
+	"std.makeArray_recursive_evalutation_order_matters.jsonnet",
+	// Jrsonnet has this overload
+	"string_times_number.jsonnet",
+	// Tailstrict semantics is partially unspecified
+	"tailstrict3.jsonnet",
 ];
 
 #[test]
@@ -244,17 +253,21 @@
 				"expected error for golden {}:\n<got>\n{result}\n</got>\n<golden>\n{golden}\n</golden>",
 				entry.path().display()
 			),
-			(Ok(result), Ok(golden)) => {
+			(Ok(result_v), Ok(golden)) => {
 				// Show diff relative to golden`.
-				let diff = JsonDiff::diff_string(&golden, &result, false);
+				let diff = JsonDiff::diff_string(&golden, &result_v, false);
 				if let Some(diff) = diff {
-					panic!(
-						"Result \n{result:#}\n\
-							and golden \n{golden:#}\n\
-							did not match structurally:\n{diff:#}\n\
-							for golden {}",
-						entry.path().display()
-					);
+					if env::var_os("UPDATE_GOLDEN").is_some() {
+						fs::write(golden_override, result)?;
+					} else {
+						panic!(
+							"Result \n{result_v:#}\n\
+								and golden \n{golden:#}\n\
+								did not match structurally:\n{diff:#}\n\
+								for golden {}",
+							entry.path().display()
+						);
+					}
 				}
 			}
 			(Err(_), Err(_)) => {