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
--- a/crates/jrsonnet-stdlib/src/sort.rs
+++ b/crates/jrsonnet-stdlib/src/sort.rs
@@ -17,6 +17,7 @@
 enum SortKeyType {
 	Number,
 	String,
+	Unspecialized,
 	Unknown,
 }
 
@@ -31,7 +32,7 @@
 			(Val::Str(_) | Val::Num(_), _) => {
 				bail!("sort elements should have the same types")
 			}
-			_ => {}
+			(_, _) => return Ok(SortKeyType::Unspecialized),
 		}
 	}
 	Ok(sort_type)
@@ -49,7 +50,7 @@
 			Val::Str(s) => s.clone(),
 			_ => unreachable!(),
 		}),
-		SortKeyType::Unknown => {
+		SortKeyType::Unknown | SortKeyType::Unspecialized => {
 			let mut err = None;
 			// evaluate_compare_op will never return equal on types, which are different from
 			// jsonnet perspective
@@ -88,7 +89,7 @@
 			Val::Str(s) => s.clone(),
 			_ => unreachable!(),
 		}),
-		SortKeyType::Unknown => {
+		SortKeyType::Unknown | SortKeyType::Unspecialized => {
 			let mut err = None;
 			// evaluate_compare_op will never return equal on types, which are different from
 			// jsonnet perspective
modifiedcrates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth
before · crates/jrsonnet-stdlib/src/strings.rs
1use std::collections::BTreeSet;23use jrsonnet_evaluator::{4	bail,5	error::{ErrorKind::*, Result},6	function::builtin,7	typed::{Either2, Typed, M1},8	val::{ArrValue, IndexableVal},9	Either, IStr, Val,10};1112#[builtin]13pub const fn builtin_codepoint(str: char) -> u32 {14	str as u3215}1617#[builtin]18pub fn builtin_substr(str: IStr, from: usize, len: usize) -> String {19	str.chars().skip(from).take(len).collect()20}2122#[builtin]23pub fn builtin_char(n: u32) -> Result<char> {24	Ok(std::char::from_u32(n).ok_or_else(|| InvalidUnicodeCodepointGot(n))?)25}2627#[builtin]28pub fn builtin_str_replace(str: String, from: IStr, to: IStr) -> String {29	str.replace(&from as &str, &to as &str)30}3132#[builtin]33pub fn builtin_escape_string_bash(str_: String) -> String {34	const QUOTE: char = '\'';35	let mut out = str_.replace(QUOTE, "'\"'\"'");36	out.insert(0, QUOTE);37	out.push(QUOTE);38	out39}4041#[builtin]42pub fn builtin_escape_string_dollars(str_: String) -> String {43	str_.replace('$', "$$")44}4546#[builtin]47pub fn builtin_is_empty(str: String) -> bool {48	str.is_empty()49}5051#[builtin]52pub fn builtin_equals_ignore_case(str1: String, str2: String) -> bool {53	str1.to_ascii_lowercase() == str2.to_ascii_lowercase()54}5556#[builtin]57pub fn builtin_splitlimit(str: IStr, c: IStr, maxsplits: Either![usize, M1]) -> ArrValue {58	use Either2::*;59	match maxsplits {60		A(n) => str.splitn(n + 1, &c as &str).map(Val::string).collect(),61		B(_) => str.split(&c as &str).map(Val::string).collect(),62	}63}6465#[builtin]66pub fn builtin_splitlimitr(str: IStr, c: IStr, maxsplits: Either![usize, M1]) -> ArrValue {67	use Either2::*;68	match maxsplits {69		A(n) =>70		// rsplitn does not implement DoubleEndedIterator so collect into71		// a temporary vec72		{73			str.rsplitn(n + 1, &c as &str)74				.map(Val::string)75				.collect::<Vec<_>>()76				.into_iter()77				.rev()78				.collect()79		}80		B(_) => str.split(&c as &str).map(Val::string).collect(),81	}82}8384#[builtin]85pub fn builtin_split(str: IStr, c: IStr) -> ArrValue {86	use Either2::*;87	builtin_splitlimit(str, c, B(M1))88}8990#[builtin]91pub fn builtin_ascii_upper(str: IStr) -> String {92	str.to_ascii_uppercase()93}9495#[builtin]96pub fn builtin_ascii_lower(str: IStr) -> String {97	str.to_ascii_lowercase()98}99100#[builtin]101pub fn builtin_find_substr(pat: IStr, str: IStr) -> ArrValue {102	if pat.is_empty() || str.is_empty() || pat.len() > str.len() {103		return ArrValue::empty();104	}105106	let str = str.as_str();107	let pat = pat.as_bytes();108	let strb = str.as_bytes();109110	let max_pos = str.len() - pat.len();111112	let mut out: Vec<Val> = Vec::new();113	for (ch_idx, (i, _)) in str114		.char_indices()115		.take_while(|(i, _)| i <= &max_pos)116		.enumerate()117	{118		if &strb[i..i + pat.len()] == pat {119			out.push(Val::Num(120				ch_idx.try_into().expect("unrealisticly long string"),121			));122		}123	}124	out.into()125}126127#[builtin]128pub fn builtin_parse_int(str: IStr) -> Result<f64> {129	if let Some(raw) = str.strip_prefix('-') {130		if raw.is_empty() {131			bail!("integer only consists of a minus")132		}133134		parse_nat::<10>(raw).map(|value| -value)135	} else {136		if str.is_empty() {137			bail!("empty integer")138		}139140		parse_nat::<10>(str.as_str())141	}142}143144#[builtin]145pub fn builtin_parse_octal(str: IStr) -> Result<f64> {146	if str.is_empty() {147		bail!("empty octal integer");148	}149150	parse_nat::<8>(str.as_str())151}152153#[builtin]154pub fn builtin_parse_hex(str: IStr) -> Result<f64> {155	if str.is_empty() {156		bail!("empty hexadecimal integer");157	}158159	parse_nat::<16>(str.as_str())160}161162fn parse_nat<const BASE: u32>(raw: &str) -> Result<f64> {163	const ZERO_CODE: u32 = '0' as u32;164	const UPPER_A_CODE: u32 = 'A' as u32;165	const LOWER_A_CODE: u32 = 'a' as u32;166167	#[inline]168	fn checked_sub_if(condition: bool, lhs: u32, rhs: u32) -> Option<u32> {169		if condition {170			lhs.checked_sub(rhs)171		} else {172			None173		}174	}175176	debug_assert!(177		1 <= BASE && BASE <= 16,178		"integer base should be between 1 and 16"179	);180181	let base = f64::from(BASE);182183	raw.chars().try_fold(0f64, |aggregate, digit| {184		let digit = digit as u32;185		// if-let-else looks better here than Option combinators186		#[allow(clippy::option_if_let_else)]187		let digit = if let Some(digit) = checked_sub_if(BASE > 10, digit, LOWER_A_CODE) {188			digit + 10189		} else if let Some(digit) = checked_sub_if(BASE > 10, digit, UPPER_A_CODE) {190			digit + 10191		} else {192			digit.checked_sub(ZERO_CODE).unwrap_or(BASE)193		};194195		if digit < BASE {196			Ok(base.mul_add(aggregate, f64::from(digit)))197		} else {198			bail!("{raw:?} is not a base {BASE} integer");199		}200	})201}202203#[cfg(feature = "exp-bigint")]204#[builtin]205pub fn builtin_bigint(v: Either![f64, IStr]) -> Result<Val> {206	use jrsonnet_evaluator::runtime_error;207	use Either2::*;208	Ok(match v {209		A(a) => {210			Val::BigInt(Box::new(a.to_string().parse().map_err(|e| {211				runtime_error!("number is not convertible to bigint: {e}")212			})?))213		}214		B(b) => Val::BigInt(Box::new(215			b.as_str()216				.parse()217				.map_err(|e| runtime_error!("bad bigint: {e}"))?,218		)),219	})220}221222#[builtin]223pub fn builtin_string_chars(str: IStr) -> ArrValue {224	ArrValue::chars(str.chars())225}226227#[builtin]228pub fn builtin_lstrip_chars(str: IStr, chars: IndexableVal) -> Result<IStr> {229	if str.is_empty() || chars.is_empty() {230		return Ok(str);231	}232233	let pattern = new_trim_pattern(chars)?;234	Ok(str.as_str().trim_start_matches(pattern).into())235}236237#[builtin]238pub fn builtin_rstrip_chars(str: IStr, chars: IndexableVal) -> Result<IStr> {239	if str.is_empty() || chars.is_empty() {240		return Ok(str);241	}242243	let pattern = new_trim_pattern(chars)?;244	Ok(str.as_str().trim_end_matches(pattern).into())245}246247#[builtin]248pub fn builtin_strip_chars(str: IStr, chars: IndexableVal) -> Result<IStr> {249	if str.is_empty() || chars.is_empty() {250		return Ok(str);251	}252253	let pattern = new_trim_pattern(chars)?;254	Ok(str.as_str().trim_matches(pattern).into())255}256257#[builtin]258pub fn builtin_trim(str: IStr) -> String {259	let filter =260		|v: char| {261			v == ' '262				|| v == '\t' || v == '\n'263				|| v == '\u{000c}'264				|| v == '\r' || v == '\u{0085}'265				|| v == '\u{00a0}'266		};267	str.as_str().trim_matches(filter).to_string()268}269270fn new_trim_pattern(chars: IndexableVal) -> Result<impl Fn(char) -> bool> {271	let chars: BTreeSet<char> = match chars {272		IndexableVal::Str(chars) => chars.chars().collect(),273		IndexableVal::Arr(chars) => chars274			.iter()275			.filter_map(|it| it.map(|it| char::from_untyped(it).ok()).transpose())276			.collect::<Result<_, _>>()?,277	};278279	Ok(move |char| chars.contains(&char))280}281282#[cfg(test)]283#[allow(clippy::float_cmp)]284mod tests {285	use super::*;286287	#[test]288	fn parse_nat_base_8() {289		assert_eq!(parse_nat::<8>("0").unwrap(), 0.);290		assert_eq!(parse_nat::<8>("5").unwrap(), 5.);291		assert_eq!(parse_nat::<8>("32").unwrap(), f64::from(0o32));292		assert_eq!(parse_nat::<8>("761").unwrap(), f64::from(0o761));293	}294295	#[test]296	fn parse_nat_base_10() {297		assert_eq!(parse_nat::<10>("0").unwrap(), 0.);298		assert_eq!(parse_nat::<10>("3").unwrap(), 3.);299		assert_eq!(parse_nat::<10>("27").unwrap(), 27.);300		assert_eq!(parse_nat::<10>("123").unwrap(), 123.);301	}302303	#[test]304	fn parse_nat_base_16() {305		assert_eq!(parse_nat::<16>("0").unwrap(), 0.);306		assert_eq!(parse_nat::<16>("A").unwrap(), 10.);307		assert_eq!(parse_nat::<16>("a9").unwrap(), f64::from(0xA9));308		assert_eq!(parse_nat::<16>("BbC").unwrap(), f64::from(0xBBC));309	}310}
after · crates/jrsonnet-stdlib/src/strings.rs
1use std::collections::BTreeSet;23use jrsonnet_evaluator::{4	bail,5	error::{ErrorKind::*, Result},6	function::builtin,7	typed::{Either2, Typed, M1},8	val::{ArrValue, IndexableVal},9	Either, IStr, Val,10};1112#[builtin]13pub const fn builtin_codepoint(str: char) -> u32 {14	str as u3215}1617#[builtin]18pub fn builtin_substr(str: IStr, from: usize, len: usize) -> String {19	str.chars().skip(from).take(len).collect()20}2122#[builtin]23pub fn builtin_char(n: u32) -> Result<char> {24	Ok(std::char::from_u32(n).ok_or_else(|| InvalidUnicodeCodepointGot(n))?)25}2627#[builtin]28pub fn builtin_str_replace(str: String, from: IStr, to: IStr) -> Result<String> {29	if from.is_empty() {30		bail!("'from' string must not be zero length");31	}32	Ok(str.replace(&from as &str, &to as &str))33}3435#[builtin]36pub fn builtin_escape_string_bash(str_: String) -> String {37	const QUOTE: char = '\'';38	let mut out = str_.replace(QUOTE, "'\"'\"'");39	out.insert(0, QUOTE);40	out.push(QUOTE);41	out42}4344#[builtin]45pub fn builtin_escape_string_dollars(str_: String) -> String {46	str_.replace('$', "$$")47}4849#[builtin]50pub fn builtin_is_empty(str: String) -> bool {51	str.is_empty()52}5354#[builtin]55pub fn builtin_equals_ignore_case(str1: String, str2: String) -> bool {56	str1.to_ascii_lowercase() == str2.to_ascii_lowercase()57}5859#[builtin]60pub fn builtin_splitlimit(str: IStr, c: IStr, maxsplits: Either![usize, M1]) -> ArrValue {61	use Either2::*;62	match maxsplits {63		A(n) => str.splitn(n + 1, &c as &str).map(Val::string).collect(),64		B(_) => str.split(&c as &str).map(Val::string).collect(),65	}66}6768#[builtin]69pub fn builtin_splitlimitr(str: IStr, c: IStr, maxsplits: Either![usize, M1]) -> ArrValue {70	use Either2::*;71	match maxsplits {72		A(n) =>73		// rsplitn does not implement DoubleEndedIterator so collect into74		// a temporary vec75		{76			str.rsplitn(n + 1, &c as &str)77				.map(Val::string)78				.collect::<Vec<_>>()79				.into_iter()80				.rev()81				.collect()82		}83		B(_) => str.split(&c as &str).map(Val::string).collect(),84	}85}8687#[builtin]88pub fn builtin_split(str: IStr, c: IStr) -> ArrValue {89	use Either2::*;90	builtin_splitlimit(str, c, B(M1))91}9293#[builtin]94pub fn builtin_ascii_upper(str: IStr) -> String {95	str.to_ascii_uppercase()96}9798#[builtin]99pub fn builtin_ascii_lower(str: IStr) -> String {100	str.to_ascii_lowercase()101}102103#[builtin]104pub fn builtin_find_substr(pat: IStr, str: IStr) -> ArrValue {105	if pat.is_empty() || str.is_empty() || pat.len() > str.len() {106		return ArrValue::empty();107	}108109	let str = str.as_str();110	let pat = pat.as_bytes();111	let strb = str.as_bytes();112113	let max_pos = str.len() - pat.len();114115	let mut out: Vec<Val> = Vec::new();116	for (ch_idx, (i, _)) in str117		.char_indices()118		.take_while(|(i, _)| i <= &max_pos)119		.enumerate()120	{121		if &strb[i..i + pat.len()] == pat {122			out.push(Val::Num(123				ch_idx.try_into().expect("unrealisticly long string"),124			));125		}126	}127	out.into()128}129130#[builtin]131pub fn builtin_parse_int(str: IStr) -> Result<f64> {132	if let Some(raw) = str.strip_prefix('-') {133		if raw.is_empty() {134			bail!("integer only consists of a minus")135		}136137		parse_nat::<10>(raw).map(|value| -value)138	} else {139		if str.is_empty() {140			bail!("empty integer")141		}142143		parse_nat::<10>(str.as_str())144	}145}146147#[builtin]148pub fn builtin_parse_octal(str: IStr) -> Result<f64> {149	if str.is_empty() {150		bail!("empty octal integer");151	}152153	parse_nat::<8>(str.as_str())154}155156#[builtin]157pub fn builtin_parse_hex(str: IStr) -> Result<f64> {158	if str.is_empty() {159		bail!("empty hexadecimal integer");160	}161162	parse_nat::<16>(str.as_str())163}164165fn parse_nat<const BASE: u32>(raw: &str) -> Result<f64> {166	const ZERO_CODE: u32 = '0' as u32;167	const UPPER_A_CODE: u32 = 'A' as u32;168	const LOWER_A_CODE: u32 = 'a' as u32;169170	#[inline]171	fn checked_sub_if(condition: bool, lhs: u32, rhs: u32) -> Option<u32> {172		if condition {173			lhs.checked_sub(rhs)174		} else {175			None176		}177	}178179	debug_assert!(180		1 <= BASE && BASE <= 16,181		"integer base should be between 1 and 16"182	);183184	let base = f64::from(BASE);185186	raw.chars().try_fold(0f64, |aggregate, digit| {187		let digit = digit as u32;188		// if-let-else looks better here than Option combinators189		#[allow(clippy::option_if_let_else)]190		let digit = if let Some(digit) = checked_sub_if(BASE > 10, digit, LOWER_A_CODE) {191			digit + 10192		} else if let Some(digit) = checked_sub_if(BASE > 10, digit, UPPER_A_CODE) {193			digit + 10194		} else {195			digit.checked_sub(ZERO_CODE).unwrap_or(BASE)196		};197198		if digit < BASE {199			Ok(base.mul_add(aggregate, f64::from(digit)))200		} else {201			bail!("{raw:?} is not a base {BASE} integer");202		}203	})204}205206#[cfg(feature = "exp-bigint")]207#[builtin]208pub fn builtin_bigint(v: Either![f64, IStr]) -> Result<Val> {209	use jrsonnet_evaluator::runtime_error;210	use Either2::*;211	Ok(match v {212		A(a) => {213			Val::BigInt(Box::new(a.to_string().parse().map_err(|e| {214				runtime_error!("number is not convertible to bigint: {e}")215			})?))216		}217		B(b) => Val::BigInt(Box::new(218			b.as_str()219				.parse()220				.map_err(|e| runtime_error!("bad bigint: {e}"))?,221		)),222	})223}224225#[builtin]226pub fn builtin_string_chars(str: IStr) -> ArrValue {227	ArrValue::chars(str.chars())228}229230#[builtin]231pub fn builtin_lstrip_chars(str: IStr, chars: IndexableVal) -> Result<IStr> {232	if str.is_empty() || chars.is_empty() {233		return Ok(str);234	}235236	let pattern = new_trim_pattern(chars)?;237	Ok(str.as_str().trim_start_matches(pattern).into())238}239240#[builtin]241pub fn builtin_rstrip_chars(str: IStr, chars: IndexableVal) -> Result<IStr> {242	if str.is_empty() || chars.is_empty() {243		return Ok(str);244	}245246	let pattern = new_trim_pattern(chars)?;247	Ok(str.as_str().trim_end_matches(pattern).into())248}249250#[builtin]251pub fn builtin_strip_chars(str: IStr, chars: IndexableVal) -> Result<IStr> {252	if str.is_empty() || chars.is_empty() {253		return Ok(str);254	}255256	let pattern = new_trim_pattern(chars)?;257	Ok(str.as_str().trim_matches(pattern).into())258}259260#[builtin]261pub fn builtin_trim(str: IStr) -> String {262	let filter =263		|v: char| {264			v == ' '265				|| v == '\t' || v == '\n'266				|| v == '\u{000c}'267				|| v == '\r' || v == '\u{0085}'268				|| v == '\u{00a0}'269		};270	str.as_str().trim_matches(filter).to_string()271}272273fn new_trim_pattern(chars: IndexableVal) -> Result<impl Fn(char) -> bool> {274	let chars: BTreeSet<char> = match chars {275		IndexableVal::Str(chars) => chars.chars().collect(),276		IndexableVal::Arr(chars) => chars277			.iter()278			.filter_map(|it| it.map(|it| char::from_untyped(it).ok()).transpose())279			.collect::<Result<_, _>>()?,280	};281282	Ok(move |char| chars.contains(&char))283}284285#[cfg(test)]286#[allow(clippy::float_cmp)]287mod tests {288	use super::*;289290	#[test]291	fn parse_nat_base_8() {292		assert_eq!(parse_nat::<8>("0").unwrap(), 0.);293		assert_eq!(parse_nat::<8>("5").unwrap(), 5.);294		assert_eq!(parse_nat::<8>("32").unwrap(), f64::from(0o32));295		assert_eq!(parse_nat::<8>("761").unwrap(), f64::from(0o761));296	}297298	#[test]299	fn parse_nat_base_10() {300		assert_eq!(parse_nat::<10>("0").unwrap(), 0.);301		assert_eq!(parse_nat::<10>("3").unwrap(), 3.);302		assert_eq!(parse_nat::<10>("27").unwrap(), 27.);303		assert_eq!(parse_nat::<10>("123").unwrap(), 123.);304	}305306	#[test]307	fn parse_nat_base_16() {308		assert_eq!(parse_nat::<16>("0").unwrap(), 0.);309		assert_eq!(parse_nat::<16>("A").unwrap(), 10.);310		assert_eq!(parse_nat::<16>("a9").unwrap(), f64::from(0xA9));311		assert_eq!(parse_nat::<16>("BbC").unwrap(), f64::from(0xBBC));312	}313}
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(_)) => {