git.delta.rocks / jrsonnet / refs/commits / 4d5d46c772ee

difftreelog

Add location info to all sub-expressions

Kohei Suzuki2021-09-22parent: #62c9d0d.patch.diff
in: master

2 files changed

modifiedcrates/jrsonnet-parser/src/expr.rsdiffbeforeafterboth
before · crates/jrsonnet-parser/src/expr.rs
1use jrsonnet_gc::{unsafe_empty_trace, Finalize, Trace};2use jrsonnet_interner::IStr;3#[cfg(feature = "deserialize")]4use serde::Deserialize;5#[cfg(feature = "serialize")]6use serde::Serialize;7use std::{8	fmt::{Debug, Display},9	ops::Deref,10	path::{Path, PathBuf},11	rc::Rc,12};1314#[cfg_attr(feature = "serialize", derive(Serialize))]15#[cfg_attr(feature = "deserialize", derive(Deserialize))]16#[derive(Debug, PartialEq, Trace)]17#[trivially_drop]18pub enum FieldName {19	/// {fixed: 2}20	Fixed(IStr),21	/// {["dyn"+"amic"]: 3}22	Dyn(LocExpr),23}2425#[cfg_attr(feature = "serialize", derive(Serialize))]26#[cfg_attr(feature = "deserialize", derive(Deserialize))]27#[derive(Debug, Clone, Copy, PartialEq, Trace)]28#[trivially_drop]29pub enum Visibility {30	/// :31	Normal,32	/// ::33	Hidden,34	/// :::35	Unhide,36}3738impl Visibility {39	pub fn is_visible(&self) -> bool {40		matches!(self, Self::Normal | Self::Unhide)41	}42}4344#[cfg_attr(feature = "serialize", derive(Serialize))]45#[cfg_attr(feature = "deserialize", derive(Deserialize))]46#[derive(Clone, Debug, PartialEq, Trace)]47#[trivially_drop]48pub struct AssertStmt(pub LocExpr, pub Option<LocExpr>);4950#[cfg_attr(feature = "serialize", derive(Serialize))]51#[cfg_attr(feature = "deserialize", derive(Deserialize))]52#[derive(Debug, PartialEq, Trace)]53#[trivially_drop]54pub struct FieldMember {55	pub name: FieldName,56	pub plus: bool,57	pub params: Option<ParamsDesc>,58	pub visibility: Visibility,59	pub value: LocExpr,60}6162#[cfg_attr(feature = "serialize", derive(Serialize))]63#[cfg_attr(feature = "deserialize", derive(Deserialize))]64#[derive(Debug, PartialEq, Trace)]65#[trivially_drop]66pub enum Member {67	Field(FieldMember),68	BindStmt(BindSpec),69	AssertStmt(AssertStmt),70}7172#[cfg_attr(feature = "serialize", derive(Serialize))]73#[cfg_attr(feature = "deserialize", derive(Deserialize))]74#[derive(Debug, Clone, Copy, PartialEq, Trace)]75#[trivially_drop]76pub enum UnaryOpType {77	Plus,78	Minus,79	BitNot,80	Not,81}8283impl Display for UnaryOpType {84	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {85		use UnaryOpType::*;86		write!(87			f,88			"{}",89			match self {90				Plus => "+",91				Minus => "-",92				BitNot => "~",93				Not => "!",94			}95		)96	}97}9899#[cfg_attr(feature = "serialize", derive(Serialize))]100#[cfg_attr(feature = "deserialize", derive(Deserialize))]101#[derive(Debug, Clone, Copy, PartialEq, Trace)]102#[trivially_drop]103pub enum BinaryOpType {104	Mul,105	Div,106107	/// Implemented as intrinsic, put here for completeness108	Mod,109110	Add,111	Sub,112113	Lhs,114	Rhs,115116	Lt,117	Gt,118	Lte,119	Gte,120121	BitAnd,122	BitOr,123	BitXor,124125	Eq,126	Neq,127128	And,129	Or,130131	// Equialent to std.objectHasEx(a, b, true)132	In,133}134135impl Display for BinaryOpType {136	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {137		use BinaryOpType::*;138		write!(139			f,140			"{}",141			match self {142				Mul => "*",143				Div => "/",144				Mod => "%",145				Add => "+",146				Sub => "-",147				Lhs => "<<",148				Rhs => ">>",149				Lt => "<",150				Gt => ">",151				Lte => "<=",152				Gte => ">=",153				BitAnd => "&",154				BitOr => "|",155				BitXor => "^",156				Eq => "==",157				Neq => "!=",158				And => "&&",159				Or => "||",160				In => "in",161			}162		)163	}164}165166/// name, default value167#[cfg_attr(feature = "serialize", derive(Serialize))]168#[cfg_attr(feature = "deserialize", derive(Deserialize))]169#[derive(Debug, PartialEq, Trace)]170#[trivially_drop]171pub struct Param(pub IStr, pub Option<LocExpr>);172173/// Defined function parameters174#[cfg_attr(feature = "serialize", derive(Serialize))]175#[cfg_attr(feature = "deserialize", derive(Deserialize))]176#[derive(Debug, Clone, PartialEq)]177pub struct ParamsDesc(pub Rc<Vec<Param>>);178179/// Safety:180/// AST is acyclic, and there should be no gc pointers181unsafe impl Trace for ParamsDesc {182	unsafe_empty_trace!();183}184impl Finalize for ParamsDesc {}185186impl Deref for ParamsDesc {187	type Target = Vec<Param>;188	fn deref(&self) -> &Self::Target {189		&self.0190	}191}192193#[cfg_attr(feature = "serialize", derive(Serialize))]194#[cfg_attr(feature = "deserialize", derive(Deserialize))]195#[derive(Debug, PartialEq, Trace)]196#[trivially_drop]197pub struct ArgsDesc {198	pub unnamed: Vec<LocExpr>,199	pub named: Vec<(IStr, LocExpr)>,200}201impl ArgsDesc {202	pub fn new(unnamed: Vec<LocExpr>, named: Vec<(IStr, LocExpr)>) -> Self {203		Self { unnamed, named }204	}205}206207#[cfg_attr(feature = "serialize", derive(Serialize))]208#[cfg_attr(feature = "deserialize", derive(Deserialize))]209#[derive(Debug, Clone, PartialEq, Trace)]210#[trivially_drop]211pub struct BindSpec {212	pub name: IStr,213	pub params: Option<ParamsDesc>,214	pub value: LocExpr,215}216217#[cfg_attr(feature = "serialize", derive(Serialize))]218#[cfg_attr(feature = "deserialize", derive(Deserialize))]219#[derive(Debug, PartialEq, Trace)]220#[trivially_drop]221pub struct IfSpecData(pub LocExpr);222223#[cfg_attr(feature = "serialize", derive(Serialize))]224#[cfg_attr(feature = "deserialize", derive(Deserialize))]225#[derive(Debug, PartialEq, Trace)]226#[trivially_drop]227pub struct ForSpecData(pub IStr, pub LocExpr);228229#[cfg_attr(feature = "serialize", derive(Serialize))]230#[cfg_attr(feature = "deserialize", derive(Deserialize))]231#[derive(Debug, PartialEq, Trace)]232#[trivially_drop]233pub enum CompSpec {234	IfSpec(IfSpecData),235	ForSpec(ForSpecData),236}237238#[cfg_attr(feature = "serialize", derive(Serialize))]239#[cfg_attr(feature = "deserialize", derive(Deserialize))]240#[derive(Debug, PartialEq, Trace)]241#[trivially_drop]242pub struct ObjComp {243	pub pre_locals: Vec<BindSpec>,244	pub key: LocExpr,245	pub plus: bool,246	pub value: LocExpr,247	pub post_locals: Vec<BindSpec>,248	pub compspecs: Vec<CompSpec>,249}250251#[cfg_attr(feature = "serialize", derive(Serialize))]252#[cfg_attr(feature = "deserialize", derive(Deserialize))]253#[derive(Debug, PartialEq, Trace)]254#[trivially_drop]255pub enum ObjBody {256	MemberList(Vec<Member>),257	ObjComp(ObjComp),258}259260#[cfg_attr(feature = "serialize", derive(Serialize))]261#[cfg_attr(feature = "deserialize", derive(Deserialize))]262#[derive(Debug, PartialEq, Clone, Copy, Trace)]263#[trivially_drop]264pub enum LiteralType {265	This,266	Super,267	Dollar,268	Null,269	True,270	False,271}272273#[cfg_attr(feature = "serialize", derive(Serialize))]274#[cfg_attr(feature = "deserialize", derive(Deserialize))]275#[derive(Debug, PartialEq, Trace)]276#[trivially_drop]277pub struct SliceDesc {278	pub start: Option<LocExpr>,279	pub end: Option<LocExpr>,280	pub step: Option<LocExpr>,281}282283/// Syntax base284#[cfg_attr(feature = "serialize", derive(Serialize))]285#[cfg_attr(feature = "deserialize", derive(Deserialize))]286#[derive(Debug, PartialEq, Trace)]287#[trivially_drop]288pub enum Expr {289	Literal(LiteralType),290291	/// String value: "hello"292	Str(IStr),293	/// Number: 1, 2.0, 2e+20294	Num(f64),295	/// Variable name: test296	Var(IStr),297298	/// Array of expressions: [1, 2, "Hello"]299	Arr(Vec<LocExpr>),300	/// Array comprehension:301	/// ```jsonnet302	///  ingredients: [303	///    { kind: kind, qty: 4 / 3 }304	///    for kind in [305	///      'Honey Syrup',306	///      'Lemon Juice',307	///      'Farmers Gin',308	///    ]309	///  ],310	/// ```311	ArrComp(LocExpr, Vec<CompSpec>),312313	/// Object: {a: 2}314	Obj(ObjBody),315	/// Object extension: var1 {b: 2}316	ObjExtend(LocExpr, ObjBody),317318	/// (obj)319	Parened(LocExpr),320321	/// -2322	UnaryOp(UnaryOpType, LocExpr),323	/// 2 - 2324	BinaryOp(LocExpr, BinaryOpType, LocExpr),325	/// assert 2 == 2 : "Math is broken"326	AssertExpr(AssertStmt, LocExpr),327	/// local a = 2; { b: a }328	LocalExpr(Vec<BindSpec>, LocExpr),329330	/// import "hello"331	Import(PathBuf),332	/// importStr "file.txt"333	ImportStr(PathBuf),334	/// error "I'm broken"335	ErrorStmt(LocExpr),336	/// a(b, c)337	Apply(LocExpr, ArgsDesc, bool),338	/// a[b]339	Index(LocExpr, LocExpr),340	/// function(x) x341	Function(ParamsDesc, LocExpr),342	/// std.primitiveEquals343	Intrinsic(IStr),344	/// if true == false then 1 else 2345	IfElse {346		cond: IfSpecData,347		cond_then: LocExpr,348		cond_else: Option<LocExpr>,349	},350	Slice(LocExpr, SliceDesc),351}352353/// file, begin offset, end offset354#[cfg_attr(feature = "serialize", derive(Serialize))]355#[cfg_attr(feature = "deserialize", derive(Deserialize))]356#[derive(Clone, PartialEq, Trace)]357#[trivially_drop]358pub struct ExprLocation(pub Rc<Path>, pub usize, pub usize);359360impl Debug for ExprLocation {361	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {362		write!(f, "{:?}:{:?}-{:?}", self.0, self.1, self.2)363	}364}365366/// Holds AST expression and its location in source file367#[cfg_attr(feature = "serialize", derive(Serialize))]368#[cfg_attr(feature = "deserialize", derive(Deserialize))]369#[derive(Clone, PartialEq)]370pub struct LocExpr(pub Rc<Expr>, pub Option<ExprLocation>);371/// Safety:372/// AST is acyclic, and there should be no gc pointers373unsafe impl Trace for LocExpr {374	unsafe_empty_trace!();375}376impl Finalize for LocExpr {}377378impl Debug for LocExpr {379	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {380		if f.alternate() {381			write!(f, "{:#?}", self.0)?;382		} else {383			write!(f, "{:?}", self.0)?;384		}385		if let Some(loc) = &self.1 {386			write!(f, " from {:?}", loc)?;387		}388		Ok(())389	}390}391392/// Creates LocExpr from Expr and ExprLocation components393#[macro_export]394macro_rules! loc_expr {395	($expr:expr, $need_loc:expr,($name:expr, $start:expr, $end:expr)) => {396		LocExpr(397			std::rc::Rc::new($expr),398			if $need_loc {399				Some(ExprLocation($name, $start, $end))400			} else {401				None402			},403		)404	};405}406407/// Creates LocExpr without location info408#[macro_export]409macro_rules! loc_expr_todo {410	($expr:expr) => {411		LocExpr(Rc::new($expr), None)412	};413}
modifiedcrates/jrsonnet-parser/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-parser/src/lib.rs
+++ b/crates/jrsonnet-parser/src/lib.rs
@@ -17,12 +17,12 @@
 
 macro_rules! expr_bin {
 	($a:ident $op:ident $b:ident) => {
-		loc_expr_todo!(Expr::BinaryOp($a, $op, $b))
+		Expr::BinaryOp($a, $op, $b)
 	};
 }
 macro_rules! expr_un {
 	($op:ident $a:ident) => {
-		loc_expr_todo!(Expr::UnaryOp($op, $a))
+		Expr::UnaryOp($op, $a)
 	};
 }
 
@@ -55,9 +55,6 @@
 
 		rule keyword(id: &'static str) -> ()
 			= ##parse_string_literal(id) end_of_ident()
-		// Adds location data information to existing expression
-		rule l(s: &ParserSettings, x: rule<Expr>) -> LocExpr
-			= start:position!() v:x() end:position!() {loc_expr!(v, s.loc_data, (s.file_name.clone(), start, end))}
 
 		pub rule param(s: &ParserSettings) -> expr::Param = name:$(id()) expr:(_ "=" _ expr:expr(s){expr})? { expr::Param(name.into(), expr) }
 		pub rule params(s: &ParserSettings) -> expr::ParamsDesc
@@ -166,45 +163,45 @@
 			= keyword("for") _ id:$(id()) _ keyword("in") _ cond:expr(s) {ForSpecData(id.into(), cond)}
 		pub rule compspec(s: &ParserSettings) -> Vec<expr::CompSpec>
 			= s:(i:ifspec(s) { expr::CompSpec::IfSpec(i) } / f:forspec(s) {expr::CompSpec::ForSpec(f)} ) ** _ {s}
-		pub rule local_expr(s: &ParserSettings) -> LocExpr
-			= l(s,<keyword("local") _ binds:bind(s) ** comma() _ ";" _ expr:expr(s) { Expr::LocalExpr(binds, expr) }>)
-		pub rule string_expr(s: &ParserSettings) -> LocExpr
-			= l(s, <s:string() {Expr::Str(s.into())}>)
-		pub rule obj_expr(s: &ParserSettings) -> LocExpr
-			= l(s,<"{" _ body:objinside(s) _ "}" {Expr::Obj(body)}>)
-		pub rule array_expr(s: &ParserSettings) -> LocExpr
-			= l(s,<"[" _ elems:(expr(s) ** comma()) _ comma()? "]" {Expr::Arr(elems)}>)
-		pub rule array_comp_expr(s: &ParserSettings) -> LocExpr
-			= l(s,<"[" _ expr:expr(s) _ comma()? _ forspec:forspec(s) _ others:(others: compspec(s) _ {others})? "]" {
+		pub rule local_expr(s: &ParserSettings) -> Expr
+			= keyword("local") _ binds:bind(s) ** comma() _ ";" _ expr:expr(s) { Expr::LocalExpr(binds, expr) }
+		pub rule string_expr(s: &ParserSettings) -> Expr
+			= s:string() {Expr::Str(s.into())}
+		pub rule obj_expr(s: &ParserSettings) -> Expr
+			= "{" _ body:objinside(s) _ "}" {Expr::Obj(body)}
+		pub rule array_expr(s: &ParserSettings) -> Expr
+			= "[" _ elems:(expr(s) ** comma()) _ comma()? "]" {Expr::Arr(elems)}
+		pub rule array_comp_expr(s: &ParserSettings) -> Expr
+			= "[" _ expr:expr(s) _ comma()? _ forspec:forspec(s) _ others:(others: compspec(s) _ {others})? "]" {
 				let mut specs = vec![CompSpec::ForSpec(forspec)];
 				specs.extend(others.unwrap_or_default());
 				Expr::ArrComp(expr, specs)
-			}>)
-		pub rule number_expr(s: &ParserSettings) -> LocExpr
-			= l(s,<n:number() { expr::Expr::Num(n) }>)
-		pub rule var_expr(s: &ParserSettings) -> LocExpr
-			= l(s,<n:$(id()) { expr::Expr::Var(n.into()) }>)
-		pub rule if_then_else_expr(s: &ParserSettings) -> LocExpr
-			= l(s,<cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse{
+			}
+		pub rule number_expr(s: &ParserSettings) -> Expr
+			= n:number() { expr::Expr::Num(n) }
+		pub rule var_expr(s: &ParserSettings) -> Expr
+			= n:$(id()) { expr::Expr::Var(n.into()) }
+		pub rule if_then_else_expr(s: &ParserSettings) -> Expr
+			= cond:ifspec(s) _ keyword("then") _ cond_then:expr(s) cond_else:(_ keyword("else") _ e:expr(s) {e})? {Expr::IfElse{
 				cond,
 				cond_then,
 				cond_else,
-			}}>)
+			}}
 
-		pub rule literal(s: &ParserSettings) -> LocExpr
-			= l(s,<v:(
+		pub rule literal(s: &ParserSettings) -> Expr
+			= v:(
 				keyword("null") {LiteralType::Null}
 				/ keyword("true") {LiteralType::True}
 				/ keyword("false") {LiteralType::False}
 				/ keyword("self") {LiteralType::This}
 				/ keyword("$") {LiteralType::Dollar}
 				/ keyword("super") {LiteralType::Super}
-			) {Expr::Literal(v)}>)
+			) {Expr::Literal(v)}
 
-		pub rule expr_basic(s: &ParserSettings) -> LocExpr
+		pub rule expr_basic(s: &ParserSettings) -> Expr
 			= literal(s)
 
-			/ quiet!{l(s,<"$intrinsic(" name:$(id()) ")" {Expr::Intrinsic(name.into())}>)}
+			/ quiet!{"$intrinsic(" name:$(id()) ")" {Expr::Intrinsic(name.into())}}
 
 			/ string_expr(s) / number_expr(s)
 			/ array_expr(s)
@@ -212,17 +209,17 @@
 			/ array_expr(s)
 			/ array_comp_expr(s)
 
-			/ l(s,<keyword("importstr") _ path:string() {Expr::ImportStr(PathBuf::from(path))}>)
-			/ l(s,<keyword("import") _ path:string() {Expr::Import(PathBuf::from(path))}>)
+			/ keyword("importstr") _ path:string() {Expr::ImportStr(PathBuf::from(path))}
+			/ keyword("import") _ path:string() {Expr::Import(PathBuf::from(path))}
 
 			/ var_expr(s)
 			/ local_expr(s)
 			/ if_then_else_expr(s)
 
-			/ l(s,<keyword("function") _ "(" _ params:params(s) _ ")" _ expr:expr(s) {Expr::Function(params, expr)}>)
-			/ l(s,<assertion:assertion(s) _ ";" _ expr:expr(s) { Expr::AssertExpr(assertion, expr) }>)
+			/ keyword("function") _ "(" _ params:params(s) _ ")" _ expr:expr(s) {Expr::Function(params, expr)}
+			/ assertion:assertion(s) _ ";" _ expr:expr(s) { Expr::AssertExpr(assertion, expr) }
 
-			/ l(s,<keyword("error") _ expr:expr(s) { Expr::ErrorStmt(expr) }>)
+			/ keyword("error") _ expr:expr(s) { Expr::ErrorStmt(expr) }
 
 		rule slice_part(s: &ParserSettings) -> Option<LocExpr>
 			= e:(_ e:expr(s) _{e})? {e}
@@ -246,7 +243,9 @@
 		use BinaryOpType::*;
 		use UnaryOpType::*;
 		rule expr(s: &ParserSettings) -> LocExpr
-			= start:position!() a:precedence! {
+			= precedence! {
+				start:position!() v:@ end:position!() { loc_expr!(v, s.loc_data, (s.file_name.clone(), start, end)) }
+				--
 				a:(@) _ binop(<"||">) _ b:@ {expr_bin!(a Or b)}
 				--
 				a:(@) _ binop(<"&&">) _ b:@ {expr_bin!(a And b)}
@@ -280,23 +279,15 @@
 						unaryop(<"!">) _ b:@ {expr_un!(Not b)}
 						unaryop(<"~">) _ b:@ {expr_un!(BitNot b)}
 				--
-				a:(@) _ "[" _ s:slice_desc(s) _ "]" {loc_expr_todo!(Expr::Slice(a, s))}
-				a:(@) _ "." _ s:$(id()) {loc_expr_todo!(Expr::Index(a, el!(Expr::Str(s.into()))))}
-				a:(@) _ "[" _ s:expr(s) _ "]" {loc_expr_todo!(Expr::Index(a, s))}
-				a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {loc_expr_todo!(Expr::Apply(a, args, ts.is_some()))}
-				a:(@) _ "{" _ body:objinside(s) _ "}" {loc_expr_todo!(Expr::ObjExtend(a, body))}
+				a:(@) _ "[" _ e:slice_desc(s) _ "]" {Expr::Slice(a, e)}
+				a:(@) _ "." _ e:$(id()) {Expr::Index(a, el!(Expr::Str(e.into())))}
+				a:(@) _ "[" _ e:expr(s) _ "]" {Expr::Index(a, e)}
+				a:(@) _ "(" _ args:args(s) _ ")" ts:(_ keyword("tailstrict"))? {Expr::Apply(a, args, ts.is_some())}
+				a:(@) _ "{" _ body:objinside(s) _ "}" {Expr::ObjExtend(a, body)}
 				--
 				e:expr_basic(s) {e}
-				"(" _ e:expr(s) _ ")" {loc_expr_todo!(Expr::Parened(e))}
-			} end:position!() {
-				let LocExpr(e, _) = a;
-				LocExpr(e, if s.loc_data {
-					Some(ExprLocation(s.file_name.clone(), start, end))
-				} else {
-					None
-				})
+				"(" _ e:expr(s) _ ")" {Expr::Parened(e)}
 			}
-			/ e:expr_basic(s) {e}
 
 		pub rule jsonnet(s: &ParserSettings) -> LocExpr = _ e:expr(s) _ {e}
 	}
@@ -334,6 +325,12 @@
 		};
 	}
 
+	macro_rules! el_loc {
+		($expr:expr, $loc:expr$(,)?) => {
+			LocExpr(std::rc::Rc::new($expr), Some($loc))
+		};
+	}
+
 	mod expressions {
 		use super::*;
 
@@ -576,6 +573,62 @@
 		parse!(jrsonnet_stdlib::STDLIB_STR);
 	}
 
+	#[test]
+	fn add_location_info_to_all_sub_expressions() {
+		use Expr::*;
+
+		let file_name: std::rc::Rc<std::path::Path> = PathBuf::from("/test.jsonnet").into();
+		let expr = parse(
+			"{} { local x = 1, x: x } + {}",
+			&ParserSettings {
+				loc_data: true,
+				file_name: file_name.clone(),
+			},
+		)
+		.unwrap();
+		assert_eq!(
+			expr,
+			el_loc!(
+				BinaryOp(
+					el_loc!(
+						ObjExtend(
+							el_loc!(
+								Obj(ObjBody::MemberList(vec![])),
+								ExprLocation(file_name.clone(), 0, 2)
+							),
+							ObjBody::MemberList(vec![
+								Member::BindStmt(BindSpec {
+									name: "x".into(),
+									params: None,
+									value: el_loc!(
+										Num(1.0),
+										ExprLocation(file_name.clone(), 15, 16)
+									)
+								}),
+								Member::Field(FieldMember {
+									name: FieldName::Fixed("x".into()),
+									plus: false,
+									params: None,
+									visibility: Visibility::Normal,
+									value: el_loc!(
+										Var("x".into()),
+										ExprLocation(file_name.clone(), 21, 22)
+									),
+								})
+							])
+						),
+						ExprLocation(file_name.clone(), 0, 24)
+					),
+					BinaryOpType::Add,
+					el_loc!(
+						Obj(ObjBody::MemberList(vec![])),
+						ExprLocation(file_name.clone(), 27, 29)
+					),
+				),
+				ExprLocation(file_name.clone(), 0, 29),
+			),
+		);
+	}
 	// From source code
 	/*
 	#[bench]