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 "config_clang.h"
16 #include "clang/AST/CXXInheritance.h"
22 class Foo : public css::foo::XBaa {
25 Then XBaa has acquire and release methods inherited from XInterface.
26 These are hard lifecycle controls.
28 If you see another class:
34 this is a bug =) since aFooMember assumes heap allocated lifecycle and
35 not delete on last 'release'.
42 public loplugin::FilteringPlugin
<RefCounting
>
45 explicit RefCounting(loplugin::InstantiationData
const & data
): FilteringPlugin(data
)
48 virtual bool preRun() override
{ return true; }
50 virtual void run() override
53 TraverseDecl(compiler
.getASTContext().getTranslationUnitDecl());
56 bool VisitFieldDecl(const FieldDecl
*);
57 bool VisitVarDecl(const VarDecl
*);
58 bool VisitFunctionDecl(const FunctionDecl
*);
59 bool VisitTypeLoc(clang::TypeLoc typeLoc
);
60 bool VisitCXXDeleteExpr(const CXXDeleteExpr
*);
61 bool VisitBinaryOperator(const BinaryOperator
*);
62 bool VisitReturnStmt(const ReturnStmt
*);
64 // Creation of temporaries with one argument are represented by
65 // CXXFunctionalCastExpr, while any other number of arguments are
66 // represented by CXXTemporaryObjectExpr:
67 bool VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr
const * expr
)
68 { return visitTemporaryObjectExpr(expr
); }
69 bool VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr
const * expr
)
70 { return visitTemporaryObjectExpr(expr
); }
73 void checkUnoReference(QualType qt
, const Decl
* decl
,
74 const RecordDecl
* parent
, const std::string
& rDeclName
);
76 bool visitTemporaryObjectExpr(Expr
const * expr
);
77 bool isCastingReference(const Expr
* expr
);
78 bool isCallingGetOnWeakRef(const Expr
* expr
);
81 bool containsXInterfaceSubclass(const clang::Type
* pType0
);
83 bool containsXInterfaceSubclass(const QualType
& qType
) {
84 return containsXInterfaceSubclass(qType
.getTypePtr());
87 bool containsXInterfaceSubclass(const clang::Type
* pType0
) {
90 const clang::Type
* pType
= pType0
->getUnqualifiedDesugaredType();
93 const CXXRecordDecl
* pRecordDecl
= pType
->getAsCXXRecordDecl();
95 pRecordDecl
= pRecordDecl
->getCanonicalDecl();
96 // these classes override acquire/release and forwards to its parent
97 if (loplugin::isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("ListenerMultiplexerBase").GlobalNamespace()); })) { // module UnoTools
100 if (loplugin::isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("GridEventForwarder").Namespace("toolkit").GlobalNamespace()); })) { // module toolkit
103 if (loplugin::isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("OWeakSubObject").GlobalNamespace()); })) { // module svx
106 if (loplugin::isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("OSbaWeakSubObject").Namespace("dbaui").GlobalNamespace()); })) { // module dbaccess
109 // FIXME This class has private operator new, and I cannot figure out how it can be dynamically instantiated
110 if (loplugin::isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("XPropertyList").GlobalNamespace()); })) { // module svx
114 if (loplugin::isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("OBookmarkContainer").Namespace("dbaccess").GlobalNamespace()); })) { // module dbaccess
119 if (loplugin::isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("OCollection").Namespace("dbaccess").GlobalNamespace()); })) { // module dbaccess
124 const ClassTemplateSpecializationDecl
* pTemplate
= dyn_cast
<ClassTemplateSpecializationDecl
>(pRecordDecl
);
126 // Probably good templates:
127 loplugin::DeclCheck
dc(pTemplate
);
128 if ((dc
.Struct("FindUnoInstanceHint").AnonymousNamespace()
130 || (dc
.Class("OMultiInstanceAutoRegistration").Namespace("abp")
132 || (dc
.Class("Reference").Namespace("uno").Namespace("star")
133 .Namespace("sun").Namespace("com").GlobalNamespace())
134 || (dc
.Class("WeakReference").Namespace("uno").Namespace("star")
135 .Namespace("sun").Namespace("com").GlobalNamespace())
136 || (dc
.Class("Sequence").Namespace("uno").Namespace("star")
137 .Namespace("sun").Namespace("com").GlobalNamespace())
138 || (dc
.Class("WeakCppRef").Namespace("accessibility")
140 || (dc
.Class("OAutoRegistration").Namespace("dba")
142 || (dc
.Class("OMultiInstanceAutoRegistration").Namespace("dbp")
144 || (dc
.Class("OMultiInstanceAutoRegistration")
145 .Namespace("dbaui").GlobalNamespace())
146 || (dc
.Class("OMultiInstanceAutoRegistration")
147 .Namespace("dbaxml").GlobalNamespace())
148 || (dc
.Struct("ReferenceEqual").Namespace("io_acceptor")
150 || (dc
.Struct("ReferenceHash").Namespace("io_acceptor")
152 || (dc
.Class("OAutoRegistration").Namespace("comphelper")
154 || dc
.Class("WeakBag").Namespace("comphelper").GlobalNamespace()
155 || (dc
.Struct("class_").Namespace("service_decl")
156 .Namespace("comphelper").GlobalNamespace())
157 || (dc
.Struct("vba_service_class_").Namespace("service_decl")
158 .Namespace("comphelper").GlobalNamespace())
159 || (dc
.Struct("inheritingClass_").Namespace("service_decl")
160 .Namespace("comphelper").GlobalNamespace())
161 || (dc
.Class("OAutoRegistration").Namespace("module")
162 .Namespace("comphelper").GlobalNamespace())
163 || (dc
.Class("mem_fun1_t").Namespace("comphelper")
165 || (dc
.Class("OSimpleListenerContainer").Namespace("comphelper")
167 || (dc
.Class("OAutoRegistration").Namespace("dbmm")
169 || (dc
.Class("OAutoRegistration").Namespace("pcr")
171 || (dc
.Class("ComponentMethodGuard").Namespace("logging")
173 || (dc
.Class("OAutoRegistration").Namespace("logging")
175 || dc
.Class("Reference").Namespace("rtl").GlobalNamespace()
176 || (dc
.Class("OAutoRegistration").Namespace("sdbtools")
178 || (dc
.Struct("ReferenceEqual").Namespace("stoc_connector")
180 || (dc
.Struct("ReferenceHash").Namespace("stoc_connector")
182 || dc
.Class("mem_fun_t").StdNamespace()
183 || dc
.Class("mem_fun1_t").StdNamespace()
184 || dc
.Class("SwIterator").GlobalNamespace()
185 || (dc
.Class("SharedUNOComponent").Namespace("utl")
187 || (dc
.Class("OAutoRegistration").Namespace("utl")
189 || (dc
.Class("DeleteUnoReferenceOnDeinit").Namespace("vcl")
191 || (dc
.Struct("OInterfaceCompare").Namespace("xmloff")
198 if (pType
->isPointerType()) {
201 } else if (pType
->isArrayType()) {
202 const clang::ArrayType
* pArrayType
= dyn_cast
<clang::ArrayType
>(pType
);
203 QualType elementType
= pArrayType
->getElementType();
204 return containsXInterfaceSubclass(elementType
);
206 return loplugin::isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("XInterface").Namespace("uno").Namespace("star").Namespace("sun").Namespace("com").GlobalNamespace()); });
210 bool containsOWeakObjectSubclass(const clang::Type
* pType0
);
212 bool containsOWeakObjectSubclass(const QualType
& qType
) {
213 return containsOWeakObjectSubclass(qType
.getTypePtr());
216 bool containsOWeakObjectSubclass(const clang::Type
* pType0
) {
219 if (pType0
->isDependentType()) {
222 const clang::Type
* pType
= pType0
->getUnqualifiedDesugaredType();
225 const CXXRecordDecl
* pRecordDecl
= pType
->getAsCXXRecordDecl();
227 // because dbaccess just has to be special...
228 loplugin::DeclCheck
dc(pRecordDecl
);
229 if (dc
.Class("DocumentEvents").Namespace("dbaccess")
230 .GlobalNamespace() ||
231 dc
.Class("OBookmarkContainer").Namespace("dbaccess")
234 // TODO not sure about these ones, just avoiding dbaccess in general for now
235 if (dc
.Class("SbaXPropertiesChangeMultiplexer").Namespace("dbaui").GlobalNamespace() ||
236 dc
.Class("SbaXSubmitMultiplexer").Namespace("dbaui").GlobalNamespace() ||
237 dc
.Class("SbaXResetMultiplexer").Namespace("dbaui").GlobalNamespace() ||
238 dc
.Class("SbaXPropertyChangeMultiplexer").Namespace("dbaui").GlobalNamespace() ||
239 dc
.Class("SbaXSQLErrorMultiplexer").Namespace("dbaui").GlobalNamespace() ||
240 dc
.Class("SbaXParameterMultiplexer").Namespace("dbaui").GlobalNamespace() ||
241 dc
.Class("SbaXRowSetApproveMultiplexer").Namespace("dbaui").GlobalNamespace() ||
242 dc
.Class("SbaXRowSetMultiplexer").Namespace("dbaui").GlobalNamespace() ||
243 dc
.Class("SbaXLoadMultiplexer").Namespace("dbaui").GlobalNamespace() ||
244 dc
.Class("SbaXVetoableChangeMultiplexer").Namespace("dbaui").GlobalNamespace())
246 // slideshow playing games here
247 if (dc
.Class("SlideView").AnonymousNamespace().Namespace("internal").Namespace("slideshow").GlobalNamespace())
249 // svx playing acquire/release games here in OWeakSubObject
250 if (dc
.Class("FmXUpdateMultiplexer").GlobalNamespace() ||
251 dc
.Class("FmXContainerMultiplexer").GlobalNamespace() ||
252 dc
.Class("FmXSelectionMultiplexer").GlobalNamespace() ||
253 dc
.Class("FmXGridControlMultiplexer").GlobalNamespace() ||
254 dc
.Class("FmXModifyMultiplexer").GlobalNamespace())
257 if (pType
->isPointerType()) {
260 } else if (pType
->isArrayType()) {
261 const clang::ArrayType
* pArrayType
= dyn_cast
<clang::ArrayType
>(pType
);
262 QualType elementType
= pArrayType
->getElementType();
263 return containsOWeakObjectSubclass(elementType
);
265 return loplugin::isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("OWeakObject").Namespace("cppu").GlobalNamespace()); });
269 bool containsSvRefBaseSubclass(const clang::Type
* pType0
) {
272 const clang::Type
* pType
= pType0
->getUnqualifiedDesugaredType();
275 const CXXRecordDecl
* pRecordDecl
= pType
->getAsCXXRecordDecl();
277 pRecordDecl
= pRecordDecl
->getCanonicalDecl();
280 const ClassTemplateSpecializationDecl
* pTemplate
= dyn_cast
<ClassTemplateSpecializationDecl
>(pRecordDecl
);
282 if (loplugin::DeclCheck(pTemplate
).Class("SvRef")
283 .Namespace("tools").GlobalNamespace())
287 for(unsigned i
=0; i
<pTemplate
->getTemplateArgs().size(); ++i
) {
288 const TemplateArgument
& rArg
= pTemplate
->getTemplateArgs()[i
];
289 if (rArg
.getKind() == TemplateArgument::ArgKind::Type
&&
290 containsSvRefBaseSubclass(rArg
.getAsType().getTypePtr()))
297 if (pType
->isPointerType()) {
300 } else if (pType
->isArrayType()) {
301 const clang::ArrayType
* pArrayType
= dyn_cast
<clang::ArrayType
>(pType
);
302 QualType elementType
= pArrayType
->getElementType();
303 return containsSvRefBaseSubclass(elementType
.getTypePtr());
305 return loplugin::isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("SvRefBase").Namespace("tools").GlobalNamespace()); });
309 bool containsSalhelperReferenceObjectSubclass(const clang::Type
* pType0
) {
312 const clang::Type
* pType
= pType0
->getUnqualifiedDesugaredType();
315 const CXXRecordDecl
* pRecordDecl
= pType
->getAsCXXRecordDecl();
317 pRecordDecl
= pRecordDecl
->getCanonicalDecl();
320 // for performance reasons we sometimes allocate temporaries on the stack
321 if (loplugin::DeclCheck(pRecordDecl
).Struct("ScSheetLimits").GlobalNamespace())
324 // the calc excel filter likes storing lots of classes either by reference or by value
325 if (loplugin::isDerivedFrom(pRecordDecl
,
326 [](Decl
const * decl
) -> bool
329 bool(loplugin::DeclCheck(decl
).Class("XclExpRecordBase").GlobalNamespace())
330 || bool(loplugin::DeclCheck(decl
).Class("XclImpChLineFormat").GlobalNamespace());
334 const ClassTemplateSpecializationDecl
* pTemplate
= dyn_cast
<ClassTemplateSpecializationDecl
>(pRecordDecl
);
336 auto const dc
= loplugin::DeclCheck(pTemplate
);
337 if (dc
.Class("Reference").Namespace("rtl").GlobalNamespace()
338 || (dc
.Class("OStoreHandle").AnonymousNamespace().Namespace("store")
340 || (dc
.Class("DeleteRtlReferenceOnDeinit").Namespace("tools")
345 for(unsigned i
=0; i
<pTemplate
->getTemplateArgs().size(); ++i
) {
346 const TemplateArgument
& rArg
= pTemplate
->getTemplateArgs()[i
];
347 if (rArg
.getKind() == TemplateArgument::ArgKind::Type
&&
348 containsSalhelperReferenceObjectSubclass(rArg
.getAsType().getTypePtr()))
355 if (pType
->isPointerType()) {
358 } else if (pType
->isArrayType()) {
359 const clang::ArrayType
* pArrayType
= dyn_cast
<clang::ArrayType
>(pType
);
360 QualType elementType
= pArrayType
->getElementType();
361 return containsSalhelperReferenceObjectSubclass(elementType
.getTypePtr());
363 return loplugin::isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("SimpleReferenceObject").Namespace("salhelper").GlobalNamespace()); });
367 static bool containsStaticTypeMethod(const CXXRecordDecl
* x
)
369 for (auto it
= x
->method_begin(); it
!= x
->method_end(); ++it
) {
371 if ( !i
->isStatic() )
373 auto ident
= i
->getIdentifier();
374 if ( ident
&& ident
->isStr("static_type") ) {
381 void RefCounting::checkUnoReference(QualType qt
, const Decl
* decl
, const RecordDecl
* parent
, const std::string
& rDeclName
)
383 if (!loplugin::TypeCheck(qt
).Class("Reference").Namespace("uno").Namespace("star").Namespace("sun").Namespace("com").GlobalNamespace())
385 const CXXRecordDecl
* pRecordDecl
= qt
->getAsCXXRecordDecl();
386 const ClassTemplateSpecializationDecl
* pTemplate
= dyn_cast
<ClassTemplateSpecializationDecl
>(pRecordDecl
);
387 const TemplateArgument
& rArg
= pTemplate
->getTemplateArgs()[0];
388 const CXXRecordDecl
* templateParam
= rArg
.getAsType()->getAsCXXRecordDecl()->getDefinition();
391 // SwXText is a special case. It is a mixin class that does not inherit from OWeakObject, so
392 // we cannot use rtl::Reference.
393 if (loplugin::DeclCheck(templateParam
).Class("SwXText"))
395 if (containsStaticTypeMethod(templateParam
))
398 DiagnosticsEngine::Warning
,
399 ("uno::Reference %0 with template parameter that does not"
400 " contain ::static_type() %1%select{|, parent is %3,}2 should"
401 " probably be using rtl::Reference instead"),
403 << rDeclName
<< qt
<< (parent
!= nullptr)
404 << (parent
!= nullptr
405 ? parent
->getQualifiedNameAsString() : std::string())
406 << decl
->getSourceRange();
409 bool RefCounting::visitTemporaryObjectExpr(Expr
const * expr
) {
410 if (ignoreLocation(expr
)) {
413 auto t
= expr
->getType();
414 if (containsSvRefBaseSubclass(t
.getTypePtr())) {
416 DiagnosticsEngine::Warning
,
417 ("Temporary object of SvRefBase subclass %0 being directly stack"
418 " managed, should be managed via tools::SvRef"),
420 << t
.getUnqualifiedType() << expr
->getSourceRange();
421 } else if (containsSalhelperReferenceObjectSubclass(t
.getTypePtr())) {
423 DiagnosticsEngine::Warning
,
424 ("Temporary object of salhelper::SimpleReferenceObject subclass %0"
425 " being directly stack managed, should be managed via"
428 << t
.getUnqualifiedType() << expr
->getSourceRange();
429 } else if (containsXInterfaceSubclass(t
)) {
431 DiagnosticsEngine::Warning
,
432 ("Temporary object of css::uno::XInterface subclass %0 being"
433 " directly stack managed, should be managed via"
434 " css::uno::Reference"),
436 << t
.getUnqualifiedType() << expr
->getSourceRange();
437 } else if (containsOWeakObjectSubclass(t
)) {
439 DiagnosticsEngine::Warning
,
440 ("Temporary object of cppu::OWeakObject subclass %0 being"
441 " directly stack managed, should be managed via"
442 " css::uno::Reference"),
444 << t
.getUnqualifiedType() << expr
->getSourceRange();
449 // check for dodgy code managing ref-counted stuff with shared_ptr or unique_ptr or similar stuff
450 bool RefCounting::VisitTypeLoc(clang::TypeLoc typeLoc
)
452 QualType firstTemplateParamType
;
453 if (auto recordType
= typeLoc
.getType()->getUnqualifiedDesugaredType()->getAs
<RecordType
>()) {
454 auto const tc
= loplugin::TypeCheck(recordType
);
455 if (tc
.ClassOrStruct("unique_ptr").StdNamespace()
456 || tc
.ClassOrStruct("weak_ptr").StdNamespace()
457 || tc
.ClassOrStruct("shared_ptr").StdNamespace()
458 || tc
.ClassOrStruct("intrusive_ptr").Namespace("boost").GlobalNamespace())
460 auto templateDecl
= dyn_cast
<ClassTemplateSpecializationDecl
>(recordType
->getDecl());
461 if (templateDecl
&& templateDecl
->getTemplateArgs().size() > 0)
462 firstTemplateParamType
= templateDecl
->getTemplateArgs()[0].getAsType();
465 if (firstTemplateParamType
.isNull())
467 if (containsSvRefBaseSubclass(firstTemplateParamType
.getTypePtr()))
470 DiagnosticsEngine::Warning
,
471 "SvRefBase subclass %0 being managed via smart pointer, should be managed via tools::SvRef",
472 typeLoc
.getBeginLoc())
473 << firstTemplateParamType
474 << typeLoc
.getSourceRange();
476 if (containsSalhelperReferenceObjectSubclass(firstTemplateParamType
.getTypePtr()))
479 DiagnosticsEngine::Warning
,
480 "salhelper::SimpleReferenceObject subclass %0 being managed via smart pointer, should be managed via rtl::Reference",
481 typeLoc
.getBeginLoc())
482 << firstTemplateParamType
483 << typeLoc
.getSourceRange();
485 // Not in general (dbaccess::DocumentEvents, dbaccess/source/core/dataaccess/databasedocument.hxx):
487 if (containsXInterfaceSubclass(firstTemplateParamType
))
490 DiagnosticsEngine::Warning
,
491 "XInterface subclass %0 being managed via smart pointer, should be managed via uno::Reference",
492 typeLoc
.getBeginLoc())
493 << firstTemplateParamType
494 << typeLoc
.getSourceRange();
497 if (containsOWeakObjectSubclass(firstTemplateParamType
.getTypePtr()))
500 DiagnosticsEngine::Warning
,
501 "cppu::OWeakObject subclass %0 being managed via smart pointer, should be managed via rtl::Reference",
502 typeLoc
.getBeginLoc())
503 << firstTemplateParamType
504 << typeLoc
.getSourceRange();
509 bool RefCounting::VisitCXXDeleteExpr(const CXXDeleteExpr
* cxxDeleteExpr
)
511 if (ignoreLocation(cxxDeleteExpr
))
513 StringRef aFileName
= getFilenameOfLocation(
514 compiler
.getSourceManager().getSpellingLoc(cxxDeleteExpr
->getBeginLoc()));
515 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/cppuhelper/source/weak.cxx"))
517 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/include/svx/svdobj.hxx"))
519 if (loplugin::isSamePathname(aFileName
, SRCDIR
"/svx/source/svdraw/svdobj.cxx"))
522 if (!cxxDeleteExpr
->getArgument())
524 auto argType
= cxxDeleteExpr
->getArgument()->getType();
525 if (argType
.isNull() || !argType
->isPointerType())
527 auto pointeeType
= argType
->getPointeeType();
528 if (containsOWeakObjectSubclass(pointeeType
))
531 DiagnosticsEngine::Warning
,
532 "cppu::OWeakObject subclass %0 being deleted via delete, should be managed via rtl::Reference",
533 cxxDeleteExpr
->getBeginLoc())
535 << cxxDeleteExpr
->getSourceRange();
540 bool RefCounting::VisitFieldDecl(const FieldDecl
* fieldDecl
) {
541 if (ignoreLocation(fieldDecl
)) {
544 if (fieldDecl
->isBitField()) {
548 // We can't call FieldDecl::getParent, which triggers an assertion at least with
549 // current trunk towards Clang 3.7 when the FieldDecl is actually an
551 if (isa
<ObjCIvarDecl
>(fieldDecl
)) {
555 if (containsSvRefBaseSubclass(fieldDecl
->getType().getTypePtr())) {
557 DiagnosticsEngine::Warning
,
558 "SvRefBase subclass %0 being directly heap managed, should be managed via tools::SvRef, "
560 fieldDecl
->getLocation())
561 << fieldDecl
->getType()
562 << fieldDecl
->getParent()
563 << fieldDecl
->getSourceRange();
566 if (containsSalhelperReferenceObjectSubclass(fieldDecl
->getType().getTypePtr())) {
568 DiagnosticsEngine::Warning
,
569 "salhelper::SimpleReferenceObject subclass %0 being directly heap managed, should be managed via rtl::Reference, "
571 fieldDecl
->getLocation())
572 << fieldDecl
->getType()
573 << fieldDecl
->getParent()
574 << fieldDecl
->getSourceRange();
577 auto const dc
= loplugin::DeclCheck(fieldDecl
->getParent());
578 if ( (dc
.Class("BaseReference").Namespace("uno").Namespace("star")
579 .Namespace("sun").Namespace("com").GlobalNamespace())
580 || (dc
.Class("Reference").Namespace("rtl").GlobalNamespace())
581 || (dc
.Union("element_alias").Namespace("detail").Namespace("cppu")
583 // this is playing some kind of game to avoid circular references
584 || (dc
.Class("ResultSetDataSupplier").Namespace("ucbhelper")
590 if (containsXInterfaceSubclass(fieldDecl
->getType())) {
592 DiagnosticsEngine::Warning
,
593 "XInterface subclass %0 being directly heap managed, should be managed via uno::Reference, "
595 fieldDecl
->getLocation())
596 << fieldDecl
->getType()
597 << fieldDecl
->getParent()
598 << fieldDecl
->getSourceRange();
601 if (containsOWeakObjectSubclass(fieldDecl
->getType())) {
603 DiagnosticsEngine::Warning
,
604 "cppu::OWeakObject subclass %0 being directly heap managed, should be managed via rtl::Reference, "
606 fieldDecl
->getLocation())
607 << fieldDecl
->getType()
608 << fieldDecl
->getParent()
609 << fieldDecl
->getSourceRange();
613 fieldDecl
->getType(), fieldDecl
,
614 fieldDecl
->getParent(), "field");
619 bool RefCounting::VisitReturnStmt(const ReturnStmt
* returnStmt
) {
620 if (ignoreLocation(returnStmt
)) {
624 if (!returnStmt
->getRetValue())
626 auto cxxNewExpr
= dyn_cast
<CXXNewExpr
>(returnStmt
->getRetValue()->IgnoreImplicit());
630 auto qt
= returnStmt
->getRetValue()->getType();
631 if (!qt
->isPointerType())
633 qt
= qt
->getPointeeType();
635 if (containsOWeakObjectSubclass(qt
)) {
637 DiagnosticsEngine::Warning
,
638 "new object of cppu::OWeakObject subclass %0 being returned via raw pointer, should be returned by via rtl::Reference",
639 returnStmt
->getBeginLoc())
641 << returnStmt
->getSourceRange();
647 bool RefCounting::VisitVarDecl(const VarDecl
* varDecl
) {
648 if (ignoreLocation(varDecl
))
651 checkUnoReference(varDecl
->getType(), varDecl
, nullptr, "var");
653 if (isa
<ParmVarDecl
>(varDecl
))
656 if (containsSvRefBaseSubclass(varDecl
->getType().getTypePtr())) {
658 DiagnosticsEngine::Warning
,
659 "SvRefBase subclass being directly stack managed, should be managed via tools::SvRef, "
660 + varDecl
->getType().getAsString(),
661 varDecl
->getLocation())
662 << varDecl
->getSourceRange();
664 if (containsSalhelperReferenceObjectSubclass(varDecl
->getType().getTypePtr())) {
665 StringRef name
{ getFilenameOfLocation(
666 compiler
.getSourceManager().getSpellingLoc(varDecl
->getLocation())) };
667 // this is playing games that it believes is safe
668 if (loplugin::isSamePathname(name
, SRCDIR
"/stoc/source/security/permissions.cxx"))
671 DiagnosticsEngine::Warning
,
672 "salhelper::SimpleReferenceObject subclass being directly stack managed, should be managed via rtl::Reference, "
673 + varDecl
->getType().getAsString(),
674 varDecl
->getLocation())
675 << varDecl
->getSourceRange();
677 if (containsXInterfaceSubclass(varDecl
->getType())) {
679 DiagnosticsEngine::Warning
,
680 "XInterface subclass being directly stack managed, should be managed via uno::Reference, "
681 + varDecl
->getType().getAsString(),
682 varDecl
->getLocation())
683 << varDecl
->getSourceRange();
685 if (containsOWeakObjectSubclass(varDecl
->getType())) {
687 DiagnosticsEngine::Warning
,
688 "cppu::OWeakObject subclass being directly stack managed, should be managed via uno::Reference, "
689 + varDecl
->getType().getAsString(),
690 varDecl
->getLocation())
691 << varDecl
->getSourceRange();
694 if (varDecl
->getType()->isPointerType() && varDecl
->getInit())
696 auto newExpr
= dyn_cast
<CXXNewExpr
>(varDecl
->getInit()->IgnoreImplicit());
699 StringRef fileName
= getFilenameOfLocation(compiler
.getSourceManager().getSpellingLoc(varDecl
->getBeginLoc()));
700 if (loplugin::isSamePathname(fileName
, SRCDIR
"/cppuhelper/source/component_context.cxx"))
702 auto pointeeType
= varDecl
->getType()->getPointeeType();
703 if (containsOWeakObjectSubclass(pointeeType
))
705 DiagnosticsEngine::Warning
,
706 "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
707 varDecl
->getLocation())
709 << varDecl
->getSourceRange();
711 if (isCastingReference(varDecl
->getInit()))
714 StringRef fileName
= getFilenameOfLocation(compiler
.getSourceManager().getSpellingLoc(varDecl
->getBeginLoc()));
715 if (loplugin::isSamePathname(fileName
, SRCDIR
"/sw/source/core/unocore/unotbl.cxx"))
717 auto pointeeType
= varDecl
->getType()->getPointeeType();
718 if (containsOWeakObjectSubclass(pointeeType
))
720 DiagnosticsEngine::Warning
,
721 "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
722 varDecl
->getLocation())
724 << varDecl
->getSourceRange();
726 if (isCallingGetOnWeakRef(varDecl
->getInit()))
728 auto pointeeType
= varDecl
->getType()->getPointeeType();
729 if (containsOWeakObjectSubclass(pointeeType
))
731 DiagnosticsEngine::Warning
,
732 "weak object being converted to strong, and then the reference dropped, and managed via raw pointer, should be managed via rtl::Reference",
733 varDecl
->getLocation())
735 << varDecl
->getSourceRange();
743 static_cast<FooChild*>(makeFoo().get());
744 where makeFoo() returns a Reference<Foo>
746 bool RefCounting::isCastingReference(const Expr
* expr
)
748 expr
= expr
->IgnoreImplicit();
749 auto castExpr
= dyn_cast
<CastExpr
>(expr
);
752 auto memberCallExpr
= dyn_cast
<CXXMemberCallExpr
>(castExpr
->getSubExpr());
755 if (!memberCallExpr
->getMethodDecl()->getIdentifier() || memberCallExpr
->getMethodDecl()->getName() != "get")
757 QualType objectType
= memberCallExpr
->getImplicitObjectArgument()->getType();
758 if (!loplugin::TypeCheck(objectType
).Class("Reference"))
760 // ignore "x.get()" where x is a var
761 auto obj
= memberCallExpr
->getImplicitObjectArgument()->IgnoreImplicit();
762 if (isa
<DeclRefExpr
>(obj
) || isa
<MemberExpr
>(obj
))
764 // if the foo in foo().get() returns "rtl::Reference<T>&" then the variable
765 // we are assigning to does not __have__ to be Reference, since the method called
766 // must already be holding a reference.
767 if (auto callExpr
= dyn_cast
<CallExpr
>(obj
))
769 if (auto callMethod
= callExpr
->getDirectCallee())
770 if (callMethod
->getReturnType()->isReferenceType())
777 if (auto memberCall2
= dyn_cast
<CXXMemberCallExpr
>(obj
))
779 if (loplugin::TypeCheck(memberCall2
->getImplicitObjectArgument()->getType()).Class("WeakReference"))
789 cast<T*>(makeFoo().get().get());
792 where makeFoo() returns a unotools::WeakReference<Foo>
793 and foo is a unotools::WeakReference<Foo> var.
795 bool RefCounting::isCallingGetOnWeakRef(const Expr
* expr
)
797 expr
= expr
->IgnoreImplicit();
798 // unwrap the cast (if any)
799 if (auto castExpr
= dyn_cast
<CastExpr
>(expr
))
800 expr
= castExpr
->getSubExpr()->IgnoreImplicit();
801 // unwrap outer get (if any)
802 if (auto memberCallExpr
= dyn_cast
<CXXMemberCallExpr
>(expr
))
804 auto methodDecl
= memberCallExpr
->getMethodDecl();
805 if (methodDecl
&& methodDecl
->getIdentifier() && methodDecl
->getName() == "get")
807 QualType objectType
= memberCallExpr
->getImplicitObjectArgument()->getType();
808 if (loplugin::TypeCheck(objectType
).Class("Reference").Namespace("rtl"))
809 expr
= memberCallExpr
->getImplicitObjectArgument()->IgnoreImplicit();
812 // check for converting a WeakReference to a strong reference via get()
813 if (auto memberCallExpr
= dyn_cast
<CXXMemberCallExpr
>(expr
))
815 auto methodDecl
= memberCallExpr
->getMethodDecl();
816 if (methodDecl
&& methodDecl
->getIdentifier() && methodDecl
->getName() == "get")
818 QualType objectType
= memberCallExpr
->getImplicitObjectArgument()->getType();
819 if (loplugin::TypeCheck(objectType
).Class("WeakReference").Namespace("unotools"))
826 bool RefCounting::VisitBinaryOperator(const BinaryOperator
* binaryOperator
)
828 if (ignoreLocation(binaryOperator
))
830 if (binaryOperator
->getOpcode() != BO_Assign
)
832 if (!binaryOperator
->getLHS()->getType()->isPointerType())
835 auto newExpr
= dyn_cast
<CXXNewExpr
>(binaryOperator
->getRHS()->IgnoreImplicit());
838 // deliberately does not want to keep track at the allocation site
839 StringRef fileName
= getFilenameOfLocation(compiler
.getSourceManager().getSpellingLoc(binaryOperator
->getBeginLoc()));
840 if (loplugin::isSamePathname(fileName
, SRCDIR
"/vcl/unx/generic/dtrans/X11_selection.cxx"))
843 auto pointeeType
= binaryOperator
->getLHS()->getType()->getPointeeType();
844 if (containsOWeakObjectSubclass(pointeeType
))
847 DiagnosticsEngine::Warning
,
848 "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
849 binaryOperator
->getBeginLoc())
851 << binaryOperator
->getSourceRange();
854 if (isCastingReference(binaryOperator
->getRHS()))
856 auto pointeeType
= binaryOperator
->getLHS()->getType()->getPointeeType();
857 if (containsOWeakObjectSubclass(pointeeType
))
859 DiagnosticsEngine::Warning
,
860 "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
861 binaryOperator
->getBeginLoc())
863 << binaryOperator
->getSourceRange();
865 if (isCallingGetOnWeakRef(binaryOperator
->getRHS()))
867 // TODO Very dodgy code, but I see no simple way of fixing it
868 StringRef fileName
= getFilenameOfLocation(compiler
.getSourceManager().getSpellingLoc(binaryOperator
->getBeginLoc()));
869 if (loplugin::isSamePathname(fileName
, SRCDIR
"/sd/source/ui/view/Outliner.cxx"))
871 auto pointeeType
= binaryOperator
->getLHS()->getType()->getPointeeType();
872 if (containsOWeakObjectSubclass(pointeeType
))
874 DiagnosticsEngine::Warning
,
875 "weak object being converted to strong, and then the reference dropped, and managed via raw pointer, should be managed via rtl::Reference",
876 binaryOperator
->getBeginLoc())
878 << binaryOperator
->getSourceRange();
883 bool RefCounting::VisitFunctionDecl(const FunctionDecl
* functionDecl
) {
884 if (ignoreLocation(functionDecl
)) {
887 // only consider base declarations, not overridden ones, or we warn on methods that
888 // are overriding stuff from external libraries
889 const CXXMethodDecl
* methodDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
);
890 if (methodDecl
&& methodDecl
->size_overridden_methods() > 0) {
893 checkUnoReference(functionDecl
->getReturnType(), functionDecl
, nullptr, "return");
897 loplugin::Plugin::Registration
< RefCounting
> refcounting("refcounting");
901 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */