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

difftreelog

fix warnings in libjsonnet.so

Yaroslav Bolyukin2024-03-17parent: #5c3244a.patch.diff
in: master

9 files changed

modifiedbindings/jsonnet/Cargo.tomldiffbeforeafterboth
--- a/bindings/jsonnet/Cargo.toml
+++ b/bindings/jsonnet/Cargo.toml
@@ -1,3 +1,10 @@
+# NOTE: This library may panic, and it is only safe to panic in FFI contexts when library is built in panic="abort" mode,
+# which is set for release builds of this library.
+# FIXME: Move this warning somewhere else, or remove panics from this library (It is not always possible, in some cases
+# there is nothing to report the error, in those cases use `abort()`)
+# NOTE: This library assumes the allocator is libc malloc or alternative, which does track allocation size for user,
+# see TODO in `jsonnet_realloc`.
+
 [package]
 name = "libjsonnet"
 description = "Rust implementation of libjsonnet.so"
modifiedbindings/jsonnet/src/import.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/import.rs
+++ b/bindings/jsonnet/src/import.rs
@@ -129,7 +129,7 @@
 /// `path` should be a NUL-terminated string
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_jpath_add(vm: &VM, path: *const c_char) {
-	let cstr = CStr::from_ptr(path);
+	let cstr = unsafe { CStr::from_ptr(path) };
 	let path = PathBuf::from(cstr.to_str().unwrap());
 	let any_resolver = vm.state.import_resolver();
 	let resolver = any_resolver
modifiedbindings/jsonnet/src/lib.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/lib.rs
+++ b/bindings/jsonnet/src/lib.rs
@@ -146,7 +146,9 @@
 		if sz == 0 {
 			return std::ptr::null_mut();
 		}
-		return std::alloc::alloc(Layout::from_size_align(sz, std::mem::align_of::<u8>()).unwrap());
+		return unsafe {
+			std::alloc::alloc(Layout::from_size_align(sz, std::mem::align_of::<u8>()).unwrap())
+		};
 	}
 	// TODO: Somehow store size of allocation, because its real size is probally not 16 :D
 	// OR (Alternative way of fixing this TODO)
@@ -154,10 +156,10 @@
 	// TODO: so it should work in normal cases. Maybe force allocator for this library?
 	let old_layout = Layout::from_size_align(16, std::mem::align_of::<u8>()).unwrap();
 	if sz == 0 {
-		std::alloc::dealloc(buf, old_layout);
+		unsafe { std::alloc::dealloc(buf, old_layout) };
 		return std::ptr::null_mut();
 	}
-	std::alloc::realloc(buf, old_layout, sz)
+	unsafe { std::alloc::realloc(buf, old_layout, sz) }
 }
 
 /// Clean up a JSON subtree.
@@ -192,7 +194,7 @@
 	filename: *const c_char,
 	error: &mut c_int,
 ) -> *const c_char {
-	let filename = parse_path(CStr::from_ptr(filename));
+	let filename = unsafe { parse_path(CStr::from_ptr(filename)) };
 	match vm
 		.state
 		.import(filename)
@@ -226,8 +228,8 @@
 	snippet: *const c_char,
 	error: &mut c_int,
 ) -> *const c_char {
-	let filename = CStr::from_ptr(filename);
-	let snippet = CStr::from_ptr(snippet);
+	let filename = unsafe { CStr::from_ptr(filename) };
+	let snippet = unsafe { CStr::from_ptr(snippet) };
 	match vm
 		.state
 		.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())
@@ -275,7 +277,7 @@
 	out.push(0);
 	let v = out.as_ptr();
 	std::mem::forget(out);
-	v as *const c_char
+	v.cast::<c_char>()
 }
 
 /// # Safety
@@ -285,7 +287,7 @@
 	filename: *const c_char,
 	error: &mut c_int,
 ) -> *const c_char {
-	let filename = parse_path(CStr::from_ptr(filename));
+	let filename = unsafe { parse_path(CStr::from_ptr(filename)) };
 	match vm
 		.state
 		.import(filename)
@@ -313,8 +315,8 @@
 	snippet: *const c_char,
 	error: &mut c_int,
 ) -> *const c_char {
-	let filename = CStr::from_ptr(filename);
-	let snippet = CStr::from_ptr(snippet);
+	let filename = unsafe { CStr::from_ptr(filename) };
+	let snippet = unsafe { CStr::from_ptr(snippet) };
 	match vm
 		.state
 		.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())
@@ -367,7 +369,7 @@
 	filename: *const c_char,
 	error: &mut c_int,
 ) -> *const c_char {
-	let filename = parse_path(CStr::from_ptr(filename));
+	let filename = unsafe { parse_path(CStr::from_ptr(filename)) };
 	match vm
 		.state
 		.import(filename)
@@ -395,8 +397,8 @@
 	snippet: *const c_char,
 	error: &mut c_int,
 ) -> *const c_char {
-	let filename = CStr::from_ptr(filename);
-	let snippet = CStr::from_ptr(snippet);
+	let filename = unsafe { CStr::from_ptr(filename) };
+	let snippet = unsafe { CStr::from_ptr(snippet) };
 	match vm
 		.state
 		.evaluate_snippet(filename.to_str().unwrap(), snippet.to_str().unwrap())
modifiedbindings/jsonnet/src/native.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/native.rs
+++ b/bindings/jsonnet/src/native.rs
@@ -17,7 +17,7 @@
 /// failure, which will appear in Jsonnet as an error. The `argv` pointer is an array whose size
 /// matches the array of parameters supplied when the native callback was originally registered.
 ///
-/// - `ctx` User pointer, given in jsonnet_native_callback.
+/// - `ctx` User pointer, given in `jsonnet_native_callback`.
 /// - `argv` Array of arguments from Jsonnet code.
 /// - `param` success Set this byref param to 1 to indicate success and 0 for failure.
 /// Returns the content of the imported file, or an error message.
@@ -69,17 +69,19 @@
 	ctx: *const c_void,
 	mut raw_params: *const *const c_char,
 ) {
-	let name = CStr::from_ptr(name).to_str().expect("name is not utf-8");
+	let name = unsafe { CStr::from_ptr(name).to_str().expect("name is not utf-8") };
 	let mut params = Vec::new();
 	loop {
-		if (*raw_params).is_null() {
+		if (unsafe { *raw_params }).is_null() {
 			break;
 		}
-		let param = CStr::from_ptr(*raw_params)
-			.to_str()
-			.expect("param name is not utf-8");
+		let param = unsafe {
+			CStr::from_ptr(*raw_params)
+				.to_str()
+				.expect("param name is not utf-8")
+		};
 		params.push(param.into());
-		raw_params = raw_params.offset(1);
+		raw_params = unsafe { raw_params.offset(1) };
 	}
 
 	let any_resolver = vm.state.context_initializer();
@@ -91,5 +93,5 @@
 			name,
 			#[allow(deprecated)]
 			NativeCallback::new(params, JsonnetNativeCallbackHandler { ctx, cb }),
-		)
+		);
 }
modifiedbindings/jsonnet/src/val_make.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/val_make.rs
+++ b/bindings/jsonnet/src/val_make.rs
@@ -16,7 +16,7 @@
 /// `v` should be a NUL-terminated string
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_json_make_string(_vm: &VM, val: *const c_char) -> *mut Val {
-	let val = CStr::from_ptr(val);
+	let val = unsafe { CStr::from_ptr(val) };
 	let val = val.to_str().expect("string is not utf-8");
 	Box::into_raw(Box::new(Val::string(val)))
 }
modifiedbindings/jsonnet/src/val_modify.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/val_modify.rs
+++ b/bindings/jsonnet/src/val_modify.rs
@@ -47,7 +47,7 @@
 ) {
 	match obj {
 		Val::Obj(old) => old
-			.extend_field(CStr::from_ptr(name).to_str().unwrap().into())
+			.extend_field(unsafe { CStr::from_ptr(name).to_str().unwrap().into() })
 			.value(val.clone()),
 		_ => panic!("should receive object"),
 	}
modifiedbindings/jsonnet/src/vars_tlas.rsdiffbeforeafterboth
--- a/bindings/jsonnet/src/vars_tlas.rs
+++ b/bindings/jsonnet/src/vars_tlas.rs
@@ -16,8 +16,8 @@
 /// `name`, `code` should be a NUL-terminated strings
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_ext_var(vm: &VM, name: *const c_char, value: *const c_char) {
-	let name = CStr::from_ptr(name);
-	let value = CStr::from_ptr(value);
+	let name = unsafe { CStr::from_ptr(name) };
+	let value = unsafe { CStr::from_ptr(value) };
 
 	let any_initializer = vm.state.context_initializer();
 	any_initializer
@@ -39,8 +39,8 @@
 /// `name`, `code` should be a NUL-terminated strings
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_ext_code(vm: &VM, name: *const c_char, code: *const c_char) {
-	let name = CStr::from_ptr(name);
-	let code = CStr::from_ptr(code);
+	let name = unsafe { CStr::from_ptr(name) };
+	let code = unsafe { CStr::from_ptr(code) };
 
 	let any_initializer = vm.state.context_initializer();
 	any_initializer
@@ -63,8 +63,8 @@
 /// `name`, `value` should be a NUL-terminated strings
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_tla_var(vm: &mut VM, name: *const c_char, value: *const c_char) {
-	let name = CStr::from_ptr(name);
-	let value = CStr::from_ptr(value);
+	let name = unsafe { CStr::from_ptr(name) };
+	let value = unsafe { CStr::from_ptr(value) };
 	vm.tla_args.insert(
 		name.to_str().expect("name is not utf-8").into(),
 		TlaArg::String(value.to_str().expect("value is not utf-8").into()),
@@ -80,8 +80,8 @@
 /// `name`, `code` should be a NUL-terminated strings
 #[no_mangle]
 pub unsafe extern "C" fn jsonnet_tla_code(vm: &mut VM, name: *const c_char, code: *const c_char) {
-	let name = CStr::from_ptr(name);
-	let code = CStr::from_ptr(code);
+	let name = unsafe { CStr::from_ptr(name) };
+	let code = unsafe { CStr::from_ptr(code) };
 
 	let name: IStr = name.to_str().expect("name is not utf-8").into();
 	let code: IStr = code.to_str().expect("code is not utf-8").into();
modifiedcmds/jrsonnet-fmt/src/main.rsdiffbeforeafterboth
before · cmds/jrsonnet-fmt/src/main.rs
1use std::{2	any::type_name,3	fs,4	io::{self, Write},5	path::PathBuf,6	process,7	rc::Rc,8};910use children::{children_between, trivia_before};11use clap::Parser;12use dprint_core::formatting::{13	condition_helpers::is_multiple_lines, condition_resolvers::true_resolver,14	ConditionResolverContext, LineNumber, PrintItems, PrintOptions,15};16use jrsonnet_rowan_parser::{17	nodes::{18		Arg, ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart,19		DestructRest, Expr, ExprBase, FieldName, ForSpec, IfSpec, ImportKind, Literal, Member,20		Name, Number, ObjBody, ObjLocal, ParamsDesc, SliceDesc, SourceFile, Stmt, Suffix, Text,21		UnaryOperator, Visibility,22	},23	AstNode, AstToken as _, SyntaxToken,24};2526use crate::{27	children::trivia_after,28	comments::{format_comments, CommentLocation},29};3031mod children;32mod comments;33#[cfg(test)]34mod tests;3536pub trait Printable {37	fn print(&self, out: &mut PrintItems);38}3940macro_rules! pi {41	(@i; $($t:tt)*) => {{42		#[allow(unused_mut)]43		let mut o = dprint_core::formatting::PrintItems::new();44		pi!(@s; o: $($t)*);45		o46	}};47	(@s; $o:ident: str($e:expr $(,)?) $($t:tt)*) => {{48		$o.push_str($e);49		pi!(@s; $o: $($t)*);50	}};51	(@s; $o:ident: string($e:expr $(,)?) $($t:tt)*) => {{52		$o.push_string($e);53		pi!(@s; $o: $($t)*);54	}};55	(@s; $o:ident: nl $($t:tt)*) => {{56		$o.push_signal(dprint_core::formatting::Signal::NewLine);57		pi!(@s; $o: $($t)*);58	}};59	(@s; $o:ident: tab $($t:tt)*) => {{60		$o.push_signal(dprint_core::formatting::Signal::Tab);61		pi!(@s; $o: $($t)*);62	}};63	(@s; $o:ident: >i $($t:tt)*) => {{64		$o.push_signal(dprint_core::formatting::Signal::StartIndent);65		pi!(@s; $o: $($t)*);66	}};67	(@s; $o:ident: <i $($t:tt)*) => {{68		$o.push_signal(dprint_core::formatting::Signal::FinishIndent);69		pi!(@s; $o: $($t)*);70	}};71	(@s; $o:ident: info($v:expr) $($t:tt)*) => {{72		$o.push_info($v);73		pi!(@s; $o: $($t)*);74	}};75	(@s; $o:ident: if($s:literal, $cond:expr, $($i:tt)*) $($t:tt)*) => {{76		$o.push_condition(dprint_core::formatting::conditions::if_true(77			$s,78			$cond.clone(),79			{80				let mut o = PrintItems::new();81				p!(o, $($i)*);82				o83			},84		));85		pi!(@s; $o: $($t)*);86	}};87	(@s; $o:ident: if_else($s:literal, $cond:expr, $($i:tt)*)($($e:tt)+) $($t:tt)*) => {{88		$o.push_condition(dprint_core::formatting::conditions::if_true_or(89			$s,90			$cond.clone(),91			{92				let mut o = PrintItems::new();93				p!(o, $($i)*);94				o95			},96			{97				let mut o = PrintItems::new();98				p!(o, $($e)*);99				o100			},101		));102		pi!(@s; $o: $($t)*);103	}};104	(@s; $o:ident: if_not($s:literal, $cond:expr, $($e:tt)*) $($t:tt)*) => {{105		$o.push_condition(dprint_core::formatting::conditions::if_true_or(106			$s,107			$cond.clone(),108			{109				let o = PrintItems::new();110				o111			},112			{113				let mut o = PrintItems::new();114				p!(o, $($e)*);115				o116			},117		));118		pi!(@s; $o: $($t)*);119	}};120	(@s; $o:ident: {$expr:expr} $($t:tt)*) => {{121		$expr.print($o);122		pi!(@s; $o: $($t)*);123	}};124	(@s; $o:ident: items($expr:expr) $($t:tt)*) => {{125		$o.extend($expr);126		pi!(@s; $o: $($t)*);127	}};128	(@s; $o:ident: if ($e:expr)($($then:tt)*) $($t:tt)*) => {{129		if $e {130			pi!(@s; $o: $($then)*);131		}132		pi!(@s; $o: $($t)*);133	}};134	(@s; $o:ident: ifelse ($e:expr)($($then:tt)*)($($else:tt)*) $($t:tt)*) => {{135		if $e {136			pi!(@s; $o: $($then)*);137		} else {138			pi!(@s; $o: $($else)*);139		}140		pi!(@s; $o: $($t)*);141	}};142	(@s; $i:ident:) => {}143}144macro_rules! p {145	($o:ident, $($t:tt)*) => {146		pi!(@s; $o: $($t)*)147	};148}149pub(crate) use p;150pub(crate) use pi;151152impl<P> Printable for Option<P>153where154	P: Printable,155{156	fn print(&self, out: &mut PrintItems) {157		if let Some(v) = self {158			v.print(out)159		} else {160			p!(161				out,162				string(format!(163					"/*missing {}*/",164					type_name::<P>().replace("jrsonnet_rowan_parser::generated::nodes::", "")165				),)166			)167		}168	}169}170171impl Printable for SyntaxToken {172	fn print(&self, out: &mut PrintItems) {173		p!(out, string(self.to_string()))174	}175}176177impl Printable for Text {178	fn print(&self, out: &mut PrintItems) {179		p!(out, string(format!("{}", self)))180	}181}182impl Printable for Number {183	fn print(&self, out: &mut PrintItems) {184		p!(out, string(format!("{}", self)))185	}186}187188impl Printable for Name {189	fn print(&self, out: &mut PrintItems) {190		p!(out, { self.ident_lit() })191	}192}193194impl Printable for DestructRest {195	fn print(&self, out: &mut PrintItems) {196		p!(out, str("..."));197		if let Some(name) = self.into() {198			p!(out, { name });199		}200	}201}202203impl Printable for Destruct {204	fn print(&self, out: &mut PrintItems) {205		match self {206			Destruct::DestructFull(f) => {207				p!(out, { f.name() })208			}209			Destruct::DestructSkip(_) => p!(out, str("?")),210			Destruct::DestructArray(a) => {211				p!(out, str("[") >i nl);212				for el in a.destruct_array_parts() {213					match el {214						DestructArrayPart::DestructArrayElement(e) => {215							p!(out, {e.destruct()} str(",") nl)216						}217						DestructArrayPart::DestructRest(d) => {218							p!(out, {d} str(",") nl)219						}220					}221				}222				p!(out, <i str("]"));223			}224			Destruct::DestructObject(o) => {225				p!(out, str("{") >i nl);226				for item in o.destruct_object_fields() {227					p!(out, { item.field() });228					if let Some(des) = item.destruct() {229						p!(out, str(": ") {des})230					}231					if let Some(def) = item.expr() {232						p!(out, str(" = ") {def});233					}234					p!(out, str(",") nl);235				}236				if let Some(rest) = o.destruct_rest() {237					p!(out, {rest} nl)238				}239				p!(out, <i str("}"));240			}241		}242	}243}244245impl Printable for FieldName {246	fn print(&self, out: &mut PrintItems) {247		match self {248			FieldName::FieldNameFixed(f) => {249				if let Some(id) = f.id() {250					p!(out, { id })251				} else if let Some(str) = f.text() {252					p!(out, { str })253				} else {254					p!(out, str("/*missing FieldName*/"))255				}256			}257			FieldName::FieldNameDynamic(d) => {258				p!(out, str("[") {d.expr()} str("]"))259			}260		}261	}262}263264impl Printable for Visibility {265	fn print(&self, out: &mut PrintItems) {266		p!(out, string(self.to_string()))267	}268}269270impl Printable for ObjLocal {271	fn print(&self, out: &mut PrintItems) {272		p!(out, str("local ") {self.bind()})273	}274}275276impl Printable for Assertion {277	fn print(&self, out: &mut PrintItems) {278		p!(out, str("assert ") {self.condition()});279		if self.colon_token().is_some() || self.message().is_some() {280			p!(out, str(": ") {self.message()})281		}282	}283}284285impl Printable for ParamsDesc {286	fn print(&self, out: &mut PrintItems) {287		p!(out, str("(") >i nl);288		for param in self.params() {289			p!(out, { param.destruct() });290			if param.assign_token().is_some() || param.expr().is_some() {291				p!(out, str(" = ") {param.expr()})292			}293			p!(out, str(",") nl)294		}295		p!(out, <i str(")"));296	}297}298impl Printable for ArgsDesc {299	fn print(&self, out: &mut PrintItems) {300		let start = LineNumber::new("start");301		let end = LineNumber::new("end");302		let multi_line = Rc::new(move |condition_context: &mut ConditionResolverContext| {303			is_multiple_lines(condition_context, start, end).map(|v| !v)304		});305		p!(out, str("(") info(start) if("start args", multi_line, >i nl));306		let (children, end_comments) = children_between::<Arg>(307			self.syntax().clone(),308			self.l_paren_token().map(Into::into).as_ref(),309			self.r_paren_token().map(Into::into).as_ref(),310			None,311		);312		let mut args = children.into_iter().peekable();313		while let Some(ele) = args.next() {314			if ele.should_start_with_newline {315				p!(out, nl);316			}317			format_comments(&ele.before_trivia, CommentLocation::AboveItem, out);318			let arg = ele.value;319			if arg.name().is_some() || arg.assign_token().is_some() {320				p!(out, {arg.name()} str(" = "));321			}322			let comma_between = if args.peek().is_some() {323				true_resolver()324			} else {325				multi_line.clone()326			};327			p!(out, {arg.expr()} if("arg comma", comma_between, str(",") if_not("between args", multi_line, str(" "))));328			format_comments(&ele.inline_trivia, CommentLocation::ItemInline, out);329			p!(out, if("between args", multi_line, nl));330		}331		p!(out, if("end args", multi_line, <i info(end)) str(")"));332	}333}334impl Printable for SliceDesc {335	fn print(&self, out: &mut PrintItems) {336		p!(out, str("["));337		if self.from().is_some() {338			p!(out, { self.from() });339		}340		p!(out, str(":"));341		if self.end().is_some() {342			p!(out, { self.end().map(|e| e.expr()) })343		}344		// Keep only one : in case if we don't need step345		if self.step().is_some() {346			p!(out, str(":") {self.step().map(|e|e.expr())});347		}348		p!(out, str("]"));349	}350}351352impl Printable for Member {353	fn print(&self, out: &mut PrintItems) {354		match self {355			Member::MemberBindStmt(b) => {356				p!(out, { b.obj_local() })357			}358			Member::MemberAssertStmt(ass) => {359				p!(out, { ass.assertion() })360			}361			Member::MemberFieldNormal(n) => {362				p!(out, {n.field_name()} if(n.plus_token().is_some())({n.plus_token()}) {n.visibility()} str(" ") {n.expr()})363			}364			Member::MemberFieldMethod(m) => {365				p!(out, {m.field_name()} {m.params_desc()} {m.visibility()} str(" ") {m.expr()})366			}367		}368	}369}370371impl Printable for ObjBody {372	fn print(&self, out: &mut PrintItems) {373		match self {374			ObjBody::ObjBodyComp(l) => {375				let (children, mut end_comments) = children_between::<Member>(376					l.syntax().clone(),377					l.l_brace_token().map(Into::into).as_ref(),378					Some(379						&(l.comp_specs()380							.next()381							.expect("at least one spec is defined")382							.syntax()383							.clone())384						.into(),385					),386					None,387				);388				let trailing_for_comp = end_comments.extract_trailing();389				p!(out, str("{") >i nl);390				for mem in children.into_iter() {391					if mem.should_start_with_newline {392						p!(out, nl);393					}394					format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);395					p!(out, {mem.value} str(","));396					format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);397					p!(out, nl)398				}399400				if end_comments.should_start_with_newline {401					p!(out, nl);402				}403				format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);404405				let (compspecs, end_comments) = children_between::<CompSpec>(406					l.syntax().clone(),407					l.member_comps()408						.last()409						.map(|m| m.syntax().clone())410						.map(Into::into)411						.or_else(|| l.l_brace_token().map(Into::into))412						.as_ref(),413					l.r_brace_token().map(Into::into).as_ref(),414					Some(trailing_for_comp),415				);416				for mem in compspecs.into_iter() {417					if mem.should_start_with_newline {418						p!(out, nl);419					}420					format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);421					p!(out, { mem.value });422					format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);423				}424				if end_comments.should_start_with_newline {425					p!(out, nl);426				}427				format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);428429				p!(out, nl <i str("}"));430			}431			ObjBody::ObjBodyMemberList(l) => {432				let (children, end_comments) = children_between::<Member>(433					l.syntax().clone(),434					l.l_brace_token().map(Into::into).as_ref(),435					l.r_brace_token().map(Into::into).as_ref(),436					None,437				);438				if children.is_empty() && end_comments.is_empty() {439					p!(out, str("{ }"));440					return;441				}442				p!(out, str("{") >i nl);443				for (i, mem) in children.into_iter().enumerate() {444					if mem.should_start_with_newline && i != 0 {445						p!(out, nl);446					}447					format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);448					p!(out, {mem.value} str(","));449					format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);450					p!(out, nl)451				}452453				if end_comments.should_start_with_newline {454					p!(out, nl);455				}456				format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);457				p!(out, <i str("}"));458			}459		}460	}461}462impl Printable for UnaryOperator {463	fn print(&self, out: &mut PrintItems) {464		p!(out, string(self.text().to_string()))465	}466}467impl Printable for BinaryOperator {468	fn print(&self, out: &mut PrintItems) {469		p!(out, string(self.text().to_string()))470	}471}472impl Printable for Bind {473	fn print(&self, out: &mut PrintItems) {474		match self {475			Bind::BindDestruct(d) => {476				p!(out, {d.into()} str(" = ") {d.value()})477			}478			Bind::BindFunction(f) => {479				p!(out, {f.name()} {f.params()} str(" = ") {f.value()})480			}481		}482	}483}484impl Printable for Literal {485	fn print(&self, out: &mut PrintItems) {486		p!(out, string(self.syntax().to_string()))487	}488}489impl Printable for ImportKind {490	fn print(&self, out: &mut PrintItems) {491		p!(out, string(self.syntax().to_string()))492	}493}494impl Printable for ForSpec {495	fn print(&self, out: &mut PrintItems) {496		p!(out, str("for ") {self.bind()} str(" in ") {self.expr()})497	}498}499impl Printable for IfSpec {500	fn print(&self, out: &mut PrintItems) {501		p!(out, str("if ") {self.expr()})502	}503}504impl Printable for CompSpec {505	fn print(&self, out: &mut PrintItems) {506		match self {507			CompSpec::ForSpec(f) => f.print(out),508			CompSpec::IfSpec(i) => i.print(out),509		}510	}511}512impl Printable for Expr {513	fn print(&self, out: &mut PrintItems) {514		let (stmts, _ending) = children_between::<Stmt>(515			self.syntax().clone(),516			None,517			self.expr_base()518				.as_ref()519				.map(ExprBase::syntax)520				.cloned()521				.map(Into::into)522				.as_ref(),523			None,524		);525		for stmt in stmts {526			p!(out, { stmt.value });527		}528		p!(out, { self.expr_base() });529		let (suffixes, _ending) = children_between::<Suffix>(530			self.syntax().clone(),531			self.expr_base()532				.as_ref()533				.map(ExprBase::syntax)534				.cloned()535				.map(Into::into)536				.as_ref(),537			None,538			None,539		);540		for suffix in suffixes {541			p!(out, { suffix.value });542		}543	}544}545impl Printable for Suffix {546	fn print(&self, out: &mut PrintItems) {547		match self {548			Suffix::SuffixIndex(i) => {549				if i.question_mark_token().is_some() {550					p!(out, str("?"));551				}552				p!(out, str(".") {i.index()});553			}554			Suffix::SuffixIndexExpr(e) => {555				if e.question_mark_token().is_some() {556					p!(out, str(".?"));557				}558				p!(out, str("[") {e.index()} str("]"))559			}560			Suffix::SuffixSlice(d) => {561				p!(out, { d.slice_desc() })562			}563			Suffix::SuffixApply(a) => {564				p!(out, { a.args_desc() })565			}566		}567	}568}569impl Printable for Stmt {570	fn print(&self, out: &mut PrintItems) {571		match self {572			Stmt::StmtLocal(l) => {573				let (binds, end_comments) = children_between::<Bind>(574					l.syntax().clone(),575					l.local_kw_token().map(Into::into).as_ref(),576					l.semi_token().map(Into::into).as_ref(),577					None,578				);579				if binds.len() == 1 {580					let bind = &binds[0];581					format_comments(&bind.before_trivia, CommentLocation::AboveItem, out);582					p!(out, str("local ") {bind.value});583				// TODO: keep end_comments, child.inline_trivia somehow, force multiple locals formatting in case of presence?584				} else {585					p!(out,str("local") >i nl);586					for bind in binds {587						if bind.should_start_with_newline {588							p!(out, nl);589						}590						format_comments(&bind.before_trivia, CommentLocation::AboveItem, out);591						p!(out, {bind.value} str(","));592						format_comments(&bind.inline_trivia, CommentLocation::ItemInline, out);593						p!(out, nl)594					}595					if end_comments.should_start_with_newline {596						p!(out, nl)597					}598					format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);599					p!(out,<i);600				}601				p!(out,str(";") nl);602			}603			Stmt::StmtAssert(a) => {604				p!(out, {a.assertion()} str(";") nl)605			}606		}607	}608}609impl Printable for ExprBase {610	fn print(&self, out: &mut PrintItems) {611		match self {612			Self::ExprBinary(b) => {613				p!(out, {b.lhs_work()} str(" ") {b.binary_operator()} str(" ") {b.rhs_work()})614			}615			Self::ExprUnary(u) => p!(out, {u.unary_operator()} {u.rhs()}),616			// Self::ExprSlice(s) => {617			// 	p!(new: {s.expr()} {s.slice_desc()})618			// }619			// Self::ExprIndex(i) => {620			// 	p!(new: {i.expr()} str(".") {i.index()})621			// }622			// Self::ExprIndexExpr(i) => p!(new: {i.base()} str("[") {i.index()} str("]")),623			// Self::ExprApply(a) => {624			// 	let mut pi = p!(new: {a.expr()} {a.args_desc()});625			// 	if a.tailstrict_kw_token().is_some() {626			// 		p!(out,str(" tailstrict"));627			// 	}628			// 	pi629			// }630			Self::ExprObjExtend(ex) => {631				p!(out, {ex.lhs_work()} str(" ") {ex.rhs_work()})632			}633			Self::ExprParened(p) => {634				p!(out, str("(") {p.expr()} str(")"))635			}636			Self::ExprString(s) => p!(out, { s.text() }),637			Self::ExprNumber(n) => p!(out, { n.number() }),638			Self::ExprArray(a) => {639				p!(out, str("[") >i nl);640				for el in a.exprs() {641					p!(out, {el} str(",") nl);642				}643				p!(out, <i str("]"));644			}645			Self::ExprObject(obj) => {646				p!(out, { obj.obj_body() })647			}648			Self::ExprArrayComp(arr) => {649				p!(out, str("[") {arr.expr()});650				for spec in arr.comp_specs() {651					p!(out, str(" ") {spec});652				}653				p!(out, str("]"));654			}655			Self::ExprImport(v) => {656				p!(out, {v.import_kind()} str(" ") {v.text()})657			}658			Self::ExprVar(n) => p!(out, { n.name() }),659			// Self::ExprLocal(l) => {660			// }661			Self::ExprIfThenElse(ite) => {662				p!(out, str("if ") {ite.cond()} str(" then ") {ite.then().map(|t| t.expr())});663				if ite.else_kw_token().is_some() || ite.else_().is_some() {664					p!(out, str(" else ") {ite.else_().map(|t| t.expr())})665				}666			}667			Self::ExprFunction(f) => p!(out, str("function") {f.params_desc()} nl {f.expr()}),668			// Self::ExprAssert(a) => p!(new: {a.assertion()} str("; ") {a.expr()}),669			Self::ExprError(e) => p!(out, str("error ") {e.expr()}),670			Self::ExprLiteral(l) => {671				p!(out, { l.literal() })672			}673		}674	}675}676677impl Printable for SourceFile {678	fn print(&self, out: &mut PrintItems) {679		let before = trivia_before(680			self.syntax().clone(),681			self.expr()682				.map(|e| e.syntax().clone())683				.map(Into::into)684				.as_ref(),685		);686		let after = trivia_after(687			self.syntax().clone(),688			self.expr()689				.map(|e| e.syntax().clone())690				.map(Into::into)691				.as_ref(),692		);693		format_comments(&before, CommentLocation::AboveItem, out);694		p!(out, {self.expr()} nl);695		format_comments(&after, CommentLocation::EndOfItems, out)696	}697}698699struct FormatOptions {700	// 0 for hard tabs701	indent: u8,702}703fn format(input: &str, opts: &FormatOptions) -> Option<String> {704	let (parsed, errors) = jrsonnet_rowan_parser::parse(input);705	if !errors.is_empty() {706		let mut builder = hi_doc::SnippetBuilder::new(input);707		for error in errors {708			builder709				.error(hi_doc::Text::single(710					format!("{:?}", error.error).chars(),711					Default::default(),712				))713				.range(714					error.range.start().into()715						..=(usize::from(error.range.end()) - 1).max(error.range.start().into()),716				)717				.build();718		}719		let snippet = builder.build();720		let ansi = hi_doc::source_to_ansi(&snippet);721		eprintln!("{ansi}");722		// It is possible to recover from this failure, but the output may be broken, as formatter is free to skip723		// ERROR rowan nodes.724		// Recovery needs to be enabled for LSP, though.725		//726		// TODO: Verify how formatter interacts in cases of missing positional values, i.e `if cond then /*missing Expr*/ else residual`.727		return None;728	}729	Some(dprint_core::formatting::format(730		|| {731			let mut out = PrintItems::new();732			parsed.print(&mut out);733			out734		},735		PrintOptions {736			indent_width: if opts.indent == 0 {737				// Reasonable max length for both 2 and 4 space sized tabs.738				3739			} else {740				opts.indent741			},742			max_width: 100,743			use_tabs: opts.indent == 0,744			new_line_text: "\n",745		},746	))747}748749#[derive(Parser)]750struct Opts {751	/// Treat input as code, reformat it instead of reading file.752	#[clap(long, short = 'e')]753	exec: bool,754	/// Path to be reformatted if `--exec` if unset, otherwise code itself.755	input: String,756	/// Replace code with formatted in-place, instead of printing it to stdout.757	/// Only applicable if `--exec` is unset.758	#[clap(long, short = 'i')]759	in_place: bool,760761	/// Exit with error if formatted does not match input762	#[arg(long)]763	test: bool,764	/// Number of spaces to indent with765	///766	/// 0 for guess from input (default), and use hard tabs if unable to guess.767	#[arg(long, default_value = "0")]768	indent: u8,769	/// Force hard tab for indentation770	#[arg(long)]771	hard_tabs: bool,772773	/// Debug option: how many times to call reformatting in case of unstable dprint output resolution.774	///775	/// 0 for not retrying to reformat.776	#[arg(long, default_value = "0")]777	conv_limit: usize,778}779780#[derive(thiserror::Error, Debug)]781enum Error {782	#[error("--in-place is incompatible with --exec")]783	InPlaceExec,784	#[error("io: {0}")]785	Io(#[from] io::Error),786	#[error("persist: {0}")]787	Persist(#[from] tempfile::PersistError),788	#[error("parsing failed, refusing to reformat corrupted input")]789	Parse,790}791792fn main_result() -> Result<(), Error> {793	eprintln!("jrsonnet-fmt is a prototype of a jsonnet code formatter, do not expect it to produce meaningful results right now.");794	eprintln!("It is not expected for its output to match other implementations, it will be completly separate implementation with maybe different name.");795	let mut opts = Opts::parse();796	let input = if opts.exec {797		if opts.in_place {798			return Err(Error::InPlaceExec);799		}800		opts.input.clone()801	} else {802		fs::read_to_string(&opts.input)?803	};804805	if opts.indent == 0 {806		// Sane default.807		// TODO: Implement actual guessing.808		opts.hard_tabs = true;809	}810811	let mut iteration = 0;812	let mut formatted = input.clone();813	let mut tmp;814	// https://github.com/dprint/dprint/pull/423815	loop {816		let Some(reformatted) = format(817			&formatted,818			&FormatOptions {819				indent: if opts.indent == 0 || opts.hard_tabs {820					0821				} else {822					opts.indent823				},824			},825		) else {826			return Err(Error::Parse);827		};828		tmp = reformatted.trim().to_owned();829		if formatted == tmp {830			break;831		}832		formatted = tmp;833		if opts.conv_limit == 0 {834			break;835		}836		iteration += 1;837		if iteration > opts.conv_limit {838			panic!("formatting not converged");839		}840	}841	formatted.push('\n');842	if opts.test && formatted != input {843		process::exit(1);844	}845	if opts.in_place {846		let path = PathBuf::from(opts.input);847		let mut temp = tempfile::NamedTempFile::new_in(path.parent().expect(848			"not failed during read, this path is not a directory, and there is a parent",849		))?;850		temp.write_all(formatted.as_bytes())?;851		temp.flush()?;852		temp.persist(&path)?;853	} else {854		print!("{formatted}")855	}856	Ok(())857}858859fn main() {860	if let Err(e) = main_result() {861		eprintln!("{e}");862		process::exit(1);863	}864}
after · cmds/jrsonnet-fmt/src/main.rs
1use std::{2	any::type_name,3	fs,4	io::{self, Write},5	path::PathBuf,6	process,7	rc::Rc,8};910use children::{children_between, trivia_before};11use clap::Parser;12use dprint_core::formatting::{13	condition_helpers::is_multiple_lines, condition_resolvers::true_resolver,14	ConditionResolverContext, LineNumber, PrintItems, PrintOptions,15};16use jrsonnet_rowan_parser::{17	nodes::{18		Arg, ArgsDesc, Assertion, BinaryOperator, Bind, CompSpec, Destruct, DestructArrayPart,19		DestructRest, Expr, ExprBase, FieldName, ForSpec, IfSpec, ImportKind, Literal, Member,20		Name, Number, ObjBody, ObjLocal, ParamsDesc, SliceDesc, SourceFile, Stmt, Suffix, Text,21		UnaryOperator, Visibility,22	},23	AstNode, AstToken as _, SyntaxToken,24};2526use crate::{27	children::trivia_after,28	comments::{format_comments, CommentLocation},29};3031mod children;32mod comments;33#[cfg(test)]34mod tests;3536pub trait Printable {37	fn print(&self, out: &mut PrintItems);38}3940macro_rules! pi {41	(@i; $($t:tt)*) => {{42		#[allow(unused_mut)]43		let mut o = dprint_core::formatting::PrintItems::new();44		pi!(@s; o: $($t)*);45		o46	}};47	(@s; $o:ident: str($e:expr $(,)?) $($t:tt)*) => {{48		$o.push_str($e);49		pi!(@s; $o: $($t)*);50	}};51	(@s; $o:ident: string($e:expr $(,)?) $($t:tt)*) => {{52		$o.push_string($e);53		pi!(@s; $o: $($t)*);54	}};55	(@s; $o:ident: nl $($t:tt)*) => {{56		$o.push_signal(dprint_core::formatting::Signal::NewLine);57		pi!(@s; $o: $($t)*);58	}};59	(@s; $o:ident: tab $($t:tt)*) => {{60		$o.push_signal(dprint_core::formatting::Signal::Tab);61		pi!(@s; $o: $($t)*);62	}};63	(@s; $o:ident: >i $($t:tt)*) => {{64		$o.push_signal(dprint_core::formatting::Signal::StartIndent);65		pi!(@s; $o: $($t)*);66	}};67	(@s; $o:ident: <i $($t:tt)*) => {{68		$o.push_signal(dprint_core::formatting::Signal::FinishIndent);69		pi!(@s; $o: $($t)*);70	}};71	(@s; $o:ident: info($v:expr) $($t:tt)*) => {{72		$o.push_info($v);73		pi!(@s; $o: $($t)*);74	}};75	(@s; $o:ident: if($s:literal, $cond:expr, $($i:tt)*) $($t:tt)*) => {{76		$o.push_condition(dprint_core::formatting::conditions::if_true(77			$s,78			$cond.clone(),79			{80				let mut o = PrintItems::new();81				p!(o, $($i)*);82				o83			},84		));85		pi!(@s; $o: $($t)*);86	}};87	(@s; $o:ident: if_else($s:literal, $cond:expr, $($i:tt)*)($($e:tt)+) $($t:tt)*) => {{88		$o.push_condition(dprint_core::formatting::conditions::if_true_or(89			$s,90			$cond.clone(),91			{92				let mut o = PrintItems::new();93				p!(o, $($i)*);94				o95			},96			{97				let mut o = PrintItems::new();98				p!(o, $($e)*);99				o100			},101		));102		pi!(@s; $o: $($t)*);103	}};104	(@s; $o:ident: if_not($s:literal, $cond:expr, $($e:tt)*) $($t:tt)*) => {{105		$o.push_condition(dprint_core::formatting::conditions::if_true_or(106			$s,107			$cond.clone(),108			{109				let o = PrintItems::new();110				o111			},112			{113				let mut o = PrintItems::new();114				p!(o, $($e)*);115				o116			},117		));118		pi!(@s; $o: $($t)*);119	}};120	(@s; $o:ident: {$expr:expr} $($t:tt)*) => {{121		$expr.print($o);122		pi!(@s; $o: $($t)*);123	}};124	(@s; $o:ident: items($expr:expr) $($t:tt)*) => {{125		$o.extend($expr);126		pi!(@s; $o: $($t)*);127	}};128	(@s; $o:ident: if ($e:expr)($($then:tt)*) $($t:tt)*) => {{129		if $e {130			pi!(@s; $o: $($then)*);131		}132		pi!(@s; $o: $($t)*);133	}};134	(@s; $o:ident: ifelse ($e:expr)($($then:tt)*)($($else:tt)*) $($t:tt)*) => {{135		if $e {136			pi!(@s; $o: $($then)*);137		} else {138			pi!(@s; $o: $($else)*);139		}140		pi!(@s; $o: $($t)*);141	}};142	(@s; $i:ident:) => {}143}144macro_rules! p {145	($o:ident, $($t:tt)*) => {146		pi!(@s; $o: $($t)*)147	};148}149pub(crate) use p;150pub(crate) use pi;151152impl<P> Printable for Option<P>153where154	P: Printable,155{156	fn print(&self, out: &mut PrintItems) {157		if let Some(v) = self {158			v.print(out)159		} else {160			p!(161				out,162				string(format!(163					"/*missing {}*/",164					type_name::<P>().replace("jrsonnet_rowan_parser::generated::nodes::", "")165				),)166			)167		}168	}169}170171impl Printable for SyntaxToken {172	fn print(&self, out: &mut PrintItems) {173		p!(out, string(self.to_string()))174	}175}176177impl Printable for Text {178	fn print(&self, out: &mut PrintItems) {179		p!(out, string(format!("{}", self)))180	}181}182impl Printable for Number {183	fn print(&self, out: &mut PrintItems) {184		p!(out, string(format!("{}", self)))185	}186}187188impl Printable for Name {189	fn print(&self, out: &mut PrintItems) {190		p!(out, { self.ident_lit() })191	}192}193194impl Printable for DestructRest {195	fn print(&self, out: &mut PrintItems) {196		p!(out, str("..."));197		if let Some(name) = self.into() {198			p!(out, { name });199		}200	}201}202203impl Printable for Destruct {204	fn print(&self, out: &mut PrintItems) {205		match self {206			Destruct::DestructFull(f) => {207				p!(out, { f.name() })208			}209			Destruct::DestructSkip(_) => p!(out, str("?")),210			Destruct::DestructArray(a) => {211				p!(out, str("[") >i nl);212				for el in a.destruct_array_parts() {213					match el {214						DestructArrayPart::DestructArrayElement(e) => {215							p!(out, {e.destruct()} str(",") nl)216						}217						DestructArrayPart::DestructRest(d) => {218							p!(out, {d} str(",") nl)219						}220					}221				}222				p!(out, <i str("]"));223			}224			Destruct::DestructObject(o) => {225				p!(out, str("{") >i nl);226				for item in o.destruct_object_fields() {227					p!(out, { item.field() });228					if let Some(des) = item.destruct() {229						p!(out, str(": ") {des})230					}231					if let Some(def) = item.expr() {232						p!(out, str(" = ") {def});233					}234					p!(out, str(",") nl);235				}236				if let Some(rest) = o.destruct_rest() {237					p!(out, {rest} nl)238				}239				p!(out, <i str("}"));240			}241		}242	}243}244245impl Printable for FieldName {246	fn print(&self, out: &mut PrintItems) {247		match self {248			FieldName::FieldNameFixed(f) => {249				if let Some(id) = f.id() {250					p!(out, { id })251				} else if let Some(str) = f.text() {252					p!(out, { str })253				} else {254					p!(out, str("/*missing FieldName*/"))255				}256			}257			FieldName::FieldNameDynamic(d) => {258				p!(out, str("[") {d.expr()} str("]"))259			}260		}261	}262}263264impl Printable for Visibility {265	fn print(&self, out: &mut PrintItems) {266		p!(out, string(self.to_string()))267	}268}269270impl Printable for ObjLocal {271	fn print(&self, out: &mut PrintItems) {272		p!(out, str("local ") {self.bind()})273	}274}275276impl Printable for Assertion {277	fn print(&self, out: &mut PrintItems) {278		p!(out, str("assert ") {self.condition()});279		if self.colon_token().is_some() || self.message().is_some() {280			p!(out, str(": ") {self.message()})281		}282	}283}284285impl Printable for ParamsDesc {286	fn print(&self, out: &mut PrintItems) {287		p!(out, str("(") >i nl);288		for param in self.params() {289			p!(out, { param.destruct() });290			if param.assign_token().is_some() || param.expr().is_some() {291				p!(out, str(" = ") {param.expr()})292			}293			p!(out, str(",") nl)294		}295		p!(out, <i str(")"));296	}297}298impl Printable for ArgsDesc {299	fn print(&self, out: &mut PrintItems) {300		let start = LineNumber::new("start");301		let end = LineNumber::new("end");302		let multi_line = Rc::new(move |condition_context: &mut ConditionResolverContext| {303			is_multiple_lines(condition_context, start, end).map(|v| !v)304		});305		p!(out, str("(") info(start) if("start args", multi_line, >i nl));306		let (children, end_comments) = children_between::<Arg>(307			self.syntax().clone(),308			self.l_paren_token().map(Into::into).as_ref(),309			self.r_paren_token().map(Into::into).as_ref(),310			None,311		);312		let mut args = children.into_iter().peekable();313		while let Some(ele) = args.next() {314			if ele.should_start_with_newline {315				p!(out, nl);316			}317			format_comments(&ele.before_trivia, CommentLocation::AboveItem, out);318			let arg = ele.value;319			if arg.name().is_some() || arg.assign_token().is_some() {320				p!(out, {arg.name()} str(" = "));321			}322			let comma_between = if args.peek().is_some() {323				true_resolver()324			} else {325				multi_line.clone()326			};327			p!(out, {arg.expr()} if("arg comma", comma_between, str(",") if_not("between args", multi_line, str(" "))));328			format_comments(&ele.inline_trivia, CommentLocation::ItemInline, out);329			p!(out, if("between args", multi_line, nl));330		}331		if end_comments.should_start_with_newline {332			p!(out, nl);333		}334		format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);335		p!(out, if("end args", multi_line, <i info(end)) str(")"));336	}337}338impl Printable for SliceDesc {339	fn print(&self, out: &mut PrintItems) {340		p!(out, str("["));341		if self.from().is_some() {342			p!(out, { self.from() });343		}344		p!(out, str(":"));345		if self.end().is_some() {346			p!(out, { self.end().map(|e| e.expr()) })347		}348		// Keep only one : in case if we don't need step349		if self.step().is_some() {350			p!(out, str(":") {self.step().map(|e|e.expr())});351		}352		p!(out, str("]"));353	}354}355356impl Printable for Member {357	fn print(&self, out: &mut PrintItems) {358		match self {359			Member::MemberBindStmt(b) => {360				p!(out, { b.obj_local() })361			}362			Member::MemberAssertStmt(ass) => {363				p!(out, { ass.assertion() })364			}365			Member::MemberFieldNormal(n) => {366				p!(out, {n.field_name()} if(n.plus_token().is_some())({n.plus_token()}) {n.visibility()} str(" ") {n.expr()})367			}368			Member::MemberFieldMethod(m) => {369				p!(out, {m.field_name()} {m.params_desc()} {m.visibility()} str(" ") {m.expr()})370			}371		}372	}373}374375impl Printable for ObjBody {376	fn print(&self, out: &mut PrintItems) {377		match self {378			ObjBody::ObjBodyComp(l) => {379				let (children, mut end_comments) = children_between::<Member>(380					l.syntax().clone(),381					l.l_brace_token().map(Into::into).as_ref(),382					Some(383						&(l.comp_specs()384							.next()385							.expect("at least one spec is defined")386							.syntax()387							.clone())388						.into(),389					),390					None,391				);392				let trailing_for_comp = end_comments.extract_trailing();393				p!(out, str("{") >i nl);394				for mem in children.into_iter() {395					if mem.should_start_with_newline {396						p!(out, nl);397					}398					format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);399					p!(out, {mem.value} str(","));400					format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);401					p!(out, nl)402				}403404				if end_comments.should_start_with_newline {405					p!(out, nl);406				}407				format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);408409				let (compspecs, end_comments) = children_between::<CompSpec>(410					l.syntax().clone(),411					l.member_comps()412						.last()413						.map(|m| m.syntax().clone())414						.map(Into::into)415						.or_else(|| l.l_brace_token().map(Into::into))416						.as_ref(),417					l.r_brace_token().map(Into::into).as_ref(),418					Some(trailing_for_comp),419				);420				for mem in compspecs.into_iter() {421					if mem.should_start_with_newline {422						p!(out, nl);423					}424					format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);425					p!(out, { mem.value });426					format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);427				}428				if end_comments.should_start_with_newline {429					p!(out, nl);430				}431				format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);432433				p!(out, nl <i str("}"));434			}435			ObjBody::ObjBodyMemberList(l) => {436				let (children, end_comments) = children_between::<Member>(437					l.syntax().clone(),438					l.l_brace_token().map(Into::into).as_ref(),439					l.r_brace_token().map(Into::into).as_ref(),440					None,441				);442				if children.is_empty() && end_comments.is_empty() {443					p!(out, str("{ }"));444					return;445				}446				p!(out, str("{") >i nl);447				for (i, mem) in children.into_iter().enumerate() {448					if mem.should_start_with_newline && i != 0 {449						p!(out, nl);450					}451					format_comments(&mem.before_trivia, CommentLocation::AboveItem, out);452					p!(out, {mem.value} str(","));453					format_comments(&mem.inline_trivia, CommentLocation::ItemInline, out);454					p!(out, nl)455				}456457				if end_comments.should_start_with_newline {458					p!(out, nl);459				}460				format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);461				p!(out, <i str("}"));462			}463		}464	}465}466impl Printable for UnaryOperator {467	fn print(&self, out: &mut PrintItems) {468		p!(out, string(self.text().to_string()))469	}470}471impl Printable for BinaryOperator {472	fn print(&self, out: &mut PrintItems) {473		p!(out, string(self.text().to_string()))474	}475}476impl Printable for Bind {477	fn print(&self, out: &mut PrintItems) {478		match self {479			Bind::BindDestruct(d) => {480				p!(out, {d.into()} str(" = ") {d.value()})481			}482			Bind::BindFunction(f) => {483				p!(out, {f.name()} {f.params()} str(" = ") {f.value()})484			}485		}486	}487}488impl Printable for Literal {489	fn print(&self, out: &mut PrintItems) {490		p!(out, string(self.syntax().to_string()))491	}492}493impl Printable for ImportKind {494	fn print(&self, out: &mut PrintItems) {495		p!(out, string(self.syntax().to_string()))496	}497}498impl Printable for ForSpec {499	fn print(&self, out: &mut PrintItems) {500		p!(out, str("for ") {self.bind()} str(" in ") {self.expr()})501	}502}503impl Printable for IfSpec {504	fn print(&self, out: &mut PrintItems) {505		p!(out, str("if ") {self.expr()})506	}507}508impl Printable for CompSpec {509	fn print(&self, out: &mut PrintItems) {510		match self {511			CompSpec::ForSpec(f) => f.print(out),512			CompSpec::IfSpec(i) => i.print(out),513		}514	}515}516impl Printable for Expr {517	fn print(&self, out: &mut PrintItems) {518		let (stmts, _ending) = children_between::<Stmt>(519			self.syntax().clone(),520			None,521			self.expr_base()522				.as_ref()523				.map(ExprBase::syntax)524				.cloned()525				.map(Into::into)526				.as_ref(),527			None,528		);529		for stmt in stmts {530			p!(out, { stmt.value });531		}532		p!(out, { self.expr_base() });533		let (suffixes, _ending) = children_between::<Suffix>(534			self.syntax().clone(),535			self.expr_base()536				.as_ref()537				.map(ExprBase::syntax)538				.cloned()539				.map(Into::into)540				.as_ref(),541			None,542			None,543		);544		for suffix in suffixes {545			p!(out, { suffix.value });546		}547	}548}549impl Printable for Suffix {550	fn print(&self, out: &mut PrintItems) {551		match self {552			Suffix::SuffixIndex(i) => {553				if i.question_mark_token().is_some() {554					p!(out, str("?"));555				}556				p!(out, str(".") {i.index()});557			}558			Suffix::SuffixIndexExpr(e) => {559				if e.question_mark_token().is_some() {560					p!(out, str(".?"));561				}562				p!(out, str("[") {e.index()} str("]"))563			}564			Suffix::SuffixSlice(d) => {565				p!(out, { d.slice_desc() })566			}567			Suffix::SuffixApply(a) => {568				p!(out, { a.args_desc() })569			}570		}571	}572}573impl Printable for Stmt {574	fn print(&self, out: &mut PrintItems) {575		match self {576			Stmt::StmtLocal(l) => {577				let (binds, end_comments) = children_between::<Bind>(578					l.syntax().clone(),579					l.local_kw_token().map(Into::into).as_ref(),580					l.semi_token().map(Into::into).as_ref(),581					None,582				);583				if binds.len() == 1 {584					let bind = &binds[0];585					format_comments(&bind.before_trivia, CommentLocation::AboveItem, out);586					p!(out, str("local ") {bind.value});587				// TODO: keep end_comments, child.inline_trivia somehow, force multiple locals formatting in case of presence?588				} else {589					p!(out,str("local") >i nl);590					for bind in binds {591						if bind.should_start_with_newline {592							p!(out, nl);593						}594						format_comments(&bind.before_trivia, CommentLocation::AboveItem, out);595						p!(out, {bind.value} str(","));596						format_comments(&bind.inline_trivia, CommentLocation::ItemInline, out);597						p!(out, nl)598					}599					if end_comments.should_start_with_newline {600						p!(out, nl)601					}602					format_comments(&end_comments.trivia, CommentLocation::EndOfItems, out);603					p!(out,<i);604				}605				p!(out,str(";") nl);606			}607			Stmt::StmtAssert(a) => {608				p!(out, {a.assertion()} str(";") nl)609			}610		}611	}612}613impl Printable for ExprBase {614	fn print(&self, out: &mut PrintItems) {615		match self {616			Self::ExprBinary(b) => {617				p!(out, {b.lhs_work()} str(" ") {b.binary_operator()} str(" ") {b.rhs_work()})618			}619			Self::ExprUnary(u) => p!(out, {u.unary_operator()} {u.rhs()}),620			// Self::ExprSlice(s) => {621			// 	p!(new: {s.expr()} {s.slice_desc()})622			// }623			// Self::ExprIndex(i) => {624			// 	p!(new: {i.expr()} str(".") {i.index()})625			// }626			// Self::ExprIndexExpr(i) => p!(new: {i.base()} str("[") {i.index()} str("]")),627			// Self::ExprApply(a) => {628			// 	let mut pi = p!(new: {a.expr()} {a.args_desc()});629			// 	if a.tailstrict_kw_token().is_some() {630			// 		p!(out,str(" tailstrict"));631			// 	}632			// 	pi633			// }634			Self::ExprObjExtend(ex) => {635				p!(out, {ex.lhs_work()} str(" ") {ex.rhs_work()})636			}637			Self::ExprParened(p) => {638				p!(out, str("(") {p.expr()} str(")"))639			}640			Self::ExprString(s) => p!(out, { s.text() }),641			Self::ExprNumber(n) => p!(out, { n.number() }),642			Self::ExprArray(a) => {643				p!(out, str("[") >i nl);644				for el in a.exprs() {645					p!(out, {el} str(",") nl);646				}647				p!(out, <i str("]"));648			}649			Self::ExprObject(obj) => {650				p!(out, { obj.obj_body() })651			}652			Self::ExprArrayComp(arr) => {653				p!(out, str("[") {arr.expr()});654				for spec in arr.comp_specs() {655					p!(out, str(" ") {spec});656				}657				p!(out, str("]"));658			}659			Self::ExprImport(v) => {660				p!(out, {v.import_kind()} str(" ") {v.text()})661			}662			Self::ExprVar(n) => p!(out, { n.name() }),663			// Self::ExprLocal(l) => {664			// }665			Self::ExprIfThenElse(ite) => {666				p!(out, str("if ") {ite.cond()} str(" then ") {ite.then().map(|t| t.expr())});667				if ite.else_kw_token().is_some() || ite.else_().is_some() {668					p!(out, str(" else ") {ite.else_().map(|t| t.expr())})669				}670			}671			Self::ExprFunction(f) => p!(out, str("function") {f.params_desc()} nl {f.expr()}),672			// Self::ExprAssert(a) => p!(new: {a.assertion()} str("; ") {a.expr()}),673			Self::ExprError(e) => p!(out, str("error ") {e.expr()}),674			Self::ExprLiteral(l) => {675				p!(out, { l.literal() })676			}677		}678	}679}680681impl Printable for SourceFile {682	fn print(&self, out: &mut PrintItems) {683		let before = trivia_before(684			self.syntax().clone(),685			self.expr()686				.map(|e| e.syntax().clone())687				.map(Into::into)688				.as_ref(),689		);690		let after = trivia_after(691			self.syntax().clone(),692			self.expr()693				.map(|e| e.syntax().clone())694				.map(Into::into)695				.as_ref(),696		);697		format_comments(&before, CommentLocation::AboveItem, out);698		p!(out, {self.expr()} nl);699		format_comments(&after, CommentLocation::EndOfItems, out)700	}701}702703struct FormatOptions {704	// 0 for hard tabs705	indent: u8,706}707fn format(input: &str, opts: &FormatOptions) -> Option<String> {708	let (parsed, errors) = jrsonnet_rowan_parser::parse(input);709	if !errors.is_empty() {710		let mut builder = hi_doc::SnippetBuilder::new(input);711		for error in errors {712			builder713				.error(hi_doc::Text::single(714					format!("{:?}", error.error).chars(),715					Default::default(),716				))717				.range(718					error.range.start().into()719						..=(usize::from(error.range.end()) - 1).max(error.range.start().into()),720				)721				.build();722		}723		let snippet = builder.build();724		let ansi = hi_doc::source_to_ansi(&snippet);725		eprintln!("{ansi}");726		// It is possible to recover from this failure, but the output may be broken, as formatter is free to skip727		// ERROR rowan nodes.728		// Recovery needs to be enabled for LSP, though.729		//730		// TODO: Verify how formatter interacts in cases of missing positional values, i.e `if cond then /*missing Expr*/ else residual`.731		return None;732	}733	Some(dprint_core::formatting::format(734		|| {735			let mut out = PrintItems::new();736			parsed.print(&mut out);737			out738		},739		PrintOptions {740			indent_width: if opts.indent == 0 {741				// Reasonable max length for both 2 and 4 space sized tabs.742				3743			} else {744				opts.indent745			},746			max_width: 100,747			use_tabs: opts.indent == 0,748			new_line_text: "\n",749		},750	))751}752753#[derive(Parser)]754struct Opts {755	/// Treat input as code, reformat it instead of reading file.756	#[clap(long, short = 'e')]757	exec: bool,758	/// Path to be reformatted if `--exec` if unset, otherwise code itself.759	input: String,760	/// Replace code with formatted in-place, instead of printing it to stdout.761	/// Only applicable if `--exec` is unset.762	#[clap(long, short = 'i')]763	in_place: bool,764765	/// Exit with error if formatted does not match input766	#[arg(long)]767	test: bool,768	/// Number of spaces to indent with769	///770	/// 0 for guess from input (default), and use hard tabs if unable to guess.771	#[arg(long, default_value = "0")]772	indent: u8,773	/// Force hard tab for indentation774	#[arg(long)]775	hard_tabs: bool,776777	/// Debug option: how many times to call reformatting in case of unstable dprint output resolution.778	///779	/// 0 for not retrying to reformat.780	#[arg(long, default_value = "0")]781	conv_limit: usize,782}783784#[derive(thiserror::Error, Debug)]785enum Error {786	#[error("--in-place is incompatible with --exec")]787	InPlaceExec,788	#[error("io: {0}")]789	Io(#[from] io::Error),790	#[error("persist: {0}")]791	Persist(#[from] tempfile::PersistError),792	#[error("parsing failed, refusing to reformat corrupted input")]793	Parse,794}795796fn main_result() -> Result<(), Error> {797	eprintln!("jrsonnet-fmt is a prototype of a jsonnet code formatter, do not expect it to produce meaningful results right now.");798	eprintln!("It is not expected for its output to match other implementations, it will be completly separate implementation with maybe different name.");799	let mut opts = Opts::parse();800	let input = if opts.exec {801		if opts.in_place {802			return Err(Error::InPlaceExec);803		}804		opts.input.clone()805	} else {806		fs::read_to_string(&opts.input)?807	};808809	if opts.indent == 0 {810		// Sane default.811		// TODO: Implement actual guessing.812		opts.hard_tabs = true;813	}814815	let mut iteration = 0;816	let mut formatted = input.clone();817	let mut tmp;818	// https://github.com/dprint/dprint/pull/423819	loop {820		let Some(reformatted) = format(821			&formatted,822			&FormatOptions {823				indent: if opts.indent == 0 || opts.hard_tabs {824					0825				} else {826					opts.indent827				},828			},829		) else {830			return Err(Error::Parse);831		};832		tmp = reformatted.trim().to_owned();833		if formatted == tmp {834			break;835		}836		formatted = tmp;837		if opts.conv_limit == 0 {838			break;839		}840		iteration += 1;841		if iteration > opts.conv_limit {842			panic!("formatting not converged");843		}844	}845	formatted.push('\n');846	if opts.test && formatted != input {847		process::exit(1);848	}849	if opts.in_place {850		let path = PathBuf::from(opts.input);851		let mut temp = tempfile::NamedTempFile::new_in(path.parent().expect(852			"not failed during read, this path is not a directory, and there is a parent",853		))?;854		temp.write_all(formatted.as_bytes())?;855		temp.flush()?;856		temp.persist(&path)?;857	} else {858		print!("{formatted}")859	}860	Ok(())861}862863fn main() {864	if let Err(e) = main_result() {865		eprintln!("{e}");866		process::exit(1);867	}868}
modifiedcrates/jrsonnet-rowan-parser/src/parser.rsdiffbeforeafterboth
--- a/crates/jrsonnet-rowan-parser/src/parser.rs
+++ b/crates/jrsonnet-rowan-parser/src/parser.rs
@@ -1,6 +1,5 @@
 use std::{cell::Cell, fmt, rc::Rc};
 
-use miette::{LabeledSpan, SourceOffset, SourceSpan};
 use rowan::{GreenNode, TextRange};
 
 use crate::{
@@ -51,28 +50,6 @@
 pub struct LocatedSyntaxError {
 	pub error: SyntaxError,
 	pub range: TextRange,
-}
-
-impl From<LocatedSyntaxError> for LabeledSpan {
-	fn from(val: LocatedSyntaxError) -> Self {
-		let span = SourceSpan::new(
-			SourceOffset::from(usize::from(val.range.start())),
-			usize::from(val.range.end() - val.range.start()),
-		);
-		dbg!(&val);
-		match val.error {
-			SyntaxError::Unexpected { expected, found } => LabeledSpan::new_with_span(
-				Some(format!("expected {expected}, found {found:?}")),
-				span,
-			),
-			SyntaxError::Missing { expected } => {
-				LabeledSpan::new_with_span(Some(format!("missing {expected}")), span)
-			}
-			SyntaxError::Custom { error } | SyntaxError::Hint { error } => {
-				LabeledSpan::new_with_span(Some(error), span)
-			}
-		}
-	}
 }
 
 impl Parser {