diff --git a/crates/core/src/addr.rs b/crates/core/src/addr.rs index 2bdfe797a..2c087f22e 100644 --- a/crates/core/src/addr.rs +++ b/crates/core/src/addr.rs @@ -210,11 +210,18 @@ impl RangeRef { if let (Some(x), Some(y)) = (CellRef::parse(a), CellRef::parse(b)) { return Some(RangeRef::new(x, y)); } - let strip = |t: &str| t.trim_start_matches('$').to_string(); + let strip = |t: &str| t.strip_prefix('$').unwrap_or(t).to_string(); if let (Some(x), Some(y)) = (letters_to_col(&strip(a)), letters_to_col(&strip(b))) { return Some(RangeRef::cols(x.min(y), x.max(y))); } - let (pa, pb) = (strip(a).parse::().ok()?, strip(b).parse::().ok()?); + let row_num = |t: &str| { + let t = t.strip_prefix('$').unwrap_or(t); + if t.is_empty() || !t.bytes().all(|c| c.is_ascii_digit()) { + return None; + } + t.parse::().ok() + }; + let (pa, pb) = (row_num(a)?, row_num(b)?); if pa == 0 || pb == 0 || pa > MAX_ROWS || pb > MAX_ROWS { return None; } @@ -234,6 +241,18 @@ impl fmt::Display for RangeRef { mod tests { use super::*; + #[test] + fn range_parse_rejects_signed_rows() { + assert!(RangeRef::parse("+1:+3").is_none()); + assert!(RangeRef::parse("-1:3").is_none()); + assert!(RangeRef::parse("$$1:3").is_none()); + assert!(RangeRef::parse(":3").is_none()); + assert!(RangeRef::parse("$$A:$$C").is_none()); + assert!(RangeRef::parse("$A:$C").is_some()); + let r = RangeRef::parse("$1:$3").unwrap(); + assert_eq!((r.start.row, r.end.row), (0, 2)); + } + #[test] fn letters_roundtrip() { for (c, s) in [(0, "A"), (25, "Z"), (26, "AA"), (51, "AZ"), (52, "BA"), (701, "ZZ"), (702, "AAA"), (16383, "XFD")] {