sc: report a dynamic-array spill cell as a value in ISFORMULA

A spilled cell is stored as a reference formula cell pointing
back at the master, so ISFORMULA reported true across the
whole spill range. Only the master holds the formula.

ISFORMULA now returns true only for the master and false for
the cells it spilled into, for both the single-cell and the
array-context range form. A classic array formula is
unchanged, as it has no dynamic-array master.

Change-Id: If4f495b45677001ad13e1f1a2a669d3d235d117b
Signed-off-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk>
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/6244
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
Tested-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com>
(cherry picked from commit 5361d8e25d09ccde6922f5758a4d2d35df5c0418)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/207894
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
Tested-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
Tomaž Vajngerl
2026-07-10 01:08:40 +02:00
committed by Xisco Fauli
parent 96691462de
commit f0b1496469
2 changed files with 85 additions and 3 deletions
+58
View File
@@ -6637,6 +6637,64 @@ CPPUNIT_TEST_FIXTURE(TestFormula2, testAutoSpillFromChooseAndLet)
m_pDoc->DeleteTab(0);
}
CPPUNIT_TEST_FIXTURE(TestFormula2, testDynamicArrayIsFormula)
{
// ISFORMULA is true for the master of a dynamic-array spill and
// false for the cells it spilled into, even though the spilled
// data came from formulas. The same input entered as a classic
// array formula keeps every cell a formula.
m_pDoc->SetAutoCalc(false);
m_pDoc->InsertTab(0, u"Sheet1"_ustr);
m_pDoc->SetString(ScAddress(1, 0, 0), u"=1+10"_ustr);
m_pDoc->SetString(ScAddress(1, 1, 0), u"=1+20"_ustr);
m_pDoc->SetString(ScAddress(1, 2, 0), u"=1+30"_ustr);
// A1 spills =B1:B3 into A1:A3 as a dynamic-array master.
ScAddress aPos(0, 0, 0);
ScCompiler aComp(*m_pDoc, aPos, m_pDoc->GetGrammar(), false, false);
std::unique_ptr<ScTokenArray> pCode = aComp.CompileString(u"=B1:B3"_ustr);
auto pCell = new ScFormulaCell(*m_pDoc, aPos, std::move(pCode));
pCell->SetAutoDynamicArrayEligible(true);
m_pDoc->SetFormulaCell(aPos, pCell);
m_pDoc->SetAutoCalc(true);
m_pDoc->CalcAll();
CPPUNIT_ASSERT(pCell->IsDynamicArrayMaster());
// A source cell is a real formula, but the cells the master
// spilled into are not.
m_pDoc->SetString(ScAddress(6, 0, 0), u"=ISFORMULA(B1)"_ustr);
CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(6, 0, 0)));
m_pDoc->SetString(ScAddress(4, 0, 0), u"=ISFORMULA(A1)"_ustr);
m_pDoc->SetString(ScAddress(4, 1, 0), u"=ISFORMULA(A2)"_ustr);
m_pDoc->SetString(ScAddress(4, 2, 0), u"=ISFORMULA(A3)"_ustr);
CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(4, 0, 0)));
CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(4, 1, 0)));
CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(4, 2, 0)));
// The range form agrees cell by cell: ISFORMULA over A1:A3 gives
// true, false, false across H1:H3.
ScMarkData aMark(m_pDoc->GetSheetLimits());
aMark.SelectOneTable(0);
m_pDoc->InsertMatrixFormula(7, 0, 7, 2, aMark, u"=ISFORMULA(A1:A3)"_ustr);
CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(7, 0, 0)));
CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(7, 1, 0)));
CPPUNIT_ASSERT_EQUAL(0.0, m_pDoc->GetValue(ScAddress(7, 2, 0)));
// The same =B1:B3 entered as a classic array formula at D1:D3
// keeps every cell a formula.
m_pDoc->InsertMatrixFormula(3, 0, 3, 2, aMark, u"=B1:B3"_ustr);
m_pDoc->SetString(ScAddress(5, 0, 0), u"=ISFORMULA(D1)"_ustr);
m_pDoc->SetString(ScAddress(5, 1, 0), u"=ISFORMULA(D2)"_ustr);
m_pDoc->SetString(ScAddress(5, 2, 0), u"=ISFORMULA(D3)"_ustr);
CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(5, 0, 0)));
CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(5, 1, 0)));
CPPUNIT_ASSERT_EQUAL(1.0, m_pDoc->GetValue(ScAddress(5, 2, 0)));
m_pDoc->DeleteTab(0);
}
CPPUNIT_PLUGIN_IMPLEMENT();
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+27 -3
View File
@@ -2865,6 +2865,31 @@ void ScInterpreter::ScIsValue()
PushInt( int(bRes) );
}
namespace {
// Whether ISFORMULA counts a cell as a formula. A dynamic-array
// spill cell is not a formula, only its master is. A classic array
// formula cell stays a formula.
bool lclReportsAsFormula(const ScDocument& rDocument, const ScAddress& rPosition)
{
if (rDocument.GetCellType(rPosition) != CELLTYPE_FORMULA)
return false;
const ScFormulaCell* pCell = rDocument.GetFormulaCell(rPosition);
if (pCell && pCell->GetMatrixFlag() == ScMatrixMode::Reference)
{
ScAddress aOrigin;
if (pCell->GetMatrixOrigin(rDocument, aOrigin))
{
const ScFormulaCell* pMaster = rDocument.GetFormulaCell(aOrigin);
if (pMaster && pMaster->IsDynamicArrayMaster())
return false;
}
}
return true;
}
}
void ScInterpreter::ScIsFormula()
{
nFuncFmtType = SvNumFormatType::LOGICAL;
@@ -2906,8 +2931,7 @@ void ScInterpreter::ScIsFormula()
for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
{
aAdr.SetRow(nRow);
ScRefCellValue aCell(mrDoc, aAdr);
pResMat->PutBoolean( (aCell.getType() == CELLTYPE_FORMULA), i,j);
pResMat->PutBoolean(lclReportsAsFormula(mrDoc, aAdr), i, j);
++j;
}
++i;
@@ -2924,7 +2948,7 @@ void ScInterpreter::ScIsFormula()
if ( !PopDoubleRefOrSingleRef( aAdr ) )
break;
bRes = (mrDoc.GetCellType(aAdr) == CELLTYPE_FORMULA);
bRes = lclReportsAsFormula(mrDoc, aAdr);
}
break;
default: