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 RecursiveASTVisitor
<RefCounting
>, public loplugin::Plugin
44 explicit RefCounting(loplugin::InstantiationData
const & data
): Plugin(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 typedef std::function
<bool(Decl
const *)> DeclChecker
;
77 bool BaseCheckNotSubclass(const CXXRecordDecl
*BaseDefinition
, void *p
) {
80 auto const & base
= *static_cast<const DeclChecker
*>(p
);
81 if (base(BaseDefinition
)) {
87 bool isDerivedFrom(const CXXRecordDecl
*decl
, DeclChecker base
) {
92 if (!decl
->hasDefinition()) {
95 if (!decl
->forallBases(
96 [&base
](const CXXRecordDecl
*BaseDefinition
) -> bool
97 { return BaseCheckNotSubclass(BaseDefinition
, &base
); },
106 bool containsXInterfaceSubclass(const clang::Type
* pType0
);
108 bool containsXInterfaceSubclass(const QualType
& qType
) {
109 return containsXInterfaceSubclass(qType
.getTypePtr());
112 bool containsXInterfaceSubclass(const clang::Type
* pType0
) {
115 const clang::Type
* pType
= pType0
->getUnqualifiedDesugaredType();
118 const CXXRecordDecl
* pRecordDecl
= pType
->getAsCXXRecordDecl();
120 pRecordDecl
= pRecordDecl
->getCanonicalDecl();
121 // these classes override acquire/release and forwards to its parent
122 if (isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("ListenerMultiplexerBase").GlobalNamespace()); })) { // module UnoTools
125 if (isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("GridEventForwarder").Namespace("toolkit").GlobalNamespace()); })) { // module toolkit
128 if (isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("OWeakSubObject").GlobalNamespace()); })) { // module svx
131 if (isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("OSbaWeakSubObject").Namespace("dbaui").GlobalNamespace()); })) { // module dbaccess
134 // FIXME This class has private operator new, and I cannot figure out how it can be dynamically instantiated
135 if (isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("XPropertyList").GlobalNamespace()); })) { // module svx
139 if (isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("OBookmarkContainer").Namespace("dbaccess").GlobalNamespace()); })) { // module dbaccess
144 const ClassTemplateSpecializationDecl
* pTemplate
= dyn_cast
<ClassTemplateSpecializationDecl
>(pRecordDecl
);
146 // Probably good templates:
147 loplugin::DeclCheck
dc(pTemplate
);
148 if ((dc
.Struct("FindUnoInstanceHint").AnonymousNamespace()
150 || (dc
.Class("OMultiInstanceAutoRegistration").Namespace("abp")
152 || (dc
.Class("Reference").Namespace("uno").Namespace("star")
153 .Namespace("sun").Namespace("com").GlobalNamespace())
154 || (dc
.Class("WeakReference").Namespace("uno").Namespace("star")
155 .Namespace("sun").Namespace("com").GlobalNamespace())
156 || (dc
.Class("Sequence").Namespace("uno").Namespace("star")
157 .Namespace("sun").Namespace("com").GlobalNamespace())
158 || (dc
.Class("WeakCppRef").Namespace("accessibility")
160 || (dc
.Class("OAutoRegistration").Namespace("dba")
162 || (dc
.Class("OMultiInstanceAutoRegistration").Namespace("dbp")
164 || (dc
.Class("OMultiInstanceAutoRegistration")
165 .Namespace("dbaui").GlobalNamespace())
166 || (dc
.Class("OMultiInstanceAutoRegistration")
167 .Namespace("dbaxml").GlobalNamespace())
168 || (dc
.Struct("ReferenceEqual").Namespace("io_acceptor")
170 || (dc
.Struct("ReferenceHash").Namespace("io_acceptor")
172 || (dc
.Class("OAutoRegistration").Namespace("comphelper")
174 || (dc
.Struct("OInterfaceCompare").Namespace("comphelper")
176 || dc
.Class("WeakBag").Namespace("comphelper").GlobalNamespace()
177 || (dc
.Struct("class_").Namespace("service_decl")
178 .Namespace("comphelper").GlobalNamespace())
179 || (dc
.Struct("vba_service_class_").Namespace("service_decl")
180 .Namespace("comphelper").GlobalNamespace())
181 || (dc
.Struct("inheritingClass_").Namespace("service_decl")
182 .Namespace("comphelper").GlobalNamespace())
183 || (dc
.Class("OAutoRegistration").Namespace("module")
184 .Namespace("comphelper").GlobalNamespace())
185 || (dc
.Class("mem_fun1_t").Namespace("comphelper")
187 || (dc
.Class("OSimpleListenerContainer").Namespace("comphelper")
189 || (dc
.Class("OAutoRegistration").Namespace("dbmm")
191 || (dc
.Class("OAutoRegistration").Namespace("pcr")
193 || (dc
.Class("ComponentMethodGuard").Namespace("logging")
195 || (dc
.Class("OAutoRegistration").Namespace("logging")
197 || dc
.Class("Reference").Namespace("rtl").GlobalNamespace()
198 || (dc
.Class("OAutoRegistration").Namespace("sdbtools")
200 || (dc
.Struct("ReferenceEqual").Namespace("stoc_connector")
202 || (dc
.Struct("ReferenceHash").Namespace("stoc_connector")
204 || dc
.Class("mem_fun_t").StdNamespace()
205 || dc
.Class("mem_fun1_t").StdNamespace()
206 || dc
.Class("SwIterator").GlobalNamespace()
207 || (dc
.Class("SharedUNOComponent").Namespace("utl")
209 || (dc
.Class("OAutoRegistration").Namespace("utl")
211 || (dc
.Class("DeleteUnoReferenceOnDeinit").Namespace("vcl")
213 || (dc
.Struct("OInterfaceCompare").Namespace("xmloff")
220 if (pType
->isPointerType()) {
223 } else if (pType
->isArrayType()) {
224 const clang::ArrayType
* pArrayType
= dyn_cast
<clang::ArrayType
>(pType
);
225 QualType elementType
= pArrayType
->getElementType();
226 return containsXInterfaceSubclass(elementType
);
228 return isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("XInterface").Namespace("uno").Namespace("star").Namespace("sun").Namespace("com").GlobalNamespace()); });
232 bool containsSvRefBaseSubclass(const clang::Type
* pType0
) {
235 const clang::Type
* pType
= pType0
->getUnqualifiedDesugaredType();
238 const CXXRecordDecl
* pRecordDecl
= pType
->getAsCXXRecordDecl();
240 pRecordDecl
= pRecordDecl
->getCanonicalDecl();
243 const ClassTemplateSpecializationDecl
* pTemplate
= dyn_cast
<ClassTemplateSpecializationDecl
>(pRecordDecl
);
245 if (loplugin::DeclCheck(pTemplate
).Class("SvRef")
246 .Namespace("tools").GlobalNamespace())
250 for(unsigned i
=0; i
<pTemplate
->getTemplateArgs().size(); ++i
) {
251 const TemplateArgument
& rArg
= pTemplate
->getTemplateArgs()[i
];
252 if (rArg
.getKind() == TemplateArgument::ArgKind::Type
&&
253 containsSvRefBaseSubclass(rArg
.getAsType().getTypePtr()))
260 if (pType
->isPointerType()) {
263 } else if (pType
->isArrayType()) {
264 const clang::ArrayType
* pArrayType
= dyn_cast
<clang::ArrayType
>(pType
);
265 QualType elementType
= pArrayType
->getElementType();
266 return containsSvRefBaseSubclass(elementType
.getTypePtr());
268 return isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("SvRefBase").Namespace("tools").GlobalNamespace()); });
272 bool containsSalhelperReferenceObjectSubclass(const clang::Type
* pType0
) {
275 const clang::Type
* pType
= pType0
->getUnqualifiedDesugaredType();
278 const CXXRecordDecl
* pRecordDecl
= pType
->getAsCXXRecordDecl();
280 pRecordDecl
= pRecordDecl
->getCanonicalDecl();
283 const ClassTemplateSpecializationDecl
* pTemplate
= dyn_cast
<ClassTemplateSpecializationDecl
>(pRecordDecl
);
285 auto const dc
= loplugin::DeclCheck(pTemplate
);
286 if (dc
.Class("Reference").Namespace("rtl").GlobalNamespace()
287 || (dc
.Class("OStoreHandle").Namespace("store")
292 for(unsigned i
=0; i
<pTemplate
->getTemplateArgs().size(); ++i
) {
293 const TemplateArgument
& rArg
= pTemplate
->getTemplateArgs()[i
];
294 if (rArg
.getKind() == TemplateArgument::ArgKind::Type
&&
295 containsSalhelperReferenceObjectSubclass(rArg
.getAsType().getTypePtr()))
302 if (pType
->isPointerType()) {
305 } else if (pType
->isArrayType()) {
306 const clang::ArrayType
* pArrayType
= dyn_cast
<clang::ArrayType
>(pType
);
307 QualType elementType
= pArrayType
->getElementType();
308 return containsSalhelperReferenceObjectSubclass(elementType
.getTypePtr());
310 return isDerivedFrom(pRecordDecl
, [](Decl
const * decl
) -> bool { return bool(loplugin::DeclCheck(decl
).Class("SimpleReferenceObject").Namespace("salhelper").GlobalNamespace()); });
314 static bool containsStaticTypeMethod(const CXXRecordDecl
* x
)
316 for (auto it
= x
->method_begin(); it
!= x
->method_end(); ++it
) {
318 if ( !i
->isStatic() )
320 auto ident
= i
->getIdentifier();
321 if ( ident
&& ident
->isStr("static_type") ) {
328 void RefCounting::checkUnoReference(QualType qt
, const Decl
* decl
, const RecordDecl
* parent
, const std::string
& rDeclName
)
330 if (loplugin::TypeCheck(qt
).Class("Reference").Namespace("uno").Namespace("star").Namespace("sun").Namespace("com").GlobalNamespace()) {
331 const CXXRecordDecl
* pRecordDecl
= qt
->getAsCXXRecordDecl();
332 const ClassTemplateSpecializationDecl
* pTemplate
= dyn_cast
<ClassTemplateSpecializationDecl
>(pRecordDecl
);
333 const TemplateArgument
& rArg
= pTemplate
->getTemplateArgs()[0];
334 const CXXRecordDecl
* templateParam
= rArg
.getAsType()->getAsCXXRecordDecl()->getDefinition();
335 if (templateParam
&& !containsStaticTypeMethod(templateParam
)) {
337 DiagnosticsEngine::Warning
,
338 ("uno::Reference %0 with template parameter that does not"
339 " contain ::static_type() %1%select{|, parent is %3,}2 should"
340 " probably be using rtl::Reference instead"),
342 << rDeclName
<< qt
<< (parent
!= nullptr)
343 << (parent
!= nullptr
344 ? parent
->getQualifiedNameAsString() : std::string())
345 << decl
->getSourceRange();
350 bool RefCounting::visitTemporaryObjectExpr(Expr
const * expr
) {
351 if (ignoreLocation(expr
)) {
354 auto t
= expr
->getType();
355 if (containsSvRefBaseSubclass(t
.getTypePtr())) {
357 DiagnosticsEngine::Warning
,
358 ("Temporary object of SvRefBase subclass %0 being directly stack"
359 " managed, should be managed via tools::SvRef"),
361 << t
.getUnqualifiedType() << expr
->getSourceRange();
362 } else if (containsSalhelperReferenceObjectSubclass(t
.getTypePtr())) {
364 DiagnosticsEngine::Warning
,
365 ("Temporary object of salhelper::SimpleReferenceObject subclass %0"
366 " being directly stack managed, should be managed via"
369 << t
.getUnqualifiedType() << expr
->getSourceRange();
370 } else if (containsXInterfaceSubclass(t
)) {
372 DiagnosticsEngine::Warning
,
373 ("Temporary object of css::uno::XInterface subclass %0 being"
374 " directly stack managed, should be managed via"
375 " css::uno::Reference"),
377 << t
.getUnqualifiedType() << expr
->getSourceRange();
382 bool RefCounting::VisitFieldDecl(const FieldDecl
* fieldDecl
) {
383 if (ignoreLocation(fieldDecl
)) {
386 if (fieldDecl
->isBitField()) {
390 // check for dodgy code managing ref-counted stuff with shared_ptr or unique_ptr or similar stuff
391 QualType firstTemplateParamType
;
392 if (auto recordType
= fieldDecl
->getType()->getUnqualifiedDesugaredType()->getAs
<RecordType
>()) {
393 auto const tc
= loplugin::TypeCheck(fieldDecl
->getType());
394 if (tc
.Class("unique_ptr").StdNamespace()
395 || tc
.Class("shared_ptr").StdNamespace()
396 || tc
.Class("intrusive_ptr").Namespace("boost").GlobalNamespace())
398 auto templateDecl
= dyn_cast
<ClassTemplateSpecializationDecl
>(recordType
->getDecl());
399 if (templateDecl
&& templateDecl
->getTemplateArgs().size() > 0)
400 firstTemplateParamType
= templateDecl
->getTemplateArgs()[0].getAsType();
404 if (containsSvRefBaseSubclass(fieldDecl
->getType().getTypePtr())) {
406 DiagnosticsEngine::Warning
,
407 "SvRefBase subclass %0 being directly heap managed, should be managed via tools::SvRef, "
409 fieldDecl
->getLocation())
410 << fieldDecl
->getType()
411 << fieldDecl
->getParent()
412 << fieldDecl
->getSourceRange();
415 if (!firstTemplateParamType
.isNull() && containsSvRefBaseSubclass(firstTemplateParamType
.getTypePtr()))
418 DiagnosticsEngine::Warning
,
419 "SvRefBase subclass %0 being managed via smart pointer, should be managed via tools::SvRef, "
421 fieldDecl
->getLocation())
422 << firstTemplateParamType
423 << fieldDecl
->getParent()
424 << fieldDecl
->getSourceRange();
427 if (containsSalhelperReferenceObjectSubclass(fieldDecl
->getType().getTypePtr())) {
429 DiagnosticsEngine::Warning
,
430 "salhelper::SimpleReferenceObject subclass %0 being directly heap managed, should be managed via rtl::Reference, "
432 fieldDecl
->getLocation())
433 << fieldDecl
->getType()
434 << fieldDecl
->getParent()
435 << fieldDecl
->getSourceRange();
438 if (!firstTemplateParamType
.isNull() && containsSalhelperReferenceObjectSubclass(firstTemplateParamType
.getTypePtr()))
441 DiagnosticsEngine::Warning
,
442 "salhelper::SimpleReferenceObject subclass %0 being managed via smart pointer, should be managed via rtl::Reference, "
444 fieldDecl
->getLocation())
445 << firstTemplateParamType
446 << fieldDecl
->getParent()
447 << fieldDecl
->getSourceRange();
450 auto const dc
= loplugin::DeclCheck(fieldDecl
->getParent());
451 if ( (dc
.Class("BaseReference").Namespace("uno").Namespace("star")
452 .Namespace("sun").Namespace("com").GlobalNamespace())
453 || (dc
.Union("element_alias").Namespace("detail").Namespace("cppu")
455 // this is playing some kind of game to avoid circular references
456 || (dc
.Class("ResultSetDataSupplier").Namespace("ucbhelper")
462 if (containsXInterfaceSubclass(fieldDecl
->getType())) {
464 DiagnosticsEngine::Warning
,
465 "XInterface subclass %0 being directly heap managed, should be managed via uno::Reference, "
467 fieldDecl
->getLocation())
468 << fieldDecl
->getType()
469 << fieldDecl
->getParent()
470 << fieldDecl
->getSourceRange();
473 // Not in general (dbaccess::DocumentEvents, dbaccess/source/core/dataaccess/databasedocument.hxx):
475 if (!firstTemplateParamType
.isNull() && containsXInterfaceSubclass(firstTemplateParamType
))
478 DiagnosticsEngine::Warning
,
479 "XInterface subclass %0 being managed via smart pointer, should be managed via uno::Reference, "
481 fieldDecl
->getLocation())
482 << firstTemplateParamType
483 << fieldDecl
->getParent()
484 << fieldDecl
->getSourceRange();
489 fieldDecl
->getType(), fieldDecl
,
490 fieldDecl
->getParent(), "field");
496 bool RefCounting::VisitVarDecl(const VarDecl
* varDecl
) {
497 if (ignoreLocation(varDecl
)) {
500 if (!isa
<ParmVarDecl
>(varDecl
)) {
501 if (containsSvRefBaseSubclass(varDecl
->getType().getTypePtr())) {
503 DiagnosticsEngine::Warning
,
504 "SvRefBase subclass being directly stack managed, should be managed via tools::SvRef, "
505 + varDecl
->getType().getAsString(),
506 varDecl
->getLocation())
507 << varDecl
->getSourceRange();
509 if (containsSalhelperReferenceObjectSubclass(varDecl
->getType().getTypePtr())) {
510 StringRef name
{ compiler
.getSourceManager().getFilename(compiler
.getSourceManager().getSpellingLoc(varDecl
->getLocation())) };
511 // this is playing games that it believes is safe
512 if (loplugin::isSamePathname(name
, SRCDIR
"/stoc/source/security/permissions.cxx"))
515 DiagnosticsEngine::Warning
,
516 "salhelper::SimpleReferenceObject subclass being directly stack managed, should be managed via rtl::Reference, "
517 + varDecl
->getType().getAsString(),
518 varDecl
->getLocation())
519 << varDecl
->getSourceRange();
521 if (containsXInterfaceSubclass(varDecl
->getType())) {
523 DiagnosticsEngine::Warning
,
524 "XInterface subclass being directly stack managed, should be managed via uno::Reference, "
525 + varDecl
->getType().getAsString(),
526 varDecl
->getLocation())
527 << varDecl
->getSourceRange();
530 checkUnoReference(varDecl
->getType(), varDecl
, nullptr, "var");
534 bool RefCounting::VisitFunctionDecl(const FunctionDecl
* functionDecl
) {
535 if (ignoreLocation(functionDecl
)) {
538 // only consider base declarations, not overridden ones, or we warn on methods that
539 // are overriding stuff from external libraries
540 const CXXMethodDecl
* methodDecl
= dyn_cast
<CXXMethodDecl
>(functionDecl
);
541 if (methodDecl
&& methodDecl
->size_overridden_methods() > 0) {
544 checkUnoReference(functionDecl
->getReturnType(), functionDecl
, nullptr, "return");
548 loplugin::Plugin::Registration
< RefCounting
> X("refcounting");
552 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */