tdf#93727 Syntax Error on # in basic

Change-Id: I400a4061de580a5c91b0e4763cad40ae88a9f738
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162639
Tested-by: Jenkins
Tested-by: Andreas Heinisch <andreas.heinisch@yahoo.de>
Reviewed-by: Andreas Heinisch <andreas.heinisch@yahoo.de>
diff --git a/basic/qa/cppunit/test_compiler_checks.cxx b/basic/qa/cppunit/test_compiler_checks.cxx
index 773719e..2b8e729 100644
--- a/basic/qa/cppunit/test_compiler_checks.cxx
+++ b/basic/qa/cppunit/test_compiler_checks.cxx
@@ -114,4 +114,57 @@ CPPUNIT_TEST_FIXTURE(CppUnit::TestFixture, testTdf149402_vba)
    CPPUNIT_ASSERT(!aMacro.HasError());
}

CPPUNIT_TEST_FIXTURE(CppUnit::TestFixture, testTdf93727_if)
{
    // #If and #End directive
    MacroSnippet aMacro("Sub doUnitTest\n"
                        "  #If 1 = 1 Then\n"
                        "      Const a = 10\n"
                        "  #End If\n"
                        "End Sub\n");
    aMacro.Compile();
    CPPUNIT_ASSERT_MESSAGE("#If directive causes compile error", !aMacro.HasError());
}

CPPUNIT_TEST_FIXTURE(CppUnit::TestFixture, testTdf93727_else)
{
    // #Else directive
    MacroSnippet aMacro("Sub doUnitTest\n"
                        "a = 0\n"
                        "#If 1 = 0 Then\n"
                        "    a = 10\n"
                        "#Else\n"
                        "    a = 20\n"
                        "#End If\n"
                        "End Sub\n");
    aMacro.Compile();
    CPPUNIT_ASSERT_MESSAGE("#Else directive causes compile error", !aMacro.HasError());
}

CPPUNIT_TEST_FIXTURE(CppUnit::TestFixture, testTdf93727_elseif)
{
    // #ElseIf directive
    MacroSnippet aMacro("Sub doUnitTest\n"
                        "a = 0\n"
                        "  #If 1 = 0 Then\n"
                        "      a = 10\n"
                        "  #ElseIf 2 = 2 Then\n"
                        "      a = 20\n"
                        "  #End If\n"
                        "End Sub\n");
    aMacro.Compile();
    CPPUNIT_ASSERT_MESSAGE("#ElseIf directive causes compile error", !aMacro.HasError());
}

CPPUNIT_TEST_FIXTURE(CppUnit::TestFixture, testTdf93727_const)
{
    // #Const directive
    MacroSnippet aMacro("#Const MaxValue = 1000\n"
                        "Sub doUnitTest\n"
                        "   Dim value As Integer\n"
                        "   value = MaxValue\n"
                        "End Sub\n");
    aMacro.Compile();
    CPPUNIT_ASSERT_MESSAGE("#Const directive causes compile error", !aMacro.HasError());
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/basic/source/comp/scanner.cxx b/basic/source/comp/scanner.cxx
index 45b65a2..8209518 100644
--- a/basic/source/comp/scanner.cxx
+++ b/basic/source/comp/scanner.cxx
@@ -29,6 +29,7 @@
#include <rtl/character.hxx>
#include <o3tl/string_view.hxx>
#include <utility>
#include <vector>

SbiScanner::SbiScanner(OUString _aBuf, StarBASIC* p)
    : aBuf(std::move(_aBuf))
@@ -209,6 +210,17 @@ bool SbiScanner::readLine()
    return true;
}

// Function to check if a string is a valid compiler directive
static bool isValidCompilerDirective(std::u16string_view directive) {
    static const std::vector<std::u16string_view> validDirectives = {
        u"if", u"elseif", u"else", u"end", u"const"
    };

    return std::any_of(validDirectives.begin(), validDirectives.end(), [&](const auto& valid) {
        return o3tl::matchIgnoreAsciiCase(directive, valid);
    });
}

bool SbiScanner::NextSym()
{
    // memorize for the EOLN-case
@@ -220,7 +232,6 @@ bool SbiScanner::NextSym()
    eScanType = SbxVARIANT;
    aSym.clear();
    bHash = bSymbol = bNumber = bSpaces = false;
    bool bCompilerDirective = false;

    // read in line?
    if (nLineIdx == -1)
@@ -256,6 +267,8 @@ bool SbiScanner::NextSym()
    if(nCol < aLine.getLength() && aLine[nCol] == '#')
    {
        sal_Int32 nLineTempIdx = nLineIdx;
        std::u16string_view candidate(aLine.subView(nCol + 1));

        do
        {
            nLineTempIdx++;
@@ -266,9 +279,20 @@ bool SbiScanner::NextSym()
        {
            ++nLineIdx;
            ++nCol;
            //ignore compiler directives (# is first non-space character)
            //handle compiler directives (# is first non-space character)
            if (nOldCol2 == 0)
                bCompilerDirective = true;
            {
                if (isValidCompilerDirective(candidate))
                {
                    // Skip the whole line if starts with a hash and is a valid compiler directive
                    nCol = 0;
                    goto eoln;
                }
                else
                {
                    GenError(ERRCODE_BASIC_SYNTAX);
                }
            }
            else
                bHash = true;
        }
@@ -660,10 +684,9 @@ bool SbiScanner::NextSym()

PrevLineCommentLbl:

    if( bPrevLineExtentsComment || (eScanType != SbxSTRING &&
                                    ( bCompilerDirective ||
                                      aSym.startsWith("'") ||
                                      aSym.equalsIgnoreAsciiCase( "REM" ) ) ) )
    if (bPrevLineExtentsComment ||
        (eScanType != SbxSTRING &&
        (aSym.startsWith("'") || aSym.equalsIgnoreAsciiCase("REM") || aSym.startsWith("#"))))
    {
        bPrevLineExtentsComment = false;
        aSym = "REM";