tdf#144353 - Handling of missing optional parameters

Don't assign a missing optional variable to a property and don't allow
the computation/comparision including missing optional attributes.

In the previous cases a ERRCODE_BASIC_NOT_OPTIONAL is raised.

Change-Id: Iab391286fcace16c271ae511304075e2a0c5c651
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/121794
Tested-by: Jenkins
Reviewed-by: Andreas Heinisch <andreas.heinisch@yahoo.de>
diff --git a/basic/qa/basic_coverage/test_optional_paramters_basic.bas b/basic/qa/basic_coverage/test_optional_paramters_basic.bas
index ed2cdc7..a684fc2 100644
--- a/basic/qa/basic_coverage/test_optional_paramters_basic.bas
+++ b/basic/qa/basic_coverage/test_optional_paramters_basic.bas
@@ -1,5 +1,9 @@
Option Explicit

Type testObject
    testInt As Integer
End Type

Function doUnitTest() As String
    TestUtil.TestInit
    verify_testOptionalsBasic
@@ -97,6 +101,30 @@ Sub verify_testOptionalsBasic()
    TestUtil.AssertEqualApprox(TestOptArrayByRefByVal(, aB), 691.2, 1E-5, "TestOptArrayByRefByVal(, B)")
    TestUtil.AssertEqualApprox(TestOptArrayByRefByVal(aA, aB), 1270.2, 1E-5, "TestOptArrayByRefByVal(A, B)")

    ' tdf#144353 - error handling of missing optional parameters (arithmetic operator)
    ' Without the fix in place, this test would have failed with:
    ' - Expected: 449 (ERRCODE_BASIC_NOT_OPTIONAL - Argument not optional)
    ' - Actual  : 549 (Actual value of the variable)
    TestUtil.AssertEqual(TestArithmeticOperator, 449, "TestArithmeticOperator")

    ' tdf#144353 - error handling of missing optional parameters (unary operator)
    ' Without the fix in place, this test would have failed with:
    ' - Expected: 449 (ERRCODE_BASIC_NOT_OPTIONAL - Argument not optional)
    ' - Actual  : 100 (Actual value of the variable)
    TestUtil.AssertEqual(TestUnaryOperator, 449, "TestUnaryOperator")

    ' tdf#144353 - error handling of missing optional parameters (assigning to a collection)
    ' Without the fix in place, this test would have failed with:
    ' - Expected: 449 (ERRCODE_BASIC_NOT_OPTIONAL - Argument not optional)
    ' - Actual  : 549 (Actual value of the variable)
    TestUtil.AssertEqual(TestCollection, 449, "TestCollection")

    ' tdf#144353 - error handling of missing optional parameters
    ' Without the fix in place, this test would have failed with:
    ' - Expected: 449 (ERRCODE_BASIC_NOT_OPTIONAL - Argument not optional)
    ' - Actual  : 448 (Actual value of the variable)
    TestUtil.AssertEqual(TestObjectError, 449, "TestObjectError")

    Exit Sub
errorHandler:
    TestUtil.ReportErrorHandler("verify_testOptionalsBasic", Err, Error$, Erl)
@@ -166,6 +194,41 @@ Function OptStringConcat(is_missingA As Boolean, A, is_missingB As Boolean, B)
    If Not is_missingB Then OptStringConcat = OptStringConcat & B
End Function

Function TestArithmeticOperator(Optional optInt)
On Error GoTo errorHandler
    optInt = optInt + 100
    TestArithmeticOperator = optInt
errorHandler:
    TestArithmeticOperator = Err()
End Function

Function TestUnaryOperator(Optional optInt)
On Error GoTo errorHandler
    If (Not optInt) Then optInt = 100
    TestUnaryOperator = optInt
errorHandler:
    TestUnaryOperator = Err()
End Function

Function TestCollection(Optional optInt)
On Error GoTo errorHandler
    Dim cA As New Collection
    cA.Add(optInt)
    TestCollection = cA.Item(1) + 100
errorHandler:
    TestCollection = Err()
End Function

Function TestObjectError(Optional optInt)
On Error GoTo errorHandler
    Dim aTestObject As Variant
    aTestObject = CreateObject("testObject")
    aTestObject.testInt = optInt
    TestObjectError = optInt
errorHandler:
    TestObjectError = Err()
End Function

Function CollectionSum(C)
    Dim idx As Integer
    CollectionSum = 0
diff --git a/basic/qa/basic_coverage/test_optional_paramters_compatible.bas b/basic/qa/basic_coverage/test_optional_paramters_compatible.bas
index 00aada0..56b3142 100644
--- a/basic/qa/basic_coverage/test_optional_paramters_compatible.bas
+++ b/basic/qa/basic_coverage/test_optional_paramters_compatible.bas
@@ -1,6 +1,10 @@
Option Compatible
Option Explicit

Type testObject
    testInt As Integer
End Type

Function doUnitTest() As String
    TestUtil.TestInit
    verify_testOptionalsCompatible
@@ -99,6 +103,30 @@ Sub verify_testOptionalsCompatible()
    TestUtil.AssertEqualApprox(TestOptArrayByRefByVal(, aB), 691.2, 1E-5, "TestOptArrayByRefByVal(, B)")
    TestUtil.AssertEqualApprox(TestOptArrayByRefByVal(aA, aB), 1270.2, 1E-5, "TestOptArrayByRefByVal(A, B)")

    ' tdf#144353 - error handling of missing optional parameters (arithmetic operator)
    ' Without the fix in place, this test would have failed with:
    ' - Expected: 449 (ERRCODE_BASIC_NOT_OPTIONAL - Argument not optional)
    ' - Actual  : 549 (Actual value of the variable)
    TestUtil.AssertEqual(TestArithmeticOperator, 449, "TestArithmeticOperator")

    ' tdf#144353 - error handling of missing optional parameters (unary operator)
    ' Without the fix in place, this test would have failed with:
    ' - Expected: 449 (ERRCODE_BASIC_NOT_OPTIONAL - Argument not optional)
    ' - Actual  : 100 (Actual value of the variable)
    TestUtil.AssertEqual(TestUnaryOperator, 449, "TestUnaryOperator")

    ' tdf#144353 - error handling of missing optional parameters (assigning to a collection)
    ' Without the fix in place, this test would have failed with:
    ' - Expected: 449 (ERRCODE_BASIC_NOT_OPTIONAL - Argument not optional)
    ' - Actual  : 549 (Actual value of the variable)
    TestUtil.AssertEqual(TestCollection, 449, "TestCollection")

    ' tdf#144353 - error handling of missing optional parameters (assigning to an object)
    ' Without the fix in place, this test would have failed with:
    ' - Expected: 449 (ERRCODE_BASIC_NOT_OPTIONAL - Argument not optional)
    ' - Actual  : 448 (Actual value of the variable)
    TestUtil.AssertEqual(TestObjectError, 449, "TestObjectError")

    Exit Sub
errorHandler:
    TestUtil.ReportErrorHandler("verify_testOptionalsCompatible", Err, Error$, Erl)
@@ -168,6 +196,41 @@ Function OptStringConcat(is_missingA As Boolean, A, is_missingB As Boolean, B)
    If Not is_missingB Then OptStringConcat = OptStringConcat & B
End Function

Function TestArithmeticOperator(Optional optInt)
On Error GoTo errorHandler
    optInt = optInt + 100
    TestArithmeticOperator = optInt
errorHandler:
    TestArithmeticOperator = Err()
End Function

Function TestUnaryOperator(Optional optInt)
On Error GoTo errorHandler
    If (Not optInt) Then optInt = 100
    TestUnaryOperator = optInt
errorHandler:
    TestUnaryOperator = Err()
End Function

Function TestCollection(Optional optInt)
On Error GoTo errorHandler
    Dim cA As New Collection
    cA.Add(optInt)
    TestCollection = cA.Item(1) + 100
errorHandler:
    TestCollection = Err()
End Function

Function TestObjectError(Optional optInt)
On Error GoTo errorHandler
    Dim aTestObject As Variant
    aTestObject = CreateObject("testObject")
    aTestObject.testInt = optInt
    TestObjectError = optInt
errorHandler:
    TestObjectError = Err()
End Function

Function CollectionSum(C)
    Dim idx As Integer
    CollectionSum = 0
diff --git a/basic/source/runtime/runtime.cxx b/basic/source/runtime/runtime.cxx
index 6ab4972..cbe62a6 100644
--- a/basic/source/runtime/runtime.cxx
+++ b/basic/source/runtime/runtime.cxx
@@ -1309,6 +1309,14 @@ void SbiRuntime::StepArith( SbxOperator eOp )
    TOSMakeTemp();
    SbxVariable* p2 = GetTOS();

    // tdf#144353 - do not compute any operation with a missing optional variable
    if ((p1->GetType() == SbxERROR && IsMissing(p1.get(), 1))
        || (p2->GetType() == SbxERROR && IsMissing(p2, 1)))
    {
        Error(ERRCODE_BASIC_NOT_OPTIONAL);
        return;
    }

    p2->ResetFlag( SbxFlagBits::Fixed );
    p2->Compute( eOp, *p1 );

@@ -1319,6 +1327,12 @@ void SbiRuntime::StepUnary( SbxOperator eOp )
{
    TOSMakeTemp();
    SbxVariable* p = GetTOS();
    // tdf#144353 - do not compute any operation with a missing optional variable
    if (p->GetType() == SbxERROR && IsMissing(p, 1))
    {
        Error(ERRCODE_BASIC_NOT_OPTIONAL);
        return;
    }
    p->Compute( eOp, *p );
}

@@ -1327,6 +1341,14 @@ void SbiRuntime::StepCompare( SbxOperator eOp )
    SbxVariableRef p1 = PopVar();
    SbxVariableRef p2 = PopVar();

    // tdf#144353 - do not compare a missing optional variable
    if ((p1->GetType() == SbxERROR && SbiRuntime::IsMissing(p1.get(), 1))
        || (p2->GetType() == SbxERROR && SbiRuntime::IsMissing(p2.get(), 1)))
    {
        SbxBase::SetError(ERRCODE_BASIC_NOT_OPTIONAL);
        return;
    }

    // Make sure objects with default params have
    // values ( and type ) set as appropriate
    SbxDataType p1Type = p1->GetType();
@@ -1607,6 +1629,13 @@ static bool checkUnoStructCopy( bool bVBA, SbxVariableRef const & refVal, SbxVar
    SbxDataType eVarType = refVar->GetType();
    SbxDataType eValType = refVal->GetType();

    // tdf#144353 - do not assign a missing optional variable to a property
    if (refVal->GetType() == SbxERROR && SbiRuntime::IsMissing(refVal.get(), 1))
    {
        SbxBase::SetError(ERRCODE_BASIC_NOT_OPTIONAL);
        return true;
    }

    if ( ( bVBA && ( eVarType == SbxEMPTY ) ) || !refVar->CanWrite() )
        return false;

diff --git a/basic/source/sbx/sbxvar.cxx b/basic/source/sbx/sbxvar.cxx
index 5900c68..b8be36e 100644
--- a/basic/source/sbx/sbxvar.cxx
+++ b/basic/source/sbx/sbxvar.cxx
@@ -280,6 +280,9 @@ SbxVariable& SbxVariable::operator=( const SbxVariable& r )
    if (this != &r)
    {
        SbxValue::operator=( r );
        // tdf#144353 - copy information about a missing parameter. See SbiRuntime::SetIsMissing.
        if (r.pInfo && !dynamic_cast<const SbxMethod*>(&r))
            pInfo = r.pInfo;
        m_aDeclareClassName = r.m_aDeclareClassName;
        m_xComListener = r.m_xComListener;
        m_pComListenerParentBasic = r.m_pComListenerParentBasic;