lok: getSlideShowInfo: interactions: check that properties are available
[LibreOffice.git] / compilerplugins / clang / refcounting.cxx
blob4140b6a106886769ee22ef804a7c938c50e381b5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include <string>
11 #include <iostream>
13 #include "check.hxx"
14 #include "plugin.hxx"
15 #include "config_clang.h"
16 #include "clang/AST/CXXInheritance.h"
18 /**
20 If you have:
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:
30 class Baz {
31 Foo aFooMember;
34 this is a bug =) since aFooMember assumes heap allocated lifecycle and
35 not delete on last 'release'.
39 namespace {
41 class RefCounting:
42 public loplugin::FilteringPlugin<RefCounting>
44 public:
45 explicit RefCounting(loplugin::InstantiationData const & data): FilteringPlugin(data)
48 virtual bool preRun() override { return true; }
50 virtual void run() override
52 if (preRun())
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); }
72 private:
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) {
88 if (!pType0)
89 return false;
90 const clang::Type* pType = pType0->getUnqualifiedDesugaredType();
91 if (!pType)
92 return false;
93 const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl();
94 if (pRecordDecl) {
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
98 return false;
100 if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("GridEventForwarder").Namespace("toolkit").GlobalNamespace()); })) { // module toolkit
101 return false;
103 if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OWeakSubObject").GlobalNamespace()); })) { // module svx
104 return false;
106 if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OSbaWeakSubObject").Namespace("dbaui").GlobalNamespace()); })) { // module dbaccess
107 return false;
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
111 return false;
113 // tdf#114596
114 if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OBookmarkContainer").Namespace("dbaccess").GlobalNamespace()); })) { // module dbaccess
115 return false;
118 // tdf#114596
119 if (loplugin::isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OCollection").Namespace("dbaccess").GlobalNamespace()); })) { // module dbaccess
120 return false;
123 if (pRecordDecl) {
124 const ClassTemplateSpecializationDecl* pTemplate = dyn_cast<ClassTemplateSpecializationDecl>(pRecordDecl);
125 if (pTemplate) {
126 // Probably good templates:
127 loplugin::DeclCheck dc(pTemplate);
128 if ((dc.Struct("FindUnoInstanceHint").AnonymousNamespace()
129 .GlobalNamespace())
130 || (dc.Class("OMultiInstanceAutoRegistration").Namespace("abp")
131 .GlobalNamespace())
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")
139 .GlobalNamespace())
140 || (dc.Class("OAutoRegistration").Namespace("dba")
141 .GlobalNamespace())
142 || (dc.Class("OMultiInstanceAutoRegistration").Namespace("dbp")
143 .GlobalNamespace())
144 || (dc.Class("OMultiInstanceAutoRegistration")
145 .Namespace("dbaui").GlobalNamespace())
146 || (dc.Class("OMultiInstanceAutoRegistration")
147 .Namespace("dbaxml").GlobalNamespace())
148 || (dc.Struct("ReferenceEqual").Namespace("io_acceptor")
149 .GlobalNamespace())
150 || (dc.Struct("ReferenceHash").Namespace("io_acceptor")
151 .GlobalNamespace())
152 || (dc.Class("OAutoRegistration").Namespace("comphelper")
153 .GlobalNamespace())
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")
164 .GlobalNamespace())
165 || (dc.Class("OSimpleListenerContainer").Namespace("comphelper")
166 .GlobalNamespace())
167 || (dc.Class("OAutoRegistration").Namespace("dbmm")
168 .GlobalNamespace())
169 || (dc.Class("OAutoRegistration").Namespace("pcr")
170 .GlobalNamespace())
171 || (dc.Class("ComponentMethodGuard").Namespace("logging")
172 .GlobalNamespace())
173 || (dc.Class("OAutoRegistration").Namespace("logging")
174 .GlobalNamespace())
175 || dc.Class("Reference").Namespace("rtl").GlobalNamespace()
176 || (dc.Class("OAutoRegistration").Namespace("sdbtools")
177 .GlobalNamespace())
178 || (dc.Struct("ReferenceEqual").Namespace("stoc_connector")
179 .GlobalNamespace())
180 || (dc.Struct("ReferenceHash").Namespace("stoc_connector")
181 .GlobalNamespace())
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")
186 .GlobalNamespace())
187 || (dc.Class("OAutoRegistration").Namespace("utl")
188 .GlobalNamespace())
189 || (dc.Class("DeleteUnoReferenceOnDeinit").Namespace("vcl")
190 .GlobalNamespace())
191 || (dc.Struct("OInterfaceCompare").Namespace("xmloff")
192 .GlobalNamespace()))
194 return false;
198 if (pType->isPointerType()) {
199 // ignore
200 return false;
201 } else if (pType->isArrayType()) {
202 const clang::ArrayType* pArrayType = dyn_cast<clang::ArrayType>(pType);
203 QualType elementType = pArrayType->getElementType();
204 return containsXInterfaceSubclass(elementType);
205 } else {
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) {
217 if (!pType0)
218 return false;
219 if (pType0->isDependentType()) {
220 return false;
222 const clang::Type* pType = pType0->getUnqualifiedDesugaredType();
223 if (!pType)
224 return false;
225 const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl();
226 if (pRecordDecl) {
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")
232 .GlobalNamespace())
233 return false;
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())
245 return false;
246 // slideshow playing games here
247 if (dc.Class("SlideView").AnonymousNamespace().Namespace("internal").Namespace("slideshow").GlobalNamespace())
248 return false;
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())
255 return false;
257 if (pType->isPointerType()) {
258 // ignore
259 return false;
260 } else if (pType->isArrayType()) {
261 const clang::ArrayType* pArrayType = dyn_cast<clang::ArrayType>(pType);
262 QualType elementType = pArrayType->getElementType();
263 return containsOWeakObjectSubclass(elementType);
264 } else {
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) {
270 if (!pType0)
271 return false;
272 const clang::Type* pType = pType0->getUnqualifiedDesugaredType();
273 if (!pType)
274 return false;
275 const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl();
276 if (pRecordDecl) {
277 pRecordDecl = pRecordDecl->getCanonicalDecl();
279 if (pRecordDecl) {
280 const ClassTemplateSpecializationDecl* pTemplate = dyn_cast<ClassTemplateSpecializationDecl>(pRecordDecl);
281 if (pTemplate) {
282 if (loplugin::DeclCheck(pTemplate).Class("SvRef")
283 .Namespace("tools").GlobalNamespace())
285 return false;
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()))
292 return true;
297 if (pType->isPointerType()) {
298 // ignore
299 return false;
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());
304 } else {
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) {
310 if (!pType0)
311 return false;
312 const clang::Type* pType = pType0->getUnqualifiedDesugaredType();
313 if (!pType)
314 return false;
315 const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl();
316 if (pRecordDecl) {
317 pRecordDecl = pRecordDecl->getCanonicalDecl();
319 if (pRecordDecl) {
320 // for performance reasons we sometimes allocate temporaries on the stack
321 if (loplugin::DeclCheck(pRecordDecl).Struct("ScSheetLimits").GlobalNamespace())
322 return false;
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
328 return
329 bool(loplugin::DeclCheck(decl).Class("XclExpRecordBase").GlobalNamespace())
330 || bool(loplugin::DeclCheck(decl).Class("XclImpChLineFormat").GlobalNamespace());
332 return false;
334 const ClassTemplateSpecializationDecl* pTemplate = dyn_cast<ClassTemplateSpecializationDecl>(pRecordDecl);
335 if (pTemplate) {
336 auto const dc = loplugin::DeclCheck(pTemplate);
337 if (dc.Class("Reference").Namespace("rtl").GlobalNamespace()
338 || (dc.Class("OStoreHandle").AnonymousNamespace().Namespace("store")
339 .GlobalNamespace())
340 || (dc.Class("DeleteRtlReferenceOnDeinit").Namespace("tools")
341 .GlobalNamespace()))
343 return false;
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()))
350 return true;
355 if (pType->isPointerType()) {
356 // ignore
357 return false;
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());
362 } else {
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) {
370 auto i = *it;
371 if ( !i->isStatic() )
372 continue;
373 auto ident = i->getIdentifier();
374 if ( ident && ident->isStr("static_type") ) {
375 return true;
378 return false;
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())
384 return;
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();
389 if (!templateParam)
390 return;
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"))
394 return;
395 if (containsStaticTypeMethod(templateParam))
396 return;
397 report(
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"),
402 decl->getLocation())
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)) {
411 return true;
413 auto t = expr->getType();
414 if (containsSvRefBaseSubclass(t.getTypePtr())) {
415 report(
416 DiagnosticsEngine::Warning,
417 ("Temporary object of SvRefBase subclass %0 being directly stack"
418 " managed, should be managed via tools::SvRef"),
419 expr->getBeginLoc())
420 << t.getUnqualifiedType() << expr->getSourceRange();
421 } else if (containsSalhelperReferenceObjectSubclass(t.getTypePtr())) {
422 report(
423 DiagnosticsEngine::Warning,
424 ("Temporary object of salhelper::SimpleReferenceObject subclass %0"
425 " being directly stack managed, should be managed via"
426 " rtl::Reference"),
427 expr->getBeginLoc())
428 << t.getUnqualifiedType() << expr->getSourceRange();
429 } else if (containsXInterfaceSubclass(t)) {
430 report(
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"),
435 expr->getBeginLoc())
436 << t.getUnqualifiedType() << expr->getSourceRange();
437 } else if (containsOWeakObjectSubclass(t)) {
438 report(
439 DiagnosticsEngine::Warning,
440 ("Temporary object of cppu::OWeakObject subclass %0 being"
441 " directly stack managed, should be managed via"
442 " css::uno::Reference"),
443 expr->getBeginLoc())
444 << t.getUnqualifiedType() << expr->getSourceRange();
446 return true;
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())
466 return true;
467 if (containsSvRefBaseSubclass(firstTemplateParamType.getTypePtr()))
469 report(
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()))
478 report(
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):
486 #if 0
487 if (containsXInterfaceSubclass(firstTemplateParamType))
489 report(
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();
496 #endif
497 if (containsOWeakObjectSubclass(firstTemplateParamType.getTypePtr()))
499 report(
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();
506 return true;
509 bool RefCounting::VisitCXXDeleteExpr(const CXXDeleteExpr * cxxDeleteExpr)
511 if (ignoreLocation(cxxDeleteExpr))
512 return true;
513 StringRef aFileName = getFilenameOfLocation(
514 compiler.getSourceManager().getSpellingLoc(cxxDeleteExpr->getBeginLoc()));
515 if (loplugin::isSamePathname(aFileName, SRCDIR "/cppuhelper/source/weak.cxx"))
516 return true;
517 if (loplugin::isSamePathname(aFileName, SRCDIR "/include/svx/svdobj.hxx"))
518 return true;
519 if (loplugin::isSamePathname(aFileName, SRCDIR "/svx/source/svdraw/svdobj.cxx"))
520 return true;
522 if (!cxxDeleteExpr->getArgument())
523 return true;
524 auto argType = cxxDeleteExpr->getArgument()->getType();
525 if (argType.isNull() || !argType->isPointerType())
526 return true;
527 auto pointeeType = argType->getPointeeType();
528 if (containsOWeakObjectSubclass(pointeeType))
530 report(
531 DiagnosticsEngine::Warning,
532 "cppu::OWeakObject subclass %0 being deleted via delete, should be managed via rtl::Reference",
533 cxxDeleteExpr->getBeginLoc())
534 << pointeeType
535 << cxxDeleteExpr->getSourceRange();
537 return true;
540 bool RefCounting::VisitFieldDecl(const FieldDecl * fieldDecl) {
541 if (ignoreLocation(fieldDecl)) {
542 return true;
544 if (fieldDecl->isBitField()) {
545 return true;
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
550 // ObjCIvarDecl.
551 if (isa<ObjCIvarDecl>(fieldDecl)) {
552 return true;
555 if (containsSvRefBaseSubclass(fieldDecl->getType().getTypePtr())) {
556 report(
557 DiagnosticsEngine::Warning,
558 "SvRefBase subclass %0 being directly heap managed, should be managed via tools::SvRef, "
559 ", parent is %1",
560 fieldDecl->getLocation())
561 << fieldDecl->getType()
562 << fieldDecl->getParent()
563 << fieldDecl->getSourceRange();
566 if (containsSalhelperReferenceObjectSubclass(fieldDecl->getType().getTypePtr())) {
567 report(
568 DiagnosticsEngine::Warning,
569 "salhelper::SimpleReferenceObject subclass %0 being directly heap managed, should be managed via rtl::Reference, "
570 "parent is %1",
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")
582 .GlobalNamespace())
583 // this is playing some kind of game to avoid circular references
584 || (dc.Class("ResultSetDataSupplier").Namespace("ucbhelper")
585 .GlobalNamespace()))
587 return true;
590 if (containsXInterfaceSubclass(fieldDecl->getType())) {
591 report(
592 DiagnosticsEngine::Warning,
593 "XInterface subclass %0 being directly heap managed, should be managed via uno::Reference, "
594 "parent is %1",
595 fieldDecl->getLocation())
596 << fieldDecl->getType()
597 << fieldDecl->getParent()
598 << fieldDecl->getSourceRange();
601 if (containsOWeakObjectSubclass(fieldDecl->getType())) {
602 report(
603 DiagnosticsEngine::Warning,
604 "cppu::OWeakObject subclass %0 being directly heap managed, should be managed via rtl::Reference, "
605 "parent is %1",
606 fieldDecl->getLocation())
607 << fieldDecl->getType()
608 << fieldDecl->getParent()
609 << fieldDecl->getSourceRange();
612 checkUnoReference(
613 fieldDecl->getType(), fieldDecl,
614 fieldDecl->getParent(), "field");
616 return true;
619 bool RefCounting::VisitReturnStmt(const ReturnStmt * returnStmt) {
620 if (ignoreLocation(returnStmt)) {
621 return true;
624 if (!returnStmt->getRetValue())
625 return true;
626 auto cxxNewExpr = dyn_cast<CXXNewExpr>(returnStmt->getRetValue()->IgnoreImplicit());
627 if (!cxxNewExpr)
628 return true;
630 auto qt = returnStmt->getRetValue()->getType();
631 if (!qt->isPointerType())
632 return false;
633 qt = qt->getPointeeType();
635 if (containsOWeakObjectSubclass(qt)) {
636 report(
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())
640 << qt
641 << returnStmt->getSourceRange();
644 return true;
647 bool RefCounting::VisitVarDecl(const VarDecl * varDecl) {
648 if (ignoreLocation(varDecl))
649 return true;
651 checkUnoReference(varDecl->getType(), varDecl, nullptr, "var");
653 if (isa<ParmVarDecl>(varDecl))
654 return true;
656 if (containsSvRefBaseSubclass(varDecl->getType().getTypePtr())) {
657 report(
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"))
669 return true;
670 report(
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())) {
678 report(
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())) {
686 report(
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());
697 if (newExpr)
699 StringRef fileName = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(varDecl->getBeginLoc()));
700 if (loplugin::isSamePathname(fileName, SRCDIR "/cppuhelper/source/component_context.cxx"))
701 return true;
702 auto pointeeType = varDecl->getType()->getPointeeType();
703 if (containsOWeakObjectSubclass(pointeeType))
704 report(
705 DiagnosticsEngine::Warning,
706 "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
707 varDecl->getLocation())
708 << pointeeType
709 << varDecl->getSourceRange();
711 if (isCastingReference(varDecl->getInit()))
713 // TODO false+ code
714 StringRef fileName = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(varDecl->getBeginLoc()));
715 if (loplugin::isSamePathname(fileName, SRCDIR "/sw/source/core/unocore/unotbl.cxx"))
716 return true;
717 auto pointeeType = varDecl->getType()->getPointeeType();
718 if (containsOWeakObjectSubclass(pointeeType))
719 report(
720 DiagnosticsEngine::Warning,
721 "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
722 varDecl->getLocation())
723 << pointeeType
724 << varDecl->getSourceRange();
726 if (isCallingGetOnWeakRef(varDecl->getInit()))
728 auto pointeeType = varDecl->getType()->getPointeeType();
729 if (containsOWeakObjectSubclass(pointeeType))
730 report(
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())
734 << pointeeType
735 << varDecl->getSourceRange();
738 return true;
742 Look for code like
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);
750 if (!castExpr)
751 return false;
752 auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(castExpr->getSubExpr());
753 if (!memberCallExpr)
754 return false;
755 if (!memberCallExpr->getMethodDecl()->getIdentifier() || memberCallExpr->getMethodDecl()->getName() != "get")
756 return false;
757 QualType objectType = memberCallExpr->getImplicitObjectArgument()->getType();
758 if (!loplugin::TypeCheck(objectType).Class("Reference"))
759 return false;
760 // ignore "x.get()" where x is a var
761 auto obj = memberCallExpr->getImplicitObjectArgument()->IgnoreImplicit();
762 if (isa<DeclRefExpr>(obj) || isa<MemberExpr>(obj))
763 return false;
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())
771 return false;
773 // Ignore
774 // WeakReference x;
775 // if (x.get.get())
776 // and similar stuff
777 if (auto memberCall2 = dyn_cast<CXXMemberCallExpr>(obj))
779 if (loplugin::TypeCheck(memberCall2->getImplicitObjectArgument()->getType()).Class("WeakReference"))
780 return false;
782 return true;
786 Look for code like
787 makeFoo().get();
789 cast<T*>(makeFoo().get().get());
791 foo.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"))
820 return true;
823 return false;
826 bool RefCounting::VisitBinaryOperator(const BinaryOperator * binaryOperator)
828 if (ignoreLocation(binaryOperator))
829 return true;
830 if (binaryOperator->getOpcode() != BO_Assign)
831 return true;
832 if (!binaryOperator->getLHS()->getType()->isPointerType())
833 return true;
835 auto newExpr = dyn_cast<CXXNewExpr>(binaryOperator->getRHS()->IgnoreImplicit());
836 if (newExpr)
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"))
841 return true;
843 auto pointeeType = binaryOperator->getLHS()->getType()->getPointeeType();
844 if (containsOWeakObjectSubclass(pointeeType))
846 report(
847 DiagnosticsEngine::Warning,
848 "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
849 binaryOperator->getBeginLoc())
850 << pointeeType
851 << binaryOperator->getSourceRange();
854 if (isCastingReference(binaryOperator->getRHS()))
856 auto pointeeType = binaryOperator->getLHS()->getType()->getPointeeType();
857 if (containsOWeakObjectSubclass(pointeeType))
858 report(
859 DiagnosticsEngine::Warning,
860 "cppu::OWeakObject subclass %0 being managed via raw pointer, should be managed via rtl::Reference",
861 binaryOperator->getBeginLoc())
862 << pointeeType
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"))
870 return true;
871 auto pointeeType = binaryOperator->getLHS()->getType()->getPointeeType();
872 if (containsOWeakObjectSubclass(pointeeType))
873 report(
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())
877 << pointeeType
878 << binaryOperator->getSourceRange();
880 return true;
883 bool RefCounting::VisitFunctionDecl(const FunctionDecl * functionDecl) {
884 if (ignoreLocation(functionDecl)) {
885 return true;
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) {
891 return true;
893 checkUnoReference(functionDecl->getReturnType(), functionDecl, nullptr, "return");
894 return true;
897 loplugin::Plugin::Registration< RefCounting > refcounting("refcounting");
901 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */