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

difftreelog

style fix clippy warnings

Yaroslav Bolyukin2024-05-19parent: #e3e6483.patch.diff
in: master

19 files changed

modifiedbindings/jsonnet/src/import.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/import.rs
+++ b/bindings/jsonnet/src/import.rs
@@ -64,7 +64,7 @@
 				self.ctx,
 				base.as_ptr(),
 				rel.as_ptr(),
-				&mut (found_here as *const _),
+				&mut found_here.cast_const(),
 				&mut buf,
 				&mut buf_len,
 			)
@@ -121,7 +121,7 @@
 		cb,
 		ctx,
 		out: RefCell::new(HashMap::new()),
-	})
+	});
 }
 
 /// # Safety
modifiedbindings/jsonnet/src/lib.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/lib.rs
+++ b/bindings/jsonnet/src/lib.rs
@@ -86,7 +86,6 @@
 	let state = State::default();
 	state.settings_mut().import_resolver = tb!(FileImportResolver::default());
 	state.settings_mut().context_initializer = tb!(jrsonnet_stdlib::ContextInitializer::new(
-		state.clone(),
 		PathResolver::new_cwd_fallback(),
 	));
 	Box::into_raw(Box::new(VM {
@@ -107,7 +106,7 @@
 /// Set the maximum stack depth.
 #[no_mangle]
 pub extern "C" fn jsonnet_max_stack(_vm: &VM, v: c_uint) {
-	set_stack_depth_limit(v as usize)
+	set_stack_depth_limit(v as usize);
 }
 
 /// Set the number of objects required before a garbage collection cycle is allowed.
@@ -175,7 +174,7 @@
 #[no_mangle]
 pub extern "C" fn jsonnet_max_trace(vm: &mut VM, v: c_uint) {
 	if let Some(format) = vm.trace_format.as_any_mut().downcast_mut::<CompactFormat>() {
-		format.max_trace = v as usize
+		format.max_trace = v as usize;
 	} else {
 		panic!("max_trace is not supported by current tracing format")
 	}
@@ -183,7 +182,7 @@
 
 /// Evaluate a file containing Jsonnet code, return a JSON string.
 ///
-/// The returned string should be cleaned up with jsonnet_realloc.
+/// The returned string should be cleaned up with `jsonnet_realloc`.
 ///
 /// # Safety
 ///
@@ -216,7 +215,7 @@
 
 /// Evaluate a string containing Jsonnet code, return a JSON string.
 ///
-/// The returned string should be cleaned up with jsonnet_realloc.
+/// The returned string should be cleaned up with `jsonnet_realloc`.
 ///
 /// # Safety
 ///
@@ -359,7 +358,7 @@
 	out.push(0);
 	let v = out.as_ptr();
 	std::mem::forget(out);
-	v as *const c_char
+	v.cast::<c_char>()
 }
 
 /// # Safety
modifiedbindings/jsonnet/src/val_extract.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/val_extract.rs
+++ b/bindings/jsonnet/src/val_extract.rs
@@ -26,7 +26,7 @@
 pub extern "C" fn jsonnet_json_extract_number(_vm: &VM, v: &Val, out: &mut c_double) -> c_int {
 	match v {
 		Val::Num(n) => {
-			*out = *n;
+			*out = n.get();
 			1
 		}
 		_ => 0,
modifiedbindings/jsonnet/src/val_make.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/val_make.rs
+++ b/bindings/jsonnet/src/val_make.rs
@@ -5,7 +5,10 @@
 	os::raw::{c_char, c_double, c_int},
 };
 
-use jrsonnet_evaluator::{val::ArrValue, ObjValue, Val};
+use jrsonnet_evaluator::{
+	val::{ArrValue, NumValue},
+	ObjValue, Val,
+};
 
 use crate::VM;
 
@@ -24,7 +27,9 @@
 /// Convert the given double to a `JsonnetJsonValue`.
 #[no_mangle]
 pub extern "C" fn jsonnet_json_make_number(_vm: &VM, v: c_double) -> *mut Val {
-	Box::into_raw(Box::new(Val::Num(v)))
+	Box::into_raw(Box::new(Val::Num(
+		NumValue::new(v).expect("jsonnet numbers are finite"),
+	)))
 }
 
 /// Convert the given `bool` (`1` or `0`) to a `JsonnetJsonValue`.
modifiedbindings/jsonnet/src/val_modify.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/val_modify.rs
+++ b/bindings/jsonnet/src/val_modify.rs
@@ -12,7 +12,7 @@
 ///
 /// # Safety
 ///
-/// `arr` should be a pointer to array value allocated by make_array, or returned by other library call
+/// `arr` should be a pointer to array value allocated by `make_array`, or returned by other library call
 /// `val` should be a pointer to value allocated using this library
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_json_array_append(_vm: &VM, arr: &mut Val, val: &Val) {
modifiedbindings/jsonnet/src/vars_tlas.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/vars_tlas.rs
+++ b/bindings/jsonnet/src/vars_tlas.rs
@@ -27,7 +27,7 @@
 		.add_ext_str(
 			name.to_str().expect("name is not utf-8").into(),
 			value.to_str().expect("value is not utf-8").into(),
-		)
+		);
 }
 
 /// Binds a Jsonnet external variable to the given code.
@@ -51,7 +51,7 @@
 			name.to_str().expect("name is not utf-8"),
 			code.to_str().expect("code is not utf-8"),
 		)
-		.expect("can't parse ext code")
+		.expect("can't parse ext code");
 }
 
 /// Binds a top-level string argument for a top-level parameter.
modifiedcmds/jrsonnet-fmt/src/children.rsdiffbeforeafterboth
--- a/cmds/jrsonnet-fmt/src/children.rs
+++ b/cmds/jrsonnet-fmt/src/children.rs
@@ -28,7 +28,7 @@
 				TS![, ;].contains(item.kind()),
 				"silently eaten token: {:?}",
 				item.kind()
-			)
+			);
 		}
 	}
 	out
@@ -48,13 +48,13 @@
 		if let Some(trivia) = item.as_token().cloned().and_then(Trivia::cast) {
 			out.push(Ok(trivia));
 		} else if CustomError::can_cast(item.kind()) {
-			out.push(Err(item.to_string()))
+			out.push(Err(item.to_string()));
 		} else {
 			assert!(
 				TS![, ;].contains(item.kind()),
 				"silently eaten token: {:?}",
 				item.kind()
-			)
+			);
 		}
 	}
 	out
@@ -115,11 +115,7 @@
 				TriviaKind::Whitespace => {
 					nl_count += t.text().bytes().filter(|b| *b == b'\n').count();
 				}
-				TriviaKind::SingleLineHashComment => {
-					nl_count += 1;
-					break;
-				}
-				TriviaKind::SingleLineSlashComment => {
+				TriviaKind::SingleLineHashComment | TriviaKind::SingleLineSlashComment => {
 					nl_count += 1;
 					break;
 				}
@@ -163,7 +159,7 @@
 				inline_trivia: Vec::new(),
 			});
 			if let Some(last_child) = last_child {
-				out.push(last_child)
+				out.push(last_child);
 			}
 			had_some = true;
 			started_next = false;
@@ -188,7 +184,7 @@
 			}
 			had_some = true;
 		} else if CustomError::can_cast(item.kind()) {
-			next.push(Err(item.to_string()))
+			next.push(Err(item.to_string()));
 		} else if loose {
 			if had_some {
 				break;
@@ -199,7 +195,7 @@
 				TS![, ;].contains(item.kind()),
 				"silently eaten token: {:?}",
 				item.kind()
-			)
+			);
 		}
 	}
 
modifiedcmds/jrsonnet-fmt/src/comments.rsdiffbeforeafterboth
--- a/cmds/jrsonnet-fmt/src/comments.rs
+++ b/cmds/jrsonnet-fmt/src/comments.rs
@@ -1,3 +1,5 @@
+use std::string::String;
+
 use dprint_core::formatting::PrintItems;
 use jrsonnet_rowan_parser::{nodes::TriviaKind, AstToken};
 
@@ -12,6 +14,7 @@
 	EndOfItems,
 }
 
+#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
 pub fn format_comments(comments: &ChildTrivia, loc: CommentLocation, out: &mut PrintItems) {
 	for c in comments {
 		let Ok(c) = c else {
@@ -62,14 +65,14 @@
 						}
 					})
 					.collect::<Vec<_>>();
-				while lines.last().map(|l| l.is_empty()).unwrap_or(false) {
+				while lines.last().is_some_and(String::is_empty) {
 					lines.pop();
 				}
 				if lines.len() == 1 && !doc {
 					if matches!(loc, CommentLocation::ItemInline) {
 						p!(out, str(" "));
 					}
-					p!(out, str("/* ") string(lines[0].trim().to_string()) str(" */") nl)
+					p!(out, str("/* ") string(lines[0].trim().to_string()) str(" */") nl);
 				} else if !lines.is_empty() {
 					fn common_ws_prefix<'a>(a: &'a str, b: &str) -> &'a str {
 						let offset = a
@@ -95,7 +98,7 @@
 					}
 					for line in lines
 						.iter_mut()
-						.skip(if immediate_start { 1 } else { 0 })
+						.skip(usize::from(immediate_start))
 						.filter(|l| !l.is_empty())
 					{
 						*line = line
@@ -127,13 +130,13 @@
 								}
 								line = new_line.to_string();
 							}
-							p!(out, string(line.to_string()) nl)
+							p!(out, string(line.to_string()) nl);
 						}
 					}
 					if doc {
 						p!(out, str(" "));
 					}
-					p!(out, str("*/") nl)
+					p!(out, str("*/") nl);
 				}
 			}
 			// TODO: Keep common padding for multiple continous lines of single-line comments
@@ -154,20 +157,20 @@
 			// ```
 			TriviaKind::SingleLineHashComment => {
 				if matches!(loc, CommentLocation::ItemInline) {
-					p!(out, str(" "))
+					p!(out, str(" "));
 				}
 				p!(out, str("# ") string(c.text().strip_prefix('#').expect("hash comment starts with #").trim().to_string()));
 				if !matches!(loc, CommentLocation::ItemInline) {
-					p!(out, nl)
+					p!(out, nl);
 				}
 			}
 			TriviaKind::SingleLineSlashComment => {
 				if matches!(loc, CommentLocation::ItemInline) {
-					p!(out, str(" "))
+					p!(out, str(" "));
 				}
 				p!(out, str("// ") string(c.text().strip_prefix("//").expect("comment starts with //").trim().to_string()));
 				if !matches!(loc, CommentLocation::ItemInline) {
-					p!(out, nl)
+					p!(out, nl);
 				}
 			}
 			// Garbage in - garbage out
modifiedcmds/jrsonnet-fmt/src/main.rsdiffbeforeafterboth
--- a/cmds/jrsonnet-fmt/src/main.rs
+++ b/cmds/jrsonnet-fmt/src/main.rs
@@ -13,6 +13,7 @@
 	condition_helpers::is_multiple_lines, condition_resolvers::true_resolver,
 	ConditionResolverContext, LineNumber, PrintItems, PrintOptions,
 };
+use hi_doc::Formatting;
 use jrsonnet_rowan_parser::{
 	nodes::{
 		Arg, ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart,
@@ -155,7 +156,7 @@
 {
 	fn print(&self, out: &mut PrintItems) {
 		if let Some(v) = self {
-			v.print(out)
+			v.print(out);
 		} else {
 			p!(
 				out,
@@ -163,31 +164,31 @@
 					"/*missing {}*/",
 					type_name::<P>().replace("jrsonnet_rowan_parser::generated::nodes::", "")
 				),)
-			)
+			);
 		}
 	}
 }
 
 impl Printable for SyntaxToken {
 	fn print(&self, out: &mut PrintItems) {
-		p!(out, string(self.to_string()))
+		p!(out, string(self.to_string()));
 	}
 }
 
 impl Printable for Text {
 	fn print(&self, out: &mut PrintItems) {
-		p!(out, string(format!("{}", self)))
+		p!(out, string(format!("{}", self)));
 	}
 }
 impl Printable for Number {
 	fn print(&self, out: &mut PrintItems) {
-		p!(out, string(format!("{}", self)))
+		p!(out, string(format!("{}", self)));
 	}
 }
 
 impl Printable for Name {
 	fn print(&self, out: &mut PrintItems) {
-		p!(out, { self.ident_lit() })
+		p!(out, { self.ident_lit() });
 	}
 }
 
@@ -203,30 +204,30 @@
 impl Printable for Destruct {
 	fn print(&self, out: &mut PrintItems) {
 		match self {
-			Destruct::DestructFull(f) => {
-				p!(out, { f.name() })
+			Self::DestructFull(f) => {
+				p!(out, { f.name() });
 			}
-			Destruct::DestructSkip(_) => p!(out, str("?")),
-			Destruct::DestructArray(a) => {
+			Self::DestructSkip(_) => p!(out, str("?")),
+			Self::DestructArray(a) => {
 				p!(out, str("[") >i nl);
 				for el in a.destruct_array_parts() {
 					match el {
 						DestructArrayPart::DestructArrayElement(e) => {
-							p!(out, {e.destruct()} str(",") nl)
+							p!(out, {e.destruct()} str(",") nl);
 						}
 						DestructArrayPart::DestructRest(d) => {
-							p!(out, {d} str(",") nl)
+							p!(out, {d} str(",") nl);
 						}
 					}
 				}
 				p!(out, <i str("]"));
 			}
-			Destruct::DestructObject(o) => {
+			Self::DestructObject(o) => {
 				p!(out, str("{") >i nl);
 				for item in o.destruct_object_fields() {
 					p!(out, { item.field() });
 					if let Some(des) = item.destruct() {
-						p!(out, str(": ") {des})
+						p!(out, str(": ") {des});
 					}
 					if let Some(def) = item.expr() {
 						p!(out, str(" = ") {def});
@@ -234,7 +235,7 @@
 					p!(out, str(",") nl);
 				}
 				if let Some(rest) = o.destruct_rest() {
-					p!(out, {rest} nl)
+					p!(out, {rest} nl);
 				}
 				p!(out, <i str("}"));
 			}
@@ -245,17 +246,17 @@
 impl Printable for FieldName {
 	fn print(&self, out: &mut PrintItems) {
 		match self {
-			FieldName::FieldNameFixed(f) => {
+			Self::FieldNameFixed(f) => {
 				if let Some(id) = f.id() {
-					p!(out, { id })
+					p!(out, { id });
 				} else if let Some(str) = f.text() {
-					p!(out, { str })
+					p!(out, { str });
 				} else {
-					p!(out, str("/*missing FieldName*/"))
+					p!(out, str("/*missing FieldName*/"));
 				}
 			}
-			FieldName::FieldNameDynamic(d) => {
-				p!(out, str("[") {d.expr()} str("]"))
+			Self::FieldNameDynamic(d) => {
+				p!(out, str("[") {d.expr()} str("]"));
 			}
 		}
 	}
@@ -263,13 +264,13 @@
 
 impl Printable for Visibility {
 	fn print(&self, out: &mut PrintItems) {
-		p!(out, string(self.to_string()))
+		p!(out, string(self.to_string()));
 	}
 }
 
 impl Printable for ObjLocal {
 	fn print(&self, out: &mut PrintItems) {
-		p!(out, str("local ") {self.bind()})
+		p!(out, str("local ") {self.bind()});
 	}
 }
 
@@ -277,7 +278,7 @@
 	fn print(&self, out: &mut PrintItems) {
 		p!(out, str("assert ") {self.condition()});
 		if self.colon_token().is_some() || self.message().is_some() {
-			p!(out, str(": ") {self.message()})
+			p!(out, str(": ") {self.message()});
 		}
 	}
 }
@@ -288,9 +289,9 @@
 		for param in self.params() {
 			p!(out, { param.destruct() });
 			if param.assign_token().is_some() || param.expr().is_some() {
-				p!(out, str(" = ") {param.expr()})
+				p!(out, str(" = ") {param.expr()});
 			}
-			p!(out, str(",") nl)
+			p!(out, str(",") nl);
 		}
 		p!(out, <i str(")"));
 	}
@@ -343,7 +344,7 @@
 		}
 		p!(out, str(":"));
 		if self.end().is_some() {
-			p!(out, { self.end().map(|e| e.expr()) })
+			p!(out, { self.end().map(|e| e.expr()) });
 		}
 		// Keep only one : in case if we don't need step
 		if self.step().is_some() {
@@ -357,16 +358,16 @@
 	fn print(&self, out: &mut PrintItems) {
 		match self {
 			Self::MemberBindStmt(b) => {
-				p!(out, { b.obj_local() })
+				p!(out, { b.obj_local() });
 			}
 			Self::MemberAssertStmt(ass) => {
-				p!(out, { ass.assertion() })
+				p!(out, { ass.assertion() });
 			}
 			Self::MemberFieldNormal(n) => {
-				p!(out, {n.field_name()} if(n.plus_token().is_some())({n.plus_token()}) {n.visibility()} str(" ") {n.expr()})
+				p!(out, {n.field_name()} if(n.plus_token().is_some())({n.plus_token()}) {n.visibility()} str(" ") {n.expr()});
 			}
 			Self::MemberFieldMethod(m) => {
-				p!(out, {m.field_name()} {m.params_desc()} {m.visibility()} str(" ") {m.expr()})
+				p!(out, {m.field_name()} {m.params_desc()} {m.visibility()} str(" ") {m.expr()});
 			}
 		}
 	}
@@ -375,7 +376,7 @@
 impl Printable for ObjBody {
 	fn print(&self, out: &mut PrintItems) {
 		match self {
-			ObjBody::ObjBodyComp(l) => {
+			Self::ObjBodyComp(l) => {
 				let (children, mut end_comments) = children_between::<Member>(
 					l.syntax().clone(),
 					l.l_brace_token().map(Into::into).as_ref(),
@@ -391,14 +392,14 @@
 				);
 				let trailing_for_comp = end_comments.extract_trailing();
 				p!(out, str("{") >i nl);
-				for mem in children.into_iter() {
+				for mem in children {
 					if mem.should_start_with_newline {
 						p!(out, nl);
 					}
 					format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);
 					p!(out, {mem.value} str(","));
 					format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);
-					p!(out, nl)
+					p!(out, nl);
 				}
 
 				if end_comments.should_start_with_newline {
@@ -417,7 +418,7 @@
 					l.r_brace_token().map(Into::into).as_ref(),
 					Some(trailing_for_comp),
 				);
-				for mem in compspecs.into_iter() {
+				for mem in compspecs {
 					if mem.should_start_with_newline {
 						p!(out, nl);
 					}
@@ -432,7 +433,7 @@
 
 				p!(out, nl <i str("}"));
 			}
-			ObjBody::ObjBodyMemberList(l) => {
+			Self::ObjBodyMemberList(l) => {
 				let (children, end_comments) = children_between::<Member>(
 					l.syntax().clone(),
 					l.l_brace_token().map(Into::into).as_ref(),
@@ -451,7 +452,7 @@
 					format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);
 					p!(out, {mem.value} str(","));
 					format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);
-					p!(out, nl)
+					p!(out, nl);
 				}
 
 				if end_comments.should_start_with_newline {
@@ -465,51 +466,51 @@
 }
 impl Printable for UnaryOperator {
 	fn print(&self, out: &mut PrintItems) {
-		p!(out, string(self.text().to_string()))
+		p!(out, string(self.text().to_string()));
 	}
 }
 impl Printable for BinaryOperator {
 	fn print(&self, out: &mut PrintItems) {
-		p!(out, string(self.text().to_string()))
+		p!(out, string(self.text().to_string()));
 	}
 }
 impl Printable for Bind {
 	fn print(&self, out: &mut PrintItems) {
 		match self {
-			Bind::BindDestruct(d) => {
-				p!(out, {d.into()} str(" = ") {d.value()})
+			Self::BindDestruct(d) => {
+				p!(out, {d.into()} str(" = ") {d.value()});
 			}
-			Bind::BindFunction(f) => {
-				p!(out, {f.name()} {f.params()} str(" = ") {f.value()})
+			Self::BindFunction(f) => {
+				p!(out, {f.name()} {f.params()} str(" = ") {f.value()});
 			}
 		}
 	}
 }
 impl Printable for Literal {
 	fn print(&self, out: &mut PrintItems) {
-		p!(out, string(self.syntax().to_string()))
+		p!(out, string(self.syntax().to_string()));
 	}
 }
 impl Printable for ImportKind {
 	fn print(&self, out: &mut PrintItems) {
-		p!(out, string(self.syntax().to_string()))
+		p!(out, string(self.syntax().to_string()));
 	}
 }
 impl Printable for ForSpec {
 	fn print(&self, out: &mut PrintItems) {
-		p!(out, str("for ") {self.bind()} str(" in ") {self.expr()})
+		p!(out, str("for ") {self.bind()} str(" in ") {self.expr()});
 	}
 }
 impl Printable for IfSpec {
 	fn print(&self, out: &mut PrintItems) {
-		p!(out, str("if ") {self.expr()})
+		p!(out, str("if ") {self.expr()});
 	}
 }
 impl Printable for CompSpec {
 	fn print(&self, out: &mut PrintItems) {
 		match self {
-			CompSpec::ForSpec(f) => f.print(out),
-			CompSpec::IfSpec(i) => i.print(out),
+			Self::ForSpec(f) => f.print(out),
+			Self::IfSpec(i) => i.print(out),
 		}
 	}
 }
@@ -549,23 +550,23 @@
 impl Printable for Suffix {
 	fn print(&self, out: &mut PrintItems) {
 		match self {
-			Suffix::SuffixIndex(i) => {
+			Self::SuffixIndex(i) => {
 				if i.question_mark_token().is_some() {
 					p!(out, str("?"));
 				}
 				p!(out, str(".") {i.index()});
 			}
-			Suffix::SuffixIndexExpr(e) => {
+			Self::SuffixIndexExpr(e) => {
 				if e.question_mark_token().is_some() {
 					p!(out, str(".?"));
 				}
-				p!(out, str("[") {e.index()} str("]"))
+				p!(out, str("[") {e.index()} str("]"));
 			}
-			Suffix::SuffixSlice(d) => {
-				p!(out, { d.slice_desc() })
+			Self::SuffixSlice(d) => {
+				p!(out, { d.slice_desc() });
 			}
-			Suffix::SuffixApply(a) => {
-				p!(out, { a.args_desc() })
+			Self::SuffixApply(a) => {
+				p!(out, { a.args_desc() });
 			}
 		}
 	}
@@ -573,7 +574,7 @@
 impl Printable for Stmt {
 	fn print(&self, out: &mut PrintItems) {
 		match self {
-			Stmt::StmtLocal(l) => {
+			Self::StmtLocal(l) => {
 				let (binds, end_comments) = children_between::<Bind>(
 					l.syntax().clone(),
 					l.local_kw_token().map(Into::into).as_ref(),
@@ -594,18 +595,18 @@
 						format_comments(&bind.before_trivia, CommentLocation::AboveItem, out);
 						p!(out, {bind.value} str(","));
 						format_comments(&bind.inline_trivia, CommentLocation::ItemInline, out);
-						p!(out, nl)
+						p!(out, nl);
 					}
 					if end_comments.should_start_with_newline {
-						p!(out, nl)
+						p!(out, nl);
 					}
 					format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);
 					p!(out,<i);
 				}
 				p!(out,str(";") nl);
 			}
-			Stmt::StmtAssert(a) => {
-				p!(out, {a.assertion()} str(";") nl)
+			Self::StmtAssert(a) => {
+				p!(out, {a.assertion()} str(";") nl);
 			}
 		}
 	}
@@ -614,7 +615,7 @@
 	fn print(&self, out: &mut PrintItems) {
 		match self {
 			Self::ExprBinary(b) => {
-				p!(out, {b.lhs_work()} str(" ") {b.binary_operator()} str(" ") {b.rhs_work()})
+				p!(out, {b.lhs_work()} str(" ") {b.binary_operator()} str(" ") {b.rhs_work()});
 			}
 			Self::ExprUnary(u) => p!(out, {u.unary_operator()} {u.rhs()}),
 			// Self::ExprSlice(s) => {
@@ -632,10 +633,10 @@
 			// 	pi
 			// }
 			Self::ExprObjExtend(ex) => {
-				p!(out, {ex.lhs_work()} str(" ") {ex.rhs_work()})
+				p!(out, {ex.lhs_work()} str(" ") {ex.rhs_work()});
 			}
 			Self::ExprParened(p) => {
-				p!(out, str("(") {p.expr()} str(")"))
+				p!(out, str("(") {p.expr()} str(")"));
 			}
 			Self::ExprString(s) => p!(out, { s.text() }),
 			Self::ExprNumber(n) => p!(out, { n.number() }),
@@ -647,7 +648,7 @@
 				p!(out, <i str("]"));
 			}
 			Self::ExprObject(obj) => {
-				p!(out, { obj.obj_body() })
+				p!(out, { obj.obj_body() });
 			}
 			Self::ExprArrayComp(arr) => {
 				p!(out, str("[") {arr.expr()});
@@ -657,7 +658,7 @@
 				p!(out, str("]"));
 			}
 			Self::ExprImport(v) => {
-				p!(out, {v.import_kind()} str(" ") {v.text()})
+				p!(out, {v.import_kind()} str(" ") {v.text()});
 			}
 			Self::ExprVar(n) => p!(out, { n.name() }),
 			// Self::ExprLocal(l) => {
@@ -665,14 +666,14 @@
 			Self::ExprIfThenElse(ite) => {
 				p!(out, str("if ") {ite.cond()} str(" then ") {ite.then().map(|t| t.expr())});
 				if ite.else_kw_token().is_some() || ite.else_().is_some() {
-					p!(out, str(" else ") {ite.else_().map(|t| t.expr())})
+					p!(out, str(" else ") {ite.else_().map(|t| t.expr())});
 				}
 			}
 			Self::ExprFunction(f) => p!(out, str("function") {f.params_desc()} nl {f.expr()}),
 			// Self::ExprAssert(a) => p!(new: {a.assertion()} str("; ") {a.expr()}),
 			Self::ExprError(e) => p!(out, str("error ") {e.expr()}),
 			Self::ExprLiteral(l) => {
-				p!(out, { l.literal() })
+				p!(out, { l.literal() });
 			}
 		}
 	}
@@ -696,7 +697,7 @@
 		);
 		format_comments(&before, CommentLocation::AboveItem, out);
 		p!(out, {self.expr()} nl);
-		format_comments(&after, CommentLocation::EndOfItems, out)
+		format_comments(&after, CommentLocation::EndOfItems, out);
 	}
 }
 
@@ -712,7 +713,7 @@
 			builder
 				.error(hi_doc::Text::single(
 					format!("{:?}", error.error).chars(),
-					Default::default(),
+					Formatting::default(),
 				))
 				.range(
 					error.range.start().into()
@@ -751,6 +752,7 @@
 }
 
 #[derive(Parser)]
+#[allow(clippy::struct_excessive_bools)]
 struct Opts {
 	/// Treat input as code, reformat it instead of reading file.
 	#[clap(long, short = 'e')]
@@ -814,7 +816,7 @@
 
 	let mut iteration = 0;
 	let mut formatted = input.clone();
-	let mut tmp;
+	let mut convergence_tmp;
 	// https://github.com/dprint/dprint/pull/423
 	loop {
 		let Some(reformatted) = format(
@@ -829,18 +831,16 @@
 		) else {
 			return Err(Error::Parse);
 		};
-		tmp = reformatted.trim().to_owned();
-		if formatted == tmp {
+		convergence_tmp = reformatted.trim().to_owned();
+		if formatted == convergence_tmp {
 			break;
 		}
-		formatted = tmp;
+		formatted = convergence_tmp;
 		if opts.conv_limit == 0 {
 			break;
 		}
 		iteration += 1;
-		if iteration > opts.conv_limit {
-			panic!("formatting not converged");
-		}
+		assert!(iteration <= opts.conv_limit, "formatting not converged");
 	}
 	formatted.push('\n');
 	if opts.test && formatted != input {
@@ -855,7 +855,7 @@
 		temp.flush()?;
 		temp.persist(&path)?;
 	} else {
-		print!("{formatted}")
+		print!("{formatted}");
 	}
 	Ok(())
 }
modifiedcrates/jrsonnet-rowan-parser/src/lex.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/lex.rs
+++ b/crates/jrsonnet-rowan-parser/src/lex.rs
@@ -37,9 +37,10 @@
 			// In kinds, string blocks is parsed at least as `|||`
 			lexer.bump(3);
 			let res = lex_str_block(&mut lexer);
-			debug_assert!(lexer.next().is_none(), "str_block is lexed");
+			let next = lexer.next();
+			assert!(next.is_none(), "str_block is lexed");
 			match res {
-				Ok(_) => {}
+				Ok(()) => {}
 				Err(e) => {
 					kind = Ok(match e {
 						StringBlockError::UnexpectedEnd => ERROR_STRING_BLOCK_UNEXPECTED_END,
@@ -48,7 +49,7 @@
 							ERROR_STRING_BLOCK_MISSING_TERMINATION
 						}
 						StringBlockError::MissingIndent => ERROR_STRING_BLOCK_MISSING_INDENT,
-					})
+					});
 				}
 			}
 		}
modifiedcrates/jrsonnet-rowan-parser/src/marker.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/marker.rs
+++ b/crates/jrsonnet-rowan-parser/src/marker.rs
@@ -146,7 +146,7 @@
 		p: &mut Parser,
 		kind: SyntaxKind,
 		error: Option<SyntaxError>,
-	) -> CompletedMarker {
+	) -> Self {
 		let new_m = p.start();
 		match &mut p.events[self.start_event_idx] {
 			Event::Start { forward_parent, .. } => {
@@ -173,10 +173,10 @@
 		}
 		completed
 	}
-	pub fn wrap(self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker {
+	pub fn wrap(self, p: &mut Parser, kind: SyntaxKind) -> Self {
 		self.wrap_raw(p, kind, None)
 	}
-	pub fn wrap_error(self, p: &mut Parser, msg: impl AsRef<str>) -> CompletedMarker {
+	pub fn wrap_error(self, p: &mut Parser, msg: impl AsRef<str>) -> Self {
 		self.wrap_raw(
 			p,
 			SyntaxKind::ERROR_CUSTOM,
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}4849#[derive(Debug)]50pub struct LocatedSyntaxError {51	pub error: SyntaxError,52	pub range: TextRange,53}5455impl Parser {56	pub fn new(kinds: Vec<SyntaxKind>) -> Self {57		Self {58			kinds,59			offset: 0,60			events: vec![],61			entered: 0,62			last_error_token: 0,63			hints: vec![],64			expected_syntax_tracking_state: Rc::new(Cell::new(ExpectedSyntax::Unnamed(TS![]))),65			steps: Cell::new(0),66		}67	}68	pub fn clear_outdated_hints(&mut self) {69		let amount = self70			.hints71			.iter()72			.rev()73			.take_while(|h| h.0 > self.entered)74			.count();75		self.hints.truncate(self.hints.len() - amount)76	}77	fn clear_expected_syntaxes(&mut self) {78		self.expected_syntax_tracking_state79			.set(ExpectedSyntax::Unnamed(TS![]));80	}81	pub fn start(&mut self) -> Marker {82		let start_event_idx = self.events.len();83		self.events.push(Event::Pending);84		self.entered += 1;85		Marker::new(start_event_idx)86	}87	// pub fn start_ranger(&mut self) -> Ranger {88	// 	let pos = self.offset;89	// 	Ranger { pos }90	// }91	pub fn parse(mut self) -> Vec<Event> {92		let m = self.start();93		expr(&mut self);94		if !self.at(EOF) {95			let m = self.start();96			while !self.at(EOF) {97				self.bump();98			}99			m.complete_error(&mut self, "unexpected tokens after end");100		}101		m.complete(&mut self, SOURCE_FILE);102103		self.events104	}105106	pub(crate) fn expect(&mut self, kind: SyntaxKind) {107		self.expect_with_recovery_set(kind, TS![])108	}109110	pub(crate) fn expect_with_recovery_set(111		&mut self,112		kind: SyntaxKind,113		recovery_set: SyntaxKindSet,114	) {115		if self.at(kind) {116			if kind != EOF {117				self.bump();118			}119		} else {120			self.error_with_recovery_set(recovery_set);121		}122	}123124	// pub(crate) fn expect_with_no_skip(&mut self, kind: SyntaxKind) {125	// 	if self.at(kind) {126	// 		self.bump();127	// 	} else {128	// 		self.error_with_no_skip();129	// 	}130	// }131	pub fn error_with_no_skip(&mut self) -> CompletedMarker {132		self.error_with_recovery_set(SyntaxKindSet::ALL)133	}134135	pub fn error_with_recovery_set(&mut self, recovery_set: SyntaxKindSet) -> CompletedMarker {136		let expected = self.expected_syntax_tracking_state.get();137		self.expected_syntax_tracking_state138			.set(ExpectedSyntax::Unnamed(TS![]));139140		if self.at_end() || self.at_ts(recovery_set) {141			let m = self.start();142			return m.complete_missing(self, expected);143		}144145		let current_token = self.current();146147		self.last_error_token = self.offset;148149		let m = self.start();150		self.bump();151		let m = m.complete_unexpected(self, expected, current_token);152		self.clear_expected_syntaxes();153		m154	}155	fn bump_assert(&mut self, kind: SyntaxKind) {156		assert!(self.at(kind), "expected {:?}", kind);157		self.bump_remap(self.current());158	}159	fn bump(&mut self) {160		self.bump_remap(self.current());161	}162	fn bump_remap(&mut self, kind: SyntaxKind) {163		assert_ne!(self.offset, self.kinds.len(), "already at end");164		self.events.push(Event::Token { kind });165		self.offset += 1;166		self.clear_expected_syntaxes();167	}168	fn step(&self) {169		use std::fmt::Write;170		let steps = self.steps.get();171		if steps >= 15000000 {172			let mut out = "seems like parsing is stuck".to_owned();173			{174				let last = 20;175				write!(out, "\n\nLast {} events:", last).unwrap();176				for (i, event) in self177					.events178					.iter()179					.skip(self.events.len().saturating_sub(last))180					.enumerate()181				{182					write!(out, "\n{i}. {event:?}").unwrap();183				}184			}185			{186				let next = 20;187				write!(out, "\n\nNext {next} tokens:").unwrap();188				for (i, tok) in self.kinds.iter().skip(self.offset).take(next).enumerate() {189					write!(out, "\n{i}. {tok:?}").unwrap();190				}191			}192			panic!("{out}")193		}194		self.steps.set(steps + 1);195	}196	fn nth(&self, i: usize) -> SyntaxKind {197		self.step();198		let mut offset = self.offset;199		for _ in 0..i {200			offset += 1;201		}202		self.kinds.get(offset).copied().unwrap_or(EOF)203	}204	fn current(&self) -> SyntaxKind {205		self.nth(0)206	}207	#[must_use]208	pub(crate) fn expected_syntax_name(&mut self, name: &'static str) -> ExpectedSyntaxGuard {209		self.expected_syntax_tracking_state210			.set(ExpectedSyntax::Named(name));211212		ExpectedSyntaxGuard::new(Rc::clone(&self.expected_syntax_tracking_state))213	}214	pub fn at(&mut self, kind: SyntaxKind) -> bool {215		self.nth_at(0, kind)216	}217	pub fn nth_at(&mut self, n: usize, kind: SyntaxKind) -> bool {218		if n == 0 {219			if let ExpectedSyntax::Unnamed(kinds) = self.expected_syntax_tracking_state.get() {220				let kinds = kinds.with(kind);221				self.expected_syntax_tracking_state222					.set(ExpectedSyntax::Unnamed(kinds))223			}224		}225		self.nth(n) == kind226	}227	pub fn at_ts(&mut self, set: SyntaxKindSet) -> bool {228		if let ExpectedSyntax::Unnamed(kinds) = self.expected_syntax_tracking_state.get() {229			let kinds = kinds.union(set);230			self.expected_syntax_tracking_state231				.set(ExpectedSyntax::Unnamed(kinds))232		}233		set.contains(self.current())234	}235	pub fn at_end(&mut self) -> bool {236		self.at(EOF)237	}238}239pub(crate) struct ExpectedSyntaxGuard {240	expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>,241}242243impl ExpectedSyntaxGuard {244	fn new(expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>) -> Self {245		Self {246			expected_syntax_tracking_state,247		}248	}249}250251impl Drop for ExpectedSyntaxGuard {252	fn drop(&mut self) {253		self.expected_syntax_tracking_state254			.set(ExpectedSyntax::Unnamed(TS![]));255	}256}257258#[derive(Clone, Debug, Copy)]259pub enum ExpectedSyntax {260	Named(&'static str),261	Unnamed(SyntaxKindSet),262}263impl fmt::Display for ExpectedSyntax {264	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {265		match self {266			ExpectedSyntax::Named(name) => write!(f, "{name}"),267			ExpectedSyntax::Unnamed(set) => write!(f, "{set}"),268		}269	}270}271272fn expr(p: &mut Parser) -> CompletedMarker {273	let m = p.start();274	while p.at(T![local]) || p.at(T![assert]) {275		let m = p.start();276277		if p.at(T![local]) {278			p.bump();279			loop {280				if p.at(T![;]) {281					p.bump();282					break;283				}284				bind(p);285286				if p.at(T![,]) {287					p.bump();288					continue;289				}290				p.expect(T![;]);291				break;292			}293			m.complete(p, STMT_LOCAL);294		} else {295			assertion(p);296			p.expect(T![;]);297			m.complete(p, STMT_ASSERT);298		}299	}300	match expr_binding_power(p, 0) {301		Ok(m) => m,302		Err(m) => m,303	};304	m.complete(p, EXPR)305}306fn expr_binding_power(307	p: &mut Parser,308	minimum_binding_power: u8,309) -> Result<CompletedMarker, CompletedMarker> {310	let mut lhs = lhs(p)?;311312	while let Some(op) = BinaryOperatorKind::cast(p.current())313		.or_else(|| p.at(T!['{']).then_some(BinaryOperatorKind::MetaObjectApply))314	{315		let (left_binding_power, right_binding_power) = op.binding_power();316		if left_binding_power < minimum_binding_power {317			break;318		}319320		// Object apply is not a real operator, we dont have something to bump321		if op != BinaryOperatorKind::MetaObjectApply {322			p.bump();323		}324325		let m = lhs.wrap(p, EXPR).precede(p);326		let parsed_rhs = expr_binding_power(p, right_binding_power)327			.map(|v| v.precede(p).complete(p, EXPR))328			.is_ok();329		lhs = m.complete(330			p,331			if op == BinaryOperatorKind::MetaObjectApply {332				EXPR_OBJ_EXTEND333			} else {334				EXPR_BINARY335			},336		);337338		if !parsed_rhs {339			break;340		}341	}342	Ok(lhs)343}344345const COMPSPEC: SyntaxKindSet = TS![for if];346fn compspec(p: &mut Parser) -> CompletedMarker {347	assert!(p.at_ts(COMPSPEC));348	if p.at(T![for]) {349		let m = p.start();350		p.bump();351		destruct(p);352		p.expect(T![in]);353		expr(p);354		m.complete(p, FOR_SPEC)355	} else if p.at(T![if]) {356		let m = p.start();357		p.bump();358		expr(p);359		m.complete(p, IF_SPEC)360	} else {361		unreachable!()362	}363}364365fn comma(p: &mut Parser) -> bool {366	comma_with_alternatives(p, TS![])367}368fn comma_with_alternatives(p: &mut Parser, set: SyntaxKindSet) -> bool {369	if p.at(T![,]) {370		p.bump();371		true372	} else if p.at_ts(set) {373		let _ex = p.expected_syntax_name("comma");374		p.expect_with_recovery_set(T![,], TS![]);375		true376	} else {377		false378	}379}380381fn field_name(p: &mut Parser) {382	let _e = p.expected_syntax_name("field name");383	let m = p.start();384	if p.at(T!['[']) {385		p.bump();386		expr(p);387		p.expect(T![']']);388		m.complete(p, FIELD_NAME_DYNAMIC);389	} else if p.at(IDENT) {390		name(p);391		m.complete(p, FIELD_NAME_FIXED);392	} else if Text::can_cast(p.current()) {393		text(p);394		m.complete(p, FIELD_NAME_FIXED);395	} else {396		m.forget(p);397		p.error_with_recovery_set(TS![; : :: ::: '(']);398	}399}400fn visibility(p: &mut Parser) {401	if p.at_ts(TS![: :: :::]) {402		p.bump()403	} else {404		p.error_with_recovery_set(TS![=]);405	}406}407fn assertion(p: &mut Parser) {408	let m = p.start();409	p.bump_assert(T![assert]);410	expr(p);411	if p.at(T![:]) {412		p.bump();413		expr(p);414	}415	m.complete(p, ASSERTION);416}417fn object(p: &mut Parser) -> CompletedMarker {418	let m_t = p.start();419	let m = p.start();420	p.bump_assert(T!['{']);421422	let mut elems = 0;423	let mut compspecs = Vec::new();424	let mut asserts = Vec::new();425	loop {426		if p.at(T!['}']) {427			p.bump();428			break;429		}430		if p.at_ts(TS![for]) {431			if elems == 0 {432				let m = p.start();433				m.complete_missing(p, ExpectedSyntax::Named("field definition"));434			}435			while p.at_ts(COMPSPEC) {436				compspecs.push(compspec(p));437			}438			if comma_with_alternatives(p, TS![;]) {439				continue;440			}441			p.expect(R_BRACE);442			break;443		}444		let m = p.start();445		if p.at(T![local]) {446			obj_local(p);447			m.complete(p, MEMBER_BIND_STMT);448		} else if p.at(T![assert]) {449			assertion(p);450			asserts.push(m.complete(p, MEMBER_ASSERT_STMT));451		} else {452			field_name(p);453			if p.at(T![+]) {454				p.bump();455			}456			let params = if p.at(T!['(']) {457				params_desc(p);458				visibility(p);459				expr(p);460				true461			} else if p.at_ts(TS![: :: :::]) && p.nth_at(1, T![function]) {462				visibility(p);463				p.bump_assert(T![function]);464				params_desc(p);465				expr(p);466				true467			} else {468				visibility(p);469				expr(p);470				false471			};472			elems += 1;473474			if params {475				m.complete(p, MEMBER_FIELD_METHOD)476			} else {477				m.complete(p, MEMBER_FIELD_NORMAL)478			};479		};480		while p.at_ts(COMPSPEC) {481			compspecs.push(compspec(p));482		}483		if comma_with_alternatives(p, TS![;]) {484			continue;485		}486		p.expect(R_BRACE);487		break;488	}489490	if elems > 1 && !compspecs.is_empty() {491		for errored in compspecs {492			errored.wrap_error(493				p,494				"compspec may only be used if there is only one object element",495			);496		}497		m.complete(p, OBJ_BODY_MEMBER_LIST);498	} else if !compspecs.is_empty() {499		for errored in asserts {500			errored.wrap_error(p, "asserts can't be used in object comprehensions");501		}502		m.complete(p, OBJ_BODY_COMP);503	} else {504		m.complete(p, OBJ_BODY_MEMBER_LIST);505	}506	m_t.complete(p, EXPR_OBJECT)507}508fn param(p: &mut Parser) {509	let m = p.start();510	destruct(p);511	if p.at(T![=]) {512		p.bump();513		expr(p);514	}515	m.complete(p, PARAM);516}517fn params_desc(p: &mut Parser) -> CompletedMarker {518	let m = p.start();519	p.bump_assert(T!['(']);520521	loop {522		if p.at(T![')']) {523			p.bump();524			break;525		}526		param(p);527		if comma(p) {528			continue;529		}530		p.expect(T![')']);531		break;532	}533534	m.complete(p, PARAMS_DESC)535}536fn args_desc(p: &mut Parser) {537	let m = p.start();538	p.bump_assert(T!['(']);539540	let started_named = Cell::new(false);541	let mut unnamed_after_named = Vec::new();542543	loop {544		if p.at(T![')']) {545			break;546		}547548		let m = p.start();549		if p.at(IDENT) && p.nth_at(1, T![=]) {550			name(p);551			p.bump();552			expr(p);553			m.complete(p, ARG);554			started_named.set(true);555		} else {556			expr(p);557			let arg = m.complete(p, ARG);558			if started_named.get() {559				unnamed_after_named.push(arg)560			}561		}562		if comma(p) {563			continue;564		}565		break;566	}567	p.expect(T![')']);568	if p.at(T![tailstrict]) {569		p.bump()570	}571572	for errored in unnamed_after_named {573		errored.wrap_error(p, "can't use positional arguments after named");574	}575576	m.complete(p, ARGS_DESC);577}578579fn array(p: &mut Parser) -> CompletedMarker {580	// Start the list node581	let m = p.start();582	p.bump_assert(T!['[']);583584	let mut compspecs = Vec::new();585	let mut elems = 0;586587	loop {588		if p.at(T![']']) {589			p.bump();590			break;591		}592		if elems != 0 && p.at_ts(TS![for]) {593			while p.at_ts(COMPSPEC) {594				compspecs.push(compspec(p));595			}596			if comma(p) {597				continue;598			}599			p.expect(T![']']);600			break;601		}602		expr(p);603		elems += 1;604		while p.at_ts(COMPSPEC) {605			compspecs.push(compspec(p));606		}607		if comma(p) {608			continue;609		}610		p.expect(T![']']);611		break;612	}613614	if elems > 1 && !compspecs.is_empty() {615		for spec in compspecs {616			spec.wrap_error(617				p,618				"compspec may only be used if there is only one array element",619			);620		}621622		m.complete(p, EXPR_ARRAY)623	} else if !compspecs.is_empty() {624		m.complete(p, EXPR_ARRAY_COMP)625	} else {626		m.complete(p, EXPR_ARRAY)627	}628}629/// Returns true if it was slice, false if just index630#[must_use]631fn slice_desc_or_index(p: &mut Parser) -> bool {632	let m = p.start();633	p.bump();634	// TODO: do not treat :, ::, ::: as full tokens?635	// Start636	if !p.at(T![:]) && !p.at(T![::]) {637		expr(p);638	}639	if p.at(T![:]) {640		p.bump();641		// End642		if !p.at(T![']']) {643			expr(p).wrap(p, SLICE_DESC_END);644		}645		if p.at(T![:]) {646			p.bump();647			// Step648			if !p.at(T![']']) {649				expr(p).wrap(p, SLICE_DESC_STEP);650			}651		}652	} else if p.at(T![::]) {653		p.bump();654		// End655		if !p.at(T![']']) {656			expr(p).wrap(p, SLICE_DESC_END);657		}658	} else {659		// It was not a slice660		p.expect(T![']']);661		m.forget(p);662		return false;663	}664	p.expect(T![']']);665	m.complete(p, SLICE_DESC);666	true667}668669fn suffix(p: &mut Parser) {670	loop {671		let start = p.start();672		let _marker: CompletedMarker = if p.at(T![?]) {673			p.bump();674			p.expect(T![.]);675			if p.at(IDENT) {676				name(p);677				start.complete(p, SUFFIX_INDEX)678			} else if p.at(T!['[']) {679				p.bump();680				expr(p);681				p.expect(T![']']);682				start.complete(p, SUFFIX_INDEX_EXPR)683			} else {684				start.complete_missing(p, ExpectedSyntax::Named("index"))685			}686		} else if p.at(T![.]) {687			p.bump();688			name(p);689			start.complete(p, SUFFIX_INDEX)690		} else if p.at(T!['[']) {691			if slice_desc_or_index(p) {692				start.complete(p, SUFFIX_SLICE)693			} else {694				start.complete(p, SUFFIX_INDEX_EXPR)695			}696		} else if p.at(T!['(']) {697			args_desc(p);698			start.complete(p, SUFFIX_APPLY)699		} else {700			start.forget(p);701			break;702		};703	}704}705706fn lhs(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {707	let lhs = lhs_basic(p)?;708709	suffix(p);710711	Ok(lhs)712}713fn name(p: &mut Parser) {714	let m = p.start();715	p.expect(IDENT);716	m.complete(p, NAME);717}718fn destruct_rest(p: &mut Parser) {719	let m = p.start();720	p.bump_assert(T![...]);721	if p.at(IDENT) {722		p.bump()723	}724	m.complete(p, DESTRUCT_REST);725}726fn destruct_object_field(p: &mut Parser) {727	let m = p.start();728	name(p);729	if p.at(T![:]) {730		p.bump();731		destruct(p);732	};733	if p.at(T![=]) {734		p.bump();735		expr(p);736	}737	m.complete(p, DESTRUCT_OBJECT_FIELD);738}739fn obj_local(p: &mut Parser) {740	let m = p.start();741	p.bump_assert(T![local]);742	bind(p);743	m.complete(p, OBJ_LOCAL);744}745fn destruct(p: &mut Parser) -> CompletedMarker {746	let m = p.start();747	let _ex = p.expected_syntax_name("destruction specifier");748	if p.at(T![?]) {749		p.bump();750		m.complete(p, DESTRUCT_SKIP)751	} else if p.at(T!['[']) {752		p.bump();753		// let mut had_rest = false;754		loop {755			if p.at(T![']']) {756				p.bump();757				break;758			} else if p.at(T![...]) {759				// let m_err = p.start_ranger();760				destruct_rest(p);761			// if had_rest {762			// 	p.custom_error(m_err.finish(p), "only one rest can be present in array");763			// }764			// had_rest = true;765			} else {766				destruct(p);767			}768			if p.at(T![,]) {769				p.bump();770				continue;771			}772			p.expect(T![']']);773			break;774		}775		m.complete(p, DESTRUCT_ARRAY)776	} else if p.at(T!['{']) {777		p.bump();778		let mut had_rest = false;779		loop {780			if p.at(T!['}']) {781				p.bump();782				break;783			} else if p.at(T![...]) {784				// let m_err = p.start_ranger();785				destruct_rest(p);786				// if had_rest {787				// 	p.custom_error(m_err.finish(p), "only one rest can be present in object");788				// }789				had_rest = true;790			} else {791				if had_rest {792					p.error_with_recovery_set(TS![]);793				}794				destruct_object_field(p);795			}796			if p.at(T![,]) {797				p.bump();798				continue;799			}800			p.expect(T!['}']);801			break;802		}803		m.complete(p, DESTRUCT_OBJECT)804	} else if p.at(IDENT) {805		name(p);806		m.complete(p, DESTRUCT_FULL)807	} else {808		m.forget(p);809		p.error_with_recovery_set(TS![; , '}', '(', :])810	}811}812fn bind(p: &mut Parser) {813	let m = p.start();814	if p.at(IDENT) && p.nth_at(1, T!['(']) {815		name(p);816		params_desc(p);817		p.expect(T![=]);818		expr(p);819		m.complete(p, BIND_FUNCTION)820	} else if p.at(IDENT) && p.nth_at(1, T![=]) && p.nth_at(2, T![function]) {821		name(p);822		p.expect(T![=]);823		p.expect(T![function]);824		params_desc(p);825		expr(p);826		m.complete(p, BIND_FUNCTION)827	} else {828		destruct(p);829		p.expect(T![=]);830		expr(p);831		m.complete(p, BIND_DESTRUCT)832	};833}834fn text(p: &mut Parser) {835	assert!(Text::can_cast(p.current()));836	p.bump();837}838fn number(p: &mut Parser) {839	assert!(Number::can_cast(p.current()));840	p.bump();841}842fn literal(p: &mut Parser) {843	assert!(Literal::can_cast(p.current()));844	p.bump();845}846fn lhs_basic(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {847	let _e = p.expected_syntax_name("expression");848	Ok(if Literal::can_cast(p.current()) {849		let m = p.start();850		literal(p);851		m.complete(p, EXPR_LITERAL)852	} else if Text::can_cast(p.current()) {853		let m = p.start();854		text(p);855		m.complete(p, EXPR_STRING)856	} else if Number::can_cast(p.current()) {857		let m = p.start();858		number(p);859		m.complete(p, EXPR_NUMBER)860	} else if p.at(IDENT) {861		let m = p.start();862		name(p);863		m.complete(p, EXPR_VAR)864	} else if p.at(T![if]) {865		let m = p.start();866		p.bump();867		expr(p);868		p.expect(T![then]);869		expr(p).wrap(p, TRUE_EXPR);870		if p.at(T![else]) {871			p.bump();872			expr(p).wrap(p, FALSE_EXPR);873		}874		m.complete(p, EXPR_IF_THEN_ELSE)875	} else if p.at(T!['[']) {876		array(p)877	} else if p.at(T!['{']) {878		object(p)879	} else if p.at(T![function]) {880		let m = p.start();881		p.bump();882		params_desc(p);883		expr(p);884		m.complete(p, EXPR_FUNCTION)885	} else if p.at(T![error]) {886		let m = p.start();887		p.bump();888		expr(p);889		m.complete(p, EXPR_ERROR)890	} else if p.at(T![import]) || p.at(T![importstr]) || p.at(T![importbin]) {891		let m = p.start();892		p.bump();893		text(p);894		m.complete(p, EXPR_IMPORT)895	} else if let Some(op) = UnaryOperatorKind::cast(p.current()) {896		let ((), right_binding_power) = op.binding_power();897898		let m = p.start();899		p.bump();900		let _ = expr_binding_power(p, right_binding_power);901		m.complete(p, EXPR_UNARY)902	} else if p.at(T!['(']) {903		let m = p.start();904		p.bump();905		expr(p);906		p.expect(T![')']);907		m.complete(p, EXPR_PARENED)908	} else {909		return Err(p.error_with_no_skip());910	})911}912913impl Parse {914	pub fn syntax(&self) -> SyntaxNode {915		SyntaxNode::new_root(self.green_node.clone())916	}917}
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}4849#[derive(Debug)]50pub struct LocatedSyntaxError {51	pub error: SyntaxError,52	pub range: TextRange,53}5455impl Parser {56	pub fn new(kinds: Vec<SyntaxKind>) -> Self {57		Self {58			kinds,59			offset: 0,60			events: vec![],61			entered: 0,62			last_error_token: 0,63			hints: vec![],64			expected_syntax_tracking_state: Rc::new(Cell::new(ExpectedSyntax::Unnamed(TS![]))),65			steps: Cell::new(0),66		}67	}68	pub fn clear_outdated_hints(&mut self) {69		let amount = self70			.hints71			.iter()72			.rev()73			.take_while(|h| h.0 > self.entered)74			.count();75		self.hints.truncate(self.hints.len() - amount);76	}77	fn clear_expected_syntaxes(&self) {78		self.expected_syntax_tracking_state79			.set(ExpectedSyntax::Unnamed(TS![]));80	}81	pub fn start(&mut self) -> Marker {82		let start_event_idx = self.events.len();83		self.events.push(Event::Pending);84		self.entered += 1;85		Marker::new(start_event_idx)86	}87	// pub fn start_ranger(&mut self) -> Ranger {88	// 	let pos = self.offset;89	// 	Ranger { pos }90	// }91	pub fn parse(mut self) -> Vec<Event> {92		let m = self.start();93		expr(&mut self);94		if !self.at(EOF) {95			let m = self.start();96			while !self.at(EOF) {97				self.bump();98			}99			m.complete_error(&mut self, "unexpected tokens after end");100		}101		m.complete(&mut self, SOURCE_FILE);102103		self.events104	}105106	pub(crate) fn expect(&mut self, kind: SyntaxKind) {107		self.expect_with_recovery_set(kind, TS![]);108	}109110	pub(crate) fn expect_with_recovery_set(111		&mut self,112		kind: SyntaxKind,113		recovery_set: SyntaxKindSet,114	) {115		if self.at(kind) {116			if kind != EOF {117				self.bump();118			}119		} else {120			self.error_with_recovery_set(recovery_set);121		}122	}123124	// pub(crate) fn expect_with_no_skip(&mut self, kind: SyntaxKind) {125	// 	if self.at(kind) {126	// 		self.bump();127	// 	} else {128	// 		self.error_with_no_skip();129	// 	}130	// }131	pub fn error_with_no_skip(&mut self) -> CompletedMarker {132		self.error_with_recovery_set(SyntaxKindSet::ALL)133	}134135	pub fn error_with_recovery_set(&mut self, recovery_set: SyntaxKindSet) -> CompletedMarker {136		let expected = self.expected_syntax_tracking_state.get();137		self.expected_syntax_tracking_state138			.set(ExpectedSyntax::Unnamed(TS![]));139140		if self.at_end() || self.at_ts(recovery_set) {141			let m = self.start();142			return m.complete_missing(self, expected);143		}144145		let current_token = self.current();146147		self.last_error_token = self.offset;148149		let m = self.start();150		self.bump();151		let m = m.complete_unexpected(self, expected, current_token);152		self.clear_expected_syntaxes();153		m154	}155	fn bump_assert(&mut self, kind: SyntaxKind) {156		assert!(self.at(kind), "expected {kind:?}");157		self.bump_remap(self.current());158	}159	fn bump(&mut self) {160		self.bump_remap(self.current());161	}162	fn bump_remap(&mut self, kind: SyntaxKind) {163		assert_ne!(self.offset, self.kinds.len(), "already at end");164		self.events.push(Event::Token { kind });165		self.offset += 1;166		self.clear_expected_syntaxes();167	}168	fn step(&self) {169		use std::fmt::Write;170		let steps = self.steps.get();171		if steps >= 15_000_000 {172			let mut out = "seems like parsing is stuck".to_owned();173			{174				let last = 20;175				write!(out, "\n\nLast {last} events:").unwrap();176				for (i, event) in self177					.events178					.iter()179					.skip(self.events.len().saturating_sub(last))180					.enumerate()181				{182					write!(out, "\n{i}. {event:?}").unwrap();183				}184			}185			{186				let next = 20;187				write!(out, "\n\nNext {next} tokens:").unwrap();188				for (i, tok) in self.kinds.iter().skip(self.offset).take(next).enumerate() {189					write!(out, "\n{i}. {tok:?}").unwrap();190				}191			}192			panic!("{out}")193		}194		self.steps.set(steps + 1);195	}196	fn nth(&self, i: usize) -> SyntaxKind {197		self.step();198		let mut offset = self.offset;199		for _ in 0..i {200			offset += 1;201		}202		self.kinds.get(offset).copied().unwrap_or(EOF)203	}204	fn current(&self) -> SyntaxKind {205		self.nth(0)206	}207	#[must_use]208	pub(crate) fn expected_syntax_name(&self, name: &'static str) -> ExpectedSyntaxGuard {209		self.expected_syntax_tracking_state210			.set(ExpectedSyntax::Named(name));211212		ExpectedSyntaxGuard::new(Rc::clone(&self.expected_syntax_tracking_state))213	}214	pub fn at(&self, kind: SyntaxKind) -> bool {215		self.nth_at(0, kind)216	}217	pub fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool {218		if n == 0 {219			if let ExpectedSyntax::Unnamed(kinds) = self.expected_syntax_tracking_state.get() {220				let kinds = kinds.with(kind);221				self.expected_syntax_tracking_state222					.set(ExpectedSyntax::Unnamed(kinds));223			}224		}225		self.nth(n) == kind226	}227	pub fn at_ts(&self, set: SyntaxKindSet) -> bool {228		if let ExpectedSyntax::Unnamed(kinds) = self.expected_syntax_tracking_state.get() {229			let kinds = kinds.union(set);230			self.expected_syntax_tracking_state231				.set(ExpectedSyntax::Unnamed(kinds));232		}233		set.contains(self.current())234	}235	pub fn at_end(&self) -> bool {236		self.at(EOF)237	}238}239pub struct ExpectedSyntaxGuard {240	expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>,241}242243impl ExpectedSyntaxGuard {244	fn new(expected_syntax_tracking_state: Rc<Cell<ExpectedSyntax>>) -> Self {245		Self {246			expected_syntax_tracking_state,247		}248	}249}250251impl Drop for ExpectedSyntaxGuard {252	fn drop(&mut self) {253		self.expected_syntax_tracking_state254			.set(ExpectedSyntax::Unnamed(TS![]));255	}256}257258#[derive(Clone, Debug, Copy)]259pub enum ExpectedSyntax {260	Named(&'static str),261	Unnamed(SyntaxKindSet),262}263impl fmt::Display for ExpectedSyntax {264	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {265		match self {266			Self::Named(name) => write!(f, "{name}"),267			Self::Unnamed(set) => write!(f, "{set}"),268		}269	}270}271272fn expr(p: &mut Parser) -> CompletedMarker {273	let m = p.start();274	while p.at(T![local]) || p.at(T![assert]) {275		let m = p.start();276277		if p.at(T![local]) {278			p.bump();279			loop {280				if p.at(T![;]) {281					p.bump();282					break;283				}284				bind(p);285286				if p.at(T![,]) {287					p.bump();288					continue;289				}290				p.expect(T![;]);291				break;292			}293			m.complete(p, STMT_LOCAL);294		} else {295			assertion(p);296			p.expect(T![;]);297			m.complete(p, STMT_ASSERT);298		}299	}300	match expr_binding_power(p, 0) {301		Ok(m) | Err(m) => m,302	};303	m.complete(p, EXPR)304}305fn expr_binding_power(306	p: &mut Parser,307	minimum_binding_power: u8,308) -> Result<CompletedMarker, CompletedMarker> {309	let mut lhs = lhs(p)?;310311	while let Some(op) = BinaryOperatorKind::cast(p.current())312		.or_else(|| p.at(T!['{']).then_some(BinaryOperatorKind::MetaObjectApply))313	{314		let (left_binding_power, right_binding_power) = op.binding_power();315		if left_binding_power < minimum_binding_power {316			break;317		}318319		// Object apply is not a real operator, we dont have something to bump320		if op != BinaryOperatorKind::MetaObjectApply {321			p.bump();322		}323324		let m = lhs.wrap(p, EXPR).precede(p);325		let parsed_rhs = expr_binding_power(p, right_binding_power)326			.map(|v| v.precede(p).complete(p, EXPR))327			.is_ok();328		lhs = m.complete(329			p,330			if op == BinaryOperatorKind::MetaObjectApply {331				EXPR_OBJ_EXTEND332			} else {333				EXPR_BINARY334			},335		);336337		if !parsed_rhs {338			break;339		}340	}341	Ok(lhs)342}343344const COMPSPEC: SyntaxKindSet = TS![for if];345fn compspec(p: &mut Parser) -> CompletedMarker {346	assert!(p.at_ts(COMPSPEC));347	if p.at(T![for]) {348		let m = p.start();349		p.bump();350		destruct(p);351		p.expect(T![in]);352		expr(p);353		m.complete(p, FOR_SPEC)354	} else if p.at(T![if]) {355		let m = p.start();356		p.bump();357		expr(p);358		m.complete(p, IF_SPEC)359	} else {360		unreachable!()361	}362}363364fn comma(p: &mut Parser) -> bool {365	comma_with_alternatives(p, TS![])366}367fn comma_with_alternatives(p: &mut Parser, set: SyntaxKindSet) -> bool {368	if p.at(T![,]) {369		p.bump();370		true371	} else if p.at_ts(set) {372		let _ex = p.expected_syntax_name("comma");373		p.expect_with_recovery_set(T![,], TS![]);374		true375	} else {376		false377	}378}379380fn field_name(p: &mut Parser) {381	let _e = p.expected_syntax_name("field name");382	let m = p.start();383	if p.at(T!['[']) {384		p.bump();385		expr(p);386		p.expect(T![']']);387		m.complete(p, FIELD_NAME_DYNAMIC);388	} else if p.at(IDENT) {389		name(p);390		m.complete(p, FIELD_NAME_FIXED);391	} else if Text::can_cast(p.current()) {392		text(p);393		m.complete(p, FIELD_NAME_FIXED);394	} else {395		m.forget(p);396		p.error_with_recovery_set(TS![; : :: ::: '(']);397	}398}399fn visibility(p: &mut Parser) {400	if p.at_ts(TS![: :: :::]) {401		p.bump();402	} else {403		p.error_with_recovery_set(TS![=]);404	}405}406fn assertion(p: &mut Parser) {407	let m = p.start();408	p.bump_assert(T![assert]);409	expr(p);410	if p.at(T![:]) {411		p.bump();412		expr(p);413	}414	m.complete(p, ASSERTION);415}416fn object(p: &mut Parser) -> CompletedMarker {417	let m_t = p.start();418	let m = p.start();419	p.bump_assert(T!['{']);420421	let mut elems = 0;422	let mut compspecs = Vec::new();423	let mut asserts = Vec::new();424	loop {425		if p.at(T!['}']) {426			p.bump();427			break;428		}429		if p.at_ts(TS![for]) {430			if elems == 0 {431				let m = p.start();432				m.complete_missing(p, ExpectedSyntax::Named("field definition"));433			}434			while p.at_ts(COMPSPEC) {435				compspecs.push(compspec(p));436			}437			if comma_with_alternatives(p, TS![;]) {438				continue;439			}440			p.expect(R_BRACE);441			break;442		}443		let m = p.start();444		if p.at(T![local]) {445			obj_local(p);446			m.complete(p, MEMBER_BIND_STMT);447		} else if p.at(T![assert]) {448			assertion(p);449			asserts.push(m.complete(p, MEMBER_ASSERT_STMT));450		} else {451			field_name(p);452			if p.at(T![+]) {453				p.bump();454			}455			let params = if p.at(T!['(']) {456				params_desc(p);457				visibility(p);458				expr(p);459				true460			} else if p.at_ts(TS![: :: :::]) && p.nth_at(1, T![function]) {461				visibility(p);462				p.bump_assert(T![function]);463				params_desc(p);464				expr(p);465				true466			} else {467				visibility(p);468				expr(p);469				false470			};471			elems += 1;472473			if params {474				m.complete(p, MEMBER_FIELD_METHOD)475			} else {476				m.complete(p, MEMBER_FIELD_NORMAL)477			};478		};479		while p.at_ts(COMPSPEC) {480			compspecs.push(compspec(p));481		}482		if comma_with_alternatives(p, TS![;]) {483			continue;484		}485		p.expect(R_BRACE);486		break;487	}488489	if elems > 1 && !compspecs.is_empty() {490		for errored in compspecs {491			errored.wrap_error(492				p,493				"compspec may only be used if there is only one object element",494			);495		}496		m.complete(p, OBJ_BODY_MEMBER_LIST);497	} else if !compspecs.is_empty() {498		for errored in asserts {499			errored.wrap_error(p, "asserts can't be used in object comprehensions");500		}501		m.complete(p, OBJ_BODY_COMP);502	} else {503		m.complete(p, OBJ_BODY_MEMBER_LIST);504	}505	m_t.complete(p, EXPR_OBJECT)506}507fn param(p: &mut Parser) {508	let m = p.start();509	destruct(p);510	if p.at(T![=]) {511		p.bump();512		expr(p);513	}514	m.complete(p, PARAM);515}516fn params_desc(p: &mut Parser) -> CompletedMarker {517	let m = p.start();518	p.bump_assert(T!['(']);519520	loop {521		if p.at(T![')']) {522			p.bump();523			break;524		}525		param(p);526		if comma(p) {527			continue;528		}529		p.expect(T![')']);530		break;531	}532533	m.complete(p, PARAMS_DESC)534}535fn args_desc(p: &mut Parser) {536	let m = p.start();537	p.bump_assert(T!['(']);538539	let started_named = Cell::new(false);540	let mut unnamed_after_named = Vec::new();541542	loop {543		if p.at(T![')']) {544			break;545		}546547		let m = p.start();548		if p.at(IDENT) && p.nth_at(1, T![=]) {549			name(p);550			p.bump();551			expr(p);552			m.complete(p, ARG);553			started_named.set(true);554		} else {555			expr(p);556			let arg = m.complete(p, ARG);557			if started_named.get() {558				unnamed_after_named.push(arg);559			}560		}561		if comma(p) {562			continue;563		}564		break;565	}566	p.expect(T![')']);567	if p.at(T![tailstrict]) {568		p.bump();569	}570571	for errored in unnamed_after_named {572		errored.wrap_error(p, "can't use positional arguments after named");573	}574575	m.complete(p, ARGS_DESC);576}577578fn array(p: &mut Parser) -> CompletedMarker {579	// Start the list node580	let m = p.start();581	p.bump_assert(T!['[']);582583	let mut compspecs = Vec::new();584	let mut elems = 0;585586	loop {587		if p.at(T![']']) {588			p.bump();589			break;590		}591		if elems != 0 && p.at_ts(TS![for]) {592			while p.at_ts(COMPSPEC) {593				compspecs.push(compspec(p));594			}595			if comma(p) {596				continue;597			}598			p.expect(T![']']);599			break;600		}601		expr(p);602		elems += 1;603		while p.at_ts(COMPSPEC) {604			compspecs.push(compspec(p));605		}606		if comma(p) {607			continue;608		}609		p.expect(T![']']);610		break;611	}612613	if elems > 1 && !compspecs.is_empty() {614		for spec in compspecs {615			spec.wrap_error(616				p,617				"compspec may only be used if there is only one array element",618			);619		}620621		m.complete(p, EXPR_ARRAY)622	} else if !compspecs.is_empty() {623		m.complete(p, EXPR_ARRAY_COMP)624	} else {625		m.complete(p, EXPR_ARRAY)626	}627}628/// Returns true if it was slice, false if just index629#[must_use]630fn slice_desc_or_index(p: &mut Parser) -> bool {631	let m = p.start();632	p.bump();633	// TODO: do not treat :, ::, ::: as full tokens?634	// Start635	if !p.at(T![:]) && !p.at(T![::]) {636		expr(p);637	}638	if p.at(T![:]) {639		p.bump();640		// End641		if !p.at(T![']']) {642			expr(p).wrap(p, SLICE_DESC_END);643		}644		if p.at(T![:]) {645			p.bump();646			// Step647			if !p.at(T![']']) {648				expr(p).wrap(p, SLICE_DESC_STEP);649			}650		}651	} else if p.at(T![::]) {652		p.bump();653		// End654		if !p.at(T![']']) {655			expr(p).wrap(p, SLICE_DESC_END);656		}657	} else {658		// It was not a slice659		p.expect(T![']']);660		m.forget(p);661		return false;662	}663	p.expect(T![']']);664	m.complete(p, SLICE_DESC);665	true666}667668fn suffix(p: &mut Parser) {669	loop {670		let start = p.start();671		let _marker: CompletedMarker = if p.at(T![?]) {672			p.bump();673			p.expect(T![.]);674			if p.at(IDENT) {675				name(p);676				start.complete(p, SUFFIX_INDEX)677			} else if p.at(T!['[']) {678				p.bump();679				expr(p);680				p.expect(T![']']);681				start.complete(p, SUFFIX_INDEX_EXPR)682			} else {683				start.complete_missing(p, ExpectedSyntax::Named("index"))684			}685		} else if p.at(T![.]) {686			p.bump();687			name(p);688			start.complete(p, SUFFIX_INDEX)689		} else if p.at(T!['[']) {690			if slice_desc_or_index(p) {691				start.complete(p, SUFFIX_SLICE)692			} else {693				start.complete(p, SUFFIX_INDEX_EXPR)694			}695		} else if p.at(T!['(']) {696			args_desc(p);697			start.complete(p, SUFFIX_APPLY)698		} else {699			start.forget(p);700			break;701		};702	}703}704705fn lhs(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {706	let lhs = lhs_basic(p)?;707708	suffix(p);709710	Ok(lhs)711}712fn name(p: &mut Parser) {713	let m = p.start();714	p.expect(IDENT);715	m.complete(p, NAME);716}717fn destruct_rest(p: &mut Parser) {718	let m = p.start();719	p.bump_assert(T![...]);720	if p.at(IDENT) {721		p.bump();722	}723	m.complete(p, DESTRUCT_REST);724}725fn destruct_object_field(p: &mut Parser) {726	let m = p.start();727	name(p);728	if p.at(T![:]) {729		p.bump();730		destruct(p);731	};732	if p.at(T![=]) {733		p.bump();734		expr(p);735	}736	m.complete(p, DESTRUCT_OBJECT_FIELD);737}738fn obj_local(p: &mut Parser) {739	let m = p.start();740	p.bump_assert(T![local]);741	bind(p);742	m.complete(p, OBJ_LOCAL);743}744fn destruct(p: &mut Parser) -> CompletedMarker {745	let m = p.start();746	let _ex = p.expected_syntax_name("destruction specifier");747	if p.at(T![?]) {748		p.bump();749		m.complete(p, DESTRUCT_SKIP)750	} else if p.at(T!['[']) {751		p.bump();752		// let mut had_rest = false;753		loop {754			if p.at(T![']']) {755				p.bump();756				break;757			} else if p.at(T![...]) {758				// let m_err = p.start_ranger();759				destruct_rest(p);760			// if had_rest {761			// 	p.custom_error(m_err.finish(p), "only one rest can be present in array");762			// }763			// had_rest = true;764			} else {765				destruct(p);766			}767			if p.at(T![,]) {768				p.bump();769				continue;770			}771			p.expect(T![']']);772			break;773		}774		m.complete(p, DESTRUCT_ARRAY)775	} else if p.at(T!['{']) {776		p.bump();777		let mut had_rest = false;778		loop {779			if p.at(T!['}']) {780				p.bump();781				break;782			} else if p.at(T![...]) {783				// let m_err = p.start_ranger();784				destruct_rest(p);785				// if had_rest {786				// 	p.custom_error(m_err.finish(p), "only one rest can be present in object");787				// }788				had_rest = true;789			} else {790				if had_rest {791					p.error_with_recovery_set(TS![]);792				}793				destruct_object_field(p);794			}795			if p.at(T![,]) {796				p.bump();797				continue;798			}799			p.expect(T!['}']);800			break;801		}802		m.complete(p, DESTRUCT_OBJECT)803	} else if p.at(IDENT) {804		name(p);805		m.complete(p, DESTRUCT_FULL)806	} else {807		m.forget(p);808		p.error_with_recovery_set(TS![; , '}', '(', :])809	}810}811fn bind(p: &mut Parser) {812	let m = p.start();813	if p.at(IDENT) && p.nth_at(1, T!['(']) {814		name(p);815		params_desc(p);816		p.expect(T![=]);817		expr(p);818		m.complete(p, BIND_FUNCTION)819	} else if p.at(IDENT) && p.nth_at(1, T![=]) && p.nth_at(2, T![function]) {820		name(p);821		p.expect(T![=]);822		p.expect(T![function]);823		params_desc(p);824		expr(p);825		m.complete(p, BIND_FUNCTION)826	} else {827		destruct(p);828		p.expect(T![=]);829		expr(p);830		m.complete(p, BIND_DESTRUCT)831	};832}833fn text(p: &mut Parser) {834	assert!(Text::can_cast(p.current()));835	p.bump();836}837fn number(p: &mut Parser) {838	assert!(Number::can_cast(p.current()));839	p.bump();840}841fn literal(p: &mut Parser) {842	assert!(Literal::can_cast(p.current()));843	p.bump();844}845fn lhs_basic(p: &mut Parser) -> Result<CompletedMarker, CompletedMarker> {846	let _e = p.expected_syntax_name("expression");847	Ok(if Literal::can_cast(p.current()) {848		let m = p.start();849		literal(p);850		m.complete(p, EXPR_LITERAL)851	} else if Text::can_cast(p.current()) {852		let m = p.start();853		text(p);854		m.complete(p, EXPR_STRING)855	} else if Number::can_cast(p.current()) {856		let m = p.start();857		number(p);858		m.complete(p, EXPR_NUMBER)859	} else if p.at(IDENT) {860		let m = p.start();861		name(p);862		m.complete(p, EXPR_VAR)863	} else if p.at(T![if]) {864		let m = p.start();865		p.bump();866		expr(p);867		p.expect(T![then]);868		expr(p).wrap(p, TRUE_EXPR);869		if p.at(T![else]) {870			p.bump();871			expr(p).wrap(p, FALSE_EXPR);872		}873		m.complete(p, EXPR_IF_THEN_ELSE)874	} else if p.at(T!['[']) {875		array(p)876	} else if p.at(T!['{']) {877		object(p)878	} else if p.at(T![function]) {879		let m = p.start();880		p.bump();881		params_desc(p);882		expr(p);883		m.complete(p, EXPR_FUNCTION)884	} else if p.at(T![error]) {885		let m = p.start();886		p.bump();887		expr(p);888		m.complete(p, EXPR_ERROR)889	} else if p.at(T![import]) || p.at(T![importstr]) || p.at(T![importbin]) {890		let m = p.start();891		p.bump();892		text(p);893		m.complete(p, EXPR_IMPORT)894	} else if let Some(op) = UnaryOperatorKind::cast(p.current()) {895		let ((), right_binding_power) = op.binding_power();896897		let m = p.start();898		p.bump();899		let _ = expr_binding_power(p, right_binding_power);900		m.complete(p, EXPR_UNARY)901	} else if p.at(T!['(']) {902		let m = p.start();903		p.bump();904		expr(p);905		p.expect(T![')']);906		m.complete(p, EXPR_PARENED)907	} else {908		return Err(p.error_with_no_skip());909	})910}911912impl Parse {913	pub fn syntax(&self) -> SyntaxNode {914		SyntaxNode::new_root(self.green_node.clone())915	}916}
modifiedcrates/jrsonnet-rowan-parser/src/precedence.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/precedence.rs
+++ b/crates/jrsonnet-rowan-parser/src/precedence.rs
@@ -13,8 +13,7 @@
 			Self::BitXor => (8, 9),
 			Self::BitOr => (6, 7),
 			Self::And => (4, 5),
-			Self::NullCoaelse => (2, 3),
-			Self::Or => (2, 3),
+			Self::NullCoaelse | Self::Or => (2, 3),
 			Self::ErrorNoOperator => (0, 1),
 		}
 	}
@@ -23,9 +22,7 @@
 impl UnaryOperatorKind {
 	pub fn binding_power(&self) -> ((), u8) {
 		match self {
-			Self::Minus => ((), 20),
-			Self::Not => ((), 20),
-			Self::BitNot => ((), 20),
+			Self::Minus | Self::Not | Self::BitNot => ((), 20),
 		}
 	}
 }
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
@@ -17,6 +17,7 @@
 	let _ = lex_str_block(lex);
 }
 
+#[allow(clippy::too_many_lines)]
 pub fn lex_str_block(lex: &mut Lexer<SyntaxKind>) -> Result<(), StringBlockError> {
 	struct Context<'a> {
 		source: &'a str,
@@ -78,6 +79,7 @@
 			};
 		}
 
+		#[allow(clippy::range_plus_one)]
 		fn pos(&self) -> Range<usize> {
 			if self.index == self.source.len() {
 				self.offset + self.index..self.offset + self.index
@@ -120,8 +122,7 @@
 		let end_index = ctx
 			.rest()
 			.find("|||")
-			.map(|v| v + 3)
-			.unwrap_or_else(|| ctx.rest().len());
+			.map_or_else(|| ctx.rest().len(), |v| v + 3);
 		lex.bump(ctx.index + end_index);
 	}
 
@@ -150,7 +151,7 @@
 	}
 
 	// Process leading blank lines before calculating string block indent
-	while let Some('\n') = ctx.peek() {
+	while ctx.peek() == Some('\n') {
 		ctx.next();
 	}
 
@@ -179,7 +180,7 @@
 		}
 
 		// Skip any blank lines
-		while let Some('\n') = ctx.peek() {
+		while ctx.peek() == Some('\n') {
 			ctx.next();
 		}
 
@@ -187,9 +188,11 @@
 		num_whitespace = check_whitespace(str_block_indent, ctx.rest());
 		if num_whitespace == 0 {
 			// End of the text block
-			let mut term_indent = String::with_capacity(num_whitespace);
+			// let mut term_indent = String::with_capacity(num_whitespace);
 			while let Some(' ' | '\t') = ctx.peek() {
-				term_indent.push(ctx.next().unwrap());
+				// term_indent.push(
+				ctx.next().unwrap();
+				// );
 			}
 
 			if !ctx.rest().starts_with("|||") {
modifiedcrates/jrsonnet-rowan-parser/src/token_set.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/token_set.rs
+++ b/crates/jrsonnet-rowan-parser/src/token_set.rs
@@ -10,21 +10,23 @@
 	pub const EMPTY: Self = Self(0);
 	pub const ALL: Self = Self(u128::MAX);
 
-	pub const fn new(kinds: &[SyntaxKind]) -> SyntaxKindSet {
+	pub const fn new(kinds: &[SyntaxKind]) -> Self {
 		let mut res = 0u128;
 		let mut i = 0;
 		while i < kinds.len() {
 			res |= mask(kinds[i]);
-			i += 1
+			i += 1;
 		}
-		SyntaxKindSet(res)
+		Self(res)
 	}
 
-	pub const fn union(self, other: SyntaxKindSet) -> SyntaxKindSet {
-		SyntaxKindSet(self.0 | other.0)
+	#[must_use]
+	pub const fn union(self, other: Self) -> Self {
+		Self(self.0 | other.0)
 	}
-	pub const fn with(self, kind: SyntaxKind) -> SyntaxKindSet {
-		SyntaxKindSet(self.0 | mask(kind))
+	#[must_use]
+	pub const fn with(self, kind: SyntaxKind) -> Self {
+		Self(self.0 | mask(kind))
 	}
 
 	pub fn contains(&self, kind: SyntaxKind) -> bool {
@@ -40,7 +42,7 @@
 		let mut variants = <Vec<SyntaxKind>>::new();
 		for i in 0..128 {
 			if v & 1 == 1 {
-				variants.push(SyntaxKind::from_raw(i))
+				variants.push(SyntaxKind::from_raw(i));
 			}
 			v >>= 1;
 			if v == 0 {
@@ -65,7 +67,7 @@
 		let mut variants = <Vec<SyntaxKind>>::new();
 		for i in 0..128 {
 			if v & 1 == 1 {
-				variants.push(SyntaxKind::from_raw(i))
+				variants.push(SyntaxKind::from_raw(i));
 			}
 			v >>= 1;
 			if v == 0 {
@@ -77,9 +79,7 @@
 }
 
 const fn mask(kind: SyntaxKind) -> u128 {
-	if kind as u32 > 128 {
-		panic!("mask for not a token kind")
-	}
+	assert!(kind as u32 <= 128, "mask for not a token kind");
 	1u128 << (kind as u128)
 }
 
modifiedxtask/src/sourcegen/ast.rsdiffbeforeafterboth
--- a/xtask/src/sourcegen/ast.rs
+++ b/xtask/src/sourcegen/ast.rs
@@ -72,7 +72,7 @@
 	pub fn is_many(&self) -> bool {
 		matches!(
 			self,
-			Field::Node {
+			Self::Node {
 				cardinality: Cardinality::Many,
 				..
 			}
@@ -81,44 +81,41 @@
 
 	pub fn token_name(&self) -> Option<String> {
 		match self {
-			Field::Token(token) => Some(token.clone()),
-			_ => None,
+			Self::Token(token) => Some(token.clone()),
+			Self::Node { .. } => None,
 		}
 	}
 	pub fn token_kind(&self, kinds: &KindsSrc) -> Option<TokenStream> {
 		match self {
-			Field::Token(token) => Some(kinds.token(token).expect("token exists").reference()),
-			_ => None,
+			Self::Token(token) => Some(kinds.token(token).expect("token exists").reference()),
+			Self::Node { .. } => None,
 		}
 	}
 	pub fn is_token_enum(&self, grammar: &AstSrc) -> bool {
 		match self {
-			Field::Node { ty, .. } => grammar.token_enums.iter().any(|e| &e.name == ty),
-			_ => false,
+			Self::Node { ty, .. } => grammar.token_enums.iter().any(|e| &e.name == ty),
+			Self::Token(_) => false,
 		}
 	}
 
 	pub fn method_name(&self, kinds: &KindsSrc) -> proc_macro2::Ident {
 		match self {
-			Field::Token(name) => kinds.token(name).expect("token exists").method_name(),
-			Field::Node { name, .. } => {
+			Self::Token(name) => kinds.token(name).expect("token exists").method_name(),
+			Self::Node { name, .. } => {
 				format_ident!("{}", name)
 			}
 		}
 	}
 	pub fn ty(&self) -> proc_macro2::Ident {
 		match self {
-			Field::Token(_) => format_ident!("SyntaxToken"),
-			Field::Node { ty, .. } => format_ident!("{}", ty),
+			Self::Token(_) => format_ident!("SyntaxToken"),
+			Self::Node { ty, .. } => format_ident!("{}", ty),
 		}
 	}
 }
 
 pub fn lower(kinds: &KindsSrc, grammar: &Grammar) -> AstSrc {
-	let mut res = AstSrc {
-		// tokens,
-		..Default::default()
-	};
+	let mut res = AstSrc::default();
 
 	let nodes = grammar.iter().collect::<Vec<_>>();
 
@@ -135,16 +132,15 @@
 				};
 				res.enums.push(enum_src);
 			}
-			None => match lower_token_enum(grammar, rule) {
-				Some(variants) => {
+			None => {
+				if let Some(variants) = lower_token_enum(grammar, rule) {
 					let tokens_enum_src = AstTokenEnumSrc {
 						doc: Vec::new(),
 						name,
 						variants,
 					};
 					res.token_enums.push(tokens_enum_src);
-				}
-				None => {
+				} else {
 					let mut fields = Vec::new();
 					lower_rule(&mut fields, grammar, None, rule, false);
 					let mut types = HashMap::new();
@@ -173,7 +169,7 @@
 						fields,
 					});
 				}
-			},
+			}
 		}
 	}
 
@@ -240,7 +236,7 @@
 			acc.push(field);
 		}
 		Rule::Token(token) => {
-			assert!(label.is_none(), "uexpected label: {:?}", label);
+			assert!(label.is_none(), "uexpected label: {label:?}");
 			let name = grammar[*token].name.clone();
 			let field = Field::Token(name);
 			acc.push(field);
@@ -267,7 +263,7 @@
 		}
 		Rule::Seq(rules) | Rule::Alt(rules) => {
 			for rule in rules {
-				lower_rule(acc, grammar, label, rule, in_optional)
+				lower_rule(acc, grammar, label, rule, in_optional);
 			}
 		}
 		Rule::Opt(rule) => lower_rule(acc, grammar, label, rule, true),
modifiedxtask/src/sourcegen/kinds.rsdiffbeforeafterboth
--- a/xtask/src/sourcegen/kinds.rs
+++ b/xtask/src/sourcegen/kinds.rs
@@ -41,33 +41,33 @@
 impl TokenKind {
 	pub fn grammar_name(&self) -> &str {
 		match self {
-			TokenKind::Keyword { code, .. } => code,
-			TokenKind::Literal { grammar_name, .. } => grammar_name,
-			TokenKind::Meta { grammar_name, .. } => grammar_name,
-			TokenKind::Error { grammar_name, .. } => grammar_name,
+			Self::Keyword { code, .. } => code,
+			Self::Literal { grammar_name, .. }
+			| Self::Meta { grammar_name, .. }
+			| Self::Error { grammar_name, .. } => grammar_name,
 		}
 	}
 	/// How this keyword should appear in kinds enum, screaming snake cased
 	pub fn name(&self) -> &str {
 		match self {
-			TokenKind::Keyword { name, .. } => name,
-			TokenKind::Literal { name, .. } => name,
-			TokenKind::Meta { name, .. } => name,
-			TokenKind::Error { name, .. } => name,
+			Self::Keyword { name, .. }
+			| Self::Literal { name, .. }
+			| Self::Meta { name, .. }
+			| Self::Error { name, .. } => name,
 		}
 	}
 	pub fn expand_kind(&self) -> TokenStream {
 		let name = format_ident!("{}", self.name());
 		let attr = match self {
-			TokenKind::Keyword { code, .. } => quote! {#[token(#code)]},
-			TokenKind::Literal { regex, lexer, .. } => {
+			Self::Keyword { code, .. } => quote! {#[token(#code)]},
+			Self::Literal { regex, lexer, .. } => {
 				let lexer = lexer
 					.as_deref()
 					.map(TokenStream::from_str)
 					.map(|r| r.expect("path is correct"));
 				quote! {#[regex(#regex, #lexer)]}
 			}
-			TokenKind::Error {
+			Self::Error {
 				regex, priority, ..
 			} if regex.is_some() => {
 				let priority = priority.map(|p| quote! {, priority = #p});
@@ -82,7 +82,7 @@
 	}
 	pub fn expand_t_macros(&self) -> Option<TokenStream> {
 		match self {
-			TokenKind::Keyword { code, name } => {
+			Self::Keyword { code, name } => {
 				let code = escape_token_macro(code);
 				let name = format_ident!("{name}");
 				Some(quote! {
@@ -98,29 +98,26 @@
 	/// Keywords are referenced with `T![_]` macro,
 	/// and literals are referenced directly by name
 	pub fn reference(&self) -> TokenStream {
-		match self {
-			TokenKind::Keyword { code, .. } => {
-				let code = escape_token_macro(code);
-				quote! {T![#code]}
-			}
-			_ => {
-				let name = self.name();
-				let ident = format_ident!("{name}");
-				quote! {#ident}
-			}
+		if let Self::Keyword { code, .. } = self {
+			let code = escape_token_macro(code);
+			quote! {T![#code]}
+		} else {
+			let name = self.name();
+			let ident = format_ident!("{name}");
+			quote! {#ident}
 		}
 	}
 
 	pub fn method_name(&self) -> Ident {
 		match self {
-			TokenKind::Keyword { name, .. } => {
+			Self::Keyword { name, .. } => {
 				format_ident!("{}_token", name.to_lowercase())
 			}
-			TokenKind::Literal { name, .. } => {
+			Self::Literal { name, .. } => {
 				format_ident!("{}_lit", name.to_lowercase())
 			}
-			TokenKind::Meta { name, .. } => format_ident!("{}_meta", name.to_lowercase()),
-			TokenKind::Error { name, .. } => format_ident!("{}_error", name.to_lowercase()),
+			Self::Meta { name, .. } => format_ident!("{}_meta", name.to_lowercase()),
+			Self::Error { name, .. } => format_ident!("{}_error", name.to_lowercase()),
 		}
 	}
 }
@@ -188,15 +185,14 @@
 				.is_none(),
 			"token already defined: {}",
 			token.grammar_name()
-		)
+		);
 	}
 	pub fn define_node(&mut self, node: &str) {
 		assert!(
 			self.defined_node_names.insert(node.to_owned()),
-			"node name already defined: {}",
-			node
+			"node name already defined: {node}"
 		);
-		self.nodes.push(node.to_string())
+		self.nodes.push(node.to_string());
 	}
 	pub fn token(&self, tok: &str) -> Option<&TokenKind> {
 		self.defined_tokens.get(tok)
modifiedxtask/src/sourcegen/mod.rsdiffbeforeafterboth
--- a/xtask/src/sourcegen/mod.rs
+++ b/xtask/src/sourcegen/mod.rs
@@ -49,27 +49,27 @@
 				match special {
 					SpecialName::Literal => panic!("literal is not defined: {name}"),
 					SpecialName::Meta => {
-						eprintln!("implicit meta: {}", name);
+						eprintln!("implicit meta: {name}");
 						kinds.define_token(TokenKind::Meta {
 							grammar_name: token.to_owned(),
-							name: format!("META_{}", name),
-						})
+							name: format!("META_{name}"),
+						});
 					}
 					SpecialName::Error => {
-						eprintln!("implicit error: {}", name);
+						eprintln!("implicit error: {name}");
 						kinds.define_token(TokenKind::Error {
 							grammar_name: token.to_owned(),
-							name: format!("ERROR_{}", name),
+							name: format!("ERROR_{name}"),
 							regex: None,
 							priority: None,
 							is_lexer_error: true,
-						})
+						});
 					}
 				};
 				continue;
 			};
 			let name = to_upper_snake_case(token);
-			eprintln!("implicit kw: {}", token);
+			eprintln!("implicit kw: {token}");
 			kinds.define_token(TokenKind::Keyword {
 				code: token.to_owned(),
 				name: format!("{name}_KW"),
@@ -98,14 +98,14 @@
 			"/../crates/jrsonnet-rowan-parser/src/generated/syntax_kinds.rs",
 		)),
 		&syntax_kinds,
-	)?;
+	);
 	ensure_file_contents(
 		&PathBuf::from(concat!(
 			env!("CARGO_MANIFEST_DIR"),
 			"/../crates/jrsonnet-rowan-parser/src/generated/nodes.rs",
 		)),
 		&nodes,
-	)?;
+	);
 	Ok(())
 }
 
@@ -189,6 +189,7 @@
 	reformat(&ast.to_string())
 }
 
+#[allow(clippy::too_many_lines, clippy::cognitive_complexity)]
 fn generate_nodes(kinds: &KindsSrc, grammar: &AstSrc) -> Result<String> {
 	let (node_defs, node_boilerplate_impls): (Vec<_>, Vec<_>) = grammar
 		.nodes
@@ -524,7 +525,7 @@
 fn write_doc_comment(contents: &[String], dest: &mut String) {
 	use std::fmt::Write;
 	for line in contents {
-		writeln!(dest, "///{}", line).unwrap();
+		writeln!(dest, "///{line}").unwrap();
 	}
 }
 
modifiedxtask/src/sourcegen/util.rsdiffbeforeafterboth
--- a/xtask/src/sourcegen/util.rs
+++ b/xtask/src/sourcegen/util.rs
@@ -1,3 +1,5 @@
+// FIXME: Replace various helper here with inflector?
+
 use std::{fs, path::Path};
 
 use anyhow::Result;
@@ -5,11 +7,11 @@
 
 /// Checks that the `file` has the specified `contents`. If that is not the
 /// case, updates the file and then fails the test.
-pub fn ensure_file_contents(file: &Path, contents: &str) -> Result<()> {
+pub fn ensure_file_contents(file: &Path, contents: &str) {
 	if let Ok(old_contents) = fs::read_to_string(file) {
 		if normalize_newlines(&old_contents) == normalize_newlines(contents) {
 			// File is already up to date.
-			return Ok(());
+			return;
 		}
 	}
 
@@ -18,7 +20,6 @@
 		let _ = fs::create_dir_all(parent);
 	}
 	fs::write(file, contents).unwrap();
-	Ok(())
 }
 
 // Eww, someone configured git to use crlf?
@@ -26,8 +27,9 @@
 	s.replace("\r\n", "\n")
 }
 
-pub(crate) fn pluralize(s: &str) -> String {
-	format!("{}s", s)
+pub fn pluralize(s: &str) -> String {
+	// FIXME: Inflector?
+	format!("{s}s")
 }
 
 pub fn to_upper_snake_case(s: &str) -> String {
@@ -35,7 +37,7 @@
 	let mut prev = false;
 	for c in s.chars() {
 		if c.is_ascii_uppercase() && prev {
-			buf.push('_')
+			buf.push('_');
 		}
 		prev = true;
 
@@ -48,7 +50,7 @@
 	let mut prev = false;
 	for c in s.chars() {
 		if c.is_ascii_uppercase() && prev {
-			buf.push('_')
+			buf.push('_');
 		}
 		prev = true;