loplugin:xmlimport check for bad conversions to fastparser

add a check for classes which have been partly converted to fastparser,
but not completedly.
This is to help me when I convert stuff.

and it uncovers a bug introduced with
    commit 998308c363dfad03143591aa18256d2669b4da11
    use more FastParser in SvXMLStylesContext

Change-Id: Ib50e7136da10a1a7a346102aa47efef2f543e2ac
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/102669
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
diff --git a/compilerplugins/clang/test/xmlimport.cxx b/compilerplugins/clang/test/xmlimport.cxx
index 965d493..36230ff 100644
--- a/compilerplugins/clang/test/xmlimport.cxx
+++ b/compilerplugins/clang/test/xmlimport.cxx
@@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * This file is part of the LibreOffice project.
 *
@@ -12,6 +12,13 @@
// Cannot include this, makes clang crash
//#include "xmloff/xmlimp.hxx"

#include <com/sun/star/uno/Reference.hxx>

namespace com::sun::star::xml::sax
{
class XAttributeList;
}

class SvXMLImportContext
{
public:
@@ -20,6 +27,10 @@ public:
    virtual void createFastChildContext() {}
    virtual void startFastElement() {}
    virtual void endFastElement() {}

    virtual void StartElement(const css::uno::Reference<css::xml::sax::XAttributeList>&) {}
    virtual void EndElement() {}
    virtual void Characters(const OUString&) {}
};

class Test1 : public SvXMLImportContext
@@ -51,4 +62,27 @@ public:
    virtual void endFastElement() override;
};

class Test5 : public SvXMLImportContext
{
public:
    // expected-error@+1 {{overrides startElement, but looks like a fastparser context class, no constructor that takes slowparser args [loplugin:xmlimport]}}
    virtual void
    StartElement(const css::uno::Reference<css::xml::sax::XAttributeList>& xAttrList) override;
    // expected-error@+1 {{overrides startElement, but looks like a fastparser context class, no constructor that takes slowparser args [loplugin:xmlimport]}}
    virtual void EndElement() override;
    // expected-error@+1 {{overrides startElement, but looks like a fastparser context class, no constructor that takes slowparser args [loplugin:xmlimport]}}
    virtual void Characters(const OUString&) override;
};

// no warning expected
class Test6 : public SvXMLImportContext
{
public:
    Test6(sal_uInt16, const OUString&);
    virtual void
    StartElement(const css::uno::Reference<css::xml::sax::XAttributeList>& xAttrList) override;
    virtual void EndElement() override;
    virtual void Characters(const OUString&) override;
};

/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/compilerplugins/clang/xmlimport.cxx b/compilerplugins/clang/xmlimport.cxx
index bdb41f6..d6b1aa7 100644
--- a/compilerplugins/clang/xmlimport.cxx
+++ b/compilerplugins/clang/xmlimport.cxx
@@ -52,81 +52,141 @@ public:
    bool VisitCXXMethodDecl(const CXXMethodDecl*);
};

static bool containsStartFastElementMethod(const CXXRecordDecl* cxxRecordDecl,
                                           bool& rbFoundImportContext)
static bool containsStartFastElementMethod(const CXXRecordDecl* cxxRecordDecl)
{
    auto dc = loplugin::DeclCheck(cxxRecordDecl);
    if (dc.Class("SvXMLImportContext"))
    {
        rbFoundImportContext = true;
        return false;
    }
    if (dc.Class("XFastContextHandler"))
        return false;
    for (auto it = cxxRecordDecl->method_begin(); it != cxxRecordDecl->method_end(); ++it)
    {
        auto i = *it;
        if (i->getIdentifier() && i->getName() == "startFastElement")
        {
            //            i->dump();
            return true;
        }
    }
    return false;
}

bool XmlImport::VisitCXXMethodDecl(const CXXMethodDecl* methodDecl)
{
    auto beginLoc = compat::getBeginLoc(methodDecl);
    if (!beginLoc.isValid() || ignoreLocation(beginLoc))
        return true;

    if (!methodDecl->getIdentifier())
        return true;

    auto cxxRecordDecl = methodDecl->getParent();
    if (!cxxRecordDecl || !cxxRecordDecl->getIdentifier())
        return true;
    auto className = cxxRecordDecl->getName();
    if (className == "OOXMLFactory") // writerfilter
        return true;
    if (className == "SvXMLLegacyToFastDocHandler" || className == "ImportDocumentHandler"
        || className == "ExportDocumentHandler") // reportdesign
        return true;
    if (className == "XMLEmbeddedObjectExportFilter" || className == "XMLBasicExportFilter"
        || className == "XMLTransformerBase" || className == "SvXMLMetaExport") // xmloff
        return true;
    if (!methodDecl->getIdentifier())
        return true;
    if (!(methodDecl->getName() == "createFastChildContext" || methodDecl->getName() == "characters"
          || methodDecl->getName() == "endFastElement"))
        return true;

    if (loplugin::DeclCheck(cxxRecordDecl).Class("SvXMLImportContext"))
        return true;

    bool foundImportContext = false;
    if (containsStartFastElementMethod(cxxRecordDecl, foundImportContext))
        return true;
    if (methodDecl->getName() == "createFastChildContext" || methodDecl->getName() == "characters"
        || methodDecl->getName() == "endFastElement")
    {
        auto className = cxxRecordDecl->getName();
        if (className == "OOXMLFactory") // writerfilter
            return true;
        if (className == "SvXMLLegacyToFastDocHandler" || className == "ImportDocumentHandler"
            || className == "ExportDocumentHandler") // reportdesign
            return true;
        if (className == "XMLEmbeddedObjectExportFilter" || className == "XMLBasicExportFilter"
            || className == "XMLTransformerBase" || className == "SvXMLMetaExport") // xmloff
            return true;

    bool foundStartFastElement = false;
    CXXBasePaths aPaths;
    cxxRecordDecl->lookupInBases(
        [&](const CXXBaseSpecifier* Specifier, CXXBasePath & /*Path*/) -> bool {
            if (!Specifier->getType().getTypePtr())
                return false;
            const CXXRecordDecl* baseCXXRecordDecl = Specifier->getType()->getAsCXXRecordDecl();
            if (!baseCXXRecordDecl)
                return false;
            if (baseCXXRecordDecl->isInvalidDecl())
                return false;
            foundStartFastElement
                |= containsStartFastElementMethod(baseCXXRecordDecl, foundImportContext);
            return false;
        },
        aPaths);
        if (containsStartFastElementMethod(cxxRecordDecl))
            return true;

    if (foundStartFastElement || !foundImportContext)
        return true;
        bool foundStartFastElement = false;
        bool foundImportContext = false;

    report(DiagnosticsEngine::Warning, "must override startFastElement too",
           compat::getBeginLoc(methodDecl))
        << methodDecl->getSourceRange();
        CXXBasePaths aPaths;
        cxxRecordDecl->lookupInBases(
            [&](const CXXBaseSpecifier* Specifier, CXXBasePath & /*Path*/) -> bool {
                if (!Specifier->getType().getTypePtr())
                    return false;
                const CXXRecordDecl* baseCXXRecordDecl = Specifier->getType()->getAsCXXRecordDecl();
                if (!baseCXXRecordDecl)
                    return false;
                if (baseCXXRecordDecl->isInvalidDecl())
                    return false;
                if (loplugin::DeclCheck(baseCXXRecordDecl).Class("SvXMLImportContext"))
                    foundImportContext |= true;
                else
                    foundStartFastElement |= containsStartFastElementMethod(baseCXXRecordDecl);
                return false;
            },
            aPaths);

        if (foundImportContext && !foundStartFastElement)
            report(DiagnosticsEngine::Warning, "must override startFastElement too",
                   compat::getBeginLoc(methodDecl))
                << methodDecl->getSourceRange();
    }
    else if (methodDecl->getName() == "StartElement" || methodDecl->getName() == "EndElement"
             || methodDecl->getName() == "Characters")
    {
        if (loplugin::DeclCheck(cxxRecordDecl).Class("SchXMLAxisContext"))
            return true;
        if (loplugin::DeclCheck(cxxRecordDecl).Class("SchXMLChartContext"))
            return true;
        if (loplugin::DeclCheck(cxxRecordDecl).Class("SchXMLParagraphContext"))
            return true;
        if (loplugin::DeclCheck(cxxRecordDecl).Class("SchXMLLegendContext"))
            return true;
        if (loplugin::DeclCheck(cxxRecordDecl).Class("SchXMLPropertyMappingContext"))
            return true;

        bool foundImportContext = false;
        CXXBasePaths aPaths;
        cxxRecordDecl->lookupInBases(
            [&](const CXXBaseSpecifier* Specifier, CXXBasePath & /*Path*/) -> bool {
                if (!Specifier->getType().getTypePtr())
                    return false;
                const CXXRecordDecl* baseCXXRecordDecl = Specifier->getType()->getAsCXXRecordDecl();
                if (!baseCXXRecordDecl)
                    return false;
                if (baseCXXRecordDecl->isInvalidDecl())
                    return false;
                if (loplugin::DeclCheck(baseCXXRecordDecl).Class("SvXMLImportContext"))
                    foundImportContext |= true;
                return false;
            },
            aPaths);

        if (!foundImportContext)
            return true;

        bool foundConstructor = false;
        for (auto it = cxxRecordDecl->ctor_begin(); it != cxxRecordDecl->ctor_end(); ++it)
        {
            const CXXConstructorDecl* ctor = *it;
            bool foundInt16 = false;
            for (auto paramIt = ctor->param_begin(); paramIt != ctor->param_end(); ++paramIt)
            {
                const ParmVarDecl* pvd = *paramIt;
                auto tc = loplugin::TypeCheck(pvd->getType());
                if (tc.Typedef("sal_uInt16"))
                    foundInt16 = true;
                else if (tc.LvalueReference().Const().Class("OUString") && foundInt16)
                    foundConstructor = true;
                else
                    foundInt16 = false;
                if (tc.LvalueReference().Const().Class("OUString")
                    && pvd->getName() == "rLocalName")
                    foundConstructor = true;
            }
        }

        if (!foundConstructor)
            report(DiagnosticsEngine::Warning,
                   "overrides startElement, but looks like a fastparser context class, no "
                   "constructor that takes slowparser args",
                   compat::getBeginLoc(methodDecl))
                << methodDecl->getSourceRange();
    }

    return true;
}

diff --git a/reportdesign/source/filter/xml/xmlfilter.cxx b/reportdesign/source/filter/xml/xmlfilter.cxx
index e5d84148..5c65664 100644
--- a/reportdesign/source/filter/xml/xmlfilter.cxx
+++ b/reportdesign/source/filter/xml/xmlfilter.cxx
@@ -93,7 +93,7 @@ public:

    RptMLMasterStylesContext_Impl(const RptMLMasterStylesContext_Impl&) = delete;
    RptMLMasterStylesContext_Impl& operator=(const RptMLMasterStylesContext_Impl&) = delete;
    virtual void EndElement() override;
    virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
};

}
@@ -103,7 +103,7 @@ RptMLMasterStylesContext_Impl::RptMLMasterStylesContext_Impl( ORptFilter& rImpor
{
}

void RptMLMasterStylesContext_Impl::EndElement()
void RptMLMasterStylesContext_Impl::endFastElement(sal_Int32 )
{
    FinishStyles( true );
    GetImport().FinishStyles();
diff --git a/sw/source/filter/xml/xmlfmt.cxx b/sw/source/filter/xml/xmlfmt.cxx
index e1aebad..446b5fd 100644
--- a/sw/source/filter/xml/xmlfmt.cxx
+++ b/sw/source/filter/xml/xmlfmt.cxx
@@ -738,14 +738,13 @@ protected:

public:


    SwXMLStylesContext_Impl(
            SwXMLImport& rImport,
            bool bAuto );

    virtual bool InsertStyleFamily( XmlStyleFamily nFamily ) const override;

    virtual void EndElement() override;
    virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
};

}
@@ -929,7 +928,7 @@ OUString SwXMLStylesContext_Impl::GetServiceName( XmlStyleFamily nFamily ) const
    return SvXMLStylesContext::GetServiceName( nFamily );
}

void SwXMLStylesContext_Impl::EndElement()
void SwXMLStylesContext_Impl::endFastElement(sal_Int32 )
{
    GetSwImport().InsertStyles( IsAutomaticStyle() );
}