difftreelog
test pointer-size invariant snapshots
in: master
10 files changed
crates/jrsonnet-evaluator/src/analyze.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/analyze.rs
+++ b/crates/jrsonnet-evaluator/src/analyze.rs
@@ -1959,48 +1959,6 @@
}
}
-#[cfg(test)]
-fn render_diagnostics(src: &str, diags: &[Diagnostic]) -> String {
- use std::fmt::Write;
-
- use hi_doc::{Formatting, SnippetBuilder, Text};
-
- let mut out = String::new();
- let mut unspanned = Vec::new();
- let mut spanned: Vec<&Diagnostic> = Vec::new();
- for d in diags {
- if d.span.is_some() {
- spanned.push(d);
- } else {
- unspanned.push(d);
- }
- }
- if !spanned.is_empty() {
- let mut builder = SnippetBuilder::new(src);
- for d in spanned {
- let span = d.span.as_ref().expect("spanned");
- let ab = match d.level {
- DiagLevel::Error => {
- builder.error(Text::fragment(d.message.clone(), Formatting::default()))
- }
- DiagLevel::Warning => {
- builder.warning(Text::fragment(d.message.clone(), Formatting::default()))
- }
- };
- ab.range(span.range()).build();
- }
- out.push_str(&hi_doc::source_to_ansi(&builder.build()));
- }
- for d in unspanned {
- let prefix = match d.level {
- DiagLevel::Error => "error",
- DiagLevel::Warning => "warning",
- };
- writeln!(out, "{prefix}: {}", d.message).expect("fmt");
- }
- out
-}
-
pub struct AnalysisReport {
pub lir: LExpr,
pub root_shape: ClosureShape,
@@ -2011,16 +1969,63 @@
#[cfg(test)]
mod tests {
- use std::fs;
+ #[test]
+ #[cfg(not(feature = "exp-null-coaelse"))]
+ fn snapshots() {
+ use std::fs;
- use insta::{assert_snapshot, glob};
- use jrsonnet_ir::Source;
+ use insta::{assert_snapshot, glob};
+ use jrsonnet_ir::Source;
- use super::*;
+ use super::*;
- #[test]
- #[cfg(not(feature = "exp-null-coaelse"))]
- fn snapshots() {
+ fn render_diagnostics(src: &str, diags: &[Diagnostic]) -> String {
+ use std::fmt::Write;
+
+ use hi_doc::{Formatting, SnippetBuilder, Text};
+
+ let mut out = String::new();
+ let mut unspanned = Vec::new();
+ let mut spanned: Vec<&Diagnostic> = Vec::new();
+ for d in diags {
+ if d.span.is_some() {
+ spanned.push(d);
+ } else {
+ unspanned.push(d);
+ }
+ }
+ if !spanned.is_empty() {
+ let mut builder = SnippetBuilder::new(src);
+ for d in spanned {
+ let span = d.span.as_ref().expect("spanned");
+ let ab = match d.level {
+ DiagLevel::Error => {
+ builder.error(Text::fragment(d.message.clone(), Formatting::default()))
+ }
+ DiagLevel::Warning => builder
+ .warning(Text::fragment(d.message.clone(), Formatting::default())),
+ };
+ ab.range(span.range()).build();
+ }
+ out.push_str(&hi_doc::source_to_ansi(&builder.build()));
+ }
+ for d in unspanned {
+ let prefix = match d.level {
+ DiagLevel::Error => "error",
+ DiagLevel::Warning => "warning",
+ };
+ writeln!(out, "{prefix}: {}", d.message).expect("fmt");
+ }
+ out
+ }
+ fn fmt_depth(d: u32) -> String {
+ if d == u32::MAX {
+ "none".into()
+ } else {
+ d.to_string()
+ }
+ }
+
glob!("analysis_tests/*.jsonnet", |path| {
let code = fs::read_to_string(path).expect("read test file");
let src = Source::new_virtual("<test>".into(), code.clone().into());
@@ -2041,13 +2046,5 @@
);
assert_snapshot!(rendered);
});
- }
-
- fn fmt_depth(d: u32) -> String {
- if d == u32::MAX {
- "none".into()
- } else {
- d.to_string()
- }
}
}
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/error.rs
+++ b/crates/jrsonnet-evaluator/src/error.rs
@@ -123,9 +123,9 @@
EagerCompspecCaptured,
#[error("array out of bounds: {0} is not within [0,{1})")]
- ArrayBoundsError(isize, u32),
+ ArrayBoundsError(f64, u32),
#[error("string out of bounds: {0} is not within [0,{1})")]
- StringBoundsError(usize, usize),
+ StringBoundsError(f64, u32),
#[error("assert failed: {}", format_empty_str(.0))]
AssertionFailed(IStr),
crates/jrsonnet-evaluator/src/evaluate/mod.rsdiffbeforeafterboth1use std::rc::Rc;23use jrsonnet_gcmodule::{Cc, Trace};4use jrsonnet_interner::IStr;5use jrsonnet_ir::ImportKind;6use jrsonnet_types::ValType;78use self::{9 compspec::{evaluate_arr_comp, evaluate_obj_comp},10 destructure::{build_b_thunk_uno, evaluate_local_expr, evaluate_locals_unbound},11 operator::evaluate_binary_op_special,12};13use crate::{14 Context, Error, ObjValue, ObjValueBuilder, ObjectAssertion, Result, ResultExt as _, SupThis,15 Unbound, Val,16 analyze::{17 ClosureShape, LArgsDesc, LAssertStmt, LExpr, LFieldMember, LFieldName, LFunction,18 LIndexPart, LObjAsserts, LObjBody, LObjMembers, LSlot,19 },20 arr::ArrValue,21 bail, error,22 error::{ErrorKind::*, suggest_object_fields},23 evaluate::{destructure::fill_letrec_binds, operator::evaluate_unary_op},24 function::{CallLocation, FuncDesc, FuncVal, prepared::PreparedFuncVal},25 in_frame,26 typed::FromUntyped as _,27 val::{CachedUnbound, Thunk},28 with_state,29};3031pub mod compspec;32pub mod destructure;33pub mod operator;3435// This is the amount of bytes that need to be left on the stack before increasing the size.36// It must be at least as large as the stack required by any code that does not call37// `ensure_sufficient_stack`.38const RED_ZONE: usize = 100 * 1024;3940// Only the first stack that is pushed, grows exponentially (2^n * STACK_PER_RECURSION) from then41// on. This flag has performance relevant characteristics. Don't set it too high.42const STACK_PER_RECURSION: usize = 1024 * 1024;4344/// Grows the stack on demand to prevent stack overflow. Call this in strategic locations45/// to "break up" recursive calls. E.g. almost any call to `visit_expr` or equivalent can benefit46/// from this.47///48/// Should not be sprinkled around carelessly, as it causes a little bit of overhead.49#[inline]50pub fn ensure_sufficient_stack<R>(f: impl FnOnce() -> R) -> R {51 stacker::maybe_grow(RED_ZONE, STACK_PER_RECURSION, f)52}5354pub fn evaluate_trivial(expr: &LExpr) -> Option<Val> {55 // TODO: Eager trivial array56 Some(match expr {57 LExpr::Str(s) => Val::string(s.clone()),58 LExpr::Num(n) => Val::Num(*n),59 LExpr::Bool(false) => Val::Bool(false),60 LExpr::Bool(true) => Val::Bool(true),61 LExpr::Null => Val::Null,62 _ => return None,63 })64}6566pub fn evaluate_method(ctx: Context, name: IStr, func: &Rc<LFunction>) -> Val {67 Val::Func(FuncVal::Normal(Cc::new(FuncDesc {68 name,69 body_captures: ctx.pack_captures_sup_this(&func.body_shape),70 func: func.clone(),71 })))72}7374pub fn evaluate_field_name(ctx: Context, field_name: &LFieldName) -> Result<Option<IStr>> {75 Ok(match field_name {76 LFieldName::Fixed(n) => Some(n.clone()),77 LFieldName::Dyn(expr) => in_frame(78 // TODO: Spanned<LFieldName>79 CallLocation::native(),80 || "evaluating field name".to_string(),81 || {82 let v = evaluate(ctx.clone(), expr)?;83 Ok(if matches!(v, Val::Null) {84 None85 } else {86 Some(IStr::from_untyped(v)?)87 })88 },89 )?,90 })91}9293pub fn evaluate_thunk(ctx: Context, expr: Rc<LExpr>, tailstrict: bool) -> Result<Thunk<Val>> {94 match &*expr {95 LExpr::Slot(LSlot::Local(i)) => return Ok(ctx.local(*i)),96 LExpr::Slot(LSlot::Capture(i)) => return Ok(ctx.capture(*i)),97 _ => {98 if let Some(v) = evaluate_trivial(&expr) {99 return Ok(Thunk::evaluated(v));100 }101 }102 }103 Ok(if tailstrict {104 Thunk::evaluated(evaluate(ctx, &expr)?)105 } else {106 Thunk!(move || { evaluate(ctx, &expr) })107 })108}109110mod names {111 use crate::names;112113 names! {114 anonymous: "anonymous",115 }116}117118pub fn evaluate(ctx: Context, expr: &LExpr) -> Result<Val> {119 Ok(match expr {120 LExpr::Null => Val::Null,121 LExpr::Bool(b) => Val::Bool(*b),122 LExpr::Str(s) => Val::string(s.clone()),123 LExpr::Num(n) => Val::Num(*n),124 LExpr::Slot(slot) => ctx.slot(*slot).evaluate()?,125 LExpr::BadLocal(name) => panic!("unresolvable reference: {name}"),126 LExpr::Arr { shape, items } => Val::Arr(ArrValue::expr(ctx, shape, items.clone())),127 LExpr::UnaryOp(op, value) => {128 let value = evaluate(ctx, value)?;129 evaluate_unary_op(*op, &value)?130 }131 LExpr::BinaryOp { lhs, op, rhs } => evaluate_binary_op_special(ctx, lhs, *op, rhs)?,132 LExpr::LocalExpr(local_expr) => evaluate_local_expr(ctx, local_expr)?,133 LExpr::IfElse {134 cond,135 cond_then,136 cond_else,137 } => {138 let cond_val = evaluate(ctx.clone(), cond)?;139 let Val::Bool(b) = cond_val else {140 bail!(TypeMismatch(141 "if condition",142 vec![ValType::Bool],143 cond_val.value_type()144 ))145 };146 if b {147 evaluate(ctx, cond_then)?148 } else if let Some(e) = cond_else {149 evaluate(ctx, e)?150 } else {151 Val::Null152 }153 }154 LExpr::Error(s, e) => in_frame(155 CallLocation::new(s),156 || "error statement".to_owned(),157 || bail!(RuntimeError(evaluate(ctx, e)?.to_string()?,)),158 )?,159 LExpr::AssertExpr { assert, rest } => {160 evaluate_assert(ctx.clone(), assert)?;161 evaluate(ctx, rest)?162 }163164 LExpr::Function(func) => evaluate_method(165 ctx,166 func.name.clone().unwrap_or_else(names::anonymous),167 func,168 ),169 LExpr::IdentityFunction => Val::Func(FuncVal::identity()),170 LExpr::Apply {171 applicable,172 args,173 tailstrict,174 } => evaluate_apply(175 ctx,176 applicable,177 args,178 CallLocation::new(&args.span),179 *tailstrict,180 )?,181 LExpr::Index { indexable, parts } => evaluate_index(ctx, indexable, parts)?,182 LExpr::Obj(body) => evaluate_obj_body(None, ctx, body)?,183 LExpr::ObjExtend(lhs, body) => {184 let lhs_val = evaluate(ctx.clone(), lhs)?;185 let Val::Obj(lhs_obj) = lhs_val else {186 bail!(TypeMismatch(187 "object extend lhs",188 vec![ValType::Obj],189 lhs_val.value_type(),190 ))191 };192 evaluate_obj_body(Some(lhs_obj), ctx, body)?193 }194 LExpr::ArrComp(comp) => evaluate_arr_comp(ctx, comp)?,195 LExpr::Slice(slice) => {196 use crate::typed::BoundedUsize;197 let val = evaluate(ctx.clone(), &slice.value)?;198 let indexable = val.into_indexable()?;199 let start = slice200 .start201 .as_ref()202 .map(|e| evaluate(ctx.clone(), e))203 .transpose()?204 .map(|v| -> Result<i32> {205 v.as_num()206 .ok_or_else(|| {207 TypeMismatch("slice start", vec![ValType::Num], v.value_type()).into()208 })209 .map(|n| n as i32)210 })211 .transpose()?;212 let end = slice213 .end214 .as_ref()215 .map(|e| evaluate(ctx.clone(), e))216 .transpose()?217 .map(|v| -> Result<i32> {218 v.as_num()219 .ok_or_else(|| {220 TypeMismatch("slice end", vec![ValType::Num], v.value_type()).into()221 })222 .map(|n| n as i32)223 })224 .transpose()?;225 let step = slice226 .step227 .as_ref()228 .map(|e| evaluate(ctx, e))229 .transpose()?230 .map(|v| -> Result<BoundedUsize<1, { i32::MAX as usize }>> {231 let n = v.as_num().ok_or_else(|| -> crate::Error {232 TypeMismatch("slice step", vec![ValType::Num], v.value_type()).into()233 })?;234 BoundedUsize::new(n as usize).ok_or_else(|| error!("slice step must be >= 1"))235 })236 .transpose()?;237 Val::from(indexable.slice(start, end, step)?)238 }239 LExpr::Super => Val::Obj(ctx.try_sup_this()?.standalone_super().ok_or(NoSuperFound)?),240 LExpr::Import {241 kind,242 kind_span,243 path,244 } => with_state(|state| {245 let resolved = state.resolve_from(kind_span.0.source_path(), &path.clone())?;246 Ok::<_, Error>(match kind.value {247 ImportKind::Normal => in_frame(248 CallLocation::new(&kind.span),249 || "import".to_string(),250 || state.import_resolved(resolved),251 )?,252 ImportKind::Str => Val::string(state.import_resolved_str(resolved)?),253 ImportKind::Bin => Val::arr(state.import_resolved_bin(resolved)?),254 })255 })?,256 })257}258259fn evaluate_apply(260 ctx: Context,261 applicable: &LExpr,262 args: &LArgsDesc,263 loc: CallLocation<'_>,264 tailstrict: bool,265) -> Result<Val> {266 let func_val = evaluate(ctx.clone(), applicable)?;267 let Val::Func(func) = func_val else {268 bail!(OnlyFunctionsCanBeCalledGot(func_val.value_type()))269 };270271 if func.is_identity() && args.names.is_empty() && args.unnamed.len() == 1 {272 return evaluate_thunk(ctx, args.unnamed[0].clone(), tailstrict)?.evaluate();273 }274275 let name = func.name();276277 if args.names.is_empty() && args.unnamed.len() == 1 && func.params().len() == 1 {278 use crate::function::prepared::PreparedCall;279 let prepared_inline = PreparedCall::empty();280 let arg = evaluate_thunk(ctx, args.unnamed[0].clone(), tailstrict)?;281 let arg_slice = std::slice::from_ref(&arg);282 return in_frame(283 loc,284 || format!("function <{name}> call"),285 || {286 func.evaluate_prepared(287 &prepared_inline,288 CallLocation::native(),289 arg_slice,290 &[],291 tailstrict,292 )293 },294 );295 }296297 let unnamed = args298 .unnamed299 .iter()300 .cloned()301 .map(|e| evaluate_thunk(ctx.clone(), e, tailstrict))302 .collect::<Result<Vec<_>>>()?;303304 // Fast path: positional-only multi-arg call fully covering the305 // params, no defaults.306 if args.names.is_empty() && unnamed.len() == func.params().len() {307 use crate::function::prepared::PreparedCall;308 let prepared_inline = PreparedCall::empty();309 return in_frame(310 loc,311 || format!("function <{name}> call"),312 || {313 func.evaluate_prepared(314 &prepared_inline,315 CallLocation::native(),316 &unnamed,317 &[],318 tailstrict,319 )320 },321 );322 }323324 let named = args325 .values326 .iter()327 .cloned()328 .map(|e| evaluate_thunk(ctx.clone(), e, tailstrict))329 .collect::<Result<Vec<_>>>()?;330 let prepare = PreparedFuncVal::new(func, unnamed.len(), &args.names)331 .with_description_src(loc, || format!("function <{name}> preparation"))?;332 in_frame(333 loc,334 || format!("function <{name}> call"),335 || prepare.call(CallLocation::native(), &unnamed, &named),336 )337}338339fn 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;388 let key_val = evaluate(ctx, &part.value)?;389 indexable = match (&value, &key_val) {390 (Val::Obj(obj), Val::Str(key)) => {391 let key = key.clone().into_flat();392 match obj393 .get(key.clone())394 .with_description_src(loc, || format!("field <{key}> access"))?395 {396 Some(v) => v,397 #[cfg(feature = "exp-null-coaelse")]398 None if part.null_coaelse => return Ok(Val::Null),399 None => {400 return Err(Error::from(NoSuchField(401 key.clone(),402 suggest_object_fields(obj, key.clone()),403 )))404 .with_description_src(loc, || format!("field <{key}> access"));405 }406 }407 }408 (Val::Arr(arr), Val::Num(idx)) => {409 let n = idx.get();410 if n.fract() > f64::EPSILON {411 bail!(FractionalIndex)412 }413 if n < 0.0 {414 bail!(ArrayBoundsError(415 n as isize, // truncation is fine for error display416 arr.len()417 ));418 }419 #[expect(420 clippy::cast_possible_truncation,421 clippy::cast_sign_loss,422 reason = "n is checked positive"423 )]424 let i = n as u32;425 arr.get(i)426 .with_description_src(loc, || format!("element <{i}> access"))?427 .ok_or_else(|| ArrayBoundsError(i as isize, arr.len()))?428 }429 (Val::Str(s), Val::Num(idx)) => {430 let n = idx.get();431 if n.fract() > f64::EPSILON {432 bail!(FractionalIndex)433 }434 let flat = s.clone().into_flat();435 if n < 0.0 {436 bail!(ArrayBoundsError(437 n as isize, // truncation is fine for error display438 flat.chars().count() as u32439 ));440 }441 #[expect(442 clippy::cast_possible_truncation,443 clippy::cast_sign_loss,444 reason = "n is checked positive, overflow will truncate as expected"445 )]446 let i = n as usize;447 let Some(char) = flat.chars().nth(i) else {448 bail!(StringBoundsError(i, flat.chars().count()))449 };450 Val::string(char)451 }452 #[cfg(feature = "exp-null-coaelse")]453 (Val::Null, _) if part.null_coaelse => return Ok(Val::Null),454 _ => bail!(ValueIndexMustBeTypeGot(455 value.value_type(),456 ValType::Str,457 key_val.value_type()458 )),459 };460 }461 Ok(indexable)462}463464fn evaluate_obj_body(super_obj: Option<ObjValue>, ctx: Context, body: &LObjBody) -> Result<Val> {465 match body {466 LObjBody::MemberList(members) => evaluate_obj_members(super_obj, ctx, members),467 LObjBody::ObjComp(comp) => evaluate_obj_comp(super_obj, ctx, comp),468 }469}470471pub fn evaluate_field_member_unbound<B: Unbound<Bound = Context> + Clone>(472 builder: &mut ObjValueBuilder,473 ctx: Context,474 uctx: B,475 field: &LFieldMember,476) -> Result<()> {477 #[derive(Trace)]478 struct UnboundValue<B: Trace> {479 uctx: B,480 value: Rc<(ClosureShape, LExpr)>,481 name: IStr,482 }483 impl<B: Unbound<Bound = Context>> Unbound for UnboundValue<B> {484 type Bound = Val;485 fn bind(&self, sup_this: SupThis) -> Result<Val> {486 let a_ctx = self.uctx.bind(sup_this)?;487 let b_ctx = Context::enter_using(&a_ctx, &self.value.0);488 evaluate(b_ctx, &self.value.1)489 }490 }491492 let LFieldMember {493 name,494 plus,495 visibility,496 value,497 } = field;498 let Some(name) = evaluate_field_name(ctx, name)? else {499 return Ok(());500 };501502 builder503 .field(name.clone())504 .with_add(*plus)505 .with_visibility(*visibility)506 .bindable(UnboundValue {507 uctx,508 value: value.clone(),509 name,510 })511}512pub fn evaluate_field_member_static(513 builder: &mut ObjValueBuilder,514 field_ctx: Context,515 value_ctx: Context,516 field: &LFieldMember,517) -> Result<()> {518 let LFieldMember {519 name,520 plus,521 visibility,522 value,523 } = field;524 let Some(name) = evaluate_field_name(field_ctx, name)? else {525 return Ok(());526 };527528 let thunk = build_b_thunk_uno(&value_ctx, value.clone());529 builder530 .field(name)531 .with_add(*plus)532 .with_visibility(*visibility)533 .try_thunk(thunk)?;534 Ok(())535}536537fn evaluate_obj_members(538 super_obj: Option<ObjValue>,539 ctx: Context,540 members: &LObjMembers,541) -> Result<Val> {542 let mut builder = ObjValueBuilder::with_capacity(members.fields.len());543 if let Some(sup) = super_obj {544 builder.with_super(sup);545 }546547 let needs_unbound = members.this.is_some() || members.uses_super;548549 if needs_unbound {550 let uctx = CachedUnbound::new(evaluate_locals_unbound(551 &ctx,552 &members.frame_shape,553 members.this,554 members.locals.clone(),555 ));556 for field in &members.fields {557 evaluate_field_member_unbound(&mut builder, ctx.clone(), uctx.clone(), field)?;558 }559 if let Some(asserts_block) = &members.asserts {560 builder.assert(evaluate_object_assertions_unbound(561 uctx,562 asserts_block.clone(),563 ));564 }565 } else {566 let a_ctx = ctx567 .pack_captures_sup_this(&members.frame_shape)568 .enter(|fill, ctx| {569 fill_letrec_binds(fill, &ctx, &members.locals);570 });571 for field in &members.fields {572 evaluate_field_member_static(&mut builder, ctx.clone(), a_ctx.clone(), field)?;573 }574 if let Some(asserts_block) = &members.asserts {575 builder.assert(evaluate_object_assertions_static(576 a_ctx,577 asserts_block.clone(),578 ));579 }580 }581582 Ok(Val::Obj(builder.build()))583}584585pub fn evaluate_assert(ctx: Context, assertion: &LAssertStmt) -> Result<()> {586 let LAssertStmt { cond, message } = assertion;587 let assertion_result = in_frame(588 CallLocation::new(&cond.span),589 || "assertion condition".to_owned(),590 || bool::from_untyped(evaluate(ctx.clone(), cond)?),591 )?;592 if !assertion_result {593 in_frame(594 CallLocation::new(&cond.span),595 || "assertion failure".to_owned(),596 || {597 if let Some(msg) = message {598 bail!(AssertionFailed(evaluate(ctx, msg)?.to_string()?));599 }600 bail!(AssertionFailed(Val::Null.to_string()?));601 },602 )?;603 }604 Ok(())605}606607fn evaluate_object_assertions_unbound<B: Unbound<Bound = Context>>(608 uctx: B,609 asserts: Rc<LObjAsserts>,610) -> impl ObjectAssertion {611 #[derive(Trace)]612 struct ObjectAssert<B: Trace> {613 uctx: B,614 asserts: Rc<LObjAsserts>,615 }616 impl<B: Unbound<Bound = Context>> ObjectAssertion for ObjectAssert<B> {617 fn run(&self, sup_this: SupThis) -> Result<()> {618 let a_ctx = self.uctx.bind(sup_this)?;619 let assert_env = Context::enter_using(&a_ctx, &self.asserts.shape);620 for assert in &self.asserts.asserts {621 evaluate_assert(assert_env.clone(), assert)?;622 }623 Ok(())624 }625 }626 ObjectAssert { uctx, asserts }627}628fn evaluate_object_assertions_static(629 a_ctx: Context,630 asserts: Rc<LObjAsserts>,631) -> impl ObjectAssertion {632 #[derive(Trace)]633 struct ObjectAssert {634 assert_env: Context,635 asserts: Rc<LObjAsserts>,636 }637 impl ObjectAssertion for ObjectAssert {638 fn run(&self, _sup_this: SupThis) -> Result<()> {639 for assert in &self.asserts.asserts {640 evaluate_assert(self.assert_env.clone(), assert)?;641 }642 Ok(())643 }644 }645 let assert_env = Context::enter_using(&a_ctx, &asserts.shape);646 ObjectAssert {647 assert_env,648 asserts,649 }650}crates/jrsonnet-evaluator/src/typed/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/typed/mod.rs
+++ b/crates/jrsonnet-evaluator/src/typed/mod.rs
@@ -157,9 +157,7 @@
Self::BoundedNumber(from, to) => {
if let Val::Num(n) = value {
let n = n.get();
- if from.map(|from| from > n).unwrap_or(false)
- || to.map(|to| to < n).unwrap_or(false)
- {
+ if from.is_some_and(|from| from > n) || to.is_some_and(|to| to < n) {
return Err(TypeError::BoundsFailed(n, *from, *to).into());
}
Ok(())
crates/jrsonnet-interner/src/inner.rsdiffbeforeafterboth--- a/crates/jrsonnet-interner/src/inner.rs
+++ b/crates/jrsonnet-interner/src/inner.rs
@@ -161,6 +161,12 @@
// SAFETY: header is initialized
unsafe { (*header).refcnt() }
}
+
+ pub fn len32(&self) -> u32 {
+ let header = Self::header(self);
+ // SAFETY: header is initialized
+ unsafe { (*header).size }
+ }
}
impl Clone for Inner {
crates/jrsonnet-interner/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-interner/src/lib.rs
+++ b/crates/jrsonnet-interner/src/lib.rs
@@ -53,6 +53,10 @@
pub fn cast_bytes(self) -> IBytes {
IBytes(self.0.clone())
}
+
+ pub fn len32(&self) -> u32 {
+ self.0.len32()
+ }
}
impl Deref for IStr {
crates/jrsonnet-ir-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-ir-parser/src/lib.rs
+++ b/crates/jrsonnet-ir-parser/src/lib.rs
@@ -1038,7 +1038,7 @@
}
let e = expr(&mut p)?;
if !p.at_eof() {
- return Err(p.error(format!("expected end of file, got {}", p.current_desc(),)));
+ return Err(p.error(format!("expected end of file, got {}", p.current_desc())));
}
Ok(e)
}
@@ -1051,10 +1051,7 @@
#[cfg(test)]
mod tests {
- use std::fs;
-
- use insta::{assert_snapshot, glob};
- use jrsonnet_ir::{IStr, Source};
+ use insta::assert_snapshot;
use super::*;
@@ -1159,6 +1156,11 @@
#[test]
#[cfg(not(feature = "exp-null-coaelse"))]
fn peg_snapshots() {
+ use std::fs;
+
+ use insta::glob;
+ use jrsonnet_ir::{IStr, Source};
+
glob!("../../jrsonnet-peg-parser/src", "tests/*.jsonnet", |path| {
let input = fs::read_to_string(path).expect("read test file");
let source = Source::new_virtual("<test>".into(), IStr::empty());
crates/jrsonnet-peg-parser/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-peg-parser/src/lib.rs
+++ b/crates/jrsonnet-peg-parser/src/lib.rs
@@ -433,16 +433,16 @@
#[cfg(test)]
mod tests {
- use std::fs;
+ #[test]
+ #[cfg(not(feature = "exp-null-coaelse"))]
+ fn snapshots() {
+ use std::fs;
- use insta::{assert_snapshot, glob};
- use jrsonnet_ir::{IStr, Source};
+ use insta::{assert_snapshot, glob};
+ use jrsonnet_ir::{IStr, Source};
- use crate::{ParserSettings, parse};
+ use crate::{ParserSettings, parse};
- #[test]
- #[cfg(not(feature = "exp-null-coaelse"))]
- fn snapshots() {
glob!("tests/*.jsonnet", |path| {
let input = fs::read_to_string(path).expect("read test file");
let v = parse(
tests/cpp_test_suite_golden_override/error.array_large_index.jsonnet.goldendiffbeforeafterboth--- a/tests/cpp_test_suite_golden_override/error.array_large_index.jsonnet.golden
+++ b/tests/cpp_test_suite_golden_override/error.array_large_index.jsonnet.golden
@@ -1 +1 @@
-array out of bounds: 4294967295 is not within [0,3)
\ No newline at end of file
+array out of bounds: 18446744073709552000 is not within [0,3)
\ No newline at end of file
tests/go_testdata_golden_override/string_index_negative.jsonnet.goldendiffbeforeafterboth--- a/tests/go_testdata_golden_override/string_index_negative.jsonnet.golden
+++ b/tests/go_testdata_golden_override/string_index_negative.jsonnet.golden
@@ -1 +1 @@
-array out of bounds: -1 is not within [0,4)
\ No newline at end of file
+string out of bounds: -1 is not within [0,4)
\ No newline at end of file