git.delta.rocks / jrsonnet / refs/commits / 0d591aa205cc

difftreelog

feat locationless stack frames

Yaroslav Bolyukin2021-01-25parent: #a682d0e.patch.diff
in: master

4 files changed

modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -132,7 +132,7 @@
 
 #[derive(Clone, Debug)]
 pub struct StackTraceElement {
-	pub location: ExprLocation,
+	pub location: Option<ExprLocation>,
 	pub desc: String,
 }
 #[derive(Debug, Clone)]
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -132,11 +132,15 @@
 	frame_desc: impl FnOnce() -> String,
 	f: impl FnOnce() -> Result<T>,
 ) -> Result<T> {
-	if let Some(v) = e {
-		with_state(|s| s.push(v, frame_desc, f))
-	} else {
-		f()
-	}
+	with_state(|s| s.push(e, frame_desc, f))
+}
+
+pub fn push_stack_frame<T>(
+	e: Option<&ExprLocation>,
+	frame_desc: impl FnOnce() -> String,
+	f: impl FnOnce() -> Result<T>,
+ ) -> Result<T> {
+	push(e, frame_desc, f)
 }
 
 /// Maintains stack trace and import resolution
@@ -271,7 +275,7 @@
 	/// Executes code creating a new stack frame
 	pub fn push<T>(
 		&self,
-		e: &ExprLocation,
+		e: Option<&ExprLocation>,
 		frame_desc: impl FnOnce() -> String,
 		f: impl FnOnce() -> Result<T>,
 	) -> Result<T> {
@@ -290,7 +294,7 @@
 		self.data_mut().stack_depth -= 1;
 		if let Err(mut err) = result {
 			err.trace_mut().0.push(StackTraceElement {
-				location: e.clone(),
+				location: e.cloned(),
 				desc: frame_desc(),
 			});
 			return Err(err);
@@ -336,11 +340,11 @@
 	pub fn with_tla(&self, val: Val) -> Result<Val> {
 		self.run_in_state(|| {
 			Ok(match val {
-				Val::Func(func) => func.evaluate_map(
+				Val::Func(func) => push(None, || "during TLA call".to_owned(), || Ok(func.evaluate_map(
 					self.create_default_context()?,
 					&self.settings().tla_vars,
 					true,
-				)?,
+				)?))?,
 				v => v,
 			})
 		})
modifiedcrates/jrsonnet-evaluator/src/trace/location.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/trace/location.rs
+++ b/crates/jrsonnet-evaluator/src/trace/location.rs
@@ -37,7 +37,7 @@
 	];
 	let mut with_no_known_line_ending = vec![];
 	let mut this_line_offset = 0;
-	for (pos, ch) in file.chars().enumerate() {
+	for (pos, ch) in file.chars().enumerate().chain(std::iter::once((file.len(), ' '))) {
 		column += 1;
 		match offset_map.last() {
 			Some(x) if x.0 == pos => {
modifiedcrates/jrsonnet-evaluator/src/trace/mod.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/trace/mod.rs
1mod location;23use crate::{error::Error, EvaluationState, LocError};4pub use location::*;5use std::path::PathBuf;67/// The way paths should be displayed8pub enum PathResolver {9	/// Only filename10	FileName,11	/// Absolute path12	Absolute,13	/// Path relative to base directory14	Relative(PathBuf),15}1617impl PathResolver {18	pub fn resolve(&self, from: &PathBuf) -> String {19		match self {20			Self::FileName => from.file_name().unwrap().to_string_lossy().into_owned(),21			Self::Absolute => from.to_string_lossy().into_owned(),22			Self::Relative(base) => {23				if from.is_relative() {24					return from.to_string_lossy().into_owned();25				}26				pathdiff::diff_paths(from, base)27					.unwrap()28					.to_string_lossy()29					.into_owned()30			}31		}32	}33}3435/// Implements pretty-printing of traces36pub trait TraceFormat {37	fn write_trace(38		&self,39		out: &mut dyn std::fmt::Write,40		evaluation_state: &EvaluationState,41		error: &LocError,42	) -> Result<(), std::fmt::Error>;43	// fn print_trace(44	// 	&self,45	// 	evaluation_state: &EvaluationState,46	// 	error: &LocError,47	// ) -> Result<(), std::fmt::Error> {48	// 	self.write_trace(&mut std::fmt::stdout(), evaluation_state, error)49	// }50}5152fn print_code_location(53	out: &mut impl std::fmt::Write,54	start: &CodeLocation,55	end: &CodeLocation,56) -> Result<(), std::fmt::Error> {57	if start.line == end.line {58		if start.column == end.column {59			write!(out, "{}:{}", start.line, end.column - 1)?;60		} else {61			write!(out, "{}:{}-{}", start.line, start.column - 1, end.column)?;62		}63	} else {64		write!(65			out,66			"{}:{}-{}:{}",67			start.line,68			end.column.saturating_sub(1),69			start.line,70			end.column71		)?;72	}73	Ok(())74}7576/// vanilla-like jsonnet formatting77pub struct CompactFormat {78	pub resolver: PathResolver,79	pub padding: usize,80}8182impl TraceFormat for CompactFormat {83	fn write_trace(84		&self,85		out: &mut dyn std::fmt::Write,86		evaluation_state: &EvaluationState,87		error: &LocError,88	) -> Result<(), std::fmt::Error> {89		writeln!(out, "{}", error.error())?;90		if let Error::ImportSyntaxError {91			path,92			source_code,93			error,94		} = error.error()95		{96			use std::fmt::Write;97			let mut n = self.resolver.resolve(path);98			let mut offset = error.location.offset;99			let is_eof = if offset >= source_code.len() {100				offset = source_code.len() - 1;101				true102			} else {103				false104			};105			let mut location = offset_to_location(source_code, &[offset])106				.into_iter()107				.next()108				.unwrap();109			if is_eof {110				location.column += 1;111			}112113			write!(n, ":").unwrap();114			print_code_location(&mut n, &location, &location).unwrap();115			write!(out, "{:<p$}{}", "", n, p = self.padding,)?;116		}117		let file_names = error118			.trace()119			.0120			.iter()121			.map(|el| {122				let resolved_path = self.resolver.resolve(&el.location.0);123				// TODO: Process all trace elements first124				let location = evaluation_state125					.map_source_locations(&el.location.0, &[el.location.1, el.location.2]);126				(resolved_path, location)127			})128			.map(|(mut n, location)| {129				use std::fmt::Write;130				write!(n, ":").unwrap();131				print_code_location(&mut n, &location[0], &location[1]).unwrap();132				n133			})134			.collect::<Vec<_>>();135		let align = file_names.iter().map(|e| e.len()).max().unwrap_or(0);136		for (i, (el, file)) in error.trace().0.iter().zip(file_names).enumerate() {137			if i != 0 {138				writeln!(out)?;139			}140			write!(141				out,142				"{:<p$}{:<w$}: {}",143				"",144				file,145				el.desc,146				p = self.padding,147				w = align148			)?;149		}150		Ok(())151	}152}153154pub struct JSFormat;155impl TraceFormat for JSFormat {156	fn write_trace(157		&self,158		out: &mut dyn std::fmt::Write,159		evaluation_state: &EvaluationState,160		error: &LocError,161	) -> Result<(), std::fmt::Error> {162		writeln!(out, "{}", error.error())?;163		for (i, item) in error.trace().0.iter().enumerate() {164			if i != 0 {165				writeln!(out)?;166			}167			let desc = &item.desc;168			let source = item.location.clone();169			let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);170171			write!(172				out,173				"    at {} ({}:{}:{})",174				desc,175				source.0.to_str().unwrap(),176				start_end[0].line,177				start_end[0].column,178			)?;179		}180		Ok(())181	}182}183184/// rustc-like trace displaying185#[cfg(feature = "explaining-traces")]186pub struct ExplainingFormat {187	pub resolver: PathResolver,188}189#[cfg(feature = "explaining-traces")]190impl TraceFormat for ExplainingFormat {191	fn write_trace(192		&self,193		out: &mut dyn std::fmt::Write,194		evaluation_state: &EvaluationState,195		error: &LocError,196	) -> Result<(), std::fmt::Error> {197		writeln!(out, "{}", error.error())?;198		if let Error::ImportSyntaxError {199			path,200			source_code,201			error,202		} = error.error()203		{204			let mut offset = error.location.offset;205			if offset >= source_code.len() {206				offset = source_code.len() - 1;207			}208			let mut location = offset_to_location(source_code, &[offset])209				.into_iter()210				.next()211				.unwrap();212			if location.column >= 1 {213				location.column -= 1;214			}215216			self.print_snippet(217				out,218				source_code,219				path,220				&location,221				&location,222				"^ syntax error",223			)?;224		}225		let trace = &error.trace();226		for item in trace.0.iter() {227			let desc = &item.desc;228			let source = item.location.clone();229			let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);230231			self.print_snippet(232				out,233				&evaluation_state.get_source(&source.0).unwrap(),234				&source.0,235				&start_end[0],236				&start_end[1],237				desc,238			)?;239		}240		Ok(())241	}242}243244impl ExplainingFormat {245	fn print_snippet(246		&self,247		out: &mut dyn std::fmt::Write,248		source: &str,249		origin: &PathBuf,250		start: &CodeLocation,251		end: &CodeLocation,252		desc: &str,253	) -> Result<(), std::fmt::Error> {254		use annotate_snippets::{255			display_list::{DisplayList, FormatOptions},256			snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},257		};258259		let source_fragment: String = source260			.chars()261			.skip(start.line_start_offset)262			.take(end.line_end_offset - end.line_start_offset)263			.collect();264265		let origin = self.resolver.resolve(origin);266		let snippet = Snippet {267			opt: FormatOptions {268				color: true,269				..Default::default()270			},271			title: None,272			footer: vec![],273			slices: vec![Slice {274				source: &source_fragment,275				line_start: start.line,276				origin: Some(&origin),277				fold: false,278				annotations: vec![SourceAnnotation {279					label: desc,280					annotation_type: AnnotationType::Error,281					range: (282						start.offset - start.line_start_offset,283						end.offset - start.line_start_offset,284					),285				}],286			}],287		};288289		let dl = DisplayList::from(snippet);290		writeln!(out, "{}", dl)?;291292		Ok(())293	}294}