From d9dcb8a4ccfbeb98aaeec06aaff2b804ce78b168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=C5=BE=20Vajngerl?= Date: Tue, 25 Aug 2026 01:49:40 +0000 Subject: [PATCH] sc: take the error constants from the grammar in use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The formula writer substitutes an error constant for anything that has no spelling in the target format, and it took that string from the native symbol table. That table is translated, so a build in another language wrote a translated "#REF!" into the saved formula, where only the English spelling reads back. It now takes the string from the table of the grammar being written, which changes nothing for the native grammar. Reading had a gap of its own. The OOXML table had a second "#REF!" row for the stop opcode, and the symbol map keeps the first spelling it finds in opcode order, so that row shadowed the error constant and a bare #REF! read from a file gave #NAME?. Nothing needs a spelling for the stop opcode, so the row is gone. Signed-off-by: Tomaž Vajngerl Change-Id: Idbe8ce87d6ad6346106622379ff59908a36eb7f7 Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/10163 Reviewed-by: Miklos Vajna Tested-by: Tomaž Vajngerl (cherry picked from commit 75160522f6a9de4999b824725326475ba8d713f1) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/210313 Reviewed-by: Xisco Fauli Tested-by: Jenkins --- formula/inc/core_resource.hrc | 1 - formula/source/core/api/FormulaCompiler.cxx | 6 +++--- sc/qa/unit/ucalc_formula2.cxx | 20 ++++++++++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/formula/inc/core_resource.hrc b/formula/inc/core_resource.hrc index 43fea5c6fc04..855ce20ecaa1 100644 --- a/formula/inc/core_resource.hrc +++ b/formula/inc/core_resource.hrc @@ -989,7 +989,6 @@ const std::pair RID_STRLIST_FUNCTION_NAMES_ENGLISH_OOXML[] = { "_xlfn.ORG.LIBREOFFICE.RAND.NV" , ocRandomNV }, { "_xlfn.ORG.LIBREOFFICE.RANDBETWEEN.NV" , ocRandbetweenNV }, { "_xlfn.RANDARRAY" , ocRandArray }, - { "#REF!", ocStop }, { "_xludf.", ocUDExternal }, { nullptr, -1 } }; diff --git a/formula/source/core/api/FormulaCompiler.cxx b/formula/source/core/api/FormulaCompiler.cxx index 6b2f81024f3f..c7e8633c65ec 100644 --- a/formula/source/core/api/FormulaCompiler.cxx +++ b/formula/source/core/api/FormulaCompiler.cxx @@ -2891,14 +2891,14 @@ bool FormulaCompiler::AppendTokenOrError(OUStringBuffer& rBuffer, const FormulaT // A call with no name would come out as "#NAME!()", which no reader // accepts, so the error string replaces the whole formula. rBuffer.setLength(0); - rBuffer.append(GetNativeSymbol(ocErrRef)); + rBuffer.append(mxSymbols->getSymbol(ocErrRef)); return false; } if (eOp == ocExternal || eOp == ocPush) { // A function token that lost its name. The error string stands in for // this one token only. - rBuffer.append(GetNativeSymbol(ocErrRef)); + rBuffer.append(mxSymbols->getSymbol(ocErrRef)); rpToken = maArrIterator.Next(); return true; } @@ -2913,7 +2913,7 @@ bool FormulaCompiler::AppendTokenOrError(OUStringBuffer& rBuffer, const FormulaT // The error would come out as "#NAME?$C7", which no reader accepts, // so the error string replaces the whole formula. rBuffer.setLength(0); - rBuffer.append(GetNativeSymbol(ocErrName)); + rBuffer.append(mxSymbols->getSymbol(ocErrName)); return false; } } diff --git a/sc/qa/unit/ucalc_formula2.cxx b/sc/qa/unit/ucalc_formula2.cxx index 7362a2f13f60..e4fd75af0314 100644 --- a/sc/qa/unit/ucalc_formula2.cxx +++ b/sc/qa/unit/ucalc_formula2.cxx @@ -7416,6 +7416,26 @@ CPPUNIT_TEST_FIXTURE(TestFormula2, testAUnionListPartJoinsWhenItBeginsWithARefer formula::FormulaGrammar::GRAM_OOXML); CPPUNIT_ASSERT_EQUAL(300.0, m_pDoc->GetValue(ScAddress(3, 13, 0))); + // An error constant part becomes the result of the union, wherever the blank around the + // separator is. + m_pDoc->SetFormula(ScAddress(3, 14, 0), u"=(A1, #REF!)"_ustr, + formula::FormulaGrammar::GRAM_OOXML); + CPPUNIT_ASSERT_EQUAL(FormulaError::NoRef, m_pDoc->GetErrCode(ScAddress(3, 14, 0))); + m_pDoc->SetFormula(ScAddress(3, 16, 0), u"=(A1 ,#REF!)"_ustr, + formula::FormulaGrammar::GRAM_OOXML); + CPPUNIT_ASSERT_EQUAL(FormulaError::NoRef, m_pDoc->GetErrCode(ScAddress(3, 16, 0))); + + // The same list read the way it arrives from a file: no leading equals sign, and the + // implicit intersections worked out during the parse. + { + ScCompiler aCompiler(*m_pDoc, ScAddress(3, 17, 0), formula::FormulaGrammar::GRAM_OOXML, + true, false); + std::unique_ptr pArray = aCompiler.CompileString(u"(A1 ,#REF!)"_ustr); + aCompiler.CompileTokenArray(); + m_pDoc->SetFormula(ScAddress(3, 17, 0), *pArray); + CPPUNIT_ASSERT_EQUAL(FormulaError::NoRef, m_pDoc->GetErrCode(ScAddress(3, 17, 0))); + } + // An @ part sits on the reference after it, and a bare list has no value anyway. m_pDoc->SetFormula(ScAddress(3, 15, 0), u"=(A1, _xlfn.SINGLE(B1))"_ustr, formula::FormulaGrammar::GRAM_OOXML);