git.delta.rocks / jrsonnet / refs/commits / a9100abdce9b

difftreelog

refactor fix clippy warnings

Yaroslav Bolyukin2024-04-07parent: #d349b9e.patch.diff
in: master

27 files changed

modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -150,6 +150,9 @@
 redundant_pub_crate = "allow"
 # Sometimes code is fancier without that
 manual_let_else = "allow"
+# Something is broken about that lint, can't be allowed for
+# codegenerated-stdlib block
+similar_names = "allow"
 
 #[profile.test]
 #opt-level = 1
modifiedcmds/jrsonnet-fmt/src/tests.rsdiffbeforeafterboth
--- a/cmds/jrsonnet-fmt/src/tests.rs
+++ b/cmds/jrsonnet-fmt/src/tests.rs
@@ -1,4 +1,4 @@
-use dprint_core::formatting::{PrintOptions, PrintItems};
+use dprint_core::formatting::{PrintItems, PrintOptions};
 use indoc::indoc;
 
 use crate::Printable;
modifiedcmds/jrsonnet/src/main.rsdiffbeforeafterboth
--- a/cmds/jrsonnet/src/main.rs
+++ b/cmds/jrsonnet/src/main.rs
@@ -153,7 +153,7 @@
 		if let Error::Evaluation(e) = e {
 			let mut out = String::new();
 			trace.write_trace(&mut out, &e).expect("format error");
-			eprintln!("{out}")
+			eprintln!("{out}");
 		} else {
 			eprintln!("{e}");
 		}
modifiedcrates/jrsonnet-cli/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/lib.rs
+++ b/crates/jrsonnet-cli/src/lib.rs
@@ -10,7 +10,7 @@
 	stack::{limit_stack_depth, StackDepthLimitOverrideGuard},
 	FileImportResolver,
 };
-use jrsonnet_gcmodule::with_thread_object_space;
+use jrsonnet_gcmodule::{with_thread_object_space, ObjectSpace};
 pub use manifest::*;
 pub use stdlib::*;
 pub use tla::*;
@@ -88,7 +88,7 @@
 
 impl Drop for LeakSpace {
 	fn drop(&mut self) {
-		with_thread_object_space(|s| s.leak())
+		with_thread_object_space(ObjectSpace::leak);
 	}
 }
 
@@ -102,6 +102,6 @@
 			let collected = jrsonnet_gcmodule::collect_thread_cycles();
 			eprintln!("Collected: {collected}");
 		}
-		eprintln!("Tracked: {}", jrsonnet_gcmodule::count_thread_tracked())
+		eprintln!("Tracked: {}", jrsonnet_gcmodule::count_thread_tracked());
 	}
 }
modifiedcrates/jrsonnet-cli/src/stdlib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-cli/src/stdlib.rs
+++ b/crates/jrsonnet-cli/src/stdlib.rs
@@ -39,11 +39,11 @@
 
 	fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
 		match s.find('=') {
-			Some(idx) => Ok(ExtStr {
+			Some(idx) => Ok(Self {
 				name: s[..idx].to_owned(),
 				value: s[idx + 1..].to_owned(),
 			}),
-			None => Ok(ExtStr {
+			None => Ok(Self {
 				name: s.to_owned(),
 				value: std::env::var(s).or(Err("missing env var"))?,
 			}),
@@ -109,16 +109,16 @@
 			return Ok(None);
 		}
 		let ctx = ContextInitializer::new(s.clone(), PathResolver::new_cwd_fallback());
-		for ext in self.ext_str.iter() {
+		for ext in &self.ext_str {
 			ctx.add_ext_str((&ext.name as &str).into(), (&ext.value as &str).into());
 		}
-		for ext in self.ext_str_file.iter() {
+		for ext in &self.ext_str_file {
 			ctx.add_ext_str((&ext.name as &str).into(), (&ext.value as &str).into());
 		}
-		for ext in self.ext_code.iter() {
+		for ext in &self.ext_code {
 			ctx.add_ext_code(&ext.name as &str, &ext.value as &str)?;
 		}
-		for ext in self.ext_code_file.iter() {
+		for ext in &self.ext_code_file {
 			ctx.add_ext_code(&ext.name as &str, &ext.value as &str)?;
 		}
 		Ok(Some(ctx))
modifiedcrates/jrsonnet-evaluator/src/arr/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/arr/mod.rs
+++ b/crates/jrsonnet-evaluator/src/arr/mod.rs
@@ -42,7 +42,7 @@
 		Self::new(EagerArray(values))
 	}
 
-	pub fn repeated(data: ArrValue, repeats: usize) -> Option<Self> {
+	pub fn repeated(data: Self, repeats: usize) -> Option<Self> {
 		Some(Self::new(RepeatedArray::new(data, repeats)?))
 	}
 
@@ -70,7 +70,7 @@
 		Ok(Self::eager(out))
 	}
 
-	pub fn extended(a: ArrValue, b: ArrValue) -> Self {
+	pub fn extended(a: Self, b: Self) -> Self {
 		// TODO: benchmark for an optimal value, currently just a arbitrary choice
 		const ARR_EXTEND_THRESHOLD: usize = 100;
 
modifiedcrates/jrsonnet-evaluator/src/function/arglike.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/arglike.rs
+++ b/crates/jrsonnet-evaluator/src/function/arglike.rs
@@ -61,8 +61,8 @@
 impl ArgLike for TlaArg {
 	fn evaluate_arg(&self, ctx: Context, tailstrict: bool) -> Result<Thunk<Val>> {
 		match self {
-			TlaArg::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),
-			TlaArg::Code(code) => Ok(if tailstrict {
+			Self::String(s) => Ok(Thunk::evaluated(Val::string(s.clone()))),
+			Self::Code(code) => Ok(if tailstrict {
 				Thunk::evaluated(evaluate(ctx, code)?)
 			} else {
 				Thunk::new(EvaluateThunk {
@@ -70,8 +70,8 @@
 					expr: code.clone(),
 				})
 			}),
-			TlaArg::Val(val) => Ok(Thunk::evaluated(val.clone())),
-			TlaArg::Lazy(lazy) => Ok(lazy.clone()),
+			Self::Val(val) => Ok(Thunk::evaluated(val.clone())),
+			Self::Lazy(lazy) => Ok(lazy.clone()),
 		}
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/function/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/mod.rs
+++ b/crates/jrsonnet-evaluator/src/function/mod.rs
@@ -237,7 +237,7 @@
 
 	pub fn evaluate_trivial(&self) -> Option<Val> {
 		match self {
-			FuncVal::Normal(n) => n.evaluate_trivial(),
+			Self::Normal(n) => n.evaluate_trivial(),
 			_ => None,
 		}
 	}
modifiedcrates/jrsonnet-evaluator/src/import.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/import.rs
+++ b/crates/jrsonnet-evaluator/src/import.rs
@@ -10,7 +10,7 @@
 use fs::File;
 use jrsonnet_gcmodule::Trace;
 use jrsonnet_interner::IBytes;
-use jrsonnet_parser::{SourceDirectory, SourceFile, SourcePath, SourceFifo};
+use jrsonnet_parser::{SourceDirectory, SourceFifo, SourceFile, SourcePath};
 
 use crate::{
 	bail,
modifiedcrates/jrsonnet-evaluator/src/integrations/serde.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/integrations/serde.rs
+++ b/crates/jrsonnet-evaluator/src/integrations/serde.rs
@@ -15,7 +15,7 @@
 };
 
 impl<'de> Deserialize<'de> for Val {
-	fn deserialize<D>(deserializer: D) -> Result<Val, D::Error>
+	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
 	where
 		D: serde::Deserializer<'de>,
 	{
@@ -155,10 +155,10 @@
 		S: serde::Serializer,
 	{
 		match self {
-			Val::Bool(v) => serializer.serialize_bool(*v),
-			Val::Null => serializer.serialize_none(),
-			Val::Str(s) => serializer.serialize_str(&s.clone().into_flat()),
-			Val::Num(n) => {
+			Self::Bool(v) => serializer.serialize_bool(*v),
+			Self::Null => serializer.serialize_none(),
+			Self::Str(s) => serializer.serialize_str(&s.clone().into_flat()),
+			Self::Num(n) => {
 				if n.fract() == 0.0 {
 					let n = *n as i64;
 					serializer.serialize_i64(n)
@@ -167,8 +167,8 @@
 				}
 			}
 			#[cfg(feature = "exp-bigint")]
-			Val::BigInt(b) => b.serialize(serializer),
-			Val::Arr(arr) => {
+			Self::BigInt(b) => b.serialize(serializer),
+			Self::Arr(arr) => {
 				let mut seq = serializer.serialize_seq(Some(arr.len()))?;
 				for (i, element) in arr.iter().enumerate() {
 					let mut serde_error = None;
@@ -190,7 +190,7 @@
 				}
 				seq.end()
 			}
-			Val::Obj(obj) => {
+			Self::Obj(obj) => {
 				let mut map = serializer.serialize_map(Some(obj.len()))?;
 				for (field, value) in obj.iter(
 					#[cfg(feature = "exp-preserve-order")]
@@ -215,7 +215,7 @@
 				}
 				map.end()
 			}
-			Val::Func(_) => Err(S::Error::custom("tried to manifest function")),
+			Self::Func(_) => Err(S::Error::custom("tried to manifest function")),
 		}
 	}
 }
@@ -248,9 +248,9 @@
 	type Ok = Val;
 	type Error = JrError;
 
-	fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
+	fn serialize_element<T>(&mut self, value: &T) -> Result<()>
 	where
-		T: Serialize,
+		T: ?Sized + Serialize,
 	{
 		let value = value.serialize(IntoValSerializer)?;
 		self.data.push(value);
@@ -272,9 +272,9 @@
 	type Ok = Val;
 	type Error = JrError;
 
-	fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
+	fn serialize_element<T>(&mut self, value: &T) -> Result<()>
 	where
-		T: Serialize,
+		T: ?Sized + Serialize,
 	{
 		SerializeSeq::serialize_element(self, value)
 	}
@@ -287,9 +287,9 @@
 	type Ok = Val;
 	type Error = JrError;
 
-	fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
+	fn serialize_field<T>(&mut self, value: &T) -> Result<()>
 	where
-		T: Serialize,
+		T: ?Sized + Serialize,
 	{
 		SerializeSeq::serialize_element(self, value)
 	}
@@ -302,9 +302,9 @@
 	type Ok = Val;
 	type Error = JrError;
 
-	fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
+	fn serialize_field<T>(&mut self, value: &T) -> Result<()>
 	where
-		T: Serialize,
+		T: ?Sized + Serialize,
 	{
 		SerializeSeq::serialize_element(self, value)
 	}
@@ -607,7 +607,7 @@
 }
 
 impl Val {
-	pub fn from_serde(v: impl Serialize) -> Result<Val, JrError> {
+	pub fn from_serde(v: impl Serialize) -> Result<Self, JrError> {
 		v.serialize(IntoValSerializer)
 	}
 }
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -45,7 +45,7 @@
 #[doc(hidden)]
 pub use jrsonnet_macros;
 pub use jrsonnet_parser as parser;
-use jrsonnet_parser::*;
+use jrsonnet_parser::{ExprLocation, LocExpr, ParserSettings, Source, SourcePath};
 pub use obj::*;
 use stack::check_depth;
 pub use tla::apply_tla;
modifiedcrates/jrsonnet-evaluator/src/stack.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/stack.rs
+++ b/crates/jrsonnet-evaluator/src/stack.rs
@@ -24,7 +24,7 @@
 pub struct StackOverflowError;
 impl From<StackOverflowError> for ErrorKind {
 	fn from(_: StackOverflowError) -> Self {
-		ErrorKind::StackOverflow
+		Self::StackOverflow
 	}
 }
 impl From<StackOverflowError> for Error {
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -358,7 +358,7 @@
 		};
 		a.iter()
 			.map(|r| r.and_then(T::from_untyped))
-			.collect::<Result<Vec<T>>>()
+			.collect::<Result<Self>>()
 	}
 }
 
@@ -381,7 +381,7 @@
 		Self::TYPE.check(&value)?;
 		let obj = value.as_obj().expect("typecheck should fail");
 
-		let mut out = BTreeMap::new();
+		let mut out = Self::new();
 		if V::wants_lazy() {
 			for key in obj.fields_ex(
 				false,
@@ -623,8 +623,8 @@
 
 	fn into_untyped(value: Self) -> Result<Val> {
 		match value {
-			IndexableVal::Str(s) => Ok(Val::string(s)),
-			IndexableVal::Arr(a) => Ok(Val::Arr(a)),
+			Self::Str(s) => Ok(Val::string(s)),
+			Self::Arr(a) => Ok(Val::Arr(a)),
 		}
 	}
 
modifiedcrates/jrsonnet-evaluator/src/val.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/val.rs
1use std::{2	cell::RefCell,3	fmt::{self, Debug, Display},4	mem::replace,5	rc::Rc,6};78use jrsonnet_gcmodule::{Cc, Trace};9use jrsonnet_interner::IStr;10use jrsonnet_types::ValType;1112pub use crate::arr::{ArrValue, ArrayLike};13use crate::{14	bail,15	error::{Error, ErrorKind::*},16	function::FuncVal,17	gc::{GcHashMap, TraceBox},18	manifest::{ManifestFormat, ToStringFormat},19	tb,20	typed::BoundedUsize,21	ObjValue, Result, Unbound, WeakObjValue,22};2324pub trait ThunkValue: Trace {25	type Output;26	fn get(self: Box<Self>) -> Result<Self::Output>;27}2829#[derive(Trace)]30enum ThunkInner<T: Trace> {31	Computed(T),32	Errored(Error),33	Waiting(TraceBox<dyn ThunkValue<Output = T>>),34	Pending,35}3637/// Lazily evaluated value38#[allow(clippy::module_name_repetitions)]39#[derive(Clone, Trace)]40pub struct Thunk<T: Trace>(Cc<RefCell<ThunkInner<T>>>);4142impl<T: Trace> Thunk<T> {43	pub fn evaluated(val: T) -> Self {44		Self(Cc::new(RefCell::new(ThunkInner::Computed(val))))45	}46	pub fn new(f: impl ThunkValue<Output = T> + 'static) -> Self {47		Self(Cc::new(RefCell::new(ThunkInner::Waiting(tb!(f)))))48	}49	pub fn errored(e: Error) -> Self {50		Self(Cc::new(RefCell::new(ThunkInner::Errored(e))))51	}52	pub fn result(res: Result<T, Error>) -> Self {53		match res {54			Ok(o) => Self::evaluated(o),55			Err(e) => Self::errored(e),56		}57	}58}5960impl<T> Thunk<T>61where62	T: Clone + Trace,63{64	pub fn force(&self) -> Result<()> {65		self.evaluate()?;66		Ok(())67	}6869	/// Evaluate thunk, or return cached value70	///71	/// # Errors72	///73	/// - Lazy value evaluation returned error74	/// - This method was called during inner value evaluation75	pub fn evaluate(&self) -> Result<T> {76		match &*self.0.borrow() {77			ThunkInner::Computed(v) => return Ok(v.clone()),78			ThunkInner::Errored(e) => return Err(e.clone()),79			ThunkInner::Pending => return Err(InfiniteRecursionDetected.into()),80			ThunkInner::Waiting(..) => (),81		};82		let ThunkInner::Waiting(value) = replace(&mut *self.0.borrow_mut(), ThunkInner::Pending)83		else {84			unreachable!();85		};86		let new_value = match value.0.get() {87			Ok(v) => v,88			Err(e) => {89				*self.0.borrow_mut() = ThunkInner::Errored(e.clone());90				return Err(e);91			}92		};93		*self.0.borrow_mut() = ThunkInner::Computed(new_value.clone());94		Ok(new_value)95	}96}9798pub trait ThunkMapper<Input>: Trace {99	type Output;100	fn map(self, from: Input) -> Result<Self::Output>;101}102impl<Input> Thunk<Input>103where104	Input: Trace + Clone,105{106	pub fn map<M>(self, mapper: M) -> Thunk<M::Output>107	where108		M: ThunkMapper<Input>,109		M::Output: Trace,110	{111		#[derive(Trace)]112		struct Mapped<Input: Trace, Mapper: Trace> {113			inner: Thunk<Input>,114			mapper: Mapper,115		}116		impl<Input, Mapper> ThunkValue for Mapped<Input, Mapper>117		where118			Input: Trace + Clone,119			Mapper: ThunkMapper<Input>,120		{121			type Output = Mapper::Output;122123			fn get(self: Box<Self>) -> Result<Self::Output> {124				let value = self.inner.evaluate()?;125				let mapped = self.mapper.map(value)?;126				Ok(mapped)127			}128		}129130		Thunk::new(Mapped::<Input, M> {131			inner: self,132			mapper,133		})134	}135}136137impl<T: Trace> From<Result<T>> for Thunk<T> {138	fn from(value: Result<T>) -> Self {139		match value {140			Ok(o) => Self::evaluated(o),141			Err(e) => Self::errored(e),142		}143	}144}145impl<T, V: Trace> From<T> for Thunk<V>146where147	T: ThunkValue<Output = V>,148{149	fn from(value: T) -> Self {150		Thunk::new(value)151	}152}153154impl<T: Trace + Default> Default for Thunk<T> {155	fn default() -> Self {156		Self::evaluated(T::default())157	}158}159160type CacheKey = (Option<WeakObjValue>, Option<WeakObjValue>);161162#[derive(Trace, Clone)]163pub struct CachedUnbound<I, T>164where165	I: Unbound<Bound = T>,166	T: Trace,167{168	cache: Cc<RefCell<GcHashMap<CacheKey, T>>>,169	value: I,170}171impl<I: Unbound<Bound = T>, T: Trace> CachedUnbound<I, T> {172	pub fn new(value: I) -> Self {173		Self {174			cache: Cc::new(RefCell::new(GcHashMap::new())),175			value,176		}177	}178}179impl<I: Unbound<Bound = T>, T: Clone + Trace> Unbound for CachedUnbound<I, T> {180	type Bound = T;181	fn bind(&self, sup: Option<ObjValue>, this: Option<ObjValue>) -> Result<T> {182		let cache_key = (183			sup.as_ref().map(|s| s.clone().downgrade()),184			this.as_ref().map(|t| t.clone().downgrade()),185		);186		{187			if let Some(t) = self.cache.borrow().get(&cache_key) {188				return Ok(t.clone());189			}190		}191		let bound = self.value.bind(sup, this)?;192193		{194			let mut cache = self.cache.borrow_mut();195			cache.insert(cache_key, bound.clone());196		}197198		Ok(bound)199	}200}201202impl<T: Debug + Trace> Debug for Thunk<T> {203	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {204		write!(f, "Lazy")205	}206}207impl<T: Trace> PartialEq for Thunk<T> {208	fn eq(&self, other: &Self) -> bool {209		Cc::ptr_eq(&self.0, &other.0)210	}211}212213/// Represents a Jsonnet value, which can be sliced or indexed (string or array).214#[allow(clippy::module_name_repetitions)]215pub enum IndexableVal {216	/// String.217	Str(IStr),218	/// Array.219	Arr(ArrValue),220}221impl IndexableVal {222	pub fn to_array(self) -> ArrValue {223		match self {224			IndexableVal::Str(s) => ArrValue::chars(s.chars()),225			IndexableVal::Arr(arr) => arr,226		}227	}228	/// Slice the value.229	///230	/// # Implementation231	///232	/// For strings, will create a copy of specified interval.233	///234	/// For arrays, nothing will be copied on this call, instead [`ArrValue::Slice`] view will be returned.235	pub fn slice(236		self,237		index: Option<i32>,238		end: Option<i32>,239		step: Option<BoundedUsize<1, { i32::MAX as usize }>>,240	) -> Result<Self> {241		match &self {242			IndexableVal::Str(s) => {243				let mut computed_len = None;244				let mut get_len = || {245					computed_len.map_or_else(246						|| {247							let len = s.chars().count();248							let _ = computed_len.insert(len);249							len250						},251						|len| len,252					)253				};254				let mut get_idx = |pos: Option<i32>, default| {255					match pos {256						Some(v) if v < 0 => get_len().saturating_sub((-v) as usize),257						// No need to clamp, as iterator interface is used258						Some(v) => v as usize,259						None => default,260					}261				};262263				let index = get_idx(index, 0);264				let end = get_idx(end, usize::MAX);265				let step = step.as_deref().copied().unwrap_or(1);266267				if index >= end {268					return Ok(Self::Str("".into()));269				}270271				Ok(Self::Str(272					(s.chars()273						.skip(index)274						.take(end - index)275						.step_by(step)276						.collect::<String>())277					.into(),278				))279			}280			IndexableVal::Arr(arr) => {281				let get_idx = |pos: Option<i32>, len: usize, default| match pos {282					Some(v) if v < 0 => len.saturating_sub((-v) as usize),283					Some(v) => (v as usize).min(len),284					None => default,285				};286				let index = get_idx(index, arr.len(), 0);287				let end = get_idx(end, arr.len(), arr.len());288				let step = step.as_deref().copied().unwrap_or(1);289290				if index >= end {291					return Ok(Self::Arr(ArrValue::empty()));292				}293294				Ok(Self::Arr(295					arr.clone()296						.slice(Some(index), Some(end), Some(step))297						.expect("arguments checked"),298				))299			}300		}301	}302}303304#[derive(Debug, Clone, Trace)]305pub enum StrValue {306	Flat(IStr),307	Tree(Rc<(StrValue, StrValue, usize)>),308}309impl StrValue {310	pub fn concat(a: StrValue, b: StrValue) -> Self {311		// TODO: benchmark for an optimal value, currently just a arbitrary choice312		const STRING_EXTEND_THRESHOLD: usize = 100;313314		if a.is_empty() {315			b316		} else if b.is_empty() {317			a318		} else if a.len() + b.len() < STRING_EXTEND_THRESHOLD {319			Self::Flat(format!("{a}{b}").into())320		} else {321			let len = a.len() + b.len();322			Self::Tree(Rc::new((a, b, len)))323		}324	}325	pub fn into_flat(self) -> IStr {326		#[cold]327		fn write_buf(s: &StrValue, out: &mut String) {328			match s {329				StrValue::Flat(f) => out.push_str(f),330				StrValue::Tree(t) => {331					write_buf(&t.0, out);332					write_buf(&t.1, out);333				}334			}335		}336		match self {337			StrValue::Flat(f) => f,338			StrValue::Tree(_) => {339				let mut buf = String::with_capacity(self.len());340				write_buf(&self, &mut buf);341				buf.into()342			}343		}344	}345	pub fn len(&self) -> usize {346		match self {347			StrValue::Flat(v) => v.len(),348			StrValue::Tree(t) => t.2,349		}350	}351	pub fn is_empty(&self) -> bool {352		match self {353			Self::Flat(v) => v.is_empty(),354			// Can't create non-flat empty string355			Self::Tree(_) => false,356		}357	}358}359impl<T> From<T> for StrValue360where361	IStr: From<T>,362{363	fn from(value: T) -> Self {364		Self::Flat(IStr::from(value))365	}366}367impl Display for StrValue {368	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {369		match self {370			StrValue::Flat(v) => write!(f, "{v}"),371			StrValue::Tree(t) => {372				write!(f, "{}", t.0)?;373				write!(f, "{}", t.1)374			}375		}376	}377}378impl PartialEq for StrValue {379	// False positive, into_flat returns not StrValue, but IStr, thus no infinite recursion here.380	#[allow(clippy::unconditional_recursion)]381	fn eq(&self, other: &Self) -> bool {382		let a = self.clone().into_flat();383		let b = other.clone().into_flat();384		a == b385	}386}387impl Eq for StrValue {}388impl PartialOrd for StrValue {389	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {390		Some(self.cmp(other))391	}392}393impl Ord for StrValue {394	fn cmp(&self, other: &Self) -> std::cmp::Ordering {395		let a = self.clone().into_flat();396		let b = other.clone().into_flat();397		a.cmp(&b)398	}399}400401/// Represents any valid Jsonnet value.402#[derive(Debug, Clone, Trace, Default)]403pub enum Val {404	/// Represents a Jsonnet boolean.405	Bool(bool),406	/// Represents a Jsonnet null value.407	#[default]408	Null,409	/// Represents a Jsonnet string.410	Str(StrValue),411	/// Represents a Jsonnet number.412	/// Should be finite, and not NaN413	/// This restriction isn't enforced by enum, as enum field can't be marked as private414	Num(f64),415	/// Experimental bigint416	#[cfg(feature = "exp-bigint")]417	BigInt(#[trace(skip)] Box<num_bigint::BigInt>),418	/// Represents a Jsonnet array.419	Arr(ArrValue),420	/// Represents a Jsonnet object.421	Obj(ObjValue),422	/// Represents a Jsonnet function.423	Func(FuncVal),424}425426#[cfg(target_pointer_width = "64")]427static_assertions::assert_eq_size!(Val, [u8; 24]);428429impl From<IndexableVal> for Val {430	fn from(v: IndexableVal) -> Self {431		match v {432			IndexableVal::Str(s) => Self::string(s),433			IndexableVal::Arr(a) => Self::Arr(a),434		}435	}436}437438impl Val {439	pub const fn as_bool(&self) -> Option<bool> {440		match self {441			Self::Bool(v) => Some(*v),442			_ => None,443		}444	}445	pub const fn as_null(&self) -> Option<()> {446		match self {447			Self::Null => Some(()),448			_ => None,449		}450	}451	pub fn as_str(&self) -> Option<IStr> {452		match self {453			Self::Str(s) => Some(s.clone().into_flat()),454			_ => None,455		}456	}457	pub const fn as_num(&self) -> Option<f64> {458		match self {459			Self::Num(n) => Some(*n),460			_ => None,461		}462	}463	pub fn as_arr(&self) -> Option<ArrValue> {464		match self {465			Self::Arr(a) => Some(a.clone()),466			_ => None,467		}468	}469	pub fn as_obj(&self) -> Option<ObjValue> {470		match self {471			Self::Obj(o) => Some(o.clone()),472			_ => None,473		}474	}475	pub fn as_func(&self) -> Option<FuncVal> {476		match self {477			Self::Func(f) => Some(f.clone()),478			_ => None,479		}480	}481482	/// Creates `Val::Num` after checking for numeric overflow.483	/// As numbers are `f64`, we can just check for their finity.484	pub fn new_checked_num(num: f64) -> Result<Self> {485		if num.is_finite() {486			Ok(Self::Num(num))487		} else {488			bail!("overflow")489		}490	}491492	pub const fn value_type(&self) -> ValType {493		match self {494			Self::Str(..) => ValType::Str,495			Self::Num(..) => ValType::Num,496			#[cfg(feature = "exp-bigint")]497			Self::BigInt(..) => ValType::BigInt,498			Self::Arr(..) => ValType::Arr,499			Self::Obj(..) => ValType::Obj,500			Self::Bool(_) => ValType::Bool,501			Self::Null => ValType::Null,502			Self::Func(..) => ValType::Func,503		}504	}505506	pub fn manifest(&self, format: impl ManifestFormat) -> Result<String> {507		fn manifest_dyn(val: &Val, manifest: &dyn ManifestFormat) -> Result<String> {508			manifest.manifest(val.clone())509		}510		manifest_dyn(self, &format)511	}512513	pub fn to_string(&self) -> Result<IStr> {514		Ok(match self {515			Self::Bool(true) => "true".into(),516			Self::Bool(false) => "false".into(),517			Self::Null => "null".into(),518			Self::Str(s) => s.clone().into_flat(),519			_ => self.manifest(ToStringFormat).map(IStr::from)?,520		})521	}522523	pub fn into_indexable(self) -> Result<IndexableVal> {524		Ok(match self {525			Val::Str(s) => IndexableVal::Str(s.into_flat()),526			Val::Arr(arr) => IndexableVal::Arr(arr),527			_ => bail!(ValueIsNotIndexable(self.value_type())),528		})529	}530531	pub fn function(function: impl Into<FuncVal>) -> Self {532		Self::Func(function.into())533	}534	pub fn string(string: impl Into<StrValue>) -> Self {535		Self::Str(string.into())536	}537}538539impl From<IStr> for Val {540	fn from(value: IStr) -> Self {541		Self::string(value)542	}543}544impl From<String> for Val {545	fn from(value: String) -> Self {546		Self::string(value)547	}548}549impl From<&str> for Val {550	fn from(value: &str) -> Self {551		Self::string(value)552	}553}554impl From<ObjValue> for Val {555	fn from(value: ObjValue) -> Self {556		Self::Obj(value)557	}558}559560const fn is_function_like(val: &Val) -> bool {561	matches!(val, Val::Func(_))562}563564/// Native implementation of `std.primitiveEquals`565pub fn primitive_equals(val_a: &Val, val_b: &Val) -> Result<bool> {566	Ok(match (val_a, val_b) {567		(Val::Bool(a), Val::Bool(b)) => a == b,568		(Val::Null, Val::Null) => true,569		(Val::Str(a), Val::Str(b)) => a == b,570		(Val::Num(a), Val::Num(b)) => (a - b).abs() <= f64::EPSILON,571		#[cfg(feature = "exp-bigint")]572		(Val::BigInt(a), Val::BigInt(b)) => a == b,573		(Val::Arr(_), Val::Arr(_)) => {574			bail!("primitiveEquals operates on primitive types, got array")575		}576		(Val::Obj(_), Val::Obj(_)) => {577			bail!("primitiveEquals operates on primitive types, got object")578		}579		(a, b) if is_function_like(a) && is_function_like(b) => {580			bail!("cannot test equality of functions")581		}582		(_, _) => false,583	})584}585586/// Native implementation of `std.equals`587pub fn equals(val_a: &Val, val_b: &Val) -> Result<bool> {588	if val_a.value_type() != val_b.value_type() {589		return Ok(false);590	}591	match (val_a, val_b) {592		(Val::Arr(a), Val::Arr(b)) => {593			if ArrValue::ptr_eq(a, b) {594				return Ok(true);595			}596			if a.len() != b.len() {597				return Ok(false);598			}599			for (a, b) in a.iter().zip(b.iter()) {600				if !equals(&a?, &b?)? {601					return Ok(false);602				}603			}604			Ok(true)605		}606		(Val::Obj(a), Val::Obj(b)) => {607			if ObjValue::ptr_eq(a, b) {608				return Ok(true);609			}610			let fields = a.fields(611				#[cfg(feature = "exp-preserve-order")]612				false,613			);614			if fields615				!= b.fields(616					#[cfg(feature = "exp-preserve-order")]617					false,618				) {619				return Ok(false);620			}621			for field in fields {622				if !equals(623					&a.get(field.clone())?.expect("field exists"),624					&b.get(field)?.expect("field exists"),625				)? {626					return Ok(false);627				}628			}629			Ok(true)630		}631		(a, b) => Ok(primitive_equals(a, b)?),632	}633}
modifiedcrates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -1,3 +1,5 @@
+use std::string::String;
+
 use proc_macro2::TokenStream;
 use quote::quote;
 use syn::{
@@ -205,6 +207,7 @@
 	}
 }
 
+#[allow(clippy::too_many_lines)]
 fn builtin_inner(
 	attr: BuiltinAttrs,
 	fun: ItemFn,
@@ -225,7 +228,7 @@
 		.map(|arg| ArgInfo::parse(&name, arg))
 		.collect::<Result<Vec<_>>>()?;
 
-	let params_desc = args.iter().flat_map(|a| match a {
+	let params_desc = args.iter().filter_map(|a| match a {
 		ArgInfo::Normal {
 			is_option,
 			name,
@@ -234,8 +237,7 @@
 		} => {
 			let name = name
 				.as_ref()
-				.map(|n| quote! {ParamName::new_static(#n)})
-				.unwrap_or_else(|| quote! {None});
+				.map_or_else(|| quote! {None}, |n| quote! {ParamName::new_static(#n)});
 			Some(quote! {
 				#(#cfg_attrs)*
 				BuiltinParam::new(#name, #is_option),
@@ -244,15 +246,12 @@
 		ArgInfo::Lazy { is_option, name } => {
 			let name = name
 				.as_ref()
-				.map(|n| quote! {ParamName::new_static(#n)})
-				.unwrap_or_else(|| quote! {None});
+				.map_or_else(|| quote! {None}, |n| quote! {ParamName::new_static(#n)});
 			Some(quote! {
 				BuiltinParam::new(#name, #is_option),
 			})
 		}
-		ArgInfo::Context => None,
-		ArgInfo::Location => None,
-		ArgInfo::This => None,
+		ArgInfo::Context | ArgInfo::Location | ArgInfo::This => None,
 	});
 
 	let mut id = 0usize;
@@ -275,7 +274,7 @@
 				name,
 				cfg_attrs,
 			} => {
-				let name = name.as_ref().map(|v| v.as_str()).unwrap_or("<unnamed>");
+				let name = name.as_ref().map_or("<unnamed>", String::as_str);
 				let eval = quote! {jrsonnet_evaluator::State::push_description(
 					|| format!("argument <{}> evaluation", #name),
 					|| <#ty>::from_untyped(value.evaluate()?),
@@ -390,6 +389,7 @@
 }
 
 #[derive(Default)]
+#[allow(clippy::struct_excessive_bools)]
 struct TypedAttr {
 	rename: Option<String>,
 	flatten: bool,
@@ -467,11 +467,8 @@
 				"this field should appear in output object, but it has no visible name",
 			));
 		};
-		let (is_option, ty) = if let Some(ty) = extract_type_from_option(&field.ty)? {
-			(true, ty.clone())
-		} else {
-			(false, field.ty.clone())
-		};
+		let (is_option, ty) = extract_type_from_option(&field.ty)?
+			.map_or_else(|| (false, field.ty.clone()), |ty| (true, ty.clone()));
 		if is_option && attr.flatten {
 			if !attr.flatten_ok {
 				return Err(Error::new(
@@ -551,48 +548,53 @@
 			#ident: #value,
 		}
 	}
-	fn expand_serialize(&self) -> Result<TokenStream> {
+	fn expand_serialize(&self) -> TokenStream {
 		let ident = &self.ident;
 		let ty = &self.ty;
-		Ok(if let Some(name) = self.name() {
-			let hide = if self.attr.hide {
-				quote! {.hide()}
-			} else {
-				quote! {}
-			};
-			let add = if self.attr.add {
-				quote! {.add()}
-			} else {
-				quote! {}
-			};
-			if self.is_option {
-				quote! {
-					if let Some(value) = self.#ident {
+		self.name().map_or_else(
+			|| {
+				if self.is_option {
+					quote! {
+						if let Some(value) = self.#ident {
+							<#ty as TypedObj>::serialize(value, out)?;
+						}
+					}
+				} else {
+					quote! {
+						<#ty as TypedObj>::serialize(self.#ident, out)?;
+					}
+				}
+			},
+			|name| {
+				let hide = if self.attr.hide {
+					quote! {.hide()}
+				} else {
+					quote! {}
+				};
+				let add = if self.attr.add {
+					quote! {.add()}
+				} else {
+					quote! {}
+				};
+				if self.is_option {
+					quote! {
+						if let Some(value) = self.#ident {
+							out.field(#name)
+								#hide
+								#add
+								.try_value(<#ty as Typed>::into_untyped(value)?)?;
+						}
+					}
+				} else {
+					quote! {
 						out.field(#name)
 							#hide
 							#add
-							.try_value(<#ty as Typed>::into_untyped(value)?)?;
+							.try_value(<#ty as Typed>::into_untyped(self.#ident)?)?;
 					}
 				}
-			} else {
-				quote! {
-					out.field(#name)
-						#hide
-						#add
-						.try_value(<#ty as Typed>::into_untyped(self.#ident)?)?;
-				}
-			}
-		} else if self.is_option {
-			quote! {
-				if let Some(value) = self.#ident {
-					<#ty as TypedObj>::serialize(value, out)?;
-				}
-			}
-		} else {
-			quote! {
-				<#ty as TypedObj>::serialize(self.#ident, out)?;
-			}
-		})
+			},
+		)
 	}
 }
 
@@ -623,7 +625,7 @@
 	let typed = {
 		let fields = fields
 			.iter()
-			.flat_map(TypedField::expand_field)
+			.filter_map(TypedField::expand_field)
 			.collect::<Vec<_>>();
 		quote! {
 			impl #impl_generics Typed for #ident #ty_generics #where_clause {
@@ -650,7 +652,7 @@
 	let fields_serialize = fields
 		.iter()
 		.map(TypedField::expand_serialize)
-		.collect::<Result<Vec<_>>>()?;
+		.collect::<Vec<_>>();
 
 	Ok(quote! {
 		const _: () = {
@@ -767,7 +769,7 @@
 	}
 }
 
-/// IStr formatting helper
+/// `IStr` formatting helper
 ///
 /// Using `format!("literal with no codes").into()` is slower than just `"literal with no codes".into()`
 /// This macro looks for formatting codes in the input string, and uses
modifiedcrates/jrsonnet-rowan-parser/src/parser.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/parser.rs
+++ b/crates/jrsonnet-rowan-parser/src/parser.rs
@@ -758,10 +758,10 @@
 			} else if p.at(T![...]) {
 				// let m_err = p.start_ranger();
 				destruct_rest(p);
-				// if had_rest {
-				// 	p.custom_error(m_err.finish(p), "only one rest can be present in array");
-				// }
-				// had_rest = true;
+			// if had_rest {
+			// 	p.custom_error(m_err.finish(p), "only one rest can be present in array");
+			// }
+			// had_rest = true;
 			} else {
 				destruct(p);
 			}
modifiedcrates/jrsonnet-stdlib/build.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/build.rs
+++ b/crates/jrsonnet-stdlib/build.rs
@@ -26,7 +26,9 @@
 			let dest_path = Path::new(&out_dir).join("stdlib.rs");
 			let mut f = File::create(dest_path).unwrap();
 			f.write_all(
-				("#[allow(clippy::redundant_clone)]".to_owned() + &v.to_string()).as_bytes(),
+				("#[allow(clippy::redundant_clone, clippy::similar_names)]".to_owned()
+					+ &v.to_string())
+					.as_bytes(),
 			)
 			.unwrap();
 		}
modifiedcrates/jrsonnet-stdlib/src/arrays.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/arrays.rs
+++ b/crates/jrsonnet-stdlib/src/arrays.rs
@@ -9,7 +9,7 @@
 	Either, IStr, ObjValueBuilder, Result, ResultExt, Thunk, Val,
 };
 
-pub(crate) fn eval_on_empty(on_empty: Option<Thunk<Val>>) -> Result<Val> {
+pub fn eval_on_empty(on_empty: Option<Thunk<Val>>) -> Result<Val> {
 	if let Some(on_empty) = on_empty {
 		on_empty.evaluate()
 	} else {
@@ -270,8 +270,8 @@
 	let newArrRight = arr.slice(Some(at + 1), None, None);
 
 	Ok(ArrValue::extended(
-		newArrLeft.unwrap_or(ArrValue::empty()),
-		newArrRight.unwrap_or(ArrValue::empty()),
+		newArrLeft.unwrap_or_else(ArrValue::empty),
+		newArrRight.unwrap_or_else(ArrValue::empty),
 	))
 }
 
modifiedcrates/jrsonnet-stdlib/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/lib.rs
+++ b/crates/jrsonnet-stdlib/src/lib.rs
@@ -313,10 +313,10 @@
 	settings: Rc<RefCell<Settings>>,
 }
 impl ContextInitializer {
-	pub fn new(_s: State, resolver: PathResolver) -> Self {
+	pub fn new(s: State, resolver: PathResolver) -> Self {
 		let settings = Settings {
-			ext_vars: Default::default(),
-			ext_natives: Default::default(),
+			ext_vars: HashMap::new(),
+			ext_natives: HashMap::new(),
 			trace_printer: Box::new(StdTracePrinter::new(resolver.clone())),
 			path_resolver: resolver,
 		};
@@ -324,10 +324,12 @@
 		let stdlib_obj = stdlib_uncached(settings.clone());
 		#[cfg(not(feature = "legacy-this-file"))]
 		let stdlib_thunk = Thunk::evaluated(Val::Obj(stdlib_obj));
+		#[cfg(feature = "legacy-this-file")]
+		let _ = s;
 		Self {
 			#[cfg(not(feature = "legacy-this-file"))]
 			context: {
-				let mut context = ContextBuilder::with_capacity(_s, 1);
+				let mut context = ContextBuilder::with_capacity(s, 1);
 				context.bind("std", stdlib_thunk.clone());
 				context.build()
 			},
modifiedcrates/jrsonnet-stdlib/src/manifest/toml.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/manifest/toml.rs
+++ b/crates/jrsonnet-stdlib/src/manifest/toml.rs
@@ -180,7 +180,9 @@
 		options.preserve_order,
 	) {
 		let value = value?;
-		if !is_section(&value)? {
+		if is_section(&value)? {
+			sections.push((key, value));
+		} else {
 			if !first {
 				buf.push('\n');
 			}
@@ -189,8 +191,6 @@
 			escape_key_toml_buf(&key, buf);
 			buf.push_str(" = ");
 			manifest_value(&value, false, buf, cur_padding, options)?;
-		} else {
-			sections.push((key, value));
 		}
 	}
 	for (k, v) in sections {
modifiedcrates/jrsonnet-stdlib/src/math.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/math.rs
+++ b/crates/jrsonnet-stdlib/src/math.rs
@@ -131,16 +131,19 @@
 }
 
 #[builtin]
+#[allow(clippy::float_cmp)]
 pub fn builtin_is_odd(x: f64) -> bool {
 	builtin_round(x) % 2.0 == 1.0
 }
 
 #[builtin]
+#[allow(clippy::float_cmp)]
 pub fn builtin_is_integer(x: f64) -> bool {
 	builtin_round(x) == x
 }
 
 #[builtin]
+#[allow(clippy::float_cmp)]
 pub fn builtin_is_decimal(x: f64) -> bool {
 	builtin_round(x) != x
 }
modifiedcrates/jrsonnet-stdlib/src/misc.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/misc.rs
+++ b/crates/jrsonnet-stdlib/src/misc.rs
@@ -67,11 +67,7 @@
 			v => v.manifest(JsonFormat::debug())?.into(),
 		},
 	);
-	if let Some(rest) = rest {
-		rest.evaluate()
-	} else {
-		Ok(str)
-	}
+	rest.map_or_else(|| Ok(str), |rest| rest.evaluate())
 }
 
 #[allow(clippy::comparison_chain)]
@@ -84,16 +80,15 @@
 				return Ok(false);
 			} else if b.len() == a.len() {
 				return equals(&Val::Arr(a), &Val::Arr(b));
-			} else {
-				for (a, b) in a.iter().take(b.len()).zip(b.iter()) {
-					let a = a?;
-					let b = b?;
-					if !equals(&a, &b)? {
-						return Ok(false);
-					}
+			}
+			for (a, b) in a.iter().take(b.len()).zip(b.iter()) {
+				let a = a?;
+				let b = b?;
+				if !equals(&a, &b)? {
+					return Ok(false);
 				}
-				true
 			}
+			true
 		}
 		_ => bail!("both arguments should be of the same type"),
 	})
@@ -109,17 +104,16 @@
 				return Ok(false);
 			} else if b.len() == a.len() {
 				return equals(&Val::Arr(a), &Val::Arr(b));
-			} else {
-				let a_len = a.len();
-				for (a, b) in a.iter().skip(a_len - b.len()).zip(b.iter()) {
-					let a = a?;
-					let b = b?;
-					if !equals(&a, &b)? {
-						return Ok(false);
-					}
+			}
+			let a_len = a.len();
+			for (a, b) in a.iter().skip(a_len - b.len()).zip(b.iter()) {
+				let a = a?;
+				let b = b?;
+				if !equals(&a, &b)? {
+					return Ok(false);
 				}
-				true
 			}
+			true
 		}
 		_ => bail!("both arguments should be of the same type"),
 	})
modifiedcrates/jrsonnet-stdlib/src/objects.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/objects.rs
+++ b/crates/jrsonnet-stdlib/src/objects.rs
@@ -155,7 +155,7 @@
 		if k == key {
 			continue;
 		}
-		new_obj.field(k).value(v.unwrap())
+		new_obj.field(k).value(v.unwrap());
 	}
 
 	new_obj.build()
modifiedcrates/jrsonnet-stdlib/src/sort.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/sort.rs
+++ b/crates/jrsonnet-stdlib/src/sort.rs
@@ -36,7 +36,7 @@
 
 fn get_sort_type<T>(values: &[T], key_getter: impl Fn(&T) -> &Val) -> Result<SortKeyType> {
 	let mut sort_type = SortKeyType::Unknown;
-	for i in values.iter() {
+	for i in values {
 		let i = key_getter(i);
 		match (i, sort_type) {
 			(Val::Str(_), SortKeyType::Unknown) => sort_type = SortKeyType::String,
modifiedcrates/jrsonnet-stdlib/src/strings.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/strings.rs
+++ b/crates/jrsonnet-stdlib/src/strings.rs
@@ -75,7 +75,7 @@
 		.enumerate()
 	{
 		if &strb[i..i + pat.len()] == pat {
-			out.push(Val::Num(ch_idx as f64))
+			out.push(Val::Num(ch_idx as f64));
 		}
 	}
 	out.into()
@@ -117,11 +117,6 @@
 }
 
 fn parse_nat<const BASE: u32>(raw: &str) -> Result<f64> {
-	debug_assert!(
-		1 <= BASE && BASE <= 16,
-		"integer base should be between 1 and 16"
-	);
-
 	const ZERO_CODE: u32 = '0' as u32;
 	const UPPER_A_CODE: u32 = 'A' as u32;
 	const LOWER_A_CODE: u32 = 'a' as u32;
@@ -135,10 +130,17 @@
 		}
 	}
 
-	let base = BASE as f64;
+	debug_assert!(
+		1 <= BASE && BASE <= 16,
+		"integer base should be between 1 and 16"
+	);
+
+	let base = f64::from(BASE);
 
 	raw.chars().try_fold(0f64, |aggregate, digit| {
 		let digit = digit as u32;
+		// if-let-else looks better here than Option combinators
+		#[allow(clippy::option_if_let_else)]
 		let digit = if let Some(digit) = checked_sub_if(BASE > 10, digit, LOWER_A_CODE) {
 			digit + 10
 		} else if let Some(digit) = checked_sub_if(BASE > 10, digit, UPPER_A_CODE) {
@@ -148,7 +150,7 @@
 		};
 
 		if digit < BASE {
-			Ok(base * aggregate + digit as f64)
+			Ok(base.mul_add(aggregate, f64::from(digit)))
 		} else {
 			bail!("{raw:?} is not a base {BASE} integer");
 		}
modifiedcrates/jrsonnet-types/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-types/src/lib.rs
+++ b/crates/jrsonnet-types/src/lib.rs
@@ -166,9 +166,9 @@
 
 fn print_array(a: &ComplexValType, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 	if *a == ComplexValType::Any {
-		write!(f, "array")?
+		write!(f, "array")?;
 	} else {
-		write!(f, "Array<{a}>")?
+		write!(f, "Array<{a}>")?;
 	}
 	Ok(())
 }
@@ -176,18 +176,20 @@
 impl Display for ComplexValType {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 		match self {
-			ComplexValType::Any => write!(f, "any")?,
-			ComplexValType::Simple(s) => write!(f, "{s}")?,
-			ComplexValType::Char => write!(f, "char")?,
-			ComplexValType::BoundedNumber(a, b) => write!(
+			Self::Any => write!(f, "any")?,
+			Self::Simple(s) => write!(f, "{s}")?,
+			Self::Char => write!(f, "char")?,
+			Self::BoundedNumber(a, b) => write!(
 				f,
 				"BoundedNumber<{}, {}>",
-				a.map(|e| e.to_string()).unwrap_or_else(|| "".into()),
-				b.map(|e| e.to_string()).unwrap_or_else(|| "".into())
+				a.map(|e| e.to_string())
+					.unwrap_or_else(|| "open".to_owned()),
+				b.map(|e| e.to_string())
+					.unwrap_or_else(|| "open".to_owned())
 			)?,
-			ComplexValType::ArrayRef(a) => print_array(a, f)?,
-			ComplexValType::Array(a) => print_array(a, f)?,
-			ComplexValType::ObjectRef(fields) => {
+			Self::ArrayRef(a) => print_array(a, f)?,
+			Self::Array(a) => print_array(a, f)?,
+			Self::ObjectRef(fields) => {
 				write!(f, "{{")?;
 				for (i, (k, v)) in fields.iter().enumerate() {
 					if i != 0 {
@@ -197,18 +199,18 @@
 				}
 				write!(f, "}}")?;
 			}
-			ComplexValType::AttrsOf(a) => {
-				if matches!(a, ComplexValType::Any) {
+			Self::AttrsOf(a) => {
+				if matches!(a, Self::Any) {
 					write!(f, "object")?;
 				} else {
 					write!(f, "AttrsOf<{a}>")?;
 				}
 			}
-			ComplexValType::Union(v) => write_union(f, true, v.iter())?,
-			ComplexValType::UnionRef(v) => write_union(f, true, v.iter().copied())?,
-			ComplexValType::Sum(v) => write_union(f, false, v.iter())?,
-			ComplexValType::SumRef(v) => write_union(f, false, v.iter().copied())?,
-			ComplexValType::Lazy(lazy) => write!(f, "Lazy<{lazy}>")?,
+			Self::Union(v) => write_union(f, true, v.iter())?,
+			Self::UnionRef(v) => write_union(f, true, v.iter().copied())?,
+			Self::Sum(v) => write_union(f, false, v.iter())?,
+			Self::SumRef(v) => write_union(f, false, v.iter().copied())?,
+			Self::Lazy(lazy) => write!(f, "Lazy<{lazy}>")?,
 		};
 		Ok(())
 	}
modifiedtests/suite/std_param_names.jsonnetdiffbeforeafterboth
--- a/tests/suite/std_param_names.jsonnet
+++ b/tests/suite/std_param_names.jsonnet
@@ -49,6 +49,7 @@
     min: ['a', 'b'],
     clamp: ['x', 'minVal', 'maxVal'],
     flattenArrays: ['arrs'],
+    flattenDeepArray: ['value'],
     manifestIni: ['ini'],
     manifestToml: ['value'],
     manifestTomlEx: ['value', 'indent'],