sc/inc/recursionhelper.hxx              |    5 ++++
 sc/source/core/data/column4.cxx         |   10 ++++++++
 sc/source/core/data/formulacell.cxx     |   11 +++++++--
 sc/source/core/tool/recursionhelper.cxx |   38 ++++++++++++++++++++++++++++++++
 4 files changed, 62 insertions(+), 2 deletions(-)

New commits:
commit c54c5d872ad464985ca974a7fcf28bef54530b43
Author:     Dennis Francis <dennis.fran...@collabora.com>
AuthorDate: Fri Nov 1 21:28:41 2019 +0530
Commit:     Xisco Faulí <xiscofa...@libreoffice.org>
CommitDate: Thu Nov 14 11:13:47 2019 +0100

    tdf#124270 : improve formula-group cycle detection
    
    When a cycle of formula-groups is detected, do not conclude
    that there is a circular dependency of cells. Only mark the
    cells with Err:522 when all formula-groups in the cycle have
    cleanly backed off from the dependency evaluation mode.
    
    This commit also fixes places where we overlooked to back-off from
    dependency evaluation mode on detection of a cycle of formula-groups.
    
    Additionally mark formula-groups with self references as
    "part of cycle" by setting mbPartOfCycle.
    
    Unit tests for all these fixes are in a follow-up commit.
    
    Reviewed-on: https://gerrit.libreoffice.org/82074
    Reviewed-by: Dennis Francis <dennis.fran...@collabora.com>
    Tested-by: Dennis Francis <dennis.fran...@collabora.com>
    (cherry picked from commit 0f4ba703038fb8678d4b1e7e6e0fd5e2d3025908)
    
    Change-Id: I57a88bbc88adf177d49768a5d4585b3e53729aea
    Reviewed-on: https://gerrit.libreoffice.org/82563
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/sc/inc/recursionhelper.hxx b/sc/inc/recursionhelper.hxx
index d91ee0cfbe58..60acb23c2b2d 100644
--- a/sc/inc/recursionhelper.hxx
+++ b/sc/inc/recursionhelper.hxx
@@ -51,6 +51,9 @@ class ScRecursionHelper
     ScFormulaRecursionList::iterator    aLastIterationStart;
     ScRecursionInIterationStack         aRecursionInIterationStack;
     ScFGList                            aFGList;
+    // Flag list corresponding to aFGList to indicate whether each 
formula-group
+    // is in a depedency evaluation mode or not.
+    std::vector< bool >                 aInDependencyEvalMode;
     sal_uInt16                              nRecursionCount;
     sal_uInt16                              nIteration;
     // Count of ScFormulaCell::CheckComputeDependencies in current call-stack.
@@ -107,7 +110,9 @@ public:
     /** Detects a simple cycle involving formula-groups and singleton 
formula-cells. */
     bool PushFormulaGroup(ScFormulaCell* pCell);
     void PopFormulaGroup();
+    bool AnyCycleMemberInDependencyEvalMode(ScFormulaCell* pCell);
     bool AnyParentFGInCycle();
+    void SetFormulaGroupDepEvalMode(bool bSet);
 
     void AddTemporaryGroupCell(ScFormulaCell* cell);
     void CleanTemporaryGroupCells();
diff --git a/sc/source/core/data/column4.cxx b/sc/source/core/data/column4.cxx
index d0e54711a5b7..172518888969 100644
--- a/sc/source/core/data/column4.cxx
+++ b/sc/source/core/data/column4.cxx
@@ -1680,7 +1680,17 @@ static bool 
lcl_InterpretSpan(sc::formula_block::const_iterator& rSpanIter, SCRO
                 // Evaluate from second cell in non-grouped style (no point in 
trying group-interpret again).
                 ++itSpanStart;
                 for (SCROW nIdx = nSpanStart+1; nIdx <= nSpanEnd; ++nIdx, 
++itSpanStart)
+                {
                     (*itSpanStart)->Interpret(); // We know for sure that this 
cell is dirty so directly call Interpret().
+                    // Allow early exit like above.
+                    if (mxParentGroup && mxParentGroup->mbPartOfCycle)
+                    {
+                        // Set this cell as dirty as this may be interpreted 
in InterpretTail()
+                        pCellStart->SetDirtyVar();
+                        bAllowThreading = false;
+                        return bAnyDirty;
+                    }
+                }
             }
 
             pCellStart = nullptr; // For next sub span start detection.
diff --git a/sc/source/core/data/formulacell.cxx 
b/sc/source/core/data/formulacell.cxx
index 48cb55fae27a..942a7d1801e2 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -1521,12 +1521,15 @@ bool ScFormulaCell::Interpret(SCROW nStartOffset, SCROW 
nEndOffset)
 
     ScFormulaCell* pTopCell = mxGroup ? mxGroup->mpTopCell : this;
 
-    if (pTopCell->mbSeenInPath && rRecursionHelper.GetDepComputeLevel())
+    if (pTopCell->mbSeenInPath && rRecursionHelper.GetDepComputeLevel() &&
+        rRecursionHelper.AnyCycleMemberInDependencyEvalMode(pTopCell))
     {
         // This call arose from a dependency calculation and we just found a 
cycle.
-        aResult.SetResultError( FormulaError::CircularReference );
         // This will mark all elements in the cycle as parts-of-cycle.
         ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, 
pTopCell);
+        // Reaching here does not necessarily mean a circular reference, so 
don't set Err:522 here yet.
+        // If there is a genuine circular reference, it will be marked so when 
all groups
+        // in the cycle get out of dependency evaluation mode.
         return bGroupInterpreted;
     }
 
@@ -4517,6 +4520,10 @@ struct ScDependantsCalculator
                     return false;
             }
         }
+
+        if (bHasSelfReferences)
+            mxGroup->mbPartOfCycle = true;
+
         return !bHasSelfReferences;
     }
 };
diff --git a/sc/source/core/tool/recursionhelper.cxx 
b/sc/source/core/tool/recursionhelper.cxx
index 1375048759e8..0027efc49506 100644
--- a/sc/source/core/tool/recursionhelper.cxx
+++ b/sc/source/core/tool/recursionhelper.cxx
@@ -132,16 +132,44 @@ bool ScRecursionHelper::PushFormulaGroup(ScFormulaCell* 
pCell)
 
     pCell->SetSeenInPath(true);
     aFGList.push_back(pCell);
+    aInDependencyEvalMode.push_back(false);
     return true;
 }
 
 void ScRecursionHelper::PopFormulaGroup()
 {
+    assert(aFGList.size() == aInDependencyEvalMode.size());
     if (aFGList.empty())
         return;
     ScFormulaCell* pCell = aFGList.back();
     pCell->SetSeenInPath(false);
     aFGList.pop_back();
+    aInDependencyEvalMode.pop_back();
+}
+
+bool ScRecursionHelper::AnyCycleMemberInDependencyEvalMode(ScFormulaCell* 
pCell)
+{
+    assert(pCell);
+
+    if (pCell->GetSeenInPath())
+    {
+        // Found a simple cycle of formula-groups.
+        sal_Int32 nIdx = aFGList.size();
+        assert(nIdx > 0);
+        do
+        {
+            --nIdx;
+            assert(nIdx >= 0);
+            const ScFormulaCellGroupRef& mxGroup = 
aFGList[nIdx]->GetCellGroup();
+            // Found a cycle member FG that is in dependency evaluation mode.
+            if (mxGroup && aInDependencyEvalMode[nIdx])
+                return true;
+        } while (aFGList[nIdx] != pCell);
+
+        return false;
+    }
+
+    return false;
 }
 
 bool ScRecursionHelper::AnyParentFGInCycle()
@@ -157,6 +185,14 @@ bool ScRecursionHelper::AnyParentFGInCycle()
     return false;
 }
 
+void ScRecursionHelper::SetFormulaGroupDepEvalMode(bool bSet)
+{
+    assert(aFGList.size());
+    assert(aFGList.size() == aInDependencyEvalMode.size());
+    assert(aFGList.back()->GetCellGroup());
+    aInDependencyEvalMode.back() = bSet;
+}
+
 void ScRecursionHelper::AddTemporaryGroupCell(ScFormulaCell* cell)
 {
     aTemporaryGroupCells.push_back( cell );
@@ -194,10 +230,12 @@ 
ScFormulaGroupDependencyComputeGuard::ScFormulaGroupDependencyComputeGuard(ScRec
     mrRecHelper(rRecursionHelper)
 {
     mrRecHelper.IncDepComputeLevel();
+    mrRecHelper.SetFormulaGroupDepEvalMode(true);
 }
 
 ScFormulaGroupDependencyComputeGuard::~ScFormulaGroupDependencyComputeGuard()
 {
+    mrRecHelper.SetFormulaGroupDepEvalMode(false);
     mrRecHelper.DecDepComputeLevel();
 }
 
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to