2015-07-13 16:17:00 +02:00
|
|
|
/* -*- 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/.
|
|
|
|
*/
|
2020-02-13 11:37:29 +02:00
|
|
|
#ifndef LO_CLANG_SHARED_PLUGINS
|
2015-07-13 16:17:00 +02:00
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <iostream>
|
|
|
|
|
2016-06-28 18:54:31 +02:00
|
|
|
#include "check.hxx"
|
|
|
|
#include "plugin.hxx"
|
2015-07-13 16:17:00 +02:00
|
|
|
#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'.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
2020-02-13 11:37:29 +02:00
|
|
|
namespace {
|
2015-07-13 16:17:00 +02:00
|
|
|
|
|
|
|
class RefCounting:
|
2018-08-13 17:24:26 +02:00
|
|
|
public loplugin::FilteringPlugin<RefCounting>
|
2015-07-13 16:17:00 +02:00
|
|
|
{
|
|
|
|
public:
|
2018-08-13 17:24:26 +02:00
|
|
|
explicit RefCounting(loplugin::InstantiationData const & data): FilteringPlugin(data)
|
2017-11-07 11:50:47 +01:00
|
|
|
{}
|
2015-07-13 16:17:00 +02:00
|
|
|
|
2020-02-13 11:37:29 +02:00
|
|
|
virtual bool preRun() override { return true; }
|
|
|
|
|
|
|
|
virtual void run() override
|
|
|
|
{
|
|
|
|
if (preRun())
|
|
|
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
|
|
|
}
|
2015-07-13 16:17:00 +02:00
|
|
|
|
|
|
|
bool VisitFieldDecl(const FieldDecl *);
|
|
|
|
bool VisitVarDecl(const VarDecl *);
|
2016-05-26 16:02:39 +02:00
|
|
|
bool VisitFunctionDecl(const FunctionDecl *);
|
2015-08-04 09:38:44 +02:00
|
|
|
|
2016-08-26 12:40:43 +02:00
|
|
|
// 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); }
|
|
|
|
|
2016-05-26 16:02:39 +02:00
|
|
|
private:
|
|
|
|
void checkUnoReference(QualType qt, const Decl* decl,
|
2017-06-30 14:04:42 +02:00
|
|
|
const RecordDecl* parent, const std::string& rDeclName);
|
2016-08-26 12:40:43 +02:00
|
|
|
|
|
|
|
bool visitTemporaryObjectExpr(Expr const * expr);
|
2015-07-13 16:17:00 +02:00
|
|
|
};
|
|
|
|
|
2017-09-11 10:48:12 +02:00
|
|
|
bool containsXInterfaceSubclass(const clang::Type* pType0);
|
2015-07-13 16:17:00 +02:00
|
|
|
|
|
|
|
bool containsXInterfaceSubclass(const QualType& qType) {
|
|
|
|
return containsXInterfaceSubclass(qType.getTypePtr());
|
|
|
|
}
|
|
|
|
|
2017-09-11 10:48:12 +02:00
|
|
|
bool containsXInterfaceSubclass(const clang::Type* pType0) {
|
2015-07-13 16:17:00 +02:00
|
|
|
if (!pType0)
|
|
|
|
return false;
|
2017-09-11 10:48:12 +02:00
|
|
|
const clang::Type* pType = pType0->getUnqualifiedDesugaredType();
|
2015-07-13 16:17:00 +02:00
|
|
|
if (!pType)
|
|
|
|
return false;
|
|
|
|
const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl();
|
|
|
|
if (pRecordDecl) {
|
|
|
|
pRecordDecl = pRecordDecl->getCanonicalDecl();
|
2016-01-04 12:44:22 +01:00
|
|
|
// these classes override acquire/release and forwards to its parent
|
2020-02-13 11:37:29 +02:00
|
|
|
if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("ListenerMultiplexerBase").GlobalNamespace()); })) { // module UnoTools
|
2015-07-13 16:17:00 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-02-13 11:37:29 +02:00
|
|
|
if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("GridEventForwarder").Namespace("toolkit").GlobalNamespace()); })) { // module toolkit
|
2015-07-13 16:17:00 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-02-13 11:37:29 +02:00
|
|
|
if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OWeakSubObject").GlobalNamespace()); })) { // module svx
|
2015-07-13 16:17:00 +02:00
|
|
|
return false;
|
|
|
|
}
|
2020-02-13 11:37:29 +02:00
|
|
|
if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OSbaWeakSubObject").Namespace("dbaui").GlobalNamespace()); })) { // module dbaccess
|
2015-07-13 16:17:00 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// FIXME This class has private operator new, and I cannot figure out how it can be dynamically instantiated
|
2020-02-13 11:37:29 +02:00
|
|
|
if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("XPropertyList").GlobalNamespace()); })) { // module svx
|
2015-07-13 16:17:00 +02:00
|
|
|
return false;
|
|
|
|
}
|
2018-01-31 17:52:42 +01:00
|
|
|
// tdf#114596
|
2020-02-13 11:37:29 +02:00
|
|
|
if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OBookmarkContainer").Namespace("dbaccess").GlobalNamespace()); })) { // module dbaccess
|
2018-01-31 17:52:42 +01:00
|
|
|
return false;
|
|
|
|
}
|
2019-05-29 11:33:59 +02:00
|
|
|
|
|
|
|
// tdf#114596
|
2020-02-13 11:37:29 +02:00
|
|
|
if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OCollection").Namespace("dbaccess").GlobalNamespace()); })) { // module dbaccess
|
2019-05-29 11:33:59 +02:00
|
|
|
return false;
|
|
|
|
}
|
2015-07-13 16:17:00 +02:00
|
|
|
}
|
|
|
|
if (pRecordDecl) {
|
|
|
|
const ClassTemplateSpecializationDecl* pTemplate = dyn_cast<ClassTemplateSpecializationDecl>(pRecordDecl);
|
|
|
|
if (pTemplate) {
|
2016-06-29 15:13:25 +02:00
|
|
|
// 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()))
|
2016-06-28 18:54:31 +02:00
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2015-07-13 16:17:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pType->isPointerType()) {
|
|
|
|
// ignore
|
|
|
|
return false;
|
|
|
|
} else if (pType->isArrayType()) {
|
2017-09-11 10:48:12 +02:00
|
|
|
const clang::ArrayType* pArrayType = dyn_cast<clang::ArrayType>(pType);
|
2015-07-13 16:17:00 +02:00
|
|
|
QualType elementType = pArrayType->getElementType();
|
|
|
|
return containsXInterfaceSubclass(elementType);
|
|
|
|
} else {
|
2020-02-13 11:37:29 +02:00
|
|
|
return loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("XInterface").Namespace("uno").Namespace("star").Namespace("sun").Namespace("com").GlobalNamespace()); });
|
2015-07-13 16:17:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-11 10:48:12 +02:00
|
|
|
bool containsSvRefBaseSubclass(const clang::Type* pType0) {
|
2015-08-05 10:48:40 +02:00
|
|
|
if (!pType0)
|
|
|
|
return false;
|
2017-09-11 10:48:12 +02:00
|
|
|
const clang::Type* pType = pType0->getUnqualifiedDesugaredType();
|
2015-08-05 10:48:40 +02:00
|
|
|
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) {
|
2017-06-30 11:16:03 +02:00
|
|
|
if (loplugin::DeclCheck(pTemplate).Class("SvRef")
|
|
|
|
.Namespace("tools").GlobalNamespace())
|
|
|
|
{
|
2015-08-05 10:48:40 +02:00
|
|
|
return false;
|
2017-06-30 11:16:03 +02:00
|
|
|
}
|
2015-08-05 10:48:40 +02:00
|
|
|
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()) {
|
2017-09-11 10:48:12 +02:00
|
|
|
const clang::ArrayType* pArrayType = dyn_cast<clang::ArrayType>(pType);
|
2015-08-05 10:48:40 +02:00
|
|
|
QualType elementType = pArrayType->getElementType();
|
|
|
|
return containsSvRefBaseSubclass(elementType.getTypePtr());
|
|
|
|
} else {
|
2020-02-13 11:37:29 +02:00
|
|
|
return loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("SvRefBase").Namespace("tools").GlobalNamespace()); });
|
2015-08-05 10:48:40 +02:00
|
|
|
}
|
|
|
|
}
|
2015-07-13 16:17:00 +02:00
|
|
|
|
2017-09-11 10:48:12 +02:00
|
|
|
bool containsSalhelperReferenceObjectSubclass(const clang::Type* pType0) {
|
2015-08-05 10:48:40 +02:00
|
|
|
if (!pType0)
|
|
|
|
return false;
|
2017-09-11 10:48:12 +02:00
|
|
|
const clang::Type* pType = pType0->getUnqualifiedDesugaredType();
|
2015-08-05 10:48:40 +02:00
|
|
|
if (!pType)
|
|
|
|
return false;
|
|
|
|
const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl();
|
|
|
|
if (pRecordDecl) {
|
|
|
|
pRecordDecl = pRecordDecl->getCanonicalDecl();
|
|
|
|
}
|
|
|
|
if (pRecordDecl) {
|
2019-12-14 12:05:55 +02:00
|
|
|
// for performance reasons we sometimes allocate temporaries on the stack
|
|
|
|
if (loplugin::DeclCheck(pRecordDecl).Struct("ScSheetLimits").GlobalNamespace())
|
|
|
|
return false;
|
2020-04-22 13:37:14 +02:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
2015-08-05 10:48:40 +02:00
|
|
|
const ClassTemplateSpecializationDecl* pTemplate = dyn_cast<ClassTemplateSpecializationDecl>(pRecordDecl);
|
|
|
|
if (pTemplate) {
|
2017-06-30 11:16:03 +02:00
|
|
|
auto const dc = loplugin::DeclCheck(pTemplate);
|
|
|
|
if (dc.Class("Reference").Namespace("rtl").GlobalNamespace()
|
Extend loplugin:external to warn about classes
...following up on 314f15bff08b76bf96acf99141776ef64d2f1355 "Extend
loplugin:external to warn about enums".
Cases where free functions were moved into an unnamed namespace along with a
class, to not break ADL, are in:
filter/source/svg/svgexport.cxx
sc/source/filter/excel/xelink.cxx
sc/source/filter/excel/xilink.cxx
svx/source/sdr/contact/viewobjectcontactofunocontrol.cxx
All other free functions mentioning moved classes appear to be harmless and not
give rise to (silent, even) ADL breakage. (One remaining TODO in
compilerplugins/clang/external.cxx is that derived classes are not covered by
computeAffectedTypes, even though they could also be affected by ADL-breakage---
but don't seem to be in any acutal case across the code base.)
For friend declarations using elaborate type specifiers, like
class C1 {};
class C2 { friend class C1; };
* If C2 (but not C1) is moved into an unnamed namespace, the friend declaration
must be changed to not use an elaborate type specifier (i.e., "friend C1;"; see
C++17 [namespace.memdef]/3: "If the name in a friend declaration is neither
qualified nor a template-id and the declaration is a function or an
elaborated-type-specifier, the lookup to determine whether the entity has been
previously declared shall not consider any scopes outside the innermost
enclosing namespace.")
* If C1 (but not C2) is moved into an unnamed namespace, the friend declaration
must be changed too, see <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71882>
"elaborated-type-specifier friend not looked up in unnamed namespace".
Apart from that, to keep changes simple and mostly mechanical (which should help
avoid regressions), out-of-line definitions of class members have been left in
the enclosing (named) namespace. But explicit specializations of class
templates had to be moved into the unnamed namespace to appease
<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92598> "explicit specialization of
template from unnamed namespace using unqualified-id in enclosing namespace".
Also, accompanying declarations (of e.g. typedefs or static variables) that
could arguably be moved into the unnamed namespace too have been left alone.
And in some cases, mention of affected types in blacklists in other loplugins
needed to be adapted.
And sc/qa/unit/mark_test.cxx uses a hack of including other .cxx, one of which
is sc/source/core/data/segmenttree.cxx where e.g. ScFlatUInt16SegmentsImpl is
not moved into an unnamed namespace (because it is declared in
sc/inc/segmenttree.hxx), but its base ScFlatSegmentsImpl is. GCC warns about
such combinations with enabled-by-default -Wsubobject-linkage, but "The compiler
doesn’t give this warning for types defined in the main .C file, as those are
unlikely to have multiple definitions."
(<https://gcc.gnu.org/onlinedocs/gcc-9.2.0/gcc/Warning-Options.html>) The
warned-about classes also don't have multiple definitions in the given test, so
disable the warning when including the .cxx.
Change-Id: Ib694094c0d8168be68f8fe90dfd0acbb66a3f1e4
Reviewed-on: https://gerrit.libreoffice.org/83239
Tested-by: Jenkins
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
2019-11-19 16:32:49 +01:00
|
|
|
|| (dc.Class("OStoreHandle").AnonymousNamespace().Namespace("store")
|
2017-06-30 11:16:03 +02:00
|
|
|
.GlobalNamespace()))
|
|
|
|
{
|
2015-08-05 10:48:40 +02:00
|
|
|
return false;
|
2017-06-30 11:16:03 +02:00
|
|
|
}
|
2015-08-05 10:48:40 +02:00
|
|
|
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()) {
|
2017-09-11 10:48:12 +02:00
|
|
|
const clang::ArrayType* pArrayType = dyn_cast<clang::ArrayType>(pType);
|
2015-08-05 10:48:40 +02:00
|
|
|
QualType elementType = pArrayType->getElementType();
|
|
|
|
return containsSalhelperReferenceObjectSubclass(elementType.getTypePtr());
|
|
|
|
} else {
|
2020-02-13 11:37:29 +02:00
|
|
|
return loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("SimpleReferenceObject").Namespace("salhelper").GlobalNamespace()); });
|
2015-08-05 10:48:40 +02:00
|
|
|
}
|
|
|
|
}
|
2015-07-13 16:17:00 +02:00
|
|
|
|
2016-05-26 16:02:39 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-06-30 14:04:42 +02:00
|
|
|
void RefCounting::checkUnoReference(QualType qt, const Decl* decl, const RecordDecl* parent, const std::string& rDeclName)
|
2016-05-26 16:02:39 +02:00
|
|
|
{
|
|
|
|
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,
|
2017-06-30 11:16:03 +02:00
|
|
|
("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"),
|
2016-05-26 16:02:39 +02:00
|
|
|
decl->getLocation())
|
2017-06-30 14:04:42 +02:00
|
|
|
<< rDeclName << qt << (parent != nullptr)
|
|
|
|
<< (parent != nullptr
|
|
|
|
? parent->getQualifiedNameAsString() : std::string())
|
2016-05-26 16:02:39 +02:00
|
|
|
<< decl->getSourceRange();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-26 12:40:43 +02:00
|
|
|
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"),
|
2018-08-10 12:35:21 +02:00
|
|
|
compat::getBeginLoc(expr))
|
2016-08-26 12:40:43 +02:00
|
|
|
<< 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"),
|
2018-08-10 12:35:21 +02:00
|
|
|
compat::getBeginLoc(expr))
|
2016-08-26 12:40:43 +02:00
|
|
|
<< 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"),
|
2018-08-10 12:35:21 +02:00
|
|
|
compat::getBeginLoc(expr))
|
2016-08-26 12:40:43 +02:00
|
|
|
<< t.getUnqualifiedType() << expr->getSourceRange();
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-07-13 16:17:00 +02:00
|
|
|
bool RefCounting::VisitFieldDecl(const FieldDecl * fieldDecl) {
|
|
|
|
if (ignoreLocation(fieldDecl)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (fieldDecl->isBitField()) {
|
|
|
|
return true;
|
|
|
|
}
|
2015-08-05 10:48:40 +02:00
|
|
|
|
2020-02-13 11:37:29 +02:00
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2017-06-29 08:15:20 +02:00
|
|
|
// 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>()) {
|
2017-06-30 11:16:03 +02:00
|
|
|
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())
|
2017-06-29 08:15:20 +02:00
|
|
|
{
|
|
|
|
auto templateDecl = dyn_cast<ClassTemplateSpecializationDecl>(recordType->getDecl());
|
|
|
|
if (templateDecl && templateDecl->getTemplateArgs().size() > 0)
|
|
|
|
firstTemplateParamType = templateDecl->getTemplateArgs()[0].getAsType();
|
|
|
|
}
|
|
|
|
}
|
2015-08-05 10:48:40 +02:00
|
|
|
|
|
|
|
if (containsSvRefBaseSubclass(fieldDecl->getType().getTypePtr())) {
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
2017-06-29 08:15:20 +02:00
|
|
|
"SvRefBase subclass %0 being directly heap managed, should be managed via tools::SvRef, "
|
|
|
|
", parent is %1",
|
2015-08-05 10:48:40 +02:00
|
|
|
fieldDecl->getLocation())
|
2017-06-29 08:15:20 +02:00
|
|
|
<< 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();
|
2015-08-05 10:48:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (containsSalhelperReferenceObjectSubclass(fieldDecl->getType().getTypePtr())) {
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
2017-06-29 08:15:20 +02:00
|
|
|
"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",
|
2015-08-05 10:48:40 +02:00
|
|
|
fieldDecl->getLocation())
|
2017-06-29 08:15:20 +02:00
|
|
|
<< firstTemplateParamType
|
|
|
|
<< fieldDecl->getParent()
|
|
|
|
<< fieldDecl->getSourceRange();
|
2015-08-05 10:48:40 +02:00
|
|
|
}
|
|
|
|
|
2017-06-30 11:16:03 +02:00
|
|
|
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())
|
2015-07-13 16:17:00 +02:00
|
|
|
// this is playing some kind of game to avoid circular references
|
2017-06-30 11:16:03 +02:00
|
|
|
|| (dc.Class("ResultSetDataSupplier").Namespace("ucbhelper")
|
|
|
|
.GlobalNamespace()))
|
2015-07-13 16:17:00 +02:00
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (containsXInterfaceSubclass(fieldDecl->getType())) {
|
|
|
|
report(
|
|
|
|
DiagnosticsEngine::Warning,
|
2017-06-29 08:15:20 +02:00
|
|
|
"XInterface subclass %0 being directly heap managed, should be managed via uno::Reference, "
|
|
|
|
"parent is %1",
|
|
|
|
fieldDecl->getLocation())
|
|
|
|
<< fieldDecl->getType()
|
|
|
|
<< fieldDecl->getParent()
|
|
|
|
<< fieldDecl->getSourceRange();
|
|
|
|
}
|
|
|
|
|
2017-12-01 11:24:04 +01:00
|
|
|
// Not in general (dbaccess::DocumentEvents, dbaccess/source/core/dataaccess/databasedocument.hxx):
|
|
|
|
#if 0
|
2017-06-29 08:15:20 +02:00
|
|
|
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",
|
2015-07-13 16:17:00 +02:00
|
|
|
fieldDecl->getLocation())
|
2017-06-29 08:15:20 +02:00
|
|
|
<< firstTemplateParamType
|
|
|
|
<< fieldDecl->getParent()
|
|
|
|
<< fieldDecl->getSourceRange();
|
2015-07-13 16:17:00 +02:00
|
|
|
}
|
2017-12-01 11:24:04 +01:00
|
|
|
#endif
|
2016-05-26 16:02:39 +02:00
|
|
|
|
2017-06-30 11:16:03 +02:00
|
|
|
checkUnoReference(
|
|
|
|
fieldDecl->getType(), fieldDecl,
|
|
|
|
fieldDecl->getParent(), "field");
|
2016-05-26 16:02:39 +02:00
|
|
|
|
2015-07-13 16:17:00 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool RefCounting::VisitVarDecl(const VarDecl * varDecl) {
|
|
|
|
if (ignoreLocation(varDecl)) {
|
|
|
|
return true;
|
|
|
|
}
|
2016-05-26 16:02:39 +02:00
|
|
|
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())) {
|
2019-10-07 15:29:30 +02:00
|
|
|
StringRef name { getFilenameOfLocation(
|
2018-05-29 11:28:07 +02:00
|
|
|
compiler.getSourceManager().getSpellingLoc(varDecl->getLocation())) };
|
2016-05-26 16:02:39 +02:00
|
|
|
// this is playing games that it believes is safe
|
2017-05-18 09:56:01 +02:00
|
|
|
if (loplugin::isSamePathname(name, SRCDIR "/stoc/source/security/permissions.cxx"))
|
2016-05-26 16:02:39 +02:00
|
|
|
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();
|
|
|
|
}
|
2015-08-05 10:48:40 +02:00
|
|
|
}
|
2017-06-30 11:16:03 +02:00
|
|
|
checkUnoReference(varDecl->getType(), varDecl, nullptr, "var");
|
2016-05-26 16:02:39 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RefCounting::VisitFunctionDecl(const FunctionDecl * functionDecl) {
|
|
|
|
if (ignoreLocation(functionDecl)) {
|
|
|
|
return true;
|
2015-08-05 10:48:40 +02:00
|
|
|
}
|
2017-03-02 17:56:40 +01:00
|
|
|
// only consider base declarations, not overridden ones, or we warn on methods that
|
2016-05-26 16:02:39 +02:00
|
|
|
// are overriding stuff from external libraries
|
|
|
|
const CXXMethodDecl * methodDecl = dyn_cast<CXXMethodDecl>(functionDecl);
|
|
|
|
if (methodDecl && methodDecl->size_overridden_methods() > 0) {
|
2015-08-05 10:48:40 +02:00
|
|
|
return true;
|
2015-07-13 16:17:00 +02:00
|
|
|
}
|
2017-12-15 14:20:38 +01:00
|
|
|
checkUnoReference(functionDecl->getReturnType(), functionDecl, nullptr, "return");
|
2015-07-13 16:17:00 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-02-13 11:37:29 +02:00
|
|
|
loplugin::Plugin::Registration< RefCounting > refcounting("refcounting");
|
2015-07-13 16:17:00 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-02-13 11:37:29 +02:00
|
|
|
#endif // LO_CLANG_SHARED_PLUGINS
|
|
|
|
|
2015-07-13 16:17:00 +02:00
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|