sc: let @ collapse arrays from range arithmetic, not just functions

The @ operator collapses an array operand to its upper-left
value. A function operand like SEQUENCE pushes a matrix and @
collapses it. A range arithmetic operand like (B2:B3+0) does
implicit intersection first and hands @ a scalar, so @ has
nothing to collapse.

Mark @'s operand as ForceArray in the parameter classifier and
wrap UnaryLine in a CurrentFactor scope so the classification
reaches the nested operators. Range arithmetic inside @ then
materialises as a matrix and reaches @ for the upper-left pick.

testSingleValueOperator covers @ on a range expression and a
chained @ binary.

Signed-off-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk>
Change-Id: I7f59bfa1efbceeceb9a3d7f71a0243e9a521b24a
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/5517
Tested-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com>
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
(cherry picked from commit 4148cbecbdaa3164c53d3869252a3ca714ba5726)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/207868
Tested-by: Xisco Fauli <xiscofauli@libreoffice.org>
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
Tomaž Vajngerl
2026-07-09 17:08:49 +02:00
committed by Xisco Fauli
parent ee4bd7f201
commit 14d723ea36
3 changed files with 28 additions and 1 deletions
+8 -1
View File
@@ -2417,7 +2417,14 @@ void FormulaCompiler::UnaryLine()
{
FormulaTokenRef p = mpToken;
NextToken();
UnaryLine();
// Hold the unary operator as the current factor so its
// parameter classification reaches the nested operators.
{
CurrentFactor pInnerFac(this);
pInnerFac = p;
CheckSetForceArrayParameter(mpToken, 0);
UnaryLine();
}
if (mbComputeII)
{
FormulaToken** pArg = mpCode - 1;
+19
View File
@@ -5955,6 +5955,25 @@ CPPUNIT_TEST_FIXTURE(TestFormula2, testSingleValueOperator)
// No spill into the row below.
CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(0, 1, 0)));
// The @ also collapses an array produced by range arithmetic. B2:B3+0
// is a two-cell array whose upper-left is B2. The formula sits in a
// row that does not overlap B2:B3, so implicit intersection on the
// range would otherwise fail and the @ has to drive the collapse.
m_pDoc->SetValue(ScAddress(1, 1, 0), 7.0);
m_pDoc->SetValue(ScAddress(1, 2, 0), 9.0);
m_pDoc->SetFormula(ScAddress(0, 5, 0), u"=@(B2:B3+0)"_ustr,
formula::FormulaGrammar::GRAM_NATIVE);
CPPUNIT_ASSERT_EQUAL(7.0, m_pDoc->GetValue(ScAddress(0, 5, 0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(0, 6, 0)));
// Two @-collapsed sub-expressions joined by a binary operator stay
// scalar. Without the collapse on each side the addition would see
// two arrays and produce one too, leaving the row below populated.
m_pDoc->SetFormula(ScAddress(0, 8, 0), u"=@((B2:B3+0)) + @((B2:B3+0))"_ustr,
formula::FormulaGrammar::GRAM_NATIVE);
CPPUNIT_ASSERT_EQUAL(14.0, m_pDoc->GetValue(ScAddress(0, 8, 0)));
CPPUNIT_ASSERT_EQUAL(CELLTYPE_NONE, m_pDoc->GetCellType(ScAddress(0, 9, 0)));
m_pDoc->DeleteTab(0);
}
+1
View File
@@ -249,6 +249,7 @@ const ScParameterClassification::RawData ScParameterClassification::pRawData[] =
{ ocScan, {{ Value, ForceArray, Value }, 0, ForceArrayReturn }},
{ ocSheet, {{ Reference }, 0, Value }},
{ ocSheets, {{ Reference }, 1, Value }},
{ ocSingleValue, {{ ForceArray }, 0, Value }},
{ ocSkew, {{ Reference }, 1, Value }},
{ ocSkewp, {{ Reference }, 1, Value }},
{ ocSlope, {{ ForceArray, ForceArray }, 0, Value }},