summaryrefslogtreecommitdiffstats
path: root/compilerplugins/clang/refcounting.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang/refcounting.cxx')
-rw-r--r--compilerplugins/clang/refcounting.cxx548
1 files changed, 548 insertions, 0 deletions
diff --git a/compilerplugins/clang/refcounting.cxx b/compilerplugins/clang/refcounting.cxx
new file mode 100644
index 000000000..31b1540d3
--- /dev/null
+++ b/compilerplugins/clang/refcounting.cxx
@@ -0,0 +1,548 @@
+/* -*- 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/.
+ */
+#ifndef LO_CLANG_SHARED_PLUGINS
+
+#include <string>
+#include <iostream>
+
+#include "check.hxx"
+#include "plugin.hxx"
+#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 *);
+
+ // 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 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("WeakCppRef").Namespace("accessibility")
+ .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 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()))
+ {
+ 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()) {
+ 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 && !containsStaticTypeMethod(templateParam)) {
+ 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"),
+ compat::getBeginLoc(expr))
+ << 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"),
+ compat::getBeginLoc(expr))
+ << 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"),
+ compat::getBeginLoc(expr))
+ << t.getUnqualifiedType() << expr->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;
+ }
+
+ // check for dodgy code managing ref-counted stuff with shared_ptr or unique_ptr or similar stuff
+ QualType firstTemplateParamType;
+ if (auto recordType = fieldDecl->getType()->getUnqualifiedDesugaredType()->getAs<RecordType>()) {
+ auto const tc = loplugin::TypeCheck(fieldDecl->getType());
+ if (tc.Class("unique_ptr").StdNamespace()
+ || tc.Class("shared_ptr").StdNamespace()
+ || tc.Class("intrusive_ptr").Namespace("boost").GlobalNamespace())
+ {
+ auto templateDecl = dyn_cast<ClassTemplateSpecializationDecl>(recordType->getDecl());
+ if (templateDecl && templateDecl->getTemplateArgs().size() > 0)
+ firstTemplateParamType = templateDecl->getTemplateArgs()[0].getAsType();
+ }
+ }
+
+ 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 (!firstTemplateParamType.isNull() && containsSvRefBaseSubclass(firstTemplateParamType.getTypePtr()))
+ {
+ report(
+ DiagnosticsEngine::Warning,
+ "SvRefBase subclass %0 being managed via smart pointer, should be managed via tools::SvRef, "
+ "parent is %1",
+ fieldDecl->getLocation())
+ << firstTemplateParamType
+ << 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();
+ }
+
+ if (!firstTemplateParamType.isNull() && containsSalhelperReferenceObjectSubclass(firstTemplateParamType.getTypePtr()))
+ {
+ report(
+ DiagnosticsEngine::Warning,
+ "salhelper::SimpleReferenceObject subclass %0 being managed via smart pointer, should be managed via rtl::Reference, "
+ "parent is %1",
+ fieldDecl->getLocation())
+ << firstTemplateParamType
+ << 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.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();
+ }
+
+// Not in general (dbaccess::DocumentEvents, dbaccess/source/core/dataaccess/databasedocument.hxx):
+#if 0
+ if (!firstTemplateParamType.isNull() && containsXInterfaceSubclass(firstTemplateParamType))
+ {
+ report(
+ DiagnosticsEngine::Warning,
+ "XInterface subclass %0 being managed via smart pointer, should be managed via uno::Reference, "
+ "parent is %1",
+ fieldDecl->getLocation())
+ << firstTemplateParamType
+ << fieldDecl->getParent()
+ << fieldDecl->getSourceRange();
+ }
+#endif
+
+ checkUnoReference(
+ fieldDecl->getType(), fieldDecl,
+ fieldDecl->getParent(), "field");
+
+ return true;
+}
+
+
+bool RefCounting::VisitVarDecl(const VarDecl * varDecl) {
+ if (ignoreLocation(varDecl)) {
+ return true;
+ }
+ if (!isa<ParmVarDecl>(varDecl)) {
+ 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();
+ }
+ }
+ checkUnoReference(varDecl->getType(), varDecl, nullptr, "var");
+ 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");
+
+}
+
+#endif // LO_CLANG_SHARED_PLUGINS
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */