difftreelog
feat bring back null coaelse
in: master
5 files changed
crates/jrsonnet-evaluator/src/evaluate/compspec.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/evaluate/compspec.rs
+++ b/crates/jrsonnet-evaluator/src/evaluate/compspec.rs
@@ -1,14 +1,13 @@
use std::rc::Rc;
+#[cfg(feature = "exp-object-iteration")]
+use jrsonnet_interner::IStr;
use jrsonnet_types::ValType;
use super::{
destructure::{destruct, evaluate_locals_unbound, fill_letrec_binds},
evaluate_field_member_static, evaluate_field_member_unbound,
};
-#[cfg(feature = "exp-object-iteration")]
-use jrsonnet_interner::IStr;
-
use crate::{
Context, ObjValue, ObjValueBuilder, Result, Thunk, Val,
analyze::{
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.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/obj/mod.rs
+++ b/crates/jrsonnet-evaluator/src/obj/mod.rs
@@ -430,17 +430,17 @@
/// Exists when super appears outside of `super.field`/`"field" in super` expressions
/// Exclusive to jrsonnet.
///
- /// Might return `NoSuperFound` error.
- pub fn standalone_super(&self) -> Result<ObjValue> {
+ /// Returns None if no `super` found
+ pub fn standalone_super(&self) -> Option<ObjValue> {
if !self.sup.super_exists() {
- bail!(NoSuperFound)
+ return None;
}
let mut out = ObjValue::builder();
out.extend_with_core(StandaloneSuperCore {
sup: self.sup,
this: self.this.clone(),
});
- Ok(out.build())
+ Some(out.build())
}
pub fn this(&self) -> &ObjValue {
&self.this
tests/cpp_test_suite_golden_override/error.field_not_exist.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.field_not_exist.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.field_not_exist.jsonnet.golden
@@ -1 +1,2 @@
-no such field: y
\ No newline at end of file
+no such field: y
+ error.field_not_exist.jsonnet:17:10-10: field <y> access
\ No newline at end of file
tests/go_testdata_golden_override/builtinObjectRemoveKey_super_assert.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/builtinObjectRemoveKey_super_assert.jsonnet.golden
+++ b/tests/go_testdata_golden_override/builtinObjectRemoveKey_super_assert.jsonnet.golden
@@ -1,2 +1,3 @@
no such field: x
+ builtinObjectRemoveKey_super_assert.jsonnet:2:15-15: field <x> access
builtinObjectRemoveKey_super_assert.jsonnet:2:10-15: assertion condition
\ No newline at end of file