Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/compilerplugins/clang/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 37 kB image not shown  

Quelle  refcounting.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */


#include <string>
#include <iostream>

#include "check.hxx"
#include "plugin.hxx"
#include "config_clang.h"
#include "clang/AST/CXXInheritance.h"

/**

If you have:

    class Foo : public css::foo::XBaa {
    };

Then XBaa has acquire and release methods inherited from XInterface.
These are hard lifecycle controls.

If you see another class:

    class Baz {
        Foo aFooMember;
    };

this is a bug =) since aFooMember assumes heap allocated lifecycle and
not delete on last 'release'.

*/


namespace {

class RefCounting:
    public loplugin::FilteringPlugin<RefCounting>
{
public:
    explicit RefCounting(loplugin::InstantiationData const & data): FilteringPlugin(data)
    {}

    virtual bool preRun() override { return true; }

    virtual void run() override
    {
        if (preRun())
            TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
    }

    bool VisitFieldDecl(const FieldDecl *);
    bool VisitVarDecl(const VarDecl *);
    bool VisitFunctionDecl(const FunctionDecl *);
    bool VisitTypeLoc(clang::TypeLoc typeLoc);
    bool VisitCXXDeleteExpr(const CXXDeleteExpr *);
    bool VisitBinaryOperator(const BinaryOperator *);
    bool VisitReturnStmt(const ReturnStmt *);

    // Creation of temporaries with one argument are represented by
    // CXXFunctionalCastExpr, while any other number of arguments are
    // represented by CXXTemporaryObjectExpr:
    bool VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr const * expr)
    { return visitTemporaryObjectExpr(expr); }
    bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr)
    { return visitTemporaryObjectExpr(expr); }

private:
    void checkUnoReference(QualType qt, const Decl* decl,
                           const RecordDecl* parent, const std::string& rDeclName);

    bool visitTemporaryObjectExpr(Expr const * expr);
    bool isCastingReference(const Expr* expr);
    bool isCallingGetOnWeakRef(const Expr* expr);
};

bool containsXInterfaceSubclass(const clang::Type* pType0);

bool containsXInterfaceSubclass(const QualType& qType) {
    return containsXInterfaceSubclass(qType.getTypePtr());
}

bool containsXInterfaceSubclass(const clang::Type* pType0) {
    if (!pType0)
        return false;
    const clang::Type* pType = pType0->getUnqualifiedDesugaredType();
    if (!pType)
        return false;
    const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl();
    if (pRecordDecl) {
        pRecordDecl = pRecordDecl->getCanonicalDecl();
        // these classes override acquire/release and forwards to its parent
        if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("ListenerMultiplexerBase").GlobalNamespace()); })) { // module UnoTools
            return false;
        }
        if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("GridEventForwarder").Namespace("toolkit").GlobalNamespace()); })) { // module toolkit
            return false;
        }
        if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OWeakSubObject").GlobalNamespace()); })) { // module svx
            return false;
        }
        if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OSbaWeakSubObject").Namespace("dbaui").GlobalNamespace()); })) { // module dbaccess
            return false;
        }
        // FIXME This class has private operator new, and I cannot figure out how it can be dynamically instantiated
        if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("XPropertyList").GlobalNamespace()); })) { // module svx
            return false;
        }
        // tdf#114596
        if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OBookmarkContainer").Namespace("dbaccess").GlobalNamespace()); })) { // module dbaccess
            return false;
        }

        // tdf#114596
        if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OCollection").Namespace("dbaccess").GlobalNamespace()); })) { // module dbaccess
            return false;
        }
    }
    if (pRecordDecl) {
        const ClassTemplateSpecializationDecl* pTemplate = dyn_cast<ClassTemplateSpecializationDecl>(pRecordDecl);
        if (pTemplate) {
            // Probably good templates:
            loplugin::DeclCheck dc(pTemplate);
            if ((dc.Struct("FindUnoInstanceHint").AnonymousNamespace()
                 .GlobalNamespace())
                || (dc.Class("OMultiInstanceAutoRegistration").Namespace("abp")
                    .GlobalNamespace())
                || (dc.Class("Reference").Namespace("uno").Namespace("star")
                    .Namespace("sun").Namespace("com").GlobalNamespace())
                || (dc.Class("WeakReference").Namespace("uno").Namespace("star")
                    .Namespace("sun").Namespace("com").GlobalNamespace())
                || (dc.Class("Sequence").Namespace("uno").Namespace("star")
                    .Namespace("sun").Namespace("com").GlobalNamespace())
                || (dc.Class("OAutoRegistration").Namespace("dba")
                    .GlobalNamespace())
                || (dc.Class("OMultiInstanceAutoRegistration").Namespace("dbp")
                    .GlobalNamespace())
                || (dc.Class("OMultiInstanceAutoRegistration")
                    .Namespace("dbaui").GlobalNamespace())
                || (dc.Class("OMultiInstanceAutoRegistration")
                    .Namespace("dbaxml").GlobalNamespace())
                || (dc.Struct("ReferenceEqual").Namespace("io_acceptor")
                    .GlobalNamespace())
                || (dc.Struct("ReferenceHash").Namespace("io_acceptor")
                    .GlobalNamespace())
                || (dc.Class("OAutoRegistration").Namespace("comphelper")
                    .GlobalNamespace())
                || dc.Class("WeakBag").Namespace("comphelper").GlobalNamespace()
                || (dc.Struct("class_").Namespace("service_decl")
                    .Namespace("comphelper").GlobalNamespace())
                || (dc.Struct("vba_service_class_").Namespace("service_decl")
                    .Namespace("comphelper").GlobalNamespace())
                || (dc.Struct("inheritingClass_").Namespace("service_decl")
                    .Namespace("comphelper").GlobalNamespace())
                || (dc.Class("OAutoRegistration").Namespace("module")
                    .Namespace("comphelper").GlobalNamespace())
                || (dc.Class("mem_fun1_t").Namespace("comphelper")
                    .GlobalNamespace())
                || (dc.Class("OSimpleListenerContainer").Namespace("comphelper")
                    .GlobalNamespace())
                || (dc.Class("OAutoRegistration").Namespace("dbmm")
                    .GlobalNamespace())
                || (dc.Class("OAutoRegistration").Namespace("pcr")
                    .GlobalNamespace())
                || (dc.Class("ComponentMethodGuard").Namespace("logging")
                    .GlobalNamespace())
                || (dc.Class("OAutoRegistration").Namespace("logging")
                    .GlobalNamespace())
                || dc.Class("Reference").Namespace("rtl").GlobalNamespace()
                || (dc.Class("OAutoRegistration").Namespace("sdbtools")
                    .GlobalNamespace())
                || (dc.Struct("ReferenceEqual").Namespace("stoc_connector")
                    .GlobalNamespace())
                || (dc.Struct("ReferenceHash").Namespace("stoc_connector")
                    .GlobalNamespace())
                || dc.Class("mem_fun_t").StdNamespace()
                || dc.Class("mem_fun1_t").StdNamespace()
                || dc.Class("SwIterator").GlobalNamespace()
                || (dc.Class("SharedUNOComponent").Namespace("utl")
                    .GlobalNamespace())
                || (dc.Class("OAutoRegistration").Namespace("utl")
                    .GlobalNamespace())
                || (dc.Class("DeleteUnoReferenceOnDeinit").Namespace("vcl")
                    .GlobalNamespace())
                || (dc.Struct("OInterfaceCompare").Namespace("xmloff")
                    .GlobalNamespace()))
            {
                return false;
            }
        }
    }
    if (pType->isPointerType()) {
        // ignore
        return false;
    } else if (pType->isArrayType()) {
        const clang::ArrayType* pArrayType = dyn_cast<clang::ArrayType>(pType);
        QualType elementType = pArrayType->getElementType();
        return containsXInterfaceSubclass(elementType);
    } else {
        return loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("XInterface").Namespace("uno").Namespace("star").Namespace("sun").Namespace("com").GlobalNamespace()); });
    }
}

bool containsOWeakObjectSubclass(const clang::Type* pType0);

bool containsOWeakObjectSubclass(const QualType& qType) {
    return containsOWeakObjectSubclass(qType.getTypePtr());
}

bool containsOWeakObjectSubclass(const clang::Type* pType0) {
    if (!pType0)
        return false;
    if (pType0->isDependentType()) {
         return false;
    }
    const clang::Type* pType = pType0->getUnqualifiedDesugaredType();
    if (!pType)
        return false;
    const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl();
    if (pRecordDecl) {
        // because dbaccess just has to be special...
        loplugin::DeclCheck dc(pRecordDecl);
        if (dc.Class("DocumentEvents").Namespace("dbaccess")
                .GlobalNamespace() ||
            dc.Class("OBookmarkContainer").Namespace("dbaccess")
                .GlobalNamespace())
            return false;
        // TODO not sure about these ones, just avoiding dbaccess in general for now
        if (dc.Class("SbaXPropertiesChangeMultiplexer").Namespace("dbaui").GlobalNamespace() ||
            dc.Class("SbaXSubmitMultiplexer").Namespace("dbaui").GlobalNamespace() ||
            dc.Class("SbaXResetMultiplexer").Namespace("dbaui").GlobalNamespace() ||
            dc.Class("SbaXPropertyChangeMultiplexer").Namespace("dbaui").GlobalNamespace() ||
            dc.Class("SbaXSQLErrorMultiplexer").Namespace("dbaui").GlobalNamespace() ||
            dc.Class("SbaXParameterMultiplexer").Namespace("dbaui").GlobalNamespace() ||
            dc.Class("SbaXRowSetApproveMultiplexer").Namespace("dbaui").GlobalNamespace() ||
            dc.Class("SbaXRowSetMultiplexer").Namespace("dbaui").GlobalNamespace() ||
            dc.Class("SbaXLoadMultiplexer").Namespace("dbaui").GlobalNamespace() ||
            dc.Class("SbaXVetoableChangeMultiplexer").Namespace("dbaui").GlobalNamespace())
            return false;
        // slideshow playing games here
        if (dc.Class("SlideView").AnonymousNamespace().Namespace("internal").Namespace("slideshow").GlobalNamespace())
            return false;
        // svx playing acquire/release games here in OWeakSubObject
        if (dc.Class("FmXUpdateMultiplexer").GlobalNamespace() ||
            dc.Class("FmXContainerMultiplexer").GlobalNamespace() ||
            dc.Class("FmXSelectionMultiplexer").GlobalNamespace() ||
            dc.Class("FmXGridControlMultiplexer").GlobalNamespace() ||
            dc.Class("FmXModifyMultiplexer").GlobalNamespace())
            return false;
    }
    if (pType->isPointerType()) {
        // ignore
        return false;
    } else if (pType->isArrayType()) {
        const clang::ArrayType* pArrayType = dyn_cast<clang::ArrayType>(pType);
        QualType elementType = pArrayType->getElementType();
        return containsOWeakObjectSubclass(elementType);
    } else {
        return loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OWeakObject").Namespace("cppu").GlobalNamespace()); });
    }
}

bool containsSvRefBaseSubclass(const clang::Type* pType0) {
    if (!pType0)
        return false;
    const clang::Type* pType = pType0->getUnqualifiedDesugaredType();
    if (!pType)
        return false;
    const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl();
    if (pRecordDecl) {
        pRecordDecl = pRecordDecl->getCanonicalDecl();
    }
    if (pRecordDecl) {
        const ClassTemplateSpecializationDecl* pTemplate = dyn_cast<ClassTemplateSpecializationDecl>(pRecordDecl);
        if (pTemplate) {
            if (loplugin::DeclCheck(pTemplate).Class("SvRef")
                .Namespace("tools").GlobalNamespace())
            {
                return false;
            }
            for(unsigned i=0; i<pTemplate->getTemplateArgs().size(); ++i) {
                const TemplateArgument& rArg = pTemplate->getTemplateArgs()[i];
                if (rArg.getKind() == TemplateArgument::ArgKind::Type &&
                    containsSvRefBaseSubclass(rArg.getAsType().getTypePtr()))
                {
                    return true;
                }
            }
        }
    }
    if (pType->isPointerType()) {
        // ignore
        return false;
    } else if (pType->isArrayType()) {
        const clang::ArrayType* pArrayType = dyn_cast<clang::ArrayType>(pType);
        QualType elementType = pArrayType->getElementType();
        return containsSvRefBaseSubclass(elementType.getTypePtr());
    } else {
        return loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("SvRefBase").Namespace("tools").GlobalNamespace()); });
    }
}

bool containsSalhelperReferenceObjectSubclass(const clang::Type* pType0) {
    if (!pType0)
        return false;
    const clang::Type* pType = pType0->getUnqualifiedDesugaredType();
    if (!pType)
        return false;
    const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl();
    if (pRecordDecl) {
        pRecordDecl = pRecordDecl->getCanonicalDecl();
    }
    if (pRecordDecl) {
        // for performance reasons we sometimes allocate temporaries on the stack
        if (loplugin::DeclCheck(pRecordDecl).Struct("ScSheetLimits").GlobalNamespace())
            return false;

        // the calc excel filter likes storing lots of classes either by reference or by value
        if (loplugin::isDerivedFrom(pRecordDecl,
                [](Decl const * decl) -> bool
                {
                    return
                        bool(loplugin::DeclCheck(decl).Class("XclExpRecordBase").GlobalNamespace())
                        || bool(loplugin::DeclCheck(decl).Class("XclImpChLineFormat").GlobalNamespace());
                }))
            return false;

        const ClassTemplateSpecializationDecl* pTemplate = dyn_cast<ClassTemplateSpecializationDecl>(pRecordDecl);
        if (pTemplate) {
            auto const dc = loplugin::DeclCheck(pTemplate);
            if (dc.Class("Reference").Namespace("rtl").GlobalNamespace()
                || (dc.Class("OStoreHandle").AnonymousNamespace().Namespace("store")
                    .GlobalNamespace())
                || (dc.Class("DeleteRtlReferenceOnDeinit").Namespace("tools")
                    .GlobalNamespace()))
            {
                return false;
            }
            for(unsigned i=0; i<pTemplate->getTemplateArgs().size(); ++i) {
                const TemplateArgument& rArg = pTemplate->getTemplateArgs()[i];
                if (rArg.getKind() == TemplateArgument::ArgKind::Type &&
                    containsSalhelperReferenceObjectSubclass(rArg.getAsType().getTypePtr()))
                {
                    return true;
                }
            }
        }
    }
    if (pType->isPointerType()) {
        // ignore
        return false;
    } else if (pType->isArrayType()) {
        const clang::ArrayType* pArrayType = dyn_cast<clang::ArrayType>(pType);
        QualType elementType = pArrayType->getElementType();
        return containsSalhelperReferenceObjectSubclass(elementType.getTypePtr());
    } else {
        return loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("SimpleReferenceObject").Namespace("salhelper").GlobalNamespace()); });
    }
}

static bool containsStaticTypeMethod(const CXXRecordDecl* x)
{
    for (auto it = x->method_begin(); it != x->method_end(); ++it) {
        auto i = *it;
        if ( !i->isStatic() )
            continue;
        auto ident = i->getIdentifier();
        if ( ident && ident->isStr("static_type") ) {
            return true;
        }
    }
    return false;
}

void RefCounting::checkUnoReference(QualType qt, const Decl* decl, const RecordDecl* parent, const std::string& rDeclName)
{
    if (!loplugin::TypeCheck(qt).Class("Reference").Namespace("uno").Namespace("star").Namespace("sun").Namespace("com").GlobalNamespace())
        return;
    const CXXRecordDecl* pRecordDecl = qt->getAsCXXRecordDecl();
    const ClassTemplateSpecializationDecl* pTemplate = dyn_cast<ClassTemplateSpecializationDecl>(pRecordDecl);
    const TemplateArgument& rArg = pTemplate->getTemplateArgs()[0];
    const CXXRecordDecl* templateParam = rArg.getAsType()->getAsCXXRecordDecl()->getDefinition();
    if (!templateParam)
        return;
    // SwXText is a special case. It is a mixin class that does not inherit from OWeakObject, so
    // we cannot use rtl::Reference.
    if (loplugin::DeclCheck(templateParam).Class("SwXText"))
        return;
    if (containsStaticTypeMethod(templateParam))
        return;
    report(
        DiagnosticsEngine::Warning,
        ("uno::Reference %0 with template parameter that does not"
         " contain ::static_type() %1%select{|, parent is %3,}2 should"
         " probably be using rtl::Reference instead"),
        decl->getLocation())
      << rDeclName << qt << (parent != nullptr)
      << (parent != nullptr
          ? parent->getQualifiedNameAsString() : std::string())
      << decl->getSourceRange();
}

bool RefCounting::visitTemporaryObjectExpr(Expr const * expr) {
    if (ignoreLocation(expr)) {
        return true;
    }
    auto t = expr->getType();
    if (containsSvRefBaseSubclass(t.getTypePtr())) {
        report(
            DiagnosticsEngine::Warning,
            ("Temporary object of SvRefBase subclass %0 being directly stack"
             " managed, should be managed via tools::SvRef"),
            expr->getBeginLoc())
            << t.getUnqualifiedType() << expr->getSourceRange();
    } else if (containsSalhelperReferenceObjectSubclass(t.getTypePtr())) {
        report(
            DiagnosticsEngine::Warning,
            ("Temporary object of salhelper::SimpleReferenceObject subclass %0"
             " being directly stack managed, should be managed via"
             " rtl::Reference"),
            expr->getBeginLoc())
            << t.getUnqualifiedType() << expr->getSourceRange();
    } else if (containsXInterfaceSubclass(t)) {
        report(
            DiagnosticsEngine::Warning,
            ("Temporary object of css::uno::XInterface subclass %0 being"
             " directly stack managed, should be managed via"
             " css::uno::Reference"),
            expr->getBeginLoc())
            << t.getUnqualifiedType() << expr->getSourceRange();
    } else if (containsOWeakObjectSubclass(t)) {
        report(
            DiagnosticsEngine::Warning,
            ("Temporary object of cppu::OWeakObject subclass %0 being"
             " directly stack managed, should be managed via"
             " css::uno::Reference"),
            expr->getBeginLoc())
            << t.getUnqualifiedType() << expr->getSourceRange();
    }
    return true;
}

// check for dodgy code managing ref-counted stuff with shared_ptr or unique_ptr or similar stuff
bool RefCounting::VisitTypeLoc(clang::TypeLoc typeLoc)
{
    QualType firstTemplateParamType;
    if (auto recordType = typeLoc.getType()->getUnqualifiedDesugaredType()->getAs<RecordType>()) {
        auto const tc = loplugin::TypeCheck(recordType);
        if (tc.ClassOrStruct("unique_ptr").StdNamespace()
            || tc.ClassOrStruct("weak_ptr").StdNamespace()
            || tc.ClassOrStruct("shared_ptr").StdNamespace()
            || tc.ClassOrStruct("intrusive_ptr").Namespace("boost").GlobalNamespace())
        {
            auto templateDecl = dyn_cast<ClassTemplateSpecializationDecl>(recordType->getDecl());
            if (templateDecl && templateDecl->getTemplateArgs().size() > 0)
                firstTemplateParamType = templateDecl->getTemplateArgs()[0].getAsType();
        }
    }
    if (firstTemplateParamType.isNull())
        return true;
    if (containsSvRefBaseSubclass(firstTemplateParamType.getTypePtr()))
    {
        report(
            DiagnosticsEngine::Warning,
            "SvRefBase subclass %0 being managed via smart pointer, should be managed via tools::SvRef",
            typeLoc.getBeginLoc())
            << firstTemplateParamType
            << typeLoc.getSourceRange();
    }
    if (containsSalhelperReferenceObjectSubclass(firstTemplateParamType.getTypePtr()))
    {
        report(
            DiagnosticsEngine::Warning,
            "salhelper::SimpleReferenceObject subclass %0 being managed via smart pointer, should be managed via rtl::Reference",
            typeLoc.getBeginLoc())
            << firstTemplateParamType
            << typeLoc.getSourceRange();
    }
// Not in general (dbaccess::DocumentEvents, dbaccess/source/core/dataaccess/databasedocument.hxx):
#if 0
    if (containsXInterfaceSubclass(firstTemplateParamType))
    {
        report(
            DiagnosticsEngine::Warning,
            "XInterface subclass %0 being managed via smart pointer, should be managed via uno::Reference",
            typeLoc.getBeginLoc())
            << firstTemplateParamType
            << typeLoc.getSourceRange();
    }
#endif
    if (containsOWeakObjectSubclass(firstTemplateParamType.getTypePtr()))
    {
        report(
            DiagnosticsEngine::Warning,
            "cppu::OWeakObject subclass %0 being managed via smart pointer, should be managed via rtl::Reference",
            typeLoc.getBeginLoc())
            << firstTemplateParamType
            << typeLoc.getSourceRange();
    }
    return true;
}

bool RefCounting::VisitCXXDeleteExpr(const CXXDeleteExpr * cxxDeleteExpr)
{
    if (ignoreLocation(cxxDeleteExpr))
        return true;
    StringRef aFileName = getFilenameOfLocation(
        compiler.getSourceManager().getSpellingLoc(cxxDeleteExpr->getBeginLoc()));
    if (loplugin::isSamePathname(aFileName, SRCDIR "/cppuhelper/source/weak.cxx"))
        return true;
    if (loplugin::isSamePathname(aFileName, SRCDIR "/include/svx/svdobj.hxx"))
        return true;
    if (loplugin::isSamePathname(aFileName, SRCDIR "/svx/source/svdraw/svdobj.cxx"))
        return true;

    if (!cxxDeleteExpr->getArgument())
        return true;
    auto argType = cxxDeleteExpr->getArgument()->getType();
    if (argType.isNull() || !argType->isPointerType())
        return true;
    auto pointeeType = argType->getPointeeType();
    if (containsOWeakObjectSubclass(pointeeType))
    {
        report(
            DiagnosticsEngine::Warning,
            "cppu::OWeakObject subclass %0 being deleted via delete, should be managed via rtl::Reference",
            cxxDeleteExpr->getBeginLoc())
            << pointeeType
            << cxxDeleteExpr->getSourceRange();
    }
    return true;
}

bool RefCounting::VisitFieldDecl(const FieldDecl * fieldDecl) {
    if (ignoreLocation(fieldDecl)) {
        return true;
    }
    if (fieldDecl->isBitField()) {
        return true;
    }

    // We can't call FieldDecl::getParent, which triggers an assertion at least with
    // current trunk towards Clang 3.7 when the FieldDecl is actually an
    // ObjCIvarDecl.
    if (isa<ObjCIvarDecl>(fieldDecl)) {
        return true;
    }

    if (containsSvRefBaseSubclass(fieldDecl->getType().getTypePtr())) {
        report(
            DiagnosticsEngine::Warning,
            "SvRefBase subclass %0 being directly heap managed, should be managed via tools::SvRef, "
            ", parent is %1",
            fieldDecl->getLocation())
            << fieldDecl->getType()
            << fieldDecl->getParent()
            << fieldDecl->getSourceRange();
    }

    if (containsSalhelperReferenceObjectSubclass(fieldDecl->getType().getTypePtr())) {
        report(
            DiagnosticsEngine::Warning,
            "salhelper::SimpleReferenceObject subclass %0 being directly heap managed, should be managed via rtl::Reference, "
            "parent is %1",
            fieldDecl->getLocation())
            << fieldDecl->getType()
            << fieldDecl->getParent()
            << fieldDecl->getSourceRange();
    }

    auto const dc = loplugin::DeclCheck(fieldDecl->getParent());
    if ( (dc.Class("BaseReference").Namespace("uno").Namespace("star")
          .Namespace("sun").Namespace("com").GlobalNamespace())
         || (dc.Class("Reference").Namespace("rtl").GlobalNamespace())
         || (dc.Union("element_alias").Namespace("detail").Namespace("cppu")
             .GlobalNamespace())
         // this is playing some kind of game to avoid circular references
         || (dc.Class("ResultSetDataSupplier").Namespace("ucbhelper")
             .GlobalNamespace()))
    {
        return true;
    }

    if (containsXInterfaceSubclass(fieldDecl->getType())) {
        report(
            DiagnosticsEngine::Warning,
            "XInterface subclass %0 being directly heap managed, should be managed via uno::Reference, "
            "parent is %1",
            fieldDecl->getLocation())
            << fieldDecl->getType()
            << fieldDecl->getParent()
            << fieldDecl->getSourceRange();
    }

    if (containsOWeakObjectSubclass(fieldDecl->getType())) {
        report(
            DiagnosticsEngine::Warning,
            "cppu::OWeakObject subclass %0 being directly heap managed, should be managed via rtl::Reference, "
            "parent is %1",
            fieldDecl->getLocation())
            << fieldDecl->getType()
            << fieldDecl->getParent()
            << fieldDecl->getSourceRange();
    }

    checkUnoReference(
        fieldDecl->getType(), fieldDecl,
        fieldDecl->getParent(), "field");

    return true;
}

bool RefCounting::VisitReturnStmt(const ReturnStmt * returnStmt) {
    if (ignoreLocation(returnStmt)) {
        return true;
    }

    if (!returnStmt->getRetValue())
        return true;
    auto cxxNewExpr = dyn_cast<CXXNewExpr>(returnStmt->getRetValue()->IgnoreImplicit());
    if (!cxxNewExpr)
        return true;

    auto qt = returnStmt->getRetValue()->getType();
    if (!qt->isPointerType())
        return false;
    qt = qt->getPointeeType();

    if (containsOWeakObjectSubclass(qt)) {
        report(
            DiagnosticsEngine::Warning,
            "new object of cppu::OWeakObject subclass %0 being returned via raw pointer, should be returned by via rtl::Reference",
            returnStmt->getBeginLoc())
            << qt
            << returnStmt->getSourceRange();
    }

    return true;
}

bool RefCounting::VisitVarDecl(const VarDecl * varDecl) {
    if (ignoreLocation(varDecl))
        return true;

    checkUnoReference(varDecl->getType(), varDecl, nullptr, "var");

    if (isa<ParmVarDecl>(varDecl))
        return true;

    if (containsSvRefBaseSubclass(varDecl->getType().getTypePtr())) {
        report(
            DiagnosticsEngine::Warning,
            "SvRefBase subclass being directly stack managed, should be managed via tools::SvRef, "
            + varDecl->getType().getAsString(),
            varDecl->getLocation())
            << varDecl->getSourceRange();
    }
    if (containsSalhelperReferenceObjectSubclass(varDecl->getType().getTypePtr())) {
        StringRef name { getFilenameOfLocation(
            compiler.getSourceManager().getSpellingLoc(varDecl->getLocation())) };
        // this is playing games that it believes is safe
        if (loplugin::isSamePathname(name, SRCDIR "/stoc/source/security/permissions.cxx"))
            return true;
        report(
            DiagnosticsEngine::Warning,
            "salhelper::SimpleReferenceObject subclass being directly stack managed, should be managed via rtl::Reference, "
            + varDecl->getType().getAsString(),
            varDecl->getLocation())
            << varDecl->getSourceRange();
    }
    if (containsXInterfaceSubclass(varDecl->getType())) {
        report(
            DiagnosticsEngine::Warning,
            "XInterface subclass being directly stack managed, should be managed via uno::Reference, "
            + varDecl->getType().getAsString(),
            varDecl->getLocation())
            << varDecl->getSourceRange();
    }
    if (containsOWeakObjectSubclass(varDecl->getType())) {
        report(
            DiagnosticsEngine::Warning,
            "cppu::OWeakObject subclass being directly stack managed, should be managed via uno::Reference, "
            + varDecl->getType().getAsString(),
            varDecl->getLocation())
            << varDecl->getSourceRange();
    }

    if (varDecl->getType()->isPointerType() && varDecl->getInit())
    {
        auto newExpr = dyn_cast<CXXNewExpr>(varDecl->getInit()->IgnoreImplicit());
        if (newExpr)
        {
            StringRef fileName = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(varDecl->getBeginLoc()));
            if (loplugin::isSamePathname(fileName, SRCDIR "/cppuhelper/source/component_context.cxx"))
                return true;
            auto pointeeType = varDecl->getType()->getPointeeType();
            if (containsOWeakObjectSubclass(pointeeType))
                report(
                    DiagnosticsEngine::Warning,
                    "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
                    varDecl->getLocation())
                    << pointeeType
                    << varDecl->getSourceRange();
        }
        if (isCastingReference(varDecl->getInit()))
        {
            // TODO false+ code
            StringRef fileName = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(varDecl->getBeginLoc()));
            if (loplugin::isSamePathname(fileName, SRCDIR "/sw/source/core/unocore/unotbl.cxx"))
                return true;
            auto pointeeType = varDecl->getType()->getPointeeType();
            if (containsOWeakObjectSubclass(pointeeType))
                report(
                    DiagnosticsEngine::Warning,
                    "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
                    varDecl->getLocation())
                    << pointeeType
                    << varDecl->getSourceRange();
        }
        if (isCallingGetOnWeakRef(varDecl->getInit()))
        {
            auto pointeeType = varDecl->getType()->getPointeeType();
            if (containsOWeakObjectSubclass(pointeeType))
                report(
                    DiagnosticsEngine::Warning,
                    "weak object being converted to strong, and then the reference dropped, and managed via raw pointer, should be managed via rtl::Reference",
                    varDecl->getLocation())
                    << pointeeType
                    << varDecl->getSourceRange();
        }
    }
    return true;
}

/**
    Look for code like
        static_cast<FooChild*>(makeFoo().get());
    where makeFoo() returns a Reference<Foo>
*/

bool RefCounting::isCastingReference(const Expr* expr)
{
    expr = expr->IgnoreImplicit();
    auto castExpr = dyn_cast<CastExpr>(expr);
    if (!castExpr)
        return false;
    auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(castExpr->getSubExpr());
    if (!memberCallExpr)
        return false;
    if (!memberCallExpr->getMethodDecl()->getIdentifier() || memberCallExpr->getMethodDecl()->getName() != "get")
        return false;
    QualType objectType = memberCallExpr->getImplicitObjectArgument()->getType();
    if (!loplugin::TypeCheck(objectType).Class("Reference"))
        return false;
    // ignore "x.get()" where x is a var
    auto obj = memberCallExpr->getImplicitObjectArgument()->IgnoreImplicit();
    if (isa<DeclRefExpr>(obj) || isa<MemberExpr>(obj))
        return false;
    // if the foo in foo().get() returns "rtl::Reference<T>&" then the variable
    // we are assigning to does not __have__ to be Reference, since the method called
    // must already be holding a reference.
    if (auto callExpr = dyn_cast<CallExpr>(obj))
    {
        if (auto callMethod = callExpr->getDirectCallee())
            if (callMethod->getReturnType()->isReferenceType())
                return false;
    }
    // Ignore
    //     WeakReference x;
    //     if (x.get.get())
    // and similar stuff
    if (auto memberCall2 = dyn_cast<CXXMemberCallExpr>(obj))
    {
        if (loplugin::TypeCheck(memberCall2->getImplicitObjectArgument()->getType()).Class("WeakReference"))
                return false;
    }
    return true;
}

/**
    Look for code like
        makeFoo().get();
    or
        cast<T*>(makeFoo().get().get());
    or
        foo.get();
    where makeFoo() returns a unotools::WeakReference<Foo>
    and foo is a unotools::WeakReference<Foo> var.
*/

bool RefCounting::isCallingGetOnWeakRef(const Expr* expr)
{
    expr = expr->IgnoreImplicit();
    // unwrap the cast (if any)
    if (auto castExpr = dyn_cast<CastExpr>(expr))
        expr = castExpr->getSubExpr()->IgnoreImplicit();
    // unwrap outer get (if any)
    if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(expr))
    {
        auto methodDecl = memberCallExpr->getMethodDecl();
        if (methodDecl && methodDecl->getIdentifier() && methodDecl->getName() == "get")
        {
            QualType objectType = memberCallExpr->getImplicitObjectArgument()->getType();
            if (loplugin::TypeCheck(objectType).Class("Reference").Namespace("rtl"))
                expr = memberCallExpr->getImplicitObjectArgument()->IgnoreImplicit();
        }
    }
    // check for converting a WeakReference to a strong reference via get()
    if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(expr))
    {
        auto methodDecl = memberCallExpr->getMethodDecl();
        if (methodDecl && methodDecl->getIdentifier() && methodDecl->getName() == "get")
        {
            QualType objectType = memberCallExpr->getImplicitObjectArgument()->getType();
            if (loplugin::TypeCheck(objectType).Class("WeakReference").Namespace("unotools"))
                return true;
        }
    }
    return false;
}

bool RefCounting::VisitBinaryOperator(const BinaryOperator * binaryOperator)
{
    if (ignoreLocation(binaryOperator))
        return true;
    if (binaryOperator->getOpcode() != BO_Assign)
        return true;
    if (!binaryOperator->getLHS()->getType()->isPointerType())
        return true;

    auto newExpr = dyn_cast<CXXNewExpr>(binaryOperator->getRHS()->IgnoreImplicit());
    if (newExpr)
    {
        // deliberately does not want to keep track at the allocation site
        StringRef fileName = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(binaryOperator->getBeginLoc()));
        if (loplugin::isSamePathname(fileName, SRCDIR "/vcl/unx/generic/dtrans/X11_selection.cxx"))
            return true;

        auto pointeeType = binaryOperator->getLHS()->getType()->getPointeeType();
        if (containsOWeakObjectSubclass(pointeeType))
        {
            report(
                DiagnosticsEngine::Warning,
                "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
                binaryOperator->getBeginLoc())
                << pointeeType
                << binaryOperator->getSourceRange();
        }
    }
    if (isCastingReference(binaryOperator->getRHS()))
    {
        auto pointeeType = binaryOperator->getLHS()->getType()->getPointeeType();
        if (containsOWeakObjectSubclass(pointeeType))
            report(
                DiagnosticsEngine::Warning,
                "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
                binaryOperator->getBeginLoc())
                << pointeeType
                << binaryOperator->getSourceRange();
    }
    if (isCallingGetOnWeakRef(binaryOperator->getRHS()))
    {
        // TODO Very dodgy code, but I see no simple way of fixing it
        StringRef fileName = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(binaryOperator->getBeginLoc()));
        if (loplugin::isSamePathname(fileName, SRCDIR "/sd/source/ui/view/Outliner.cxx"))
            return true;
        auto pointeeType = binaryOperator->getLHS()->getType()->getPointeeType();
        if (containsOWeakObjectSubclass(pointeeType))
            report(
                DiagnosticsEngine::Warning,
                "weak object being converted to strong, and then the reference dropped, and managed via raw pointer, should be managed via rtl::Reference",
                binaryOperator->getBeginLoc())
                << pointeeType
                << binaryOperator->getSourceRange();
    }
    return true;
}

bool RefCounting::VisitFunctionDecl(const FunctionDecl * functionDecl) {
    if (ignoreLocation(functionDecl)) {
        return true;
    }
    // only consider base declarations, not overridden ones, or we warn on methods that
    // are overriding stuff from external libraries
    const CXXMethodDecl * methodDecl = dyn_cast<CXXMethodDecl>(functionDecl);
    if (methodDecl && methodDecl->size_overridden_methods() > 0) {
            return true;
    }
    checkUnoReference(functionDecl->getReturnType(), functionDecl, nullptr, "return");
    return true;
}

loplugin::Plugin::Registration< RefCounting > refcounting("refcounting");

}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=94 H=100 G=96

¤ Dauer der Verarbeitung: 0.13 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.