difftreelog
feat bring back null coaelse
in: master
5 files changed
crates/jrsonnet-evaluator/src/evaluate/compspec.rsdiffbeforeafterboth1use std::rc::Rc;1use std::rc::Rc;223#[cfg(feature = "exp-object-iteration")]4use jrsonnet_interner::IStr;3use jrsonnet_types::ValType;5use jrsonnet_types::ValType;465use super::{7use super::{6 destructure::{destruct, evaluate_locals_unbound, fill_letrec_binds},8 destructure::{destruct, evaluate_locals_unbound, fill_letrec_binds},7 evaluate_field_member_static, evaluate_field_member_unbound,9 evaluate_field_member_static, evaluate_field_member_unbound,8};10};9#[cfg(feature = "exp-object-iteration")]10use jrsonnet_interner::IStr;1112use crate::{11use crate::{13 Context, ObjValue, ObjValueBuilder, Result, Thunk, Val,12 Context, ObjValue, ObjValueBuilder, Result, Thunk, Val,crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth236 .transpose()?;236 .transpose()?;237 Val::from(indexable.slice(start, end, step)?)237 Val::from(indexable.slice(start, end, step)?)238 }238 }239 LExpr::Super => Val::Obj(ctx.try_sup_this()?.standalone_super()?),239 LExpr::Super => Val::Obj(ctx.try_sup_this()?.standalone_super().ok_or(NoSuperFound)?),240 LExpr::Import {240 LExpr::Import {241 kind,241 kind,242 kind_span,242 kind_span,336 )336 )337}337}338339fn evaluate_index(ctx: Context, indexable: &LExpr, parts: &[LIndexPart]) -> Result<Val> {340 let mut value = if matches!(indexable, LExpr::Super) {341 let sup_this = ctx.try_sup_this()?;342 // First part must be evaluated to get the super field name343 if parts.is_empty() {344 bail!(RuntimeError("super requires an index".into()))345 }346 let key_val = evaluate(ctx.clone(), &parts[0].value)?;347 let Val::Str(key) = &key_val else {348 bail!(ValueIndexMustBeTypeGot(349 ValType::Obj,350 ValType::Str,351 key_val.value_type(),352 ))353 };354 let field = key.clone().into_flat();355 if let Some(v) = sup_this.get_super(field.clone())? {356 // Continue with remaining parts357 let mut value = v;358 for part in &parts[1..] {359 value = index_val(ctx.clone(), CallLocation::new(&part.span), value, part)?;360 }361 return Ok(value);362 }363 let suggestions = suggest_object_fields(sup_this.this(), field.clone());364 bail!(NoSuchField(field, suggestions))365 } else {366 evaluate(ctx.clone(), indexable)?367 };368369 for part in parts {370 value = index_val(ctx.clone(), CallLocation::new(&part.span), value, part)?;371 }372 Ok(value)373}374338375fn index_val(ctx: Context, loc: CallLocation<'_>, value: Val, part: &LIndexPart) -> Result<Val> {339fn evaluate_index(ctx: Context, indexable: &LExpr, parts: &[LIndexPart]) -> Result<Val> {340 let mut parts = parts.iter();341 let mut indexable = if matches!(indexable, LExpr::Super) {342 let part = parts.next().expect("at least part should exist");343 // sup_this existence check might also be skipped here for null-coalesce...344 // But I believe this might cause errors.345 let sup_this = ctx.try_sup_this()?;346347 if !sup_this.has_super() {348 #[cfg(feature = "exp-null-coaelse")]349 if part.null_coaelse {350 return Ok(Val::Null);351 }352 bail!(NoSuperFound);353 }354 let name = evaluate(ctx.clone(), &part.value)?;355356 let Val::Str(name) = name else {357 bail!(ValueIndexMustBeTypeGot(358 ValType::Obj,359 ValType::Str,360 name.value_type(),361 ))362 };363364 let name = name.into_flat();365 match sup_this366 .get_super(name.clone())367 .with_description_src(&part.span, || format!("super field <{name}> access"))?368 {369 Some(v) => v,370 #[cfg(feature = "exp-null-coaelse")]371 None if part.null_coaelse => return Ok(Val::Null),372 None => {373 let suggestions = suggest_object_fields(374 &sup_this.standalone_super().expect("super exists"),375 name.clone(),376 );377 bail!(NoSuchField(name, suggestions))378 }379 }380 } else {381 evaluate(ctx.clone(), indexable)?382 };383384 for part in parts {385 let ctx = ctx.clone();386 let loc = CallLocation::new(&part.span);387 let value = indexable;376 let key_val = evaluate(ctx, &part.value)?;388 let key_val = evaluate(ctx, &part.value)?;377 Ok(match (&value, &key_val) {389 indexable = match (&value, &key_val) {378 (Val::Obj(obj), Val::Str(key)) => {390 (Val::Obj(obj), Val::Str(key)) => {379 let field = key.clone().into_flat();391 let key = key.clone().into_flat();380 if let Some(v) = obj392 match obj381 .get(field.clone())393 .get(key.clone())382 .with_description_src(loc, || format!("field <{field}> access"))?394 .with_description_src(loc, || format!("field <{key}> access"))?383 {395 {384 v396 Some(v) => v,385 } else {397 #[cfg(feature = "exp-null-coaelse")]386 bail!(NoSuchField(398 None if part.null_coaelse => return Ok(Val::Null),387 field.clone(),399 None => {388 suggest_object_fields(obj, field)400 return Err(Error::from(NoSuchField(389 ))401 key.clone(),390 }402 suggest_object_fields(obj, key.clone()),403 )))404 .with_description_src(loc, || format!("field <{key}> access"));405 }406 }391 }407 }392 (Val::Arr(arr), Val::Num(idx)) => {408 (Val::Arr(arr), Val::Num(idx)) => {393 let n = idx.get();409 let n = idx.get();433 };449 };434 Val::string(char)450 Val::string(char)435 }451 }452 #[cfg(feature = "exp-null-coaelse")]453 (Val::Null, _) if part.null_coaelse => return Ok(Val::Null),436 _ => bail!(ValueIndexMustBeTypeGot(454 _ => bail!(ValueIndexMustBeTypeGot(437 value.value_type(),455 value.value_type(),438 ValType::Str,456 ValType::Str,439 key_val.value_type()457 key_val.value_type()440 )),458 )),441 })459 };460 }461 Ok(indexable)442}462}443463444fn evaluate_obj_body(super_obj: Option<ObjValue>, ctx: Context, body: &LObjBody) -> Result<Val> {464fn evaluate_obj_body(super_obj: Option<ObjValue>, ctx: Context, body: &LObjBody) -> Result<Val> {crates/jrsonnet-evaluator/src/obj/mod.rsdiffbeforeafterboth430 /// Exists when super appears outside of `super.field`/`"field" in super` expressions430 /// Exists when super appears outside of `super.field`/`"field" in super` expressions431 /// Exclusive to jrsonnet.431 /// Exclusive to jrsonnet.432 ///432 ///433 /// Might return `NoSuperFound` error.433 /// Returns None if no `super` found434 pub fn standalone_super(&self) -> Result<ObjValue> {434 pub fn standalone_super(&self) -> Option<ObjValue> {435 if !self.sup.super_exists() {435 if !self.sup.super_exists() {436 bail!(NoSuperFound)436 return None;437 }437 }438 let mut out = ObjValue::builder();438 let mut out = ObjValue::builder();439 out.extend_with_core(StandaloneSuperCore {439 out.extend_with_core(StandaloneSuperCore {440 sup: self.sup,440 sup: self.sup,441 this: self.this.clone(),441 this: self.this.clone(),442 });442 });443 Ok(out.build())443 Some(out.build())444 }444 }445 pub fn this(&self) -> &ObjValue {445 pub fn this(&self) -> &ObjValue {446 &self.this446 &self.thistests/cpp_test_suite_golden_override/error.field_not_exist.jsonnet.goldendiffbeforeafterboth1no such field: y1no such field: y2 error.field_not_exist.jsonnet:17:10-10: field <y> accesstests/go_testdata_golden_override/builtinObjectRemoveKey_super_assert.jsonnet.goldendiffbeforeafterboth1no such field: x1no such field: x2 builtinObjectRemoveKey_super_assert.jsonnet:2:15-15: field <x> access2 builtinObjectRemoveKey_super_assert.jsonnet:2:10-15: assertion condition3 builtinObjectRemoveKey_super_assert.jsonnet:2:10-15: assertion condition