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}
after · 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| el.location.as_ref().map(|l| {122				use std::fmt::Write;123				let mut resolved_path = self.resolver.resolve(&l.0);124				// TODO: Process all trace elements first125				let location = evaluation_state126					.map_source_locations(&l.0, &[l.1, l.2]);127				write!(resolved_path, ":").unwrap();128				print_code_location(&mut resolved_path, &location[0], &location[1]).unwrap();129				resolved_path130			}))131			.collect::<Vec<_>>();132		let align = file_names.iter().flatten().map(|e| e.len()).max().unwrap_or(0);133		for (i, (el, file)) in error.trace().0.iter().zip(file_names).enumerate() {134			if i != 0 {135				writeln!(out)?;136			}137			write!(138				out,139				"{:<p$}{:<w$}: {}",140				"",141				file.unwrap_or_else(|| "".to_owned()),142				el.desc,143				p = self.padding,144				w = align145			)?;146		}147		Ok(())148	}149}150151pub struct JSFormat;152impl TraceFormat for JSFormat {153	fn write_trace(154		&self,155		out: &mut dyn std::fmt::Write,156		evaluation_state: &EvaluationState,157		error: &LocError,158	) -> Result<(), std::fmt::Error> {159		writeln!(out, "{}", error.error())?;160		for (i, item) in error.trace().0.iter().enumerate() {161			if i != 0 {162				writeln!(out)?;163			}164			let desc = &item.desc;165			if let Some (source) = &item.location {166				let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);167168				write!(169					out,170					"    at {} ({}:{}:{})",171					desc,172					source.0.to_str().unwrap(),173					start_end[0].line,174					start_end[0].column,175				)?;176			} else {177				write!(178					out,179					"    at {}",180					desc,181				)?;182			}183		}184		Ok(())185	}186}187188/// rustc-like trace displaying189#[cfg(feature = "explaining-traces")]190pub struct ExplainingFormat {191	pub resolver: PathResolver,192}193#[cfg(feature = "explaining-traces")]194impl TraceFormat for ExplainingFormat {195	fn write_trace(196		&self,197		out: &mut dyn std::fmt::Write,198		evaluation_state: &EvaluationState,199		error: &LocError,200	) -> Result<(), std::fmt::Error> {201		writeln!(out, "{}", error.error())?;202		if let Error::ImportSyntaxError {203			path,204			source_code,205			error,206		} = error.error()207		{208			let mut offset = error.location.offset;209			if offset >= source_code.len() {210				offset = source_code.len() - 1;211			}212			let mut location = offset_to_location(source_code, &[offset])213				.into_iter()214				.next()215				.unwrap();216			if location.column >= 1 {217				location.column -= 1;218			}219220			self.print_snippet(221				out,222				source_code,223				path,224				&location,225				&location,226				"^ syntax error",227			)?;228		}229		let trace = &error.trace();230		for item in trace.0.iter() {231			let desc = &item.desc;232			if let Some(source) = &item.location {233				let start_end = evaluation_state.map_source_locations(&source.0, &[source.1, source.2]);234				self.print_snippet(235					out,236					&evaluation_state.get_source(&source.0).unwrap(),237					&source.0,238					&start_end[0],239					&start_end[1],240					desc,241				)?;242			} else {243				write!(out, "{}", desc)?;244			}245		}246		Ok(())247	}248}249250impl ExplainingFormat {251	fn print_snippet(252		&self,253		out: &mut dyn std::fmt::Write,254		source: &str,255		origin: &PathBuf,256		start: &CodeLocation,257		end: &CodeLocation,258		desc: &str,259	) -> Result<(), std::fmt::Error> {260		use annotate_snippets::{261			display_list::{DisplayList, FormatOptions},262			snippet::{AnnotationType, Slice, Snippet, SourceAnnotation},263		};264265		let source_fragment: String = source266			.chars()267			.skip(start.line_start_offset)268			.take(end.line_end_offset - end.line_start_offset)269			.collect();270271		let origin = self.resolver.resolve(origin);272		let snippet = Snippet {273			opt: FormatOptions {274				color: true,275				..Default::default()276			},277			title: None,278			footer: vec![],279			slices: vec![Slice {280				source: &source_fragment,281				line_start: start.line,282				origin: Some(&origin),283				fold: false,284				annotations: vec![SourceAnnotation {285					label: desc,286					annotation_type: AnnotationType::Error,287					range: (288						start.offset - start.line_start_offset,289						end.offset - start.line_start_offset,290					),291				}],292			}],293		};294295		let dl = DisplayList::from(snippet);296		writeln!(out, "{}", dl)?;297298		Ok(())299	}300}