git.delta.rocks / jrsonnet / refs/commits / 90cacba70c63

difftreelog

perf(evaluator) emove expr from stacktraces

Лач2020-06-25parent: #f5b0724.patch.diff
in: master

4 files changed

modifiedcmds/jrsonnet/src/main.rsdiffbeforeafterboth
before · cmds/jrsonnet/src/main.rs
1pub mod location;23use clap::Clap;4use jsonnet_evaluator::{EvaluationSettings, EvaluationState, LocError, StackTrace, Val};5use jsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParserSettings};6use location::{offset_to_location, CodeLocation};7use std::env::current_dir;8use std::{collections::HashMap, path::PathBuf, str::FromStr};910enum Format {11	None,12	Json,13	Yaml,14}1516impl FromStr for Format {17	type Err = &'static str;18	fn from_str(s: &str) -> Result<Self, Self::Err> {19		Ok(match s {20			"none" => Format::None,21			"json" => Format::Json,22			"yaml" => Format::Yaml,23			_ => return Err("no such format"),24		})25	}26}2728#[derive(PartialEq)]29enum TraceFormat {30	CppJsonnet,31	GoJsonnet,32	Custom,33}34impl FromStr for TraceFormat {35	type Err = &'static str;36	fn from_str(s: &str) -> Result<Self, Self::Err> {37		Ok(match s {38			"cpp" => TraceFormat::CppJsonnet,39			"go" => TraceFormat::GoJsonnet,40			"default" => TraceFormat::Custom,41			_ => return Err("no such format"),42		})43	}44}4546#[derive(Clone)]47struct ExtStr {48	name: String,49	value: String,50}51impl FromStr for ExtStr {52	type Err = &'static str;53	fn from_str(s: &str) -> Result<Self, Self::Err> {54		let out: Vec<_> = s.split('=').collect();55		match out.len() {56			1 => Ok(ExtStr {57				name: out[0].to_owned(),58				value: std::env::var(out[0]).or(Err("missing env var"))?,59			}),60			2 => Ok(ExtStr {61				name: out[0].to_owned(),62				value: out[1].to_owned(),63			}),64			_ => Err("bad ext-str syntax"),65		}66	}67}6869#[derive(Clap)]70#[clap(version = "0.1.0", author = "Lach <iam@lach.pw>")]71struct Opts {72	#[clap(long, about = "Disable global std variable")]73	no_stdlib: bool,74	#[clap(long, about = "Add external string")]75	ext_str: Vec<ExtStr>,76	#[clap(long, about = "Add external string from code")]77	ext_code: Vec<ExtStr>,78	#[clap(long, about = "Add TLA")]79	tla_str: Vec<ExtStr>,80	#[clap(long, about = "Add TLA from code")]81	tla_code: Vec<ExtStr>,82	#[clap(long, short = "f", default_value = "json", possible_values = &["none", "json", "yaml"], about = "Output format, wraps resulting value to corresponding std.manifest call")]83	format: Format,84	#[clap(long, default_value = "default", possible_values = &["cpp", "go", "default"], about = "Emulated needed stacktrace display")]85	trace_format: TraceFormat,8687	#[clap(88		long,89		short = "s",90		default_value = "200",91		about = "Number of allowed stack frames"92	)]93	max_stack: usize,94	#[clap(95		long,96		short = "t",97		default_value = "20",98		about = "Max length of stack trace before cropping"99	)]100	max_trace: usize,101102	#[clap(long, short = "J", about = "Library search dir")]103	jpath: Vec<PathBuf>,104105	#[clap(106		long,107		default_value = "3",108		about = "When using --format, this option specifies string to pad output with"109	)]110	line_padding: usize,111112	#[clap(about = "File to compile", index = 1)]113	input: String,114}115116fn main() {117	let opts: Opts = Opts::parse();118	let evaluator = jsonnet_evaluator::EvaluationState::new(119		EvaluationSettings {120			max_stack_trace_size: opts.max_trace,121			max_stack_frames: opts.max_stack,122		},123		Box::new(jsonnet_evaluator::FileImportResolver {124			library_paths: opts.jpath.clone(),125		}),126	);127	if !opts.no_stdlib {128		evaluator.with_stdlib();129	}130	for ExtStr { name, value } in opts.ext_str.iter().cloned() {131		evaluator.add_ext_var(name, Val::Str(value));132	}133	for ExtStr { name, value } in opts.ext_code.iter().cloned() {134		evaluator.add_ext_var(name, evaluator.parse_evaluate_raw(&value).unwrap());135	}136	let mut input = current_dir().unwrap();137	input.push(opts.input.clone());138	let code_string = String::from_utf8(std::fs::read(opts.input.clone()).unwrap()).unwrap();139	if let Err(e) = evaluator.add_file(input.clone(), code_string.clone()) {140		print_syntax_error(e, &input, &code_string);141		std::process::exit(1);142	}143	let result = evaluator.evaluate_file(&input);144	match result {145		Ok(v) => {146			let v = match v {147				Val::Func(f) => {148					let mut desc_map = HashMap::new();149					for ExtStr { name, value } in opts.tla_str.iter().cloned() {150						desc_map.insert(name, el!(Expr::Str(value)));151					}152					for ExtStr { name, value } in opts.tla_code.iter().cloned() {153						desc_map.insert(154							name,155							jsonnet_parser::parse(156								&value,157								&ParserSettings {158									file_name: PathBuf::new(),159									loc_data: false,160								},161							)162							.unwrap(),163						);164					}165					evaluator.add_global("__tmp__tlf__".to_owned(), Val::Func(f));166					evaluator167						.evaluate_raw(el!(Expr::Apply(168							el!(Expr::Var("__tmp__tlf__".to_owned())),169							ArgsDesc(desc_map.into_iter().map(|(k, v)| Arg(Some(k), v)).collect()),170							false,171						)))172						.unwrap()173				}174				v => v,175			};176			let v = match opts.format {177				Format::Json => {178					if opts.no_stdlib {179						evaluator.with_stdlib();180					}181					evaluator.add_global("__tmp__to_json__".to_owned(), v);182					let v = evaluator.parse_evaluate_raw(&format!(183						"std.manifestJsonEx(__tmp__to_json__, \"{}\")",184						" ".repeat(opts.line_padding),185					));186					match v {187						Ok(v) => v,188						Err(err) => {189							print_error(&err, evaluator, &opts);190							std::process::exit(1);191						}192					}193				}194				Format::Yaml => {195					if opts.no_stdlib {196						evaluator.with_stdlib();197					}198					evaluator.add_global("__tmp__to_yaml__".to_owned(), v);199					let v = evaluator200						.parse_evaluate_raw("std.manifestYamlDoc(__tmp__to_yaml__, \"  \")");201					match v {202						Ok(v) => v,203						Err(err) => {204							print_error(&err, evaluator, &opts);205							std::process::exit(1);206						}207					}208				}209				_ => v,210			};211			match v {212				Val::Str(s) => println!("{}", s),213				Val::Num(n) => println!("{}", n),214				_v => eprintln!(215					"jsonnet output is not a string.\nDid you forgot to set --format, or wrap your data with std.manifestJson?"216				),217			}218		}219		Err(err) => {220			print_error(&err, evaluator, &opts);221			std::process::exit(1);222		}223	}224}225226fn print_error(err: &LocError, evaluator: EvaluationState, opts: &Opts) {227	println!("Error: {:?}", err.0);228	print_trace(&(err.1), evaluator, &opts);229}230231fn print_syntax_error(error: jsonnet_parser::ParseError, file: &PathBuf, code: &str) {232	use annotate_snippets::{233		display_list::{DisplayList, FormatOptions},234		snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},235	};236	//&("Expected: ".to_owned() + error.expected)237	let origin = file.to_str().unwrap();238	let error_message = format!("Expected: {}", error.expected);239	let snippet = Snippet {240		opt: FormatOptions {241			color: true,242			..Default::default()243		},244		title: Some(Annotation {245			label: Some(&error_message),246			id: None,247			annotation_type: AnnotationType::Error,248		}),249		footer: vec![],250		slices: vec![Slice {251			source: &code,252			line_start: 1,253			origin: Some(origin),254			fold: false,255			annotations: vec![SourceAnnotation {256				label: "At this position",257				annotation_type: AnnotationType::Error,258				range: (error.location.offset, error.location.offset + 1),259			}],260		}],261	};262263	let dl = DisplayList::from(snippet);264	println!("{}", dl);265}266267fn print_trace(trace: &StackTrace, evaluator: EvaluationState, opts: &Opts) {268	use annotate_snippets::{269		display_list::{DisplayList, FormatOptions},270		snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},271	};272	for item in trace.0.iter() {273		let desc = &item.1;274		if (item.0).1.is_none() {275			continue;276		}277		let source = (item.0).1.clone().unwrap();278		let code = evaluator.get_source(&source.0);279		if code.is_none() {280			continue;281		}282		let code = code.unwrap();283		let start_end = offset_to_location(&code, &[source.1, source.2]);284		if opts.trace_format == TraceFormat::Custom {285			let source_fragment: String = code286				.chars()287				.skip(start_end[0].line_start_offset)288				.take(start_end[1].line_end_offset - start_end[0].line_start_offset)289				.collect();290			let snippet = Snippet {291				opt: FormatOptions {292					color: true,293					..Default::default()294				},295				title: Some(Annotation {296					label: Some(&item.1),297					id: None,298					annotation_type: AnnotationType::Error,299				}),300				footer: vec![],301				slices: vec![Slice {302					source: &source_fragment,303					line_start: start_end[0].line,304					origin: Some(&source.0.to_str().unwrap()),305					fold: false,306					annotations: vec![SourceAnnotation {307						label: desc,308						annotation_type: AnnotationType::Error,309						range: (310							source.1 - start_end[0].line_start_offset,311							source.2 - start_end[0].line_start_offset,312						),313					}],314				}],315			};316317			let dl = DisplayList::from(snippet);318			println!("{}", dl);319		} else {320			print_jsonnet_pair(321				source.0.to_str().unwrap(),322				&start_end[0],323				&start_end[1],324				opts.trace_format == TraceFormat::GoJsonnet,325			);326		}327	}328}329330fn print_jsonnet_pair(file: &str, start: &CodeLocation, end: &CodeLocation, is_go: bool) {331	if is_go {332		print!("        ");333	} else {334		print!("  ");335	}336	print!("{}:", file);337	if start.line == end.line {338		// IDK why, but this is the behavior original jsonnet cpp impl shows339		if start.column == end.column || !is_go && start.column + 1 == end.column {340			println!("{}:{}", start.line, end.column)341		} else {342			println!("{}:{}-{}", start.line, start.column, end.column);343		}344	} else {345		println!(346			"({}:{})-({}:{})",347			start.line, end.column, start.line, end.column348		);349	}350}
after · cmds/jrsonnet/src/main.rs
1pub mod location;23use clap::Clap;4use jsonnet_evaluator::{EvaluationSettings, EvaluationState, LocError, StackTrace, Val};5use jsonnet_parser::{el, Arg, ArgsDesc, Expr, LocExpr, ParserSettings};6use location::{offset_to_location, CodeLocation};7use std::env::current_dir;8use std::{collections::HashMap, path::PathBuf, str::FromStr};910enum Format {11	None,12	Json,13	Yaml,14}1516impl FromStr for Format {17	type Err = &'static str;18	fn from_str(s: &str) -> Result<Self, Self::Err> {19		Ok(match s {20			"none" => Format::None,21			"json" => Format::Json,22			"yaml" => Format::Yaml,23			_ => return Err("no such format"),24		})25	}26}2728#[derive(PartialEq)]29enum TraceFormat {30	CppJsonnet,31	GoJsonnet,32	Custom,33}34impl FromStr for TraceFormat {35	type Err = &'static str;36	fn from_str(s: &str) -> Result<Self, Self::Err> {37		Ok(match s {38			"cpp" => TraceFormat::CppJsonnet,39			"go" => TraceFormat::GoJsonnet,40			"default" => TraceFormat::Custom,41			_ => return Err("no such format"),42		})43	}44}4546#[derive(Clone)]47struct ExtStr {48	name: String,49	value: String,50}51impl FromStr for ExtStr {52	type Err = &'static str;53	fn from_str(s: &str) -> Result<Self, Self::Err> {54		let out: Vec<_> = s.split('=').collect();55		match out.len() {56			1 => Ok(ExtStr {57				name: out[0].to_owned(),58				value: std::env::var(out[0]).or(Err("missing env var"))?,59			}),60			2 => Ok(ExtStr {61				name: out[0].to_owned(),62				value: out[1].to_owned(),63			}),64			_ => Err("bad ext-str syntax"),65		}66	}67}6869#[derive(Clap)]70#[clap(version = "0.1.0", author = "Lach <iam@lach.pw>")]71struct Opts {72	#[clap(long, about = "Disable global std variable")]73	no_stdlib: bool,74	#[clap(long, about = "Add external string")]75	ext_str: Vec<ExtStr>,76	#[clap(long, about = "Add external string from code")]77	ext_code: Vec<ExtStr>,78	#[clap(long, about = "Add TLA")]79	tla_str: Vec<ExtStr>,80	#[clap(long, about = "Add TLA from code")]81	tla_code: Vec<ExtStr>,82	#[clap(long, short = "f", default_value = "json", possible_values = &["none", "json", "yaml"], about = "Output format, wraps resulting value to corresponding std.manifest call")]83	format: Format,84	#[clap(long, default_value = "default", possible_values = &["cpp", "go", "default"], about = "Emulated needed stacktrace display")]85	trace_format: TraceFormat,8687	#[clap(88		long,89		short = "s",90		default_value = "200",91		about = "Number of allowed stack frames"92	)]93	max_stack: usize,94	#[clap(95		long,96		short = "t",97		default_value = "20",98		about = "Max length of stack trace before cropping"99	)]100	max_trace: usize,101102	#[clap(long, short = "J", about = "Library search dir")]103	jpath: Vec<PathBuf>,104105	#[clap(106		long,107		default_value = "3",108		about = "When using --format, this option specifies string to pad output with"109	)]110	line_padding: usize,111112	#[clap(about = "File to compile", index = 1)]113	input: String,114}115116fn main() {117	let opts: Opts = Opts::parse();118	let evaluator = jsonnet_evaluator::EvaluationState::new(119		EvaluationSettings {120			max_stack_trace_size: opts.max_trace,121			max_stack_frames: opts.max_stack,122		},123		Box::new(jsonnet_evaluator::FileImportResolver {124			library_paths: opts.jpath.clone(),125		}),126	);127	if !opts.no_stdlib {128		evaluator.with_stdlib();129	}130	for ExtStr { name, value } in opts.ext_str.iter().cloned() {131		evaluator.add_ext_var(name, Val::Str(value));132	}133	for ExtStr { name, value } in opts.ext_code.iter().cloned() {134		evaluator.add_ext_var(name, evaluator.parse_evaluate_raw(&value).unwrap());135	}136	let mut input = current_dir().unwrap();137	input.push(opts.input.clone());138	let code_string = String::from_utf8(std::fs::read(opts.input.clone()).unwrap()).unwrap();139	if let Err(e) = evaluator.add_file(input.clone(), code_string.clone()) {140		print_syntax_error(e, &input, &code_string);141		std::process::exit(1);142	}143	let result = evaluator.evaluate_file(&input);144	match result {145		Ok(v) => {146			let v = match v {147				Val::Func(f) => {148					let mut desc_map = HashMap::new();149					for ExtStr { name, value } in opts.tla_str.iter().cloned() {150						desc_map.insert(name, el!(Expr::Str(value)));151					}152					for ExtStr { name, value } in opts.tla_code.iter().cloned() {153						desc_map.insert(154							name,155							jsonnet_parser::parse(156								&value,157								&ParserSettings {158									file_name: PathBuf::new(),159									loc_data: false,160								},161							)162							.unwrap(),163						);164					}165					evaluator.add_global("__tmp__tlf__".to_owned(), Val::Func(f));166					evaluator167						.evaluate_raw(el!(Expr::Apply(168							el!(Expr::Var("__tmp__tlf__".to_owned())),169							ArgsDesc(desc_map.into_iter().map(|(k, v)| Arg(Some(k), v)).collect()),170							false,171						)))172						.unwrap()173				}174				v => v,175			};176			let v = match opts.format {177				Format::Json => {178					if opts.no_stdlib {179						evaluator.with_stdlib();180					}181					evaluator.add_global("__tmp__to_json__".to_owned(), v);182					let v = evaluator.parse_evaluate_raw(&format!(183						"std.manifestJsonEx(__tmp__to_json__, \"{}\")",184						" ".repeat(opts.line_padding),185					));186					match v {187						Ok(v) => v,188						Err(err) => {189							print_error(&err, evaluator, &opts);190							std::process::exit(1);191						}192					}193				}194				Format::Yaml => {195					if opts.no_stdlib {196						evaluator.with_stdlib();197					}198					evaluator.add_global("__tmp__to_yaml__".to_owned(), v);199					let v = evaluator200						.parse_evaluate_raw("std.manifestYamlDoc(__tmp__to_yaml__, \"  \")");201					match v {202						Ok(v) => v,203						Err(err) => {204							print_error(&err, evaluator, &opts);205							std::process::exit(1);206						}207					}208				}209				_ => v,210			};211			match v {212				Val::Str(s) => println!("{}", s),213				Val::Num(n) => println!("{}", n),214				_v => eprintln!(215					"jsonnet output is not a string.\nDid you forgot to set --format, or wrap your data with std.manifestJson?"216				),217			}218		}219		Err(err) => {220			print_error(&err, evaluator, &opts);221			std::process::exit(1);222		}223	}224}225226fn print_error(err: &LocError, evaluator: EvaluationState, opts: &Opts) {227	println!("Error: {:?}", err.0);228	print_trace(&(err.1), evaluator, &opts);229}230231fn print_syntax_error(error: jsonnet_parser::ParseError, file: &PathBuf, code: &str) {232	use annotate_snippets::{233		display_list::{DisplayList, FormatOptions},234		snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},235	};236	//&("Expected: ".to_owned() + error.expected)237	let origin = file.to_str().unwrap();238	let error_message = format!("Expected: {}", error.expected);239	let snippet = Snippet {240		opt: FormatOptions {241			color: true,242			..Default::default()243		},244		title: Some(Annotation {245			label: Some(&error_message),246			id: None,247			annotation_type: AnnotationType::Error,248		}),249		footer: vec![],250		slices: vec![Slice {251			source: &code,252			line_start: 1,253			origin: Some(origin),254			fold: false,255			annotations: vec![SourceAnnotation {256				label: "At this position",257				annotation_type: AnnotationType::Error,258				range: (error.location.offset, error.location.offset + 1),259			}],260		}],261	};262263	let dl = DisplayList::from(snippet);264	println!("{}", dl);265}266267fn print_trace(trace: &StackTrace, evaluator: EvaluationState, opts: &Opts) {268	use annotate_snippets::{269		display_list::{DisplayList, FormatOptions},270		snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},271	};272	for item in trace.0.iter() {273		let desc = &item.1;274		let source = item.0.clone();275		let code = evaluator.get_source(&source.0);276		if code.is_none() {277			continue;278		}279		let code = code.unwrap();280		let start_end = offset_to_location(&code, &[source.1, source.2]);281		if opts.trace_format == TraceFormat::Custom {282			let source_fragment: String = code283				.chars()284				.skip(start_end[0].line_start_offset)285				.take(start_end[1].line_end_offset - start_end[0].line_start_offset)286				.collect();287			let snippet = Snippet {288				opt: FormatOptions {289					color: true,290					..Default::default()291				},292				title: Some(Annotation {293					label: Some(&item.1),294					id: None,295					annotation_type: AnnotationType::Error,296				}),297				footer: vec![],298				slices: vec![Slice {299					source: &source_fragment,300					line_start: start_end[0].line,301					origin: Some(&source.0.to_str().unwrap()),302					fold: false,303					annotations: vec![SourceAnnotation {304						label: desc,305						annotation_type: AnnotationType::Error,306						range: (307							source.1 - start_end[0].line_start_offset,308							source.2 - start_end[0].line_start_offset,309						),310					}],311				}],312			};313314			let dl = DisplayList::from(snippet);315			println!("{}", dl);316		} else {317			print_jsonnet_pair(318				source.0.to_str().unwrap(),319				&start_end[0],320				&start_end[1],321				opts.trace_format == TraceFormat::GoJsonnet,322			);323		}324	}325}326327fn print_jsonnet_pair(file: &str, start: &CodeLocation, end: &CodeLocation, is_go: bool) {328	if is_go {329		print!("        ");330	} else {331		print!("  ");332	}333	print!("{}:", file);334	if start.line == end.line {335		// IDK why, but this is the behavior original jsonnet cpp impl shows336		if start.column == end.column || !is_go && start.column + 1 == end.column {337			println!("{}:{}", start.line, end.column)338		} else {339			println!("{}:{}-{}", start.line, start.column, end.column);340		}341	} else {342		println!(343			"({}:{})-({}:{})",344			start.line, end.column, start.line, end.column345		);346	}347}
modifiedcrates/jsonnet-evaluator/src/error.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/error.rs
+++ b/crates/jsonnet-evaluator/src/error.rs
@@ -1,6 +1,6 @@
 use crate::ValType;
-use jsonnet_parser::LocExpr;
-use std::path::PathBuf;
+use jsonnet_parser::ExprLocation;
+use std::{path::PathBuf, rc::Rc};
 
 #[derive(Debug, Clone)]
 pub enum Error {
@@ -38,7 +38,7 @@
 }
 
 #[derive(Clone, Debug)]
-pub struct StackTraceElement(pub LocExpr, pub String);
+pub struct StackTraceElement(pub Rc<ExprLocation>, pub String);
 #[derive(Debug, Clone)]
 pub struct StackTrace(pub Vec<StackTraceElement>);
 
modifiedcrates/jsonnet-evaluator/src/evaluate.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/evaluate.rs
+++ b/crates/jsonnet-evaluator/src/evaluate.rs
@@ -35,7 +35,7 @@
 			b.name.clone(),
 			LazyBinding::Bindable(Rc::new(move |this, super_obj| {
 				Ok(lazy_val!(closure!(clone context_creator, clone b, ||
-					push(b.value.clone(), "thunk".to_owned(), ||{
+					push(&b.value.1, "thunk", ||{
 						evaluate(
 							context_creator.0(this.clone(), super_obj.clone())?,
 							&b.value
@@ -255,7 +255,7 @@
 								visibility: visibility.clone(),
 								invoke: LazyBinding::Bindable(Rc::new(
 									closure!(clone name, clone value, clone context_creator, |this, super_obj| {
-										Ok(LazyVal::new_resolved(push(value.clone(), "object ".to_owned()+&name+" field", ||{
+										Ok(LazyVal::new_resolved(push(&value.1, "object field", ||{
 											let context = context_creator.0(this, super_obj)?;
 											evaluate(
 												context,
@@ -371,7 +371,6 @@
 
 pub fn evaluate(context: Context, expr: &LocExpr) -> Result<Val> {
 	use Expr::*;
-	let locexpr = expr.clone();
 	let LocExpr(expr, loc) = expr;
 	Ok(match &**expr {
 		Literal(LiteralType::This) => Val::Obj(
@@ -394,7 +393,7 @@
 		Num(v) => Val::Num(*v),
 		BinaryOp(v1, o, v2) => evaluate_binary_op_special(context, &v1, *o, &v2)?,
 		UnaryOp(o, v) => evaluate_unary_op(*o, &evaluate(context, v)?)?,
-		Var(name) => push(locexpr, "var".to_owned(), || {
+		Var(name) => push(loc, "var", || {
 			Val::Lazy(context.binding(&name)?).unwrap_if_lazy()
 		})?,
 		Index(LocExpr(v, _), index) if matches!(&**v, Expr::Literal(LiteralType::Super)) => {
@@ -733,7 +732,7 @@
 					if *tailstrict {
 						body()?
 					} else {
-						push(locexpr, "function call".to_owned(), body)?
+						push(loc, "function call", body)?
 					}
 				}
 				_ => panic!("{:?} is not a function", value),
@@ -741,16 +740,12 @@
 		}
 		Function(params, body) => evaluate_method(context, params.clone(), body.clone()),
 		AssertExpr(AssertStmt(value, msg), returned) => {
-			let assertion_result = push(value.clone(), "assertion condition".to_owned(), || {
+			let assertion_result = push(&value.1, "assertion condition", || {
 				evaluate(context.clone(), &value)?
 					.try_cast_bool("assertion condition should be boolean")
 			})?;
 			if assertion_result {
-				push(
-					returned.clone(),
-					"assert 'return' branch".to_owned(),
-					|| evaluate(context, returned),
-				)?
+				evaluate(context, returned)?
 			} else if let Some(msg) = msg {
 				panic!(
 					"assertion failed ({:?}): {}",
modifiedcrates/jsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jsonnet-evaluator/src/lib.rs
+++ b/crates/jsonnet-evaluator/src/lib.rs
@@ -81,15 +81,21 @@
 	pub(crate) static EVAL_STATE: RefCell<Option<EvaluationState>> = RefCell::new(None)
 }
 pub(crate) fn with_state<T>(f: impl FnOnce(&EvaluationState) -> T) -> T {
-	EVAL_STATE.with(
-		|s| f(s.borrow().as_ref().unwrap()),
-	)
+	EVAL_STATE.with(|s| f(s.borrow().as_ref().unwrap()))
 }
 pub(crate) fn create_error<T>(err: Error) -> Result<T> {
 	with_state(|s| s.error(err))
 }
-pub(crate) fn push<T>(e: LocExpr, comment: String, f: impl FnOnce() -> Result<T>) -> Result<T> {
-	with_state(|s| s.push(e, comment, f))
+pub(crate) fn push<T>(
+	e: &Option<Rc<ExprLocation>>,
+	comment: &str,
+	f: impl FnOnce() -> Result<T>,
+) -> Result<T> {
+	if e.is_some() {
+		with_state(|s| s.push(e.clone().unwrap(), comment.to_owned(), f))
+	} else {
+		f()
+	}
 }
 
 /// Maintains stack trace and import resolution
@@ -247,7 +253,12 @@
 		Context::new().extend_unbound(new_bindings, None, None, None)
 	}
 
-	pub fn push<T>(&self, e: LocExpr, comment: String, f: impl FnOnce() -> Result<T>) -> Result<T> {
+	pub fn push<T>(
+		&self,
+		e: Rc<ExprLocation>,
+		comment: String,
+		f: impl FnOnce() -> Result<T>,
+	) -> Result<T> {
 		{
 			let mut stack = self.0.stack.borrow_mut();
 			if stack.len() > self.0.settings.max_stack_frames {
@@ -302,26 +313,18 @@
 	use super::Val;
 	use crate::EvaluationState;
 	use jsonnet_parser::*;
-	use std::path::PathBuf;
+	use std::{path::PathBuf, rc::Rc};
 
 	#[test]
 	fn eval_state_stacktrace() {
 		let state = EvaluationState::default();
 		state
 			.push(
-				loc_expr!(
-					Expr::Num(0.0),
-					true,
-					(PathBuf::from("test1.jsonnet"), 10, 20)
-				),
+				Rc::new(ExprLocation(PathBuf::from("test1.jsonnet"), 10, 20)),
 				"outer".to_owned(),
 				|| {
 					state.push(
-						loc_expr!(
-							Expr::Num(0.0),
-							true,
-							(PathBuf::from("test2.jsonnet"), 30, 40)
-						),
+						Rc::new(ExprLocation(PathBuf::from("test2.jsonnet"), 30, 40)),
 						"inner".to_owned(),
 						|| {
 							state.print_stack_trace();