diff --git a/crates/calc/src/eval.rs b/crates/calc/src/eval.rs index fcc38c3..48e46b3 100644 --- a/crates/calc/src/eval.rs +++ b/crates/calc/src/eval.rs @@ -1059,10 +1059,10 @@ impl<'h> Evaluator<'h> { if h == 0 || w == 0 { return err(CellError::Ref); } - let r0 = base.range.start.row as i64 + rows; - let c0 = base.range.start.col as i64 + cols; - let (r0, r1) = if h > 0 { (r0, r0 + h - 1) } else { (r0 + h + 1, r0) }; - let (c0, c1) = if w > 0 { (c0, c0 + w - 1) } else { (c0 + w + 1, c0) }; + let r0 = (base.range.start.row as i64).saturating_add(rows); + let c0 = (base.range.start.col as i64).saturating_add(cols); + let (r0, r1) = if h > 0 { (r0, r0.saturating_add(h - 1)) } else { (r0.saturating_add(h + 1), r0) }; + let (c0, c1) = if w > 0 { (c0, c0.saturating_add(w - 1)) } else { (c0.saturating_add(w + 1), c0) }; if r0 < 0 || c0 < 0 || r1 >= MAX_ROWS as i64 || c1 >= MAX_COLS as i64 { return err(CellError::Ref); } diff --git a/crates/calc/src/tests.rs b/crates/calc/src/tests.rs index bfe697e..21bd681 100644 --- a/crates/calc/src/tests.rs +++ b/crates/calc/src/tests.rs @@ -1023,3 +1023,32 @@ fn trim_references() { t.set("K20", "=SUM(Q1:.Q9)"); assert_eq!(t.get("K20"), Value::Error(CellError::Calc)); } + +#[test] +fn offset_extreme_arguments_return_ref() { + let wb = Workbook::new(); + for formula in [ + "=OFFSET(B2,1E300,0)", + "=OFFSET(B2,0,1E300)", + "=OFFSET(B2,0,0,1E300,1)", + "=OFFSET(B2,0,0,1,1E300)", + "=OFFSET(B2,-1E300,0)", + "=OFFSET(B2,0,-1E300)", + "=OFFSET(B2,0,0,-1E300,1)", + "=OFFSET(B2,0,0,1,-1E300)", + "=OFFSET(B2,1E300,0,-1E300,1)", + "=OFFSET(B2,0,1E300,1,-1E300)", + "=OFFSET(B2,-1E300,0,-1E300,1)", + ] { + assert_eq!(crate::evaluate(&wb, 0, c("D1"), formula), Value::Error(CellError::Ref), "{formula}"); + } + for (formula, expected) in [ + ("=ROW(OFFSET(B2,1.9,0))", 3.0), + ("=COLUMN(OFFSET(B2,0,-1.9))", 1.0), + ("=ROWS(OFFSET(B2,0,0,-2,1))", 2.0), + ("=COLUMNS(OFFSET(B2,0,0,1,-2))", 2.0), + ("=ROWS(OFFSET(A1,0,0,1048576,1))", 1048576.0), + ] { + assert_eq!(crate::evaluate(&wb, 0, c("D1"), formula), Value::Number(expected), "{formula}"); + } +} diff --git a/crates/engine/tests/offset_overflow.rs b/crates/engine/tests/offset_overflow.rs new file mode 100644 index 0000000..de59888 --- /dev/null +++ b/crates/engine/tests/offset_overflow.rs @@ -0,0 +1,24 @@ +use gridcraft_core::{CellError, CellRef, Value}; +use gridcraft_engine::Session; +use serde_json::json; + +#[test] +fn cell_set_offset_extreme_arguments_return_ref() { + let mut session = Session::new(); + session.new_workbook(); + for formula in [ + "=OFFSET(B2,1E300,0)", + "=OFFSET(B2,0,1E300)", + "=OFFSET(B2,0,0,1E300,1)", + "=OFFSET(B2,0,0,1,1E300)", + "=OFFSET(B2,-1E300,0)", + "=OFFSET(B2,0,-1E300)", + "=OFFSET(B2,0,0,-1E300,1)", + "=OFFSET(B2,0,0,1,-1E300)", + "=OFFSET(B2,1E300,0,-1E300,1)", + "=OFFSET(B2,0,1E300,1,-1E300)", + ] { + session.execute("cell.set", json!({"cell": "D1", "input": formula})).unwrap(); + assert_eq!(session.doc().unwrap().wb.sheet(0).unwrap().value(CellRef::new(0, 3)), Value::Error(CellError::Ref), "{formula}"); + } +}