git.delta.rocks / jrsonnet / refs/commits / 1ddb92c00d0f

difftreelog

style fix clippy warnings

ntlrsvsuYaroslav Bolyukin2026-03-22parent: #f0c8338.patch.diff
in: master

24 files changed

modifiedCargo.lockdiffbeforeafterboth
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -676,18 +676,18 @@
 
 [[package]]
 name = "jrsonnet-gcmodule"
-version = "0.4.1"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c33f4f6cdc60f5ae94ebae3dfe7f484ae79b364225d9b19601b24c804cfd8751"
+checksum = "f95b976a79e4000bb9e07ff0709dca0ea27bcf1952d4c17d91fb7364d6145683"
 dependencies = [
  "jrsonnet-gcmodule-derive",
 ]
 
 [[package]]
 name = "jrsonnet-gcmodule-derive"
-version = "0.4.1"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b30c95b285f9bb6709f1b3e6fc69b3a25e39b32ff987587fd108f0f22be5fa3"
+checksum = "51d928626220a310ff0cec815e80cf7fe104697184352ca21c40534e0b0d72d9"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1602,7 +1602,7 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
 dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.61.2",
 ]
 
 [[package]]
modifiedCargo.tomldiffbeforeafterboth
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,7 +20,7 @@
 jrsonnet-cli = { path = "./crates/jrsonnet-cli", version = "0.5.0-pre97" }
 jrsonnet-types = { path = "./crates/jrsonnet-types", version = "0.5.0-pre97" }
 jrsonnet-formatter = { path = "./crates/jrsonnet-formatter", version = "0.5.0-pre97" }
-jrsonnet-gcmodule = { version = "0.4.1" }
+jrsonnet-gcmodule = { version = "0.4.2" }
 # Diagnostics.
 # hi-doc is my library, which handles text formatting very well, but isn't polished enough yet
 # Previous implementation was based on annotate-snippets, which I don't like for many reasons.
modifiedbindings/jsonnet/src/import.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/import.rs
+++ b/bindings/jsonnet/src/import.rs
@@ -66,8 +66,8 @@
 				base.as_ptr(),
 				rel.as_ptr(),
 				&mut found_here.cast_const(),
-				&mut buf,
-				&mut buf_len,
+				&raw mut buf,
+				&raw mut buf_len,
 			)
 		};
 		let buf_slice: &[u8] = unsafe { std::slice::from_raw_parts(buf.cast(), buf_len) };
modifiedcrates/jrsonnet-evaluator/src/evaluate/destructure.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/destructure.rs
@@ -9,6 +9,9 @@
 	evaluate_method, evaluate_named_param, Context, Pending, Thunk, Val,
 };
 
+#[cfg(feature = "exp-preserve-order")]
+use crate::evaluate;
+
 #[allow(clippy::too_many_lines)]
 #[allow(unused_variables)]
 pub fn destruct<H: BuildHasher>(
modifiedcrates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/evaluate/mod.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/mod.rs
@@ -143,7 +143,7 @@
 					false,
 				) {
 					let fctx = Pending::new();
-					let mut new_bindings = FxHashMap::with_capacity(var.capacity_hint());
+					let mut new_bindings = FxHashMap::with_capacity(var.binds_len());
 					let obj = obj.clone();
 					let value = Thunk::evaluated(Val::Arr(ArrValue::lazy(vec![
 						Thunk::evaluated(Val::string(field.clone())),
modifiedcrates/jrsonnet-evaluator/src/function/builtin.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/function/builtin.rs
+++ b/crates/jrsonnet-evaluator/src/function/builtin.rs
@@ -98,9 +98,9 @@
 
 	fn call(&self, _loc: CallLocation<'_>, args: &[Option<Thunk<Val>>]) -> Result<Val> {
 		let args = args
-			.into_iter()
+			.iter()
 			.map(|a| a.as_ref().expect("legacy natives have no default params"))
-			.map(|a| a.evaluate())
+			.map(Thunk::evaluate)
 			.collect::<Result<Vec<Val>>>()?;
 		self.handler.call(&args)
 	}
modifiedcrates/jrsonnet-evaluator/src/obj/mod.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/obj/mod.rs
+++ b/crates/jrsonnet-evaluator/src/obj/mod.rs
@@ -1,12 +1,5 @@
 use std::{
-	any::Any,
-	cell::{Cell, RefCell},
-	clone::Clone,
-	collections::hash_map::Entry,
-	fmt::{self, Debug},
-	hash::{Hash, Hasher},
-	num::Saturating,
-	ops::ControlFlow,
+	any::Any, cell::{Cell, RefCell}, clone::Clone, cmp::Reverse, collections::hash_map::Entry, fmt::{self, Debug}, hash::{Hash, Hasher}, num::Saturating, ops::ControlFlow
 };
 
 use educe::Educe;
@@ -39,18 +32,19 @@
 
 	use jrsonnet_gcmodule::Trace;
 
-	#[derive(Clone, Copy, Default, Debug, Trace)]
+	#[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]
 	pub struct FieldIndex(());
 	impl FieldIndex {
 		pub fn absolute(_v: u32) -> Self {
 			Self(())
 		}
+		#[must_use]
 		pub const fn next(self) -> Self {
 			Self(())
 		}
 	}
 
-	#[derive(Clone, Copy, Default, Debug, Trace)]
+	#[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]
 	pub struct SuperDepth(());
 	impl SuperDepth {
 		pub(super) fn deepen(self) {}
@@ -59,8 +53,6 @@
 
 #[cfg(feature = "exp-preserve-order")]
 pub mod ordering {
-	use std::cmp::Reverse;
-
 	use jrsonnet_gcmodule::Trace;
 
 	#[derive(Clone, Copy, Default, Debug, Trace, PartialEq, Eq, PartialOrd, Ord)]
@@ -69,6 +61,7 @@
 		pub fn absolute(v: u32) -> Self {
 			Self(v)
 		}
+		#[must_use]
 		pub fn next(self) -> Self {
 			Self(self.0 + 1)
 		}
@@ -78,22 +71,20 @@
 	pub struct SuperDepth(u32);
 	impl SuperDepth {
 		pub(super) fn deepen(&mut self) {
-			self.0 += 1
+			self.0 += 1;
 		}
 	}
+}
+
+use ordering::{FieldIndex, SuperDepth};
 
-	#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
-	pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);
-	impl FieldSortKey {
-		pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {
-			Self(Reverse(depth), index)
-		}
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub struct FieldSortKey(Reverse<SuperDepth>, FieldIndex);
+impl FieldSortKey {
+	pub fn new(depth: SuperDepth, index: FieldIndex) -> Self {
+		Self(Reverse(depth), index)
 	}
 }
-
-#[cfg(feature = "exp-preserve-order")]
-use ordering::FieldSortKey;
-use ordering::{FieldIndex, SuperDepth};
 
 // 0 - add
 //  12 - visibility
@@ -795,7 +786,7 @@
 struct FieldVisibilityData {
 	omitted_until: Saturating<usize>,
 	exists_visible: Option<Visibility>,
-	#[cfg(feature = "exp-preserve-order")]
+	#[allow(dead_code, reason = "used for exp-object-ordering, ZST otherwise")]
 	key: FieldSortKey,
 }
 impl FieldVisibilityData {
@@ -804,7 +795,7 @@
 			.expect("non-existing fields shall be dropped at the end of fn fields_visibility()")
 			.is_visible()
 	}
-	#[cfg(feature = "exp-preserve-order")]
+	#[allow(dead_code, reason = "used for exp-object-ordering, ZST otherwise")]
 	fn sort_key(&self) -> FieldSortKey {
 		self.key
 	}
@@ -818,12 +809,11 @@
 		let mut omit_index = Saturating(0);
 		for core in self.0.cores.iter().rev() {
 			core.0
-				.enum_fields_core(&mut super_depth, &mut |_depth, _index, name, visibility| {
+				.enum_fields_core(&mut super_depth, &mut |depth, index, name, visibility| {
 					let entry = out.entry(name);
-					let data = entry.or_insert(FieldVisibilityData {
+					let data = entry.or_insert_with(|| FieldVisibilityData {
 						exists_visible: None,
-						#[cfg(feature = "exp-preserve-order")]
-						key: FieldSortKey::new(_depth, _index),
+						key: FieldSortKey::new(depth, index),
 						omitted_until: omit_index,
 					});
 					match visibility {
modifiedcrates/jrsonnet-evaluator/src/typed/conversions.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/typed/conversions.rs
+++ b/crates/jrsonnet-evaluator/src/typed/conversions.rs
@@ -61,9 +61,16 @@
 	}
 }
 
+#[diagnostic::on_unimplemented(
+	note = "don't implement `ParseTypedObj` directly, it is automatically provided by `FromUntyped` derive"
+)]
 pub trait ParseTypedObj: Typed {
 	fn parse(obj: &ObjValue) -> Result<Self>;
 }
+
+#[diagnostic::on_unimplemented(
+	note = "don't implement `SerializeTypedObj` directly, it is automatically provided by `IntoUntyped` derive"
+)]
 pub trait SerializeTypedObj: Typed {
 	fn serialize(self, out: &mut ObjValueBuilder) -> Result<()>;
 	fn into_object(self) -> Result<ObjValue> {
modifiedcrates/jrsonnet-formatter/src/comments.rsdiffbeforeafterboth
--- a/crates/jrsonnet-formatter/src/comments.rs
+++ b/crates/jrsonnet-formatter/src/comments.rs
@@ -136,7 +136,7 @@
 								}
 								line = new_line.to_string();
 							}
-							p!(out, string(line.to_string()) nl);
+							p!(out, string(line.clone()) nl);
 						}
 					}
 					if doc {
modifiedcrates/jrsonnet-formatter/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-formatter/src/lib.rs
+++ b/crates/jrsonnet-formatter/src/lib.rs
@@ -37,8 +37,7 @@
 		format_comments(&e.trivia, CommentLocation::EndOfItems, &mut items);
 		items.into_rc_path()
 	};
-	let items =
-		new_line_group(pi!(@i; items(o.into()) items(end_comments_items.into()))).into_rc_path();
+	let items = new_line_group(pi!(@i; items(o) items(end_comments_items.into()))).into_rc_path();
 
 	let indented = with_indent(pi!(@i; nl items(items.into())));
 
@@ -355,48 +354,48 @@
 }
 impl Printable for ArgsDesc {
 	fn print(&self, out: &mut PrintItems) {
-		let start = LineNumber::new("args start line");
-		let end = LineNumber::new("args end line");
-		let multi_line = Rc::new(move |condition_context: &mut ConditionResolverContext| {
-			is_multiple_lines(condition_context, start, end)
-		});
-
-		let (children, end_comments) = children_between::<Arg>(
-			self.syntax().clone(),
-			self.l_paren_token().map(Into::into).as_ref(),
-			self.r_paren_token().map(Into::into).as_ref(),
-			None,
-		);
-
 		fn gen_args(children: Vec<Child<Arg>>, multi_line: ConditionResolver) -> PrintItems {
-			let mut _out = PrintItems::new();
-			let out = &mut _out;
+			let mut out = PrintItems::new();
 
 			let mut args = children.into_iter().peekable();
 			while let Some(ele) = args.next() {
 				if ele.should_start_with_newline {
 					p!(out, nl);
 				}
-				format_comments(&ele.before_trivia, CommentLocation::AboveItem, out);
+				format_comments(&ele.before_trivia, CommentLocation::AboveItem, &mut out);
 				let arg = ele.value;
 				if arg.name().is_some() || arg.assign_token().is_some() {
-					p!(out, {arg.name()} str(" = "));
+					p!(&mut out, {arg.name()} str(" = "));
 				}
-				p!(out, { arg.expr() });
+				p!(&mut out, { arg.expr() });
 				let has_more = args.peek().is_some();
 				if has_more {
 					p!(out, str(","));
 				} else {
 					p!(out, if("trailing comma", multi_line, str(",")));
 				}
-				format_comments(&ele.inline_trivia, CommentLocation::ItemInline, out);
+				format_comments(&ele.inline_trivia, CommentLocation::ItemInline, &mut out);
 				if has_more {
 					p!(out, if_else("arg separator", multi_line, nl)(sonl));
 				}
 			}
-			_out
+
+			out
 		}
 
+		let start = LineNumber::new("args start line");
+		let end = LineNumber::new("args end line");
+		let multi_line = Rc::new(move |condition_context: &mut ConditionResolverContext| {
+			is_multiple_lines(condition_context, start, end)
+		});
+
+		let (children, end_comments) = children_between::<Arg>(
+			self.syntax().clone(),
+			self.l_paren_token().map(Into::into).as_ref(),
+			self.r_paren_token().map(Into::into).as_ref(),
+			None,
+		);
+
 		let args_items = new_line_group(gen_args(children, multi_line.clone())).into_rc_path();
 		let args_indented = with_indent(pi!(@i; nl items(args_items.into())));
 
@@ -447,6 +446,7 @@
 }
 
 impl Printable for ObjBody {
+	#[allow(clippy::too_many_lines)]
 	fn print(&self, out: &mut PrintItems) {
 		match self {
 			Self::ObjBodyComp(l) => {
@@ -507,6 +507,30 @@
 				p!(out, nl <i str("}"));
 			}
 			Self::ObjBodyMemberList(l) => {
+				fn gen_members(
+					children: Vec<Child<Member>>,
+					multi_line: ConditionResolver,
+				) -> PrintItems {
+					let mut out = PrintItems::new();
+					let mut members = children.into_iter().peekable();
+					while let Some(mem) = members.next() {
+						if mem.should_start_with_newline {
+							p!(out, nl);
+						}
+						format_comments(&mem.before_trivia, CommentLocation::AboveItem, &mut out);
+						p!(&mut out, { mem.value });
+						let has_more = members.peek().is_some();
+						if has_more {
+							p!(out, str(","));
+						} else {
+							p!(out, if("trailing comma", multi_line, str(",")));
+						}
+						format_comments(&mem.inline_trivia, CommentLocation::ItemInline, &mut out);
+						p!(out, if_else("member separator", multi_line, nl)(sonl));
+					}
+					out
+				}
+
 				let (children, end_comments) = children_between::<Member>(
 					l.syntax().clone(),
 					l.l_brace_token().map(Into::into).as_ref(),
@@ -531,31 +555,6 @@
 					})
 				};
 
-				fn gen_members(
-					children: Vec<Child<Member>>,
-					multi_line: ConditionResolver,
-				) -> PrintItems {
-					let mut _out = PrintItems::new();
-					let out = &mut _out;
-					let mut members = children.into_iter().peekable();
-					while let Some(mem) = members.next() {
-						if mem.should_start_with_newline {
-							p!(out, nl);
-						}
-						format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);
-						p!(out, { mem.value });
-						let has_more = members.peek().is_some();
-						if has_more {
-							p!(out, str(","));
-						} else {
-							p!(out, if("trailing comma", multi_line, str(",")));
-						}
-						format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);
-						p!(out, if_else("member separator", multi_line, nl)(sonl));
-					}
-					_out
-				}
-
 				let members_items =
 					new_line_group(gen_members(children, multi_line.clone())).into_rc_path();
 
@@ -718,6 +717,27 @@
 
 impl Printable for ExprArray {
 	fn print(&self, out: &mut PrintItems) {
+		fn gen_elements(children: Vec<Child<Expr>>, multi_line: ConditionResolver) -> PrintItems {
+			let mut out = PrintItems::new();
+			let mut els = children.into_iter().peekable();
+			while let Some(el) = els.next() {
+				if el.should_start_with_newline {
+					p!(out, nl);
+				}
+				format_comments(&el.before_trivia, CommentLocation::AboveItem, &mut out);
+				p!(&mut out, { el.value });
+				let has_more = els.peek().is_some();
+				if has_more {
+					p!(out, str(","));
+				} else {
+					p!(out, if("trailing comma", multi_line, str(",")));
+				}
+				format_comments(&el.inline_trivia, CommentLocation::ItemInline, &mut out);
+				p!(out, if_else("element separator", multi_line, nl)(sonl));
+			}
+			out
+		}
+
 		let (children, end_comments) = children_between::<Expr>(
 			self.syntax().clone(),
 			self.l_brack_token().map(Into::into).as_ref(),
@@ -740,28 +760,6 @@
 			Rc::new(move |ctx: &mut ConditionResolverContext| is_multiple_lines(ctx, start, end))
 		};
 
-		fn gen_elements(children: Vec<Child<Expr>>, multi_line: ConditionResolver) -> PrintItems {
-			let mut _out = PrintItems::new();
-			let out = &mut _out;
-			let mut els = children.into_iter().peekable();
-			while let Some(el) = els.next() {
-				if el.should_start_with_newline {
-					p!(out, nl);
-				}
-				format_comments(&el.before_trivia, CommentLocation::AboveItem, out);
-				p!(out, { el.value });
-				let has_more = els.peek().is_some();
-				if has_more {
-					p!(out, str(","));
-				} else {
-					p!(out, if("trailing comma", multi_line, str(",")));
-				}
-				format_comments(&el.inline_trivia, CommentLocation::ItemInline, out);
-				p!(out, if_else("element separator", multi_line, nl)(sonl))
-			}
-			_out
-		}
-
 		let els_items = new_line_group(gen_elements(children, multi_line.clone())).into_rc_path();
 
 		let els = with_indent_eoi(multi_line, els_items.into(), end_comments);
@@ -800,7 +798,7 @@
 			Self::ExprString(s) => p!(out, { s.text() }),
 			Self::ExprNumber(n) => p!(out, { n.number() }),
 			Self::ExprArray(a) => {
-				p!(out, { a })
+				p!(out, { a });
 			}
 			Self::ExprObject(obj) => {
 				p!(out, { obj.obj_body() });
@@ -860,6 +858,11 @@
 	// 0 for hard tabs
 	pub indent: u8,
 }
+
+#[allow(
+	clippy::result_large_err,
+	reason = "TODO: there should be an intermediate representation for such reports"
+)]
 pub fn format(input: &str, opts: &FormatOptions) -> Result<String, SnippetBuilder> {
 	let (parsed, errors) = jrsonnet_rowan_parser::parse(input);
 	if !errors.is_empty() {
modifiedcrates/jrsonnet-macros/src/typed.rsdiffbeforeafterboth
--- a/crates/jrsonnet-macros/src/typed.rs
+++ b/crates/jrsonnet-macros/src/typed.rs
@@ -156,7 +156,7 @@
 		// optional flatten is handled in same way as serde
 		if self.attr.flatten {
 			return quote! {
-				#ident: <#ty as TypedObj>::parse(&obj).ok(),
+				#ident: <#ty as ParseTypedObj>::parse(&obj).ok(),
 			};
 		}
 
@@ -190,7 +190,7 @@
 		// optional flatten is handled in same way as serde
 		if self.attr.flatten {
 			return quote! {
-				#ident: <#ty as TypedObj>::parse(&obj)?,
+				#ident: <#ty as ParseTypedObj>::parse(&obj)?,
 			};
 		}
 
@@ -232,12 +232,12 @@
 				if self.is_option {
 					quote! {
 						if let Some(value) = self.#ident {
-							<#ty as TypedObj>::serialize(value, out)?;
+							<#ty as SerializeTypedObj>::serialize(value, out)?;
 						}
 					}
 				} else {
 					quote! {
-						<#ty as TypedObj>::serialize(self.#ident, out)?;
+						<#ty as SerializeTypedObj>::serialize(self.#ident, out)?;
 					}
 				}
 			},
modifiedcrates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/expr.rs
+++ b/crates/jrsonnet-parser/src/expr.rs
@@ -224,7 +224,8 @@
 	},
 	#[cfg(feature = "exp-destruct")]
 	Object {
-		fields: Vec<(IStr, Option<Destruct>, Option<Spanned<Expr>>)>,
+		#[allow(clippy::type_complexity)]
+		fields: Vec<(IStr, Option<Destruct>, Option<Rc<Spanned<Expr>>>)>,
 		rest: Option<DestructRest>,
 	},
 }
@@ -261,7 +262,7 @@
 				let mut out = 0;
 				for (_, into, _) in fields {
 					match into {
-						Some(v) => out += v.capacity_hint(),
+						Some(v) => out += v.binds_len(),
 						// Field is destructured to default name
 						None => out += 1,
 					}
modifiedcrates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -119,7 +119,7 @@
 			}
 		pub rule destruct_object(s: &ParserSettings) -> expr::Destruct
 			= "{" _
-				fields:(name:id() into:(_ ":" _ into:destruct(s) {into})? default:(_ "=" _ v:expr(s) {v})? {(name, into, default)})**comma()
+				fields:(name:id() into:(_ ":" _ into:destruct(s) {into})? default:(_ "=" _ v:expr(s) {v})? {(name, into, default.map(Rc::new))})**comma()
 				rest:(
 					comma() rest:destruct_rest()? {rest}
 					/ comma()? {None}
modifiedcrates/jrsonnet-rowan-parser/src/ast.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/ast.rs
+++ b/crates/jrsonnet-rowan-parser/src/ast.rs
@@ -2,8 +2,9 @@
 
 use crate::{SyntaxKind, SyntaxNode, SyntaxNodeChildren, SyntaxToken};
 
-/// The main trait to go from untyped `SyntaxNode`  to a typed ast. The
-/// conversion itself has zero runtime cost: ast and syntax nodes have exactly
+/// The main trait to go from untyped `SyntaxNode`  to a typed ast.
+///
+/// The conversion itself has zero runtime cost: ast and syntax nodes have exactly
 /// the same representation: a pointer to the tree root and a pointer to the
 /// node itself.
 pub trait AstNode {
modifiedcrates/jrsonnet-rowan-parser/src/event.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/event.rs
+++ b/crates/jrsonnet-rowan-parser/src/event.rs
@@ -56,7 +56,7 @@
 	fn text_offset(&self) -> TextSize {
 		if self.offset == 0 {
 			return 0.into();
-		};
+		}
 		self.lexemes.get(self.offset).map_or_else(
 			|| {
 				self.lexemes
modifiedcrates/jrsonnet-rowan-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/lib.rs
+++ b/crates/jrsonnet-rowan-parser/src/lib.rs
@@ -26,7 +26,7 @@
 
 use self::{
 	ast::support,
-	generated::nodes::{Expr, ExprBinary, ExprObjExtend},
+	generated::nodes::{Expr, ExprObjExtend},
 };
 
 pub fn parse(input: &str) -> (SourceFile, Vec<LocatedSyntaxError>) {
modifiedcrates/jrsonnet-rowan-parser/src/marker.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/marker.rs
+++ b/crates/jrsonnet-rowan-parser/src/marker.rs
@@ -141,7 +141,7 @@
 		new_m
 	}
 	/// Create new node around existing marker
-	/// If previous_pos is set - the wrapping node would not include everything that happened between wrapped node end and the current position of the parser
+	/// If `previous_pos` is set - the wrapping node would not include everything that happened between wrapped node end and the current position of the parser
 	fn wrap_raw(
 		self,
 		p: &mut Parser,
modifiedcrates/jrsonnet-rowan-parser/src/parser.rsdiffbeforeafterboth
before · crates/jrsonnet-rowan-parser/src/parser.rs
1use std::{cell::Cell, fmt, rc::Rc};23use rowan::{GreenNode, TextRange};45use crate::{6	event::Event,7	marker::{CompletedMarker, Marker},8	nodes::{BinaryOperatorKind, Literal, Number, Text, UnaryOperatorKind},9	token_set::SyntaxKindSet,10	AstToken, SyntaxKind,11	SyntaxKind::*,12	SyntaxNode, T, TS,13};1415pub struct Parse {16	pub green_node: GreenNode,17	pub errors: Vec<LocatedSyntaxError>,18}1920pub struct Parser {21	// TODO: remove all trivia before feeding to parser?22	kinds: Vec<SyntaxKind>,23	pub offset: usize,24	pub events: Vec<Event>,25	pub entered: u32,26	pub hints: Vec<(u32, TextRange, String)>,27	pub last_error_token: usize,28	expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>,29	steps: Cell<u64>,30}3132#[derive(Clone, Debug)]33pub enum SyntaxError {34	Unexpected {35		expected: ExpectedSyntax,36		found: SyntaxKind,37	},38	Missing {39		expected: ExpectedSyntax,40	},41	Custom {42		error: String,43	},44	Hint {45		error: String,46	},47}48impl fmt::Display for SyntaxError {49	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {50		match self {51			SyntaxError::Unexpected { expected, found } => {52				write!(f, "unexpected {found:?}, expecting {expected}")53			}54			SyntaxError::Missing { expected } => write!(f, "missing {expected}"),55			SyntaxError::Custom { error } => write!(f, "{error}"),56			SyntaxError::Hint { error } => write!(f, "{error}"),57		}58	}59}6061#[derive(Debug)]62pub struct LocatedSyntaxError {63	pub error: SyntaxError,64	pub range: TextRange,65}6667impl Parser {68	pub fn new(kinds: Vec<SyntaxKind>) -> Self {69		Self {70			kinds,71			offset: 0,72			events: vec![],73			entered: 0,74			last_error_token: 0,75			hints: vec![],76			expected_syntax_tracking_state: Rc::new(Cell::new(ExpectedSyntax::Unnamed(TS![]))),77			steps: Cell::new(0),78		}79	}80	pub fn clear_outdated_hints(&mut self) {81		let amount = self82			.hints83			.iter()84			.rev()85			.take_while(|h| h.0 > self.entered)86			.count();87		self.hints.truncate(self.hints.len() - amount);88	}89	fn clear_expected_syntaxes(&self) {90		self.expected_syntax_tracking_state91			.set(ExpectedSyntax::Unnamed(TS![]));92	}93	pub fn start(&mut self) -> Marker {94		let start_event_idx = self.events.len();95		self.events.push(Event::Pending);96		self.entered += 1;97		Marker::new(start_event_idx)98	}99	// pub fn start_ranger(&mut self) -> Ranger {100	// 	let pos = self.offset;101	// 	Ranger { pos }102	// }103	pub fn parse(mut self) -> Vec<Event> {104		let m = self.start();105		expr(&mut self);106		if !self.at(EOF) {107			let m = self.start();108			while !self.at(EOF) {109				self.bump();110			}111			m.complete_error(&mut self, "unexpected tokens after end");112		}113		m.complete(&mut self, SOURCE_FILE);114115		self.events116	}117118	pub(crate) fn expect(&mut self, kind: SyntaxKind) {119		self.expect_with_recovery_set(kind, TS![]);120	}121122	pub(crate) fn expect_with_recovery_set(123		&mut self,124		kind: SyntaxKind,125		recovery_set: SyntaxKindSet,126	) {127		if self.at(kind) {128			if kind != EOF {129				self.bump();130			}131		} else {132			self.error_with_recovery_set(recovery_set);133		}134	}135136	// pub(crate) fn expect_with_no_skip(&mut self, kind: SyntaxKind) {137	// 	if self.at(kind) {138	// 		self.bump();139	// 	} else {140	// 		self.error_with_no_skip();141	// 	}142	// }143	pub fn error_with_no_skip(&mut self) -> CompletedMarker {144		self.error_with_recovery_set(SyntaxKindSet::ALL)145	}146147	pub fn error_with_recovery_set(&mut self, recovery_set: SyntaxKindSet) -> CompletedMarker {148		let expected = self.expected_syntax_tracking_state.get();149		self.expected_syntax_tracking_state150			.set(ExpectedSyntax::Unnamed(TS![]));151152		if self.at_end() || self.at_ts(recovery_set) {153			let m = self.start();154			return m.complete_missing(self, expected);155		}156157		let current_token = self.current();158159		self.last_error_token = self.offset;160161		let m = self.start();162		self.bump();163		let m = m.complete_unexpected(self, expected, current_token);164		self.clear_expected_syntaxes();165		m166	}167	fn bump_assert(&mut self, kind: SyntaxKind) {168		assert!(self.at(kind), "expected {kind:?}");169		self.bump_remap(self.current());170	}171	fn bump(&mut self) {172		self.bump_remap(self.current());173	}174	fn bump_remap(&mut self, kind: SyntaxKind) {175		assert_ne!(self.offset, self.kinds.len(), "already at end");176		self.events.push(Event::Token { kind });177		self.offset += 1;178		self.clear_expected_syntaxes();179	}180	fn step(&self) {181		use std::fmt::Write;182		let steps = self.steps.get();183		if steps >= 15_000_000 {184			let mut out = "seems like parsing is stuck".to_owned();185			{186				let last = 20;187				write!(out, "\n\nLast {last} events:").unwrap();188				for (i, event) in self189					.events190					.iter()191					.skip(self.events.len().saturating_sub(last))192					.enumerate()193				{194					write!(out, "\n{i}. {event:?}").unwrap();195				}196			}197			{198				let next = 20;199				write!(out, "\n\nNext {next} tokens:").unwrap();200				for (i, tok) in self.kinds.iter().skip(self.offset).take(next).enumerate() {201					write!(out, "\n{i}. {tok:?}").unwrap();202				}203			}204			panic!("{out}")205		}206		self.steps.set(steps + 1);207	}208	fn nth(&self, i: usize) -> SyntaxKind {209		self.step();210		let mut offset = self.offset;211		for _ in 0..i {212			offset += 1;213		}214		self.kinds.get(offset).copied().unwrap_or(EOF)215	}216	fn current(&self) -> SyntaxKind {217		self.nth(0)218	}219	#[must_use]220	pub(crate) fn expected_syntax_name(&self, name: &'static str) -> ExpectedSyntaxGuard {221		self.expected_syntax_tracking_state222			.set(ExpectedSyntax::Named(name));223224		ExpectedSyntaxGuard::new(Rc::clone(&self.expected_syntax_tracking_state))225	}226	pub fn at(&self, kind: SyntaxKind) -> bool {227		self.nth_at(0, kind)228	}229	pub fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool {230		if n == 0 {231			if let ExpectedSyntax::Unnamed(kinds) = self.expected_syntax_tracking_state.get() {232				let kinds = kinds.with(kind);233				self.expected_syntax_tracking_state234					.set(ExpectedSyntax::Unnamed(kinds));235			}236		}237		self.nth(n) == kind238	}239	pub fn at_ts(&self, set: SyntaxKindSet) -> bool {240		if let ExpectedSyntax::Unnamed(kinds) = self.expected_syntax_tracking_state.get() {241			let kinds = kinds.union(set);242			self.expected_syntax_tracking_state243				.set(ExpectedSyntax::Unnamed(kinds));244		}245		set.contains(self.current())246	}247	pub fn at_end(&self) -> bool {248		self.at(EOF)249	}250}251pub struct ExpectedSyntaxGuard {252	expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>,253}254255impl ExpectedSyntaxGuard {256	fn new(expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>) -> Self {257		Self {258			expected_syntax_tracking_state,259		}260	}261}262263impl Drop for ExpectedSyntaxGuard {264	fn drop(&mut self) {265		self.expected_syntax_tracking_state266			.set(ExpectedSyntax::Unnamed(TS![]));267	}268}269270#[derive(Clone, Debug, Copy)]271pub enum ExpectedSyntax {272	Named(&'static str),273	Unnamed(SyntaxKindSet),274}275impl fmt::Display for ExpectedSyntax {276	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {277		match self {278			Self::Named(name) => write!(f, "{name}"),279			Self::Unnamed(set) => write!(f, "{set}"),280		}281	}282}283284fn expr(p: &mut Parser) -> CompletedMarker {285	let m = p.start();286	while p.at(T![local]) || p.at(T![assert]) {287		let m = p.start();288289		if p.at(T![local]) {290			p.bump();291			loop {292				if p.at(T![;]) {293					p.bump();294					break;295				}296				bind(p);297298				if p.at(T![,]) {299					p.bump();300					continue;301				}302				p.expect(T![;]);303				break;304			}305			m.complete(p, STMT_LOCAL);306		} else {307			assertion(p);308			p.expect(T![;]);309			m.complete(p, STMT_ASSERT);310		}311	}312	match expr_binding_power(p, 0) {313		Ok(m) | Err(m) => m,314	};315	m.complete(p, EXPR)316}317fn expr_binding_power(318	p: &mut Parser,319	minimum_binding_power: u8,320) -> Result<CompletedMarker, CompletedMarker> {321	let mut lhs = lhs(p)?;322323	while let Some(op) = BinaryOperatorKind::cast(p.current())324		.or_else(|| p.at(T!['{']).then_some(BinaryOperatorKind::MetaObjectApply))325	{326		let (left_binding_power, right_binding_power) = op.binding_power();327		if left_binding_power < minimum_binding_power {328			break;329		}330331		let m = lhs.wrap(p, EXPR, false);332333		// Object apply is not a real operator, we dont have something to bump334		if op != BinaryOperatorKind::MetaObjectApply {335			p.bump();336		}337338		let m = m.precede(p);339		let parsed_rhs = expr_binding_power(p, right_binding_power)340			.map(|v| v.precede(p).complete(p, EXPR))341			.is_ok();342		lhs = m.complete(343			p,344			if op == BinaryOperatorKind::MetaObjectApply {345				EXPR_OBJ_EXTEND346			} else {347				EXPR_BINARY348			},349		);350351		if !parsed_rhs {352			break;353		}354	}355	Ok(lhs)356}357358const COMPSPEC: SyntaxKindSet = TS![for if];359fn compspec(p: &mut Parser) -> CompletedMarker {360	assert!(p.at_ts(COMPSPEC));361	if p.at(T![for]) {362		let m = p.start();363		p.bump();364		destruct(p);365		p.expect(T![in]);366		expr(p);367		m.complete(p, FOR_SPEC)368	} else if p.at(T![if]) {369		let m = p.start();370		p.bump();371		expr(p);372		m.complete(p, IF_SPEC)373	} else {374		unreachable!()375	}376}377378fn comma(p: &mut Parser) -> bool {379	comma_with_alternatives(p, TS![])380}381fn comma_with_alternatives(p: &mut Parser, set: SyntaxKindSet) -> bool {382	if p.at(T![,]) {383		p.bump();384		true385	} else if p.at_ts(set) {386		let _ex = p.expected_syntax_name("comma");387		p.expect_with_recovery_set(T![,], TS![]);388		true389	} else {390		false391	}392}393394fn field_name(p: &mut Parser) {395	let _e = p.expected_syntax_name("field name");396	let m = p.start();397	if p.at(T!['[']) {398		p.bump();399		expr(p);400		p.expect(T![']']);401		m.complete(p, FIELD_NAME_DYNAMIC);402	} else if p.at(IDENT) {403		name(p);404		m.complete(p, FIELD_NAME_FIXED);405	} else if Text::can_cast(p.current()) {406		text(p);407		m.complete(p, FIELD_NAME_FIXED);408	} else {409		m.forget(p);410		// ::: it split because in TS it is being handled as : ::411		p.error_with_recovery_set(TS![; : :: '('].with(T![:::]));412	}413}414fn visibility(p: &mut Parser) {415	// ::: it split because in TS it is being handled as : ::416	if p.at_ts(TS![: ::].with(T![:::])) {417		p.bump();418	} else {419		p.error_with_recovery_set(TS![=]);420	}421}422fn assertion(p: &mut Parser) {423	let m = p.start();424	p.bump_assert(T![assert]);425	expr(p);426	if p.at(T![:]) {427		p.bump();428		expr(p);429	}430	m.complete(p, ASSERTION);431}432fn object(p: &mut Parser) -> CompletedMarker {433	let m_t = p.start();434	let m = p.start();435	p.bump_assert(T!['{']);436437	let mut elems = 0;438	let mut compspecs = Vec::new();439	let mut asserts = Vec::new();440	loop {441		if p.at(T!['}']) {442			p.bump();443			break;444		}445		if p.at_ts(TS![for]) {446			if elems == 0 {447				let m = p.start();448				m.complete_missing(p, ExpectedSyntax::Named("field definition"));449			}450			while p.at_ts(COMPSPEC) {451				compspecs.push(compspec(p));452			}453			if comma_with_alternatives(p, TS![;]) {454				continue;455			}456			p.expect(R_BRACE);457			break;458		}459		let m = p.start();460		if p.at(T![local]) {461			obj_local(p);462			m.complete(p, MEMBER_BIND_STMT);463		} else if p.at(T![assert]) {464			assertion(p);465			asserts.push(m.complete(p, MEMBER_ASSERT_STMT));466		} else {467			field_name(p);468			if p.at(T![+]) {469				p.bump();470			}471			let params = if p.at(T!['(']) {472				params_desc(p);473				visibility(p);474				expr(p);475				true476			// ::: it split because in TS it is being handled as : ::477			} else if p.at_ts(TS![: ::].with(T![:::])) && p.nth_at(1, T![function]) {478				visibility(p);479				p.bump_assert(T![function]);480				params_desc(p);481				expr(p);482				true483			} else {484				visibility(p);485				expr(p);486				false487			};488			elems += 1;489490			if params {491				m.complete(p, MEMBER_FIELD_METHOD)492			} else {493				m.complete(p, MEMBER_FIELD_NORMAL)494			};495		};496		while p.at_ts(COMPSPEC) {497			compspecs.push(compspec(p));498		}499		if comma_with_alternatives(p, TS![;]) {500			continue;501		}502		p.expect(R_BRACE);503		break;504	}505506	if elems > 1 && !compspecs.is_empty() {507		for errored in compspecs {508			errored.wrap_error(509				p,510				"compspec may only be used if there is only one object element",511				true,512			);513		}514		m.complete(p, OBJ_BODY_MEMBER_LIST);515	} else if !compspecs.is_empty() {516		for errored in asserts {517			errored.wrap_error(p, "asserts can't be used in object comprehensions", true);518		}519		m.complete(p, OBJ_BODY_COMP);520	} else {521		m.complete(p, OBJ_BODY_MEMBER_LIST);522	}523	m_t.complete(p, EXPR_OBJECT)524}525fn param(p: &mut Parser) {526	let m = p.start();527	destruct(p);528	if p.at(T![=]) {529		p.bump();530		expr(p);531	}532	m.complete(p, PARAM);533}534fn params_desc(p: &mut Parser) -> CompletedMarker {535	let m = p.start();536	p.bump_assert(T!['(']);537538	loop {539		if p.at(T![')']) {540			p.bump();541			break;542		}543		param(p);544		if comma(p) {545			continue;546		}547		p.expect(T![')']);548		break;549	}550551	m.complete(p, PARAMS_DESC)552}553fn args_desc(p: &mut Parser) {554	let m = p.start();555	p.bump_assert(T!['(']);556557	let started_named = Cell::new(false);558	let mut unnamed_after_named = Vec::new();559560	loop {561		if p.at(T![')']) {562			break;563		}564565		let m = p.start();566		if p.at(IDENT) && p.nth_at(1, T![=]) {567			name(p);568			p.bump();569			expr(p);570			m.complete(p, ARG);571			started_named.set(true);572		} else {573			expr(p);574			let arg = m.complete(p, ARG);575			if started_named.get() {576				unnamed_after_named.push(arg);577			}578		}579		if comma(p) {580			continue;581		}582		break;583	}584	p.expect(T![')']);585	if p.at(T![tailstrict]) {586		p.bump();587	}588589	for errored in unnamed_after_named {590		errored.wrap_error(p, "can't use positional arguments after named", true);591	}592593	m.complete(p, ARGS_DESC);594}595596fn array(p: &mut Parser) -> CompletedMarker {597	// Start the list node598	let m = p.start();599	p.bump_assert(T!['[']);600601	let mut compspecs = Vec::new();602	let mut elems = 0;603604	loop {605		if p.at(T![']']) {606			p.bump();607			break;608		}609		if elems != 0 && p.at_ts(TS![for]) {610			while p.at_ts(COMPSPEC) {611				compspecs.push(compspec(p));612			}613			if comma(p) {614				continue;615			}616			p.expect(T![']']);617			break;618		}619		expr(p);620		elems += 1;621		while p.at_ts(COMPSPEC) {622			compspecs.push(compspec(p));623		}624		if comma(p) {625			continue;626		}627		p.expect(T![']']);628		break;629	}630631	if elems > 1 && !compspecs.is_empty() {632		for spec in compspecs {633			spec.wrap_error(634				p,635				"compspec may only be used if there is only one array element",636				true,637			);638		}639640		m.complete(p, EXPR_ARRAY)641	} else if !compspecs.is_empty() {642		m.complete(p, EXPR_ARRAY_COMP)643	} else {644		m.complete(p, EXPR_ARRAY)645	}646}647/// Returns true if it was slice, false if just index648#[must_use]649fn slice_desc_or_index(p: &mut Parser) -> bool {650	let m = p.start();651	p.bump();652	// TODO: do not treat :, ::, ::: as full tokens?653	// Start654	if !p.at(T![:]) && !p.at(T![::]) {655		expr(p);656	}657	if p.at(T![:]) {658		p.bump();659		// End660		if !p.at(T![']']) {661			expr(p).wrap(p, SLICE_DESC_END, true);662		}663		if p.at(T![:]) {664			p.bump();665			// Step666			if !p.at(T![']']) {667				expr(p).wrap(p, SLICE_DESC_STEP, true);668			}669		}670	} else if p.at(T![::]) {671		p.bump();672		// End673		if !p.at(T![']']) {674			expr(p).wrap(p, SLICE_DESC_END, true);675		}676	} else {677		// It was not a slice678		p.expect(T![']']);679		m.forget(p);680		return false;681	}682	p.expect(T![']']);683	m.complete(p, SLICE_DESC);684	true685}686687fn suffix(p: &mut Parser) {688	loop {689		let start = p.start();690		let _marker: CompletedMarker = if p.at(T![?]) {691			p.bump();692			p.expect(T![.]);693			if p.at(IDENT) {694				name(p);695				start.complete(p, SUFFIX_INDEX)696			} else if p.at(T!['[']) {697				p.bump();698				expr(p);699				p.expect(T![']']);700				start.complete(p, SUFFIX_INDEX_EXPR)701			} else {702				start.complete_missing(p, ExpectedSyntax::Named("index"))703			}704		} else if p.at(T![.]) {705			p.bump();706			name(p);707			start.complete(p, SUFFIX_INDEX)708		} else if p.at(T!['[']) {709			if slice_desc_or_index(p) {710				start.complete(p, SUFFIX_SLICE)711			} else {712				start.complete(p, SUFFIX_INDEX_EXPR)713			}714		} else if p.at(T!['(']) {715			args_desc(p);716			start.complete(p, SUFFIX_APPLY)717		} else {718			start.forget(p);719			break;720		};721	}722}723724fn lhs(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {725	let lhs = lhs_basic(p)?;726727	suffix(p);728729	Ok(lhs)730}731fn name(p: &mut Parser) {732	let m = p.start();733	p.expect(IDENT);734	m.complete(p, NAME);735}736fn destruct_rest(p: &mut Parser) {737	let m = p.start();738	p.bump_assert(T![...]);739	if p.at(IDENT) {740		p.bump();741	}742	m.complete(p, DESTRUCT_REST);743}744fn destruct_object_field(p: &mut Parser) {745	let m = p.start();746	name(p);747	if p.at(T![:]) {748		p.bump();749		destruct(p);750	};751	if p.at(T![=]) {752		p.bump();753		expr(p);754	}755	m.complete(p, DESTRUCT_OBJECT_FIELD);756}757fn obj_local(p: &mut Parser) {758	let m = p.start();759	p.bump_assert(T![local]);760	bind(p);761	m.complete(p, OBJ_LOCAL);762}763fn destruct(p: &mut Parser) -> CompletedMarker {764	let m = p.start();765	let _ex = p.expected_syntax_name("destruction specifier");766	if p.at(T![?]) {767		p.bump();768		m.complete(p, DESTRUCT_SKIP)769	} else if p.at(T!['[']) {770		p.bump();771		// let mut had_rest = false;772		loop {773			if p.at(T![']']) {774				p.bump();775				break;776			} else if p.at(T![...]) {777				// let m_err = p.start_ranger();778				destruct_rest(p);779			// if had_rest {780			// 	p.custom_error(m_err.finish(p), "only one rest can be present in array");781			// }782			// had_rest = true;783			} else {784				destruct(p);785			}786			if p.at(T![,]) {787				p.bump();788				continue;789			}790			p.expect(T![']']);791			break;792		}793		m.complete(p, DESTRUCT_ARRAY)794	} else if p.at(T!['{']) {795		p.bump();796		let mut had_rest = false;797		loop {798			if p.at(T!['}']) {799				p.bump();800				break;801			} else if p.at(T![...]) {802				// let m_err = p.start_ranger();803				destruct_rest(p);804				// if had_rest {805				// 	p.custom_error(m_err.finish(p), "only one rest can be present in object");806				// }807				had_rest = true;808			} else {809				if had_rest {810					p.error_with_recovery_set(TS![]);811				}812				destruct_object_field(p);813			}814			if p.at(T![,]) {815				p.bump();816				continue;817			}818			p.expect(T!['}']);819			break;820		}821		m.complete(p, DESTRUCT_OBJECT)822	} else if p.at(IDENT) {823		name(p);824		m.complete(p, DESTRUCT_FULL)825	} else {826		m.forget(p);827		p.error_with_recovery_set(TS![; , '}', '(', :])828	}829}830fn bind(p: &mut Parser) {831	let m = p.start();832	if p.at(IDENT) && p.nth_at(1, T!['(']) {833		name(p);834		params_desc(p);835		p.expect(T![=]);836		expr(p);837		m.complete(p, BIND_FUNCTION)838	} else if p.at(IDENT) && p.nth_at(1, T![=]) && p.nth_at(2, T![function]) {839		name(p);840		p.expect(T![=]);841		p.expect(T![function]);842		params_desc(p);843		expr(p);844		m.complete(p, BIND_FUNCTION)845	} else {846		destruct(p);847		p.expect(T![=]);848		expr(p);849		m.complete(p, BIND_DESTRUCT)850	};851}852fn text(p: &mut Parser) {853	assert!(Text::can_cast(p.current()));854	p.bump();855}856fn number(p: &mut Parser) {857	assert!(Number::can_cast(p.current()));858	p.bump();859}860fn literal(p: &mut Parser) {861	assert!(Literal::can_cast(p.current()));862	p.bump();863}864fn lhs_basic(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {865	let _e = p.expected_syntax_name("expression");866	Ok(if Literal::can_cast(p.current()) {867		let m = p.start();868		literal(p);869		m.complete(p, EXPR_LITERAL)870	} else if Text::can_cast(p.current()) {871		let m = p.start();872		text(p);873		m.complete(p, EXPR_STRING)874	} else if Number::can_cast(p.current()) {875		let m = p.start();876		number(p);877		m.complete(p, EXPR_NUMBER)878	} else if p.at(IDENT) {879		let m = p.start();880		name(p);881		m.complete(p, EXPR_VAR)882	} else if p.at(T![if]) {883		let m = p.start();884		p.bump();885		expr(p);886		p.expect(T![then]);887		expr(p).wrap(p, TRUE_EXPR, true);888		if p.at(T![else]) {889			p.bump();890			expr(p).wrap(p, FALSE_EXPR, true);891		}892		m.complete(p, EXPR_IF_THEN_ELSE)893	} else if p.at(T!['[']) {894		array(p)895	} else if p.at(T!['{']) {896		object(p)897	} else if p.at(T![function]) {898		let m = p.start();899		p.bump();900		params_desc(p);901		expr(p);902		m.complete(p, EXPR_FUNCTION)903	} else if p.at(T![error]) {904		let m = p.start();905		p.bump();906		expr(p);907		m.complete(p, EXPR_ERROR)908	} else if p.at(T![import]) || p.at(T![importstr]) || p.at(T![importbin]) {909		let m = p.start();910		p.bump();911		text(p);912		m.complete(p, EXPR_IMPORT)913	} else if let Some(op) = UnaryOperatorKind::cast(p.current()) {914		let ((), right_binding_power) = op.binding_power();915916		let m = p.start();917		p.bump();918		let _ = expr_binding_power(p, right_binding_power);919		m.complete(p, EXPR_UNARY)920	} else if p.at(T!['(']) {921		let m = p.start();922		p.bump();923		expr(p);924		p.expect(T![')']);925		m.complete(p, EXPR_PARENED)926	} else {927		return Err(p.error_with_no_skip());928	})929}930931impl Parse {932	pub fn syntax(&self) -> SyntaxNode {933		SyntaxNode::new_root(self.green_node.clone())934	}935}
after · crates/jrsonnet-rowan-parser/src/parser.rs
1use std::{cell::Cell, fmt, rc::Rc};23use rowan::{GreenNode, TextRange};45use crate::{6	event::Event,7	marker::{CompletedMarker, Marker},8	nodes::{BinaryOperatorKind, Literal, Number, Text, UnaryOperatorKind},9	token_set::SyntaxKindSet,10	AstToken, SyntaxKind,11	SyntaxKind::*,12	SyntaxNode, T, TS,13};1415pub struct Parse {16	pub green_node: GreenNode,17	pub errors: Vec<LocatedSyntaxError>,18}1920pub struct Parser {21	// TODO: remove all trivia before feeding to parser?22	kinds: Vec<SyntaxKind>,23	pub offset: usize,24	pub events: Vec<Event>,25	pub entered: u32,26	pub hints: Vec<(u32, TextRange, String)>,27	pub last_error_token: usize,28	expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>,29	steps: Cell<u64>,30}3132#[derive(Clone, Debug)]33pub enum SyntaxError {34	Unexpected {35		expected: ExpectedSyntax,36		found: SyntaxKind,37	},38	Missing {39		expected: ExpectedSyntax,40	},41	Custom {42		error: String,43	},44	Hint {45		error: String,46	},47}48impl fmt::Display for SyntaxError {49	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {50		match self {51			SyntaxError::Unexpected { expected, found } => {52				write!(f, "unexpected {found:?}, expecting {expected}")53			}54			SyntaxError::Missing { expected } => write!(f, "missing {expected}"),55			SyntaxError::Custom { error } | SyntaxError::Hint { error } => write!(f, "{error}"),56		}57	}58}5960#[derive(Debug)]61pub struct LocatedSyntaxError {62	pub error: SyntaxError,63	pub range: TextRange,64}6566impl Parser {67	pub fn new(kinds: Vec<SyntaxKind>) -> Self {68		Self {69			kinds,70			offset: 0,71			events: vec![],72			entered: 0,73			last_error_token: 0,74			hints: vec![],75			expected_syntax_tracking_state: Rc::new(Cell::new(ExpectedSyntax::Unnamed(TS![]))),76			steps: Cell::new(0),77		}78	}79	pub fn clear_outdated_hints(&mut self) {80		let amount = self81			.hints82			.iter()83			.rev()84			.take_while(|h| h.0 > self.entered)85			.count();86		self.hints.truncate(self.hints.len() - amount);87	}88	fn clear_expected_syntaxes(&self) {89		self.expected_syntax_tracking_state90			.set(ExpectedSyntax::Unnamed(TS![]));91	}92	pub fn start(&mut self) -> Marker {93		let start_event_idx = self.events.len();94		self.events.push(Event::Pending);95		self.entered += 1;96		Marker::new(start_event_idx)97	}98	// pub fn start_ranger(&mut self) -> Ranger {99	// 	let pos = self.offset;100	// 	Ranger { pos }101	// }102	pub fn parse(mut self) -> Vec<Event> {103		let m = self.start();104		expr(&mut self);105		if !self.at(EOF) {106			let m = self.start();107			while !self.at(EOF) {108				self.bump();109			}110			m.complete_error(&mut self, "unexpected tokens after end");111		}112		m.complete(&mut self, SOURCE_FILE);113114		self.events115	}116117	pub(crate) fn expect(&mut self, kind: SyntaxKind) {118		self.expect_with_recovery_set(kind, TS![]);119	}120121	pub(crate) fn expect_with_recovery_set(122		&mut self,123		kind: SyntaxKind,124		recovery_set: SyntaxKindSet,125	) {126		if self.at(kind) {127			if kind != EOF {128				self.bump();129			}130		} else {131			self.error_with_recovery_set(recovery_set);132		}133	}134135	// pub(crate) fn expect_with_no_skip(&mut self, kind: SyntaxKind) {136	// 	if self.at(kind) {137	// 		self.bump();138	// 	} else {139	// 		self.error_with_no_skip();140	// 	}141	// }142	pub fn error_with_no_skip(&mut self) -> CompletedMarker {143		self.error_with_recovery_set(SyntaxKindSet::ALL)144	}145146	pub fn error_with_recovery_set(&mut self, recovery_set: SyntaxKindSet) -> CompletedMarker {147		let expected = self.expected_syntax_tracking_state.get();148		self.expected_syntax_tracking_state149			.set(ExpectedSyntax::Unnamed(TS![]));150151		if self.at_end() || self.at_ts(recovery_set) {152			let m = self.start();153			return m.complete_missing(self, expected);154		}155156		let current_token = self.current();157158		self.last_error_token = self.offset;159160		let m = self.start();161		self.bump();162		let m = m.complete_unexpected(self, expected, current_token);163		self.clear_expected_syntaxes();164		m165	}166	fn bump_assert(&mut self, kind: SyntaxKind) {167		assert!(self.at(kind), "expected {kind:?}");168		self.bump_remap(self.current());169	}170	fn bump(&mut self) {171		self.bump_remap(self.current());172	}173	fn bump_remap(&mut self, kind: SyntaxKind) {174		assert_ne!(self.offset, self.kinds.len(), "already at end");175		self.events.push(Event::Token { kind });176		self.offset += 1;177		self.clear_expected_syntaxes();178	}179	fn step(&self) {180		use std::fmt::Write;181		let steps = self.steps.get();182		if steps >= 15_000_000 {183			let mut out = "seems like parsing is stuck".to_owned();184			{185				let last = 20;186				write!(out, "\n\nLast {last} events:").unwrap();187				for (i, event) in self188					.events189					.iter()190					.skip(self.events.len().saturating_sub(last))191					.enumerate()192				{193					write!(out, "\n{i}. {event:?}").unwrap();194				}195			}196			{197				let next = 20;198				write!(out, "\n\nNext {next} tokens:").unwrap();199				for (i, tok) in self.kinds.iter().skip(self.offset).take(next).enumerate() {200					write!(out, "\n{i}. {tok:?}").unwrap();201				}202			}203			panic!("{out}")204		}205		self.steps.set(steps + 1);206	}207	fn nth(&self, i: usize) -> SyntaxKind {208		self.step();209		let mut offset = self.offset;210		for _ in 0..i {211			offset += 1;212		}213		self.kinds.get(offset).copied().unwrap_or(EOF)214	}215	fn current(&self) -> SyntaxKind {216		self.nth(0)217	}218	#[must_use]219	pub(crate) fn expected_syntax_name(&self, name: &'static str) -> ExpectedSyntaxGuard {220		self.expected_syntax_tracking_state221			.set(ExpectedSyntax::Named(name));222223		ExpectedSyntaxGuard::new(Rc::clone(&self.expected_syntax_tracking_state))224	}225	pub fn at(&self, kind: SyntaxKind) -> bool {226		self.nth_at(0, kind)227	}228	pub fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool {229		if n == 0 {230			if let ExpectedSyntax::Unnamed(kinds) = self.expected_syntax_tracking_state.get() {231				let kinds = kinds.with(kind);232				self.expected_syntax_tracking_state233					.set(ExpectedSyntax::Unnamed(kinds));234			}235		}236		self.nth(n) == kind237	}238	pub fn at_ts(&self, set: SyntaxKindSet) -> bool {239		if let ExpectedSyntax::Unnamed(kinds) = self.expected_syntax_tracking_state.get() {240			let kinds = kinds.union(set);241			self.expected_syntax_tracking_state242				.set(ExpectedSyntax::Unnamed(kinds));243		}244		set.contains(self.current())245	}246	pub fn at_end(&self) -> bool {247		self.at(EOF)248	}249}250pub struct ExpectedSyntaxGuard {251	expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>,252}253254impl ExpectedSyntaxGuard {255	fn new(expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>) -> Self {256		Self {257			expected_syntax_tracking_state,258		}259	}260}261262impl Drop for ExpectedSyntaxGuard {263	fn drop(&mut self) {264		self.expected_syntax_tracking_state265			.set(ExpectedSyntax::Unnamed(TS![]));266	}267}268269#[derive(Clone, Debug, Copy)]270pub enum ExpectedSyntax {271	Named(&'static str),272	Unnamed(SyntaxKindSet),273}274impl fmt::Display for ExpectedSyntax {275	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {276		match self {277			Self::Named(name) => write!(f, "{name}"),278			Self::Unnamed(set) => write!(f, "{set}"),279		}280	}281}282283fn expr(p: &mut Parser) -> CompletedMarker {284	let m = p.start();285	while p.at(T![local]) || p.at(T![assert]) {286		let m = p.start();287288		if p.at(T![local]) {289			p.bump();290			loop {291				if p.at(T![;]) {292					p.bump();293					break;294				}295				bind(p);296297				if p.at(T![,]) {298					p.bump();299					continue;300				}301				p.expect(T![;]);302				break;303			}304			m.complete(p, STMT_LOCAL);305		} else {306			assertion(p);307			p.expect(T![;]);308			m.complete(p, STMT_ASSERT);309		}310	}311	match expr_binding_power(p, 0) {312		Ok(m) | Err(m) => m,313	};314	m.complete(p, EXPR)315}316fn expr_binding_power(317	p: &mut Parser,318	minimum_binding_power: u8,319) -> Result<CompletedMarker, CompletedMarker> {320	let mut lhs = lhs(p)?;321322	while let Some(op) = BinaryOperatorKind::cast(p.current())323		.or_else(|| p.at(T!['{']).then_some(BinaryOperatorKind::MetaObjectApply))324	{325		let (left_binding_power, right_binding_power) = op.binding_power();326		if left_binding_power < minimum_binding_power {327			break;328		}329330		let m = lhs.wrap(p, EXPR, false);331332		// Object apply is not a real operator, we dont have something to bump333		if op != BinaryOperatorKind::MetaObjectApply {334			p.bump();335		}336337		let m = m.precede(p);338		let parsed_rhs = expr_binding_power(p, right_binding_power)339			.map(|v| v.precede(p).complete(p, EXPR))340			.is_ok();341		lhs = m.complete(342			p,343			if op == BinaryOperatorKind::MetaObjectApply {344				EXPR_OBJ_EXTEND345			} else {346				EXPR_BINARY347			},348		);349350		if !parsed_rhs {351			break;352		}353	}354	Ok(lhs)355}356357const COMPSPEC: SyntaxKindSet = TS![for if];358fn compspec(p: &mut Parser) -> CompletedMarker {359	assert!(p.at_ts(COMPSPEC));360	if p.at(T![for]) {361		let m = p.start();362		p.bump();363		destruct(p);364		p.expect(T![in]);365		expr(p);366		m.complete(p, FOR_SPEC)367	} else if p.at(T![if]) {368		let m = p.start();369		p.bump();370		expr(p);371		m.complete(p, IF_SPEC)372	} else {373		unreachable!()374	}375}376377fn comma(p: &mut Parser) -> bool {378	comma_with_alternatives(p, TS![])379}380fn comma_with_alternatives(p: &mut Parser, set: SyntaxKindSet) -> bool {381	if p.at(T![,]) {382		p.bump();383		true384	} else if p.at_ts(set) {385		let _ex = p.expected_syntax_name("comma");386		p.expect_with_recovery_set(T![,], TS![]);387		true388	} else {389		false390	}391}392393fn field_name(p: &mut Parser) {394	let _e = p.expected_syntax_name("field name");395	let m = p.start();396	if p.at(T!['[']) {397		p.bump();398		expr(p);399		p.expect(T![']']);400		m.complete(p, FIELD_NAME_DYNAMIC);401	} else if p.at(IDENT) {402		name(p);403		m.complete(p, FIELD_NAME_FIXED);404	} else if Text::can_cast(p.current()) {405		text(p);406		m.complete(p, FIELD_NAME_FIXED);407	} else {408		m.forget(p);409		// ::: it split because in TS it is being handled as : ::410		p.error_with_recovery_set(TS![; : :: '('].with(T![:::]));411	}412}413fn visibility(p: &mut Parser) {414	// ::: it split because in TS it is being handled as : ::415	if p.at_ts(TS![: ::].with(T![:::])) {416		p.bump();417	} else {418		p.error_with_recovery_set(TS![=]);419	}420}421fn assertion(p: &mut Parser) {422	let m = p.start();423	p.bump_assert(T![assert]);424	expr(p);425	if p.at(T![:]) {426		p.bump();427		expr(p);428	}429	m.complete(p, ASSERTION);430}431fn object(p: &mut Parser) -> CompletedMarker {432	let m_t = p.start();433	let m = p.start();434	p.bump_assert(T!['{']);435436	let mut elems = 0;437	let mut compspecs = Vec::new();438	let mut asserts = Vec::new();439	loop {440		if p.at(T!['}']) {441			p.bump();442			break;443		}444		if p.at_ts(TS![for]) {445			if elems == 0 {446				let m = p.start();447				m.complete_missing(p, ExpectedSyntax::Named("field definition"));448			}449			while p.at_ts(COMPSPEC) {450				compspecs.push(compspec(p));451			}452			if comma_with_alternatives(p, TS![;]) {453				continue;454			}455			p.expect(R_BRACE);456			break;457		}458		let m = p.start();459		if p.at(T![local]) {460			obj_local(p);461			m.complete(p, MEMBER_BIND_STMT);462		} else if p.at(T![assert]) {463			assertion(p);464			asserts.push(m.complete(p, MEMBER_ASSERT_STMT));465		} else {466			field_name(p);467			if p.at(T![+]) {468				p.bump();469			}470			let params = if p.at(T!['(']) {471				params_desc(p);472				visibility(p);473				expr(p);474				true475			// ::: it split because in TS it is being handled as : ::476			} else if p.at_ts(TS![: ::].with(T![:::])) && p.nth_at(1, T![function]) {477				visibility(p);478				p.bump_assert(T![function]);479				params_desc(p);480				expr(p);481				true482			} else {483				visibility(p);484				expr(p);485				false486			};487			elems += 1;488489			if params {490				m.complete(p, MEMBER_FIELD_METHOD)491			} else {492				m.complete(p, MEMBER_FIELD_NORMAL)493			};494		}495		while p.at_ts(COMPSPEC) {496			compspecs.push(compspec(p));497		}498		if comma_with_alternatives(p, TS![;]) {499			continue;500		}501		p.expect(R_BRACE);502		break;503	}504505	if elems > 1 && !compspecs.is_empty() {506		for errored in compspecs {507			errored.wrap_error(508				p,509				"compspec may only be used if there is only one object element",510				true,511			);512		}513		m.complete(p, OBJ_BODY_MEMBER_LIST);514	} else if !compspecs.is_empty() {515		for errored in asserts {516			errored.wrap_error(p, "asserts can't be used in object comprehensions", true);517		}518		m.complete(p, OBJ_BODY_COMP);519	} else {520		m.complete(p, OBJ_BODY_MEMBER_LIST);521	}522	m_t.complete(p, EXPR_OBJECT)523}524fn param(p: &mut Parser) {525	let m = p.start();526	destruct(p);527	if p.at(T![=]) {528		p.bump();529		expr(p);530	}531	m.complete(p, PARAM);532}533fn params_desc(p: &mut Parser) -> CompletedMarker {534	let m = p.start();535	p.bump_assert(T!['(']);536537	loop {538		if p.at(T![')']) {539			p.bump();540			break;541		}542		param(p);543		if comma(p) {544			continue;545		}546		p.expect(T![')']);547		break;548	}549550	m.complete(p, PARAMS_DESC)551}552fn args_desc(p: &mut Parser) {553	let m = p.start();554	p.bump_assert(T!['(']);555556	let started_named = Cell::new(false);557	let mut unnamed_after_named = Vec::new();558559	loop {560		if p.at(T![')']) {561			break;562		}563564		let m = p.start();565		if p.at(IDENT) && p.nth_at(1, T![=]) {566			name(p);567			p.bump();568			expr(p);569			m.complete(p, ARG);570			started_named.set(true);571		} else {572			expr(p);573			let arg = m.complete(p, ARG);574			if started_named.get() {575				unnamed_after_named.push(arg);576			}577		}578		if comma(p) {579			continue;580		}581		break;582	}583	p.expect(T![')']);584	if p.at(T![tailstrict]) {585		p.bump();586	}587588	for errored in unnamed_after_named {589		errored.wrap_error(p, "can't use positional arguments after named", true);590	}591592	m.complete(p, ARGS_DESC);593}594595fn array(p: &mut Parser) -> CompletedMarker {596	// Start the list node597	let m = p.start();598	p.bump_assert(T!['[']);599600	let mut compspecs = Vec::new();601	let mut elems = 0;602603	loop {604		if p.at(T![']']) {605			p.bump();606			break;607		}608		if elems != 0 && p.at_ts(TS![for]) {609			while p.at_ts(COMPSPEC) {610				compspecs.push(compspec(p));611			}612			if comma(p) {613				continue;614			}615			p.expect(T![']']);616			break;617		}618		expr(p);619		elems += 1;620		while p.at_ts(COMPSPEC) {621			compspecs.push(compspec(p));622		}623		if comma(p) {624			continue;625		}626		p.expect(T![']']);627		break;628	}629630	if elems > 1 && !compspecs.is_empty() {631		for spec in compspecs {632			spec.wrap_error(633				p,634				"compspec may only be used if there is only one array element",635				true,636			);637		}638639		m.complete(p, EXPR_ARRAY)640	} else if !compspecs.is_empty() {641		m.complete(p, EXPR_ARRAY_COMP)642	} else {643		m.complete(p, EXPR_ARRAY)644	}645}646/// Returns true if it was slice, false if just index647#[must_use]648fn slice_desc_or_index(p: &mut Parser) -> bool {649	let m = p.start();650	p.bump();651	// TODO: do not treat :, ::, ::: as full tokens?652	// Start653	if !p.at(T![:]) && !p.at(T![::]) {654		expr(p);655	}656	if p.at(T![:]) {657		p.bump();658		// End659		if !p.at(T![']']) {660			expr(p).wrap(p, SLICE_DESC_END, true);661		}662		if p.at(T![:]) {663			p.bump();664			// Step665			if !p.at(T![']']) {666				expr(p).wrap(p, SLICE_DESC_STEP, true);667			}668		}669	} else if p.at(T![::]) {670		p.bump();671		// End672		if !p.at(T![']']) {673			expr(p).wrap(p, SLICE_DESC_END, true);674		}675	} else {676		// It was not a slice677		p.expect(T![']']);678		m.forget(p);679		return false;680	}681	p.expect(T![']']);682	m.complete(p, SLICE_DESC);683	true684}685686fn suffix(p: &mut Parser) {687	loop {688		let start = p.start();689		let _marker: CompletedMarker = if p.at(T![?]) {690			p.bump();691			p.expect(T![.]);692			if p.at(IDENT) {693				name(p);694				start.complete(p, SUFFIX_INDEX)695			} else if p.at(T!['[']) {696				p.bump();697				expr(p);698				p.expect(T![']']);699				start.complete(p, SUFFIX_INDEX_EXPR)700			} else {701				start.complete_missing(p, ExpectedSyntax::Named("index"))702			}703		} else if p.at(T![.]) {704			p.bump();705			name(p);706			start.complete(p, SUFFIX_INDEX)707		} else if p.at(T!['[']) {708			if slice_desc_or_index(p) {709				start.complete(p, SUFFIX_SLICE)710			} else {711				start.complete(p, SUFFIX_INDEX_EXPR)712			}713		} else if p.at(T!['(']) {714			args_desc(p);715			start.complete(p, SUFFIX_APPLY)716		} else {717			start.forget(p);718			break;719		};720	}721}722723fn lhs(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {724	let lhs = lhs_basic(p)?;725726	suffix(p);727728	Ok(lhs)729}730fn name(p: &mut Parser) {731	let m = p.start();732	p.expect(IDENT);733	m.complete(p, NAME);734}735fn destruct_rest(p: &mut Parser) {736	let m = p.start();737	p.bump_assert(T![...]);738	if p.at(IDENT) {739		p.bump();740	}741	m.complete(p, DESTRUCT_REST);742}743fn destruct_object_field(p: &mut Parser) {744	let m = p.start();745	name(p);746	if p.at(T![:]) {747		p.bump();748		destruct(p);749	}750	if p.at(T![=]) {751		p.bump();752		expr(p);753	}754	m.complete(p, DESTRUCT_OBJECT_FIELD);755}756fn obj_local(p: &mut Parser) {757	let m = p.start();758	p.bump_assert(T![local]);759	bind(p);760	m.complete(p, OBJ_LOCAL);761}762fn destruct(p: &mut Parser) -> CompletedMarker {763	let m = p.start();764	let _ex = p.expected_syntax_name("destruction specifier");765	if p.at(T![?]) {766		p.bump();767		m.complete(p, DESTRUCT_SKIP)768	} else if p.at(T!['[']) {769		p.bump();770		// let mut had_rest = false;771		loop {772			if p.at(T![']']) {773				p.bump();774				break;775			} else if p.at(T![...]) {776				// let m_err = p.start_ranger();777				destruct_rest(p);778			// if had_rest {779			// 	p.custom_error(m_err.finish(p), "only one rest can be present in array");780			// }781			// had_rest = true;782			} else {783				destruct(p);784			}785			if p.at(T![,]) {786				p.bump();787				continue;788			}789			p.expect(T![']']);790			break;791		}792		m.complete(p, DESTRUCT_ARRAY)793	} else if p.at(T!['{']) {794		p.bump();795		let mut had_rest = false;796		loop {797			if p.at(T!['}']) {798				p.bump();799				break;800			} else if p.at(T![...]) {801				// let m_err = p.start_ranger();802				destruct_rest(p);803				// if had_rest {804				// 	p.custom_error(m_err.finish(p), "only one rest can be present in object");805				// }806				had_rest = true;807			} else {808				if had_rest {809					p.error_with_recovery_set(TS![]);810				}811				destruct_object_field(p);812			}813			if p.at(T![,]) {814				p.bump();815				continue;816			}817			p.expect(T!['}']);818			break;819		}820		m.complete(p, DESTRUCT_OBJECT)821	} else if p.at(IDENT) {822		name(p);823		m.complete(p, DESTRUCT_FULL)824	} else {825		m.forget(p);826		p.error_with_recovery_set(TS![; , '}', '(', :])827	}828}829fn bind(p: &mut Parser) {830	let m = p.start();831	if p.at(IDENT) && p.nth_at(1, T!['(']) {832		name(p);833		params_desc(p);834		p.expect(T![=]);835		expr(p);836		m.complete(p, BIND_FUNCTION)837	} else if p.at(IDENT) && p.nth_at(1, T![=]) && p.nth_at(2, T![function]) {838		name(p);839		p.expect(T![=]);840		p.expect(T![function]);841		params_desc(p);842		expr(p);843		m.complete(p, BIND_FUNCTION)844	} else {845		destruct(p);846		p.expect(T![=]);847		expr(p);848		m.complete(p, BIND_DESTRUCT)849	};850}851fn text(p: &mut Parser) {852	assert!(Text::can_cast(p.current()));853	p.bump();854}855fn number(p: &mut Parser) {856	assert!(Number::can_cast(p.current()));857	p.bump();858}859fn literal(p: &mut Parser) {860	assert!(Literal::can_cast(p.current()));861	p.bump();862}863fn lhs_basic(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {864	let _e = p.expected_syntax_name("expression");865	Ok(if Literal::can_cast(p.current()) {866		let m = p.start();867		literal(p);868		m.complete(p, EXPR_LITERAL)869	} else if Text::can_cast(p.current()) {870		let m = p.start();871		text(p);872		m.complete(p, EXPR_STRING)873	} else if Number::can_cast(p.current()) {874		let m = p.start();875		number(p);876		m.complete(p, EXPR_NUMBER)877	} else if p.at(IDENT) {878		let m = p.start();879		name(p);880		m.complete(p, EXPR_VAR)881	} else if p.at(T![if]) {882		let m = p.start();883		p.bump();884		expr(p);885		p.expect(T![then]);886		expr(p).wrap(p, TRUE_EXPR, true);887		if p.at(T![else]) {888			p.bump();889			expr(p).wrap(p, FALSE_EXPR, true);890		}891		m.complete(p, EXPR_IF_THEN_ELSE)892	} else if p.at(T!['[']) {893		array(p)894	} else if p.at(T!['{']) {895		object(p)896	} else if p.at(T![function]) {897		let m = p.start();898		p.bump();899		params_desc(p);900		expr(p);901		m.complete(p, EXPR_FUNCTION)902	} else if p.at(T![error]) {903		let m = p.start();904		p.bump();905		expr(p);906		m.complete(p, EXPR_ERROR)907	} else if p.at(T![import]) || p.at(T![importstr]) || p.at(T![importbin]) {908		let m = p.start();909		p.bump();910		text(p);911		m.complete(p, EXPR_IMPORT)912	} else if let Some(op) = UnaryOperatorKind::cast(p.current()) {913		let ((), right_binding_power) = op.binding_power();914915		let m = p.start();916		p.bump();917		let _ = expr_binding_power(p, right_binding_power);918		m.complete(p, EXPR_UNARY)919	} else if p.at(T!['(']) {920		let m = p.start();921		p.bump();922		expr(p);923		p.expect(T![')']);924		m.complete(p, EXPR_PARENED)925	} else {926		return Err(p.error_with_no_skip());927	})928}929930impl Parse {931	pub fn syntax(&self) -> SyntaxNode {932		SyntaxNode::new_root(self.green_node.clone())933	}934}
modifiedcrates/jrsonnet-rowan-parser/src/string_block.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/string_block.rs
+++ b/crates/jrsonnet-rowan-parser/src/string_block.rs
@@ -11,7 +11,7 @@
 
 use crate::SyntaxKind;
 
-pub(crate) fn lex_str_block_test<'d>(lex: &mut Lexer<'d, SyntaxKind>) {
+pub(crate) fn lex_str_block_test(lex: &mut Lexer<'_, SyntaxKind>) {
 	let _ = lex_str_block(lex);
 }
 
@@ -48,7 +48,7 @@
 	}
 
 	fn eat_if(&mut self, f: impl Fn(char) -> bool) -> usize {
-		if self.peek().map(f).unwrap_or(false) {
+		if self.peek().is_some_and(f) {
 			self.index += 1;
 			return 1;
 		}
@@ -141,9 +141,7 @@
 	}
 }
 
-pub fn collect_lexed_str_block<'s>(
-	input: &'s str,
-) -> Result<CollectStrBlock<'s>, StringBlockError> {
+pub fn collect_lexed_str_block(input: &str) -> Result<CollectStrBlock<'_>, StringBlockError> {
 	let mut collect = CollectStrBlock {
 		truncate: false,
 		lines: vec![],
@@ -179,7 +177,7 @@
 	}
 
 	fn mark_line(&mut self, line: &'d str) {
-		self.lines.push(line)
+		self.lines.push(line);
 	}
 }
 
modifiedcrates/jrsonnet-rowan-parser/src/tests.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/tests.rs
+++ b/crates/jrsonnet-rowan-parser/src/tests.rs
@@ -2,7 +2,6 @@
 #![cfg(test)]
 
 use hi_doc::{Formatting, SnippetBuilder, Text};
-use thiserror::Error;
 
 use crate::{parse, AstNode};
 
@@ -14,7 +13,7 @@
 	if !errors.is_empty() && !text.is_empty() {
 		writeln!(out, "===").unwrap();
 		for err in &errors {
-			writeln!(out, "{:?}", err).unwrap();
+			writeln!(out, "{err:?}").unwrap();
 		}
 		let mut code = text.to_string();
 
modifiedcrates/jrsonnet-stdlib/src/regex.rsdiffbeforeafterboth
--- a/crates/jrsonnet-stdlib/src/regex.rs
+++ b/crates/jrsonnet-stdlib/src/regex.rs
@@ -4,7 +4,7 @@
 use jrsonnet_evaluator::{
 	error::{ErrorKind::*, Result},
 	rustc_hash::FxBuildHasher,
-	typed::Typed,
+	typed::{IntoUntyped, Typed},
 	val::StrValue,
 	IStr, ObjValue, ObjValueBuilder,
 };
@@ -41,7 +41,7 @@
 	}
 }
 
-#[derive(Typed)]
+#[derive(Typed, IntoUntyped)]
 pub struct RegexMatch {
 	string: IStr,
 	captures: Vec<IStr>,
modifiedtests/tests/common.rsdiffbeforeafterboth
--- a/tests/tests/common.rs
+++ b/tests/tests/common.rs
@@ -41,6 +41,7 @@
 }
 
 #[builtin]
+#[allow(dead_code)]
 fn assert_throw(lazy: Thunk<Val>, message: String) -> Result<bool> {
 	match lazy.evaluate() {
 		Ok(_) => {
@@ -55,6 +56,7 @@
 }
 
 #[builtin]
+#[allow(dead_code)]
 fn param_names(fun: FuncVal) -> Vec<String> {
 	fun.params()
 		.iter()
modifiedtests/tests/typed_obj.rsdiffbeforeafterboth
--- a/tests/tests/typed_obj.rs
+++ b/tests/tests/typed_obj.rs
@@ -9,7 +9,7 @@
 };
 use jrsonnet_stdlib::ContextInitializer;
 
-#[derive(Clone, Typed, PartialEq, Debug)]
+#[derive(Clone, Typed, FromUntyped, IntoUntyped, PartialEq, Debug)]
 struct A {
 	a: u32,
 	b: u16,
@@ -39,7 +39,7 @@
 	Ok(())
 }
 
-#[derive(Clone, Typed, PartialEq, Debug)]
+#[derive(Clone, Typed, FromUntyped, IntoUntyped, PartialEq, Debug)]
 struct B {
 	a: u32,
 	#[typed(rename = "c")]
@@ -62,7 +62,7 @@
 	Ok(())
 }
 
-#[derive(Clone, Typed, PartialEq, Debug)]
+#[derive(Clone, Typed, FromUntyped, IntoUntyped, PartialEq, Debug)]
 struct ObjectKind {
 	#[typed(rename = "apiVersion")]
 	api_version: String,
@@ -70,7 +70,7 @@
 	kind: String,
 }
 
-#[derive(Clone, Typed, PartialEq, Debug)]
+#[derive(Clone, Typed, FromUntyped, IntoUntyped, PartialEq, Debug)]
 struct Object {
 	#[typed(flatten)]
 	kind: ObjectKind,
@@ -104,7 +104,7 @@
 	Ok(())
 }
 
-#[derive(Clone, Typed, PartialEq, Debug)]
+#[derive(Clone, Typed, FromUntyped, IntoUntyped, PartialEq, Debug)]
 struct C {
 	a: Option<u32>,
 	b: u16,
@@ -142,14 +142,14 @@
 	Ok(())
 }
 
-#[derive(Clone, Typed, PartialEq, Debug)]
+#[derive(Clone, Typed, FromUntyped, IntoUntyped, PartialEq, Debug)]
 struct D {
 	#[typed(flatten(ok))]
 	e: Option<E>,
 	b: u16,
 }
 
-#[derive(Clone, Typed, PartialEq, Debug)]
+#[derive(Clone, Typed, FromUntyped, IntoUntyped, PartialEq, Debug)]
 struct E {
 	v: u32,
 }
modifiedxtask/src/sourcegen/mod.rsdiffbeforeafterboth
--- a/xtask/src/sourcegen/mod.rs
+++ b/xtask/src/sourcegen/mod.rs
@@ -65,9 +65,9 @@
 							is_lexer_error: true,
 						});
 					}
-				};
+				}
 				continue;
-			};
+			}
 			let name = to_upper_snake_case(token);
 			eprintln!("implicit kw: {token}");
 			kinds.define_token(TokenKind::Keyword {
@@ -447,7 +447,7 @@
 			let trait_name = format_ident!("{}", trait_name);
 			let kinds: Vec<_> = nodes
 				.iter()
-				.map(|name| format_ident!("{}", to_upper_snake_case(&name.name.to_string())))
+				.map(|name| format_ident!("{}", to_upper_snake_case(&name.name)))
 				.collect();
 
 			(
@@ -555,10 +555,10 @@
 	if "{}[]()$".contains(token) {
 		let c = token.chars().next().unwrap();
 		quote! { #c }
-	} else if token.contains(|v| v == '$') {
+	} else if token.contains('$') {
 		quote! { #token }
-	} else if token.chars().all(|v| ('a'..='z').contains(&v)) {
-		let i = Ident::new(&token, Span::call_site());
+	} else if token.chars().all(|v: char| v.is_ascii_lowercase()) {
+		let i = Ident::new(token, Span::call_site());
 		quote! { #i }
 	} else {
 		let cs = token.chars().map(|c| Punct::new(c, Spacing::Joint));