1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
15 #include "clang/AST/CXXInheritance.h"
21 class Foo : public css::foo::XBaa {
24 Then XBaa has acquire and release methods inherited from XInterface.
25 These are hard lifecycle controls.
27 If you see another class:
33 this is a bug =) since aFooMember assumes heap allocated lifecycle and
34 not delete on last 'release'.
41 public loplugin::FilteringPlugin
<RefCounting
>
44 explicit RefCounting(loplugin::InstantiationData
const & data
): FilteringPlugin(data
)
47 virtual void run() override
{ TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl()); }
49 bool VisitFieldDecl(const FieldDecl
*);
50 bool VisitVarDecl(const VarDecl
*);
51 bool VisitFunctionDecl(const FunctionDecl
*);
53 // Creation of temporaries with one argument are represented by
54 // CXXFunctionalCastExpr, while any other number of arguments are
55 // represented by CXXTemporaryObjectExpr:
56 bool VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr
const * expr
)
57 { return visitTemporaryObjectExpr(expr
); }
58 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
const * expr
)
59 { return visitTemporaryObjectExpr(expr
); }
61 bool WalkUpFromObjCIvarDecl(ObjCIvarDecl
* decl
) {
62 // Don't recurse into WalkUpFromFieldDecl, as VisitFieldDecl calls
63 // FieldDecl::getParent, which triggers an assertion at least with
64 // current trunk towards Clang 3.7 when the FieldDecl is actually an
66 return VisitObjCIvarDecl(decl
);
69 void checkUnoReference(QualType qt
, const Decl
* decl
,
70 const RecordDecl
* parent
, const std::string
& rDeclName
);
72 bool visitTemporaryObjectExpr(Expr
const * expr
);
75 bool containsXInterfaceSubclass(const clang::Type
* pType0
);
77 bool containsXInterfaceSubclass(const QualType
& qType
) {
78 return containsXInterfaceSubclass(qType
.getTypePtr());
81 bool containsXInterfaceSubclass(const clang::Type
* pType0
) {
84 const clang::Type
* pType
= pType0
->getUnqualifiedDesugaredType();
87 const CXXRecordDecl
* pRecordDecl
= pType
->getAsCXXRecordDecl();
89 pRecordDecl
= pRecordDecl
->getCanonicalDecl();
90 // these classes override acquire/release and forwards to its parent
91 if (isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("ListenerMultiplexerBase").GlobalNamespace()); })) { // module UnoTools
94 if (isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("GridEventForwarder").Namespace("toolkit").GlobalNamespace()); })) { // module toolkit
97 if (isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("OWeakSubObject").GlobalNamespace()); })) { // module svx
100 if (isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("OSbaWeakSubObject").Namespace("dbaui").GlobalNamespace()); })) { // module dbaccess
103 // FIXME This class has private operator new, and I cannot figure out how it can be dynamically instantiated
104 if (isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("XPropertyList").GlobalNamespace()); })) { // module svx
108 if (isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("OBookmarkContainer").Namespace("dbaccess").GlobalNamespace()); })) { // module dbaccess
113 if (isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("OCollection").Namespace("dbaccess").GlobalNamespace()); })) { // module dbaccess
118 const ClassTemplateSpecializationDecl
* pTemplate
= dyn_cast
<ClassTemplateSpecializationDecl
>(pRecordDecl
);
120 // Probably good templates:
121 loplugin::DeclCheck
dc(pTemplate
);
122 if ((dc
.Struct("FindUnoInstanceHint").AnonymousNamespace()
124 || (dc
.Class("OMultiInstanceAutoRegistration").Namespace("abp")
126 || (dc
.Class("Reference").Namespace("uno").Namespace("star")
127 .Namespace("sun").Namespace("com").GlobalNamespace())
128 || (dc
.Class("WeakReference").Namespace("uno").Namespace("star")
129 .Namespace("sun").Namespace("com").GlobalNamespace())
130 || (dc
.Class("Sequence").Namespace("uno").Namespace("star")
131 .Namespace("sun").Namespace("com").GlobalNamespace())
132 || (dc
.Class("WeakCppRef").Namespace("accessibility")
134 || (dc
.Class("OAutoRegistration").Namespace("dba")
136 || (dc
.Class("OMultiInstanceAutoRegistration").Namespace("dbp")
138 || (dc
.Class("OMultiInstanceAutoRegistration")
139 .Namespace("dbaui").GlobalNamespace())
140 || (dc
.Class("OMultiInstanceAutoRegistration")
141 .Namespace("dbaxml").GlobalNamespace())
142 || (dc
.Struct("ReferenceEqual").Namespace("io_acceptor")
144 || (dc
.Struct("ReferenceHash").Namespace("io_acceptor")
146 || (dc
.Class("OAutoRegistration").Namespace("comphelper")
148 || dc
.Class("WeakBag").Namespace("comphelper").GlobalNamespace()
149 || (dc
.Struct("class_").Namespace("service_decl")
150 .Namespace("comphelper").GlobalNamespace())
151 || (dc
.Struct("vba_service_class_").Namespace("service_decl")
152 .Namespace("comphelper").GlobalNamespace())
153 || (dc
.Struct("inheritingClass_").Namespace("service_decl")
154 .Namespace("comphelper").GlobalNamespace())
155 || (dc
.Class("OAutoRegistration").Namespace("module")
156 .Namespace("comphelper").GlobalNamespace())
157 || (dc
.Class("mem_fun1_t").Namespace("comphelper")
159 || (dc
.Class("OSimpleListenerContainer").Namespace("comphelper")
161 || (dc
.Class("OAutoRegistration").Namespace("dbmm")
163 || (dc
.Class("OAutoRegistration").Namespace("pcr")
165 || (dc
.Class("ComponentMethodGuard").Namespace("logging")
167 || (dc
.Class("OAutoRegistration").Namespace("logging")
169 || dc
.Class("Reference").Namespace("rtl").GlobalNamespace()
170 || (dc
.Class("OAutoRegistration").Namespace("sdbtools")
172 || (dc
.Struct("ReferenceEqual").Namespace("stoc_connector")
174 || (dc
.Struct("ReferenceHash").Namespace("stoc_connector")
176 || dc
.Class("mem_fun_t").StdNamespace()
177 || dc
.Class("mem_fun1_t").StdNamespace()
178 || dc
.Class("SwIterator").GlobalNamespace()
179 || (dc
.Class("SharedUNOComponent").Namespace("utl")
181 || (dc
.Class("OAutoRegistration").Namespace("utl")
183 || (dc
.Class("DeleteUnoReferenceOnDeinit").Namespace("vcl")
185 || (dc
.Struct("OInterfaceCompare").Namespace("xmloff")
192 if (pType
->isPointerType()) {
195 } else if (pType
->isArrayType()) {
196 const clang::ArrayType
* pArrayType
= dyn_cast
<clang::ArrayType
>(pType
);
197 QualType elementType
= pArrayType
->getElementType();
198 return containsXInterfaceSubclass(elementType
);
200 return isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("XInterface").Namespace("uno").Namespace("star").Namespace("sun").Namespace("com").GlobalNamespace()); });
204 bool containsSvRefBaseSubclass(const clang::Type
* pType0
) {
207 const clang::Type
* pType
= pType0
->getUnqualifiedDesugaredType();
210 const CXXRecordDecl
* pRecordDecl
= pType
->getAsCXXRecordDecl();
212 pRecordDecl
= pRecordDecl
->getCanonicalDecl();
215 const ClassTemplateSpecializationDecl
* pTemplate
= dyn_cast
<ClassTemplateSpecializationDecl
>(pRecordDecl
);
217 if (loplugin::DeclCheck(pTemplate
).Class("SvRef")
218 .Namespace("tools").GlobalNamespace())
222 for(unsigned i
=0; i
<pTemplate
->getTemplateArgs().size(); ++i
) {
223 const TemplateArgument
& rArg
= pTemplate
->getTemplateArgs()[i
];
224 if (rArg
.getKind() == TemplateArgument::ArgKind::Type
&&
225 containsSvRefBaseSubclass(rArg
.getAsType().getTypePtr()))
232 if (pType
->isPointerType()) {
235 } else if (pType
->isArrayType()) {
236 const clang::ArrayType
* pArrayType
= dyn_cast
<clang::ArrayType
>(pType
);
237 QualType elementType
= pArrayType
->getElementType();
238 return containsSvRefBaseSubclass(elementType
.getTypePtr());
240 return isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("SvRefBase").Namespace("tools").GlobalNamespace()); });
244 bool containsSalhelperReferenceObjectSubclass(const clang::Type
* pType0
) {
247 const clang::Type
* pType
= pType0
->getUnqualifiedDesugaredType();
250 const CXXRecordDecl
* pRecordDecl
= pType
->getAsCXXRecordDecl();
252 pRecordDecl
= pRecordDecl
->getCanonicalDecl();
255 const ClassTemplateSpecializationDecl
* pTemplate
= dyn_cast
<ClassTemplateSpecializationDecl
>(pRecordDecl
);
257 auto const dc
= loplugin::DeclCheck(pTemplate
);
258 if (dc
.Class("Reference").Namespace("rtl").GlobalNamespace()
259 || (dc
.Class("OStoreHandle").Namespace("store")
264 for(unsigned i
=0; i
<pTemplate
->getTemplateArgs().size(); ++i
) {
265 const TemplateArgument
& rArg
= pTemplate
->getTemplateArgs()[i
];
266 if (rArg
.getKind() == TemplateArgument::ArgKind::Type
&&
267 containsSalhelperReferenceObjectSubclass(rArg
.getAsType().getTypePtr()))
274 if (pType
->isPointerType()) {
277 } else if (pType
->isArrayType()) {
278 const clang::ArrayType
* pArrayType
= dyn_cast
<clang::ArrayType
>(pType
);
279 QualType elementType
= pArrayType
->getElementType();
280 return containsSalhelperReferenceObjectSubclass(elementType
.getTypePtr());
282 return isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("SimpleReferenceObject").Namespace("salhelper").GlobalNamespace()); });
286 static bool containsStaticTypeMethod(const CXXRecordDecl
* x
)
288 for (auto it
= x
->method_begin(); it
!= x
->method_end(); ++it
) {
290 if ( !i
->isStatic() )
292 auto ident
= i
->getIdentifier();
293 if ( ident
&& ident
->isStr("static_type") ) {
300 void RefCounting::checkUnoReference(QualType qt
, const Decl
* decl
, const RecordDecl
* parent
, const std::string
& rDeclName
)
302 if (loplugin::TypeCheck(qt
).Class("Reference").Namespace("uno").Namespace("star").Namespace("sun").Namespace("com").GlobalNamespace()) {
303 const CXXRecordDecl
* pRecordDecl
= qt
->getAsCXXRecordDecl();
304 const ClassTemplateSpecializationDecl
* pTemplate
= dyn_cast
<ClassTemplateSpecializationDecl
>(pRecordDecl
);
305 const TemplateArgument
& rArg
= pTemplate
->getTemplateArgs()[0];
306 const CXXRecordDecl
* templateParam
= rArg
.getAsType()->getAsCXXRecordDecl()->getDefinition();
307 if (templateParam
&& !containsStaticTypeMethod(templateParam
)) {
309 DiagnosticsEngine::Warning
,
310 ("uno::Reference %0 with template parameter that does not"
311 " contain ::static_type() %1%select{|, parent is %3,}2 should"
312 " probably be using rtl::Reference instead"),
314 << rDeclName
<< qt
<< (parent
!= nullptr)
315 << (parent
!= nullptr
316 ? parent
->getQualifiedNameAsString() : std::string())
317 << decl
->getSourceRange();
322 bool RefCounting::visitTemporaryObjectExpr(Expr
const * expr
) {
323 if (ignoreLocation(expr
)) {
326 auto t
= expr
->getType();
327 if (containsSvRefBaseSubclass(t
.getTypePtr())) {
329 DiagnosticsEngine::Warning
,
330 ("Temporary object of SvRefBase subclass %0 being directly stack"
331 " managed, should be managed via tools::SvRef"),
332 compat::getBeginLoc(expr
))
333 << t
.getUnqualifiedType() << expr
->getSourceRange();
334 } else if (containsSalhelperReferenceObjectSubclass(t
.getTypePtr())) {
336 DiagnosticsEngine::Warning
,
337 ("Temporary object of salhelper::SimpleReferenceObject subclass %0"
338 " being directly stack managed, should be managed via"
340 compat::getBeginLoc(expr
))
341 << t
.getUnqualifiedType() << expr
->getSourceRange();
342 } else if (containsXInterfaceSubclass(t
)) {
344 DiagnosticsEngine::Warning
,
345 ("Temporary object of css::uno::XInterface subclass %0 being"
346 " directly stack managed, should be managed via"
347 " css::uno::Reference"),
348 compat::getBeginLoc(expr
))
349 << t
.getUnqualifiedType() << expr
->getSourceRange();
354 bool RefCounting::VisitFieldDecl(const FieldDecl
* fieldDecl
) {
355 if (ignoreLocation(fieldDecl
)) {
358 if (fieldDecl
->isBitField()) {
362 // check for dodgy code managing ref-counted stuff with shared_ptr or unique_ptr or similar stuff
363 QualType firstTemplateParamType
;
364 if (auto recordType
= fieldDecl
->getType()->getUnqualifiedDesugaredType()->getAs
<RecordType
>()) {
365 auto const tc
= loplugin::TypeCheck(fieldDecl
->getType());
366 if (tc
.Class("unique_ptr").StdNamespace()
367 || tc
.Class("shared_ptr").StdNamespace()
368 || tc
.Class("intrusive_ptr").Namespace("boost").GlobalNamespace())
370 auto templateDecl
= dyn_cast
<ClassTemplateSpecializationDecl
>(recordType
->getDecl());
371 if (templateDecl
&& templateDecl
->getTemplateArgs().size() > 0)
372 firstTemplateParamType
= templateDecl
->getTemplateArgs()[0].getAsType();
376 if (containsSvRefBaseSubclass(fieldDecl
->getType().getTypePtr())) {
378 DiagnosticsEngine::Warning
,
379 "SvRefBase subclass %0 being directly heap managed, should be managed via tools::SvRef, "
381 fieldDecl
->getLocation())
382 << fieldDecl
->getType()
383 << fieldDecl
->getParent()
384 << fieldDecl
->getSourceRange();
387 if (!firstTemplateParamType
.isNull() && containsSvRefBaseSubclass(firstTemplateParamType
.getTypePtr()))
390 DiagnosticsEngine::Warning
,
391 "SvRefBase subclass %0 being managed via smart pointer, should be managed via tools::SvRef, "
393 fieldDecl
->getLocation())
394 << firstTemplateParamType
395 << fieldDecl
->getParent()
396 << fieldDecl
->getSourceRange();
399 if (containsSalhelperReferenceObjectSubclass(fieldDecl
->getType().getTypePtr())) {
401 DiagnosticsEngine::Warning
,
402 "salhelper::SimpleReferenceObject subclass %0 being directly heap managed, should be managed via rtl::Reference, "
404 fieldDecl
->getLocation())
405 << fieldDecl
->getType()
406 << fieldDecl
->getParent()
407 << fieldDecl
->getSourceRange();
410 if (!firstTemplateParamType
.isNull() && containsSalhelperReferenceObjectSubclass(firstTemplateParamType
.getTypePtr()))
413 DiagnosticsEngine::Warning
,
414 "salhelper::SimpleReferenceObject subclass %0 being managed via smart pointer, should be managed via rtl::Reference, "
416 fieldDecl
->getLocation())
417 << firstTemplateParamType
418 << fieldDecl
->getParent()
419 << fieldDecl
->getSourceRange();
422 auto const dc
= loplugin::DeclCheck(fieldDecl
->getParent());
423 if ( (dc
.Class("BaseReference").Namespace("uno").Namespace("star")
424 .Namespace("sun").Namespace("com").GlobalNamespace())
425 || (dc
.Union("element_alias").Namespace("detail").Namespace("cppu")
427 // this is playing some kind of game to avoid circular references
428 || (dc
.Class("ResultSetDataSupplier").Namespace("ucbhelper")
434 if (containsXInterfaceSubclass(fieldDecl
->getType())) {
436 DiagnosticsEngine::Warning
,
437 "XInterface subclass %0 being directly heap managed, should be managed via uno::Reference, "
439 fieldDecl
->getLocation())
440 << fieldDecl
->getType()
441 << fieldDecl
->getParent()
442 << fieldDecl
->getSourceRange();
445 // Not in general (dbaccess::DocumentEvents, dbaccess/source/core/dataaccess/databasedocument.hxx):
447 if (!firstTemplateParamType
.isNull() && containsXInterfaceSubclass(firstTemplateParamType
))
450 DiagnosticsEngine::Warning
,
451 "XInterface subclass %0 being managed via smart pointer, should be managed via uno::Reference, "
453 fieldDecl
->getLocation())
454 << firstTemplateParamType
455 << fieldDecl
->getParent()
456 << fieldDecl
->getSourceRange();
461 fieldDecl
->getType(), fieldDecl
,
462 fieldDecl
->getParent(), "field");
468 bool RefCounting::VisitVarDecl(const VarDecl
* varDecl
) {
469 if (ignoreLocation(varDecl
)) {
472 if (!isa
<ParmVarDecl
>(varDecl
)) {
473 if (containsSvRefBaseSubclass(varDecl
->getType().getTypePtr())) {
475 DiagnosticsEngine::Warning
,
476 "SvRefBase subclass being directly stack managed, should be managed via tools::SvRef, "
477 + varDecl
->getType().getAsString(),
478 varDecl
->getLocation())
479 << varDecl
->getSourceRange();
481 if (containsSalhelperReferenceObjectSubclass(varDecl
->getType().getTypePtr())) {
482 StringRef name
{ getFilenameOfLocation(
483 compiler
.getSourceManager().getSpellingLoc(varDecl
->getLocation())) };
484 // this is playing games that it believes is safe
485 if (loplugin::isSamePathname(name
, SRCDIR
"/stoc/source/security/permissions.cxx"))
488 DiagnosticsEngine::Warning
,
489 "salhelper::SimpleReferenceObject subclass being directly stack managed, should be managed via rtl::Reference, "
490 + varDecl
->getType().getAsString(),
491 varDecl
->getLocation())
492 << varDecl
->getSourceRange();
494 if (containsXInterfaceSubclass(varDecl
->getType())) {
496 DiagnosticsEngine::Warning
,
497 "XInterface subclass being directly stack managed, should be managed via uno::Reference, "
498 + varDecl
->getType().getAsString(),
499 varDecl
->getLocation())
500 << varDecl
->getSourceRange();
503 checkUnoReference(varDecl
->getType(), varDecl
, nullptr, "var");
507 bool RefCounting::VisitFunctionDecl(const FunctionDecl
* functionDecl
) {
508 if (ignoreLocation(functionDecl
)) {
511 // only consider base declarations, not overridden ones, or we warn on methods that
512 // are overriding stuff from external libraries
513 const CXXMethodDecl
* methodDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
);
514 if (methodDecl
&& methodDecl
->size_overridden_methods() > 0) {
517 checkUnoReference(functionDecl
->getReturnType(), functionDecl
, nullptr, "return");
521 loplugin::Plugin::Registration
< RefCounting
> X("refcounting");
525 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */