avoid windres preprocessor quoting-messups with current cygwin
[LibreOffice.git] / compilerplugins / clang / refcounting.cxx
blob8133e281d8aa0837a0b2d2f2d6d470a5e3f3c30d
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 "clang/AST/CXXInheritance.h"
17 /**
19 If you have:
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:
29 class Baz {
30 Foo aFooMember;
33 this is a bug =) since aFooMember assumes heap allocated lifecycle and
34 not delete on last 'release'.
38 namespace {
40 class RefCounting:
41 public RecursiveASTVisitor<RefCounting>, public loplugin::Plugin
43 public:
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
65 // ObjCIvarDecl.
66 return VisitObjCIvarDecl(decl);
68 private:
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) {
78 if (!BaseDefinition)
79 return true;
80 auto const & base = *static_cast<const DeclChecker *>(p);
81 if (base(BaseDefinition)) {
82 return false;
84 return true;
87 bool isDerivedFrom(const CXXRecordDecl *decl, DeclChecker base) {
88 if (!decl)
89 return false;
90 if (base(decl))
91 return true;
92 if (!decl->hasDefinition()) {
93 return false;
95 if (!decl->forallBases(
96 [&base](const CXXRecordDecl *BaseDefinition) -> bool
97 { return BaseCheckNotSubclass(BaseDefinition, &base); },
98 true))
100 return true;
102 return false;
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) {
113 if (!pType0)
114 return false;
115 const clang::Type* pType = pType0->getUnqualifiedDesugaredType();
116 if (!pType)
117 return false;
118 const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl();
119 if (pRecordDecl) {
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
123 return false;
125 if (isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("GridEventForwarder").Namespace("toolkit").GlobalNamespace()); })) { // module toolkit
126 return false;
128 if (isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OWeakSubObject").GlobalNamespace()); })) { // module svx
129 return false;
131 if (isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OSbaWeakSubObject").Namespace("dbaui").GlobalNamespace()); })) { // module dbaccess
132 return false;
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
136 return false;
138 // tdf#114596
139 if (isDerivedFrom(pRecordDecl, [](Decl const * decl) -> bool { return bool(loplugin::DeclCheck(decl).Class("OBookmarkContainer").Namespace("dbaccess").GlobalNamespace()); })) { // module dbaccess
140 return false;
143 if (pRecordDecl) {
144 const ClassTemplateSpecializationDecl* pTemplate = dyn_cast<ClassTemplateSpecializationDecl>(pRecordDecl);
145 if (pTemplate) {
146 // Probably good templates:
147 loplugin::DeclCheck dc(pTemplate);
148 if ((dc.Struct("FindUnoInstanceHint").AnonymousNamespace()
149 .GlobalNamespace())
150 || (dc.Class("OMultiInstanceAutoRegistration").Namespace("abp")
151 .GlobalNamespace())
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")
159 .GlobalNamespace())
160 || (dc.Class("OAutoRegistration").Namespace("dba")
161 .GlobalNamespace())
162 || (dc.Class("OMultiInstanceAutoRegistration").Namespace("dbp")
163 .GlobalNamespace())
164 || (dc.Class("OMultiInstanceAutoRegistration")
165 .Namespace("dbaui").GlobalNamespace())
166 || (dc.Class("OMultiInstanceAutoRegistration")
167 .Namespace("dbaxml").GlobalNamespace())
168 || (dc.Struct("ReferenceEqual").Namespace("io_acceptor")
169 .GlobalNamespace())
170 || (dc.Struct("ReferenceHash").Namespace("io_acceptor")
171 .GlobalNamespace())
172 || (dc.Class("OAutoRegistration").Namespace("comphelper")
173 .GlobalNamespace())
174 || (dc.Struct("OInterfaceCompare").Namespace("comphelper")
175 .GlobalNamespace())
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")
186 .GlobalNamespace())
187 || (dc.Class("OSimpleListenerContainer").Namespace("comphelper")
188 .GlobalNamespace())
189 || (dc.Class("OAutoRegistration").Namespace("dbmm")
190 .GlobalNamespace())
191 || (dc.Class("OAutoRegistration").Namespace("pcr")
192 .GlobalNamespace())
193 || (dc.Class("ComponentMethodGuard").Namespace("logging")
194 .GlobalNamespace())
195 || (dc.Class("OAutoRegistration").Namespace("logging")
196 .GlobalNamespace())
197 || dc.Class("Reference").Namespace("rtl").GlobalNamespace()
198 || (dc.Class("OAutoRegistration").Namespace("sdbtools")
199 .GlobalNamespace())
200 || (dc.Struct("ReferenceEqual").Namespace("stoc_connector")
201 .GlobalNamespace())
202 || (dc.Struct("ReferenceHash").Namespace("stoc_connector")
203 .GlobalNamespace())
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")
208 .GlobalNamespace())
209 || (dc.Class("OAutoRegistration").Namespace("utl")
210 .GlobalNamespace())
211 || (dc.Class("DeleteUnoReferenceOnDeinit").Namespace("vcl")
212 .GlobalNamespace())
213 || (dc.Struct("OInterfaceCompare").Namespace("xmloff")
214 .GlobalNamespace()))
216 return false;
220 if (pType->isPointerType()) {
221 // ignore
222 return false;
223 } else if (pType->isArrayType()) {
224 const clang::ArrayType* pArrayType = dyn_cast<clang::ArrayType>(pType);
225 QualType elementType = pArrayType->getElementType();
226 return containsXInterfaceSubclass(elementType);
227 } else {
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) {
233 if (!pType0)
234 return false;
235 const clang::Type* pType = pType0->getUnqualifiedDesugaredType();
236 if (!pType)
237 return false;
238 const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl();
239 if (pRecordDecl) {
240 pRecordDecl = pRecordDecl->getCanonicalDecl();
242 if (pRecordDecl) {
243 const ClassTemplateSpecializationDecl* pTemplate = dyn_cast<ClassTemplateSpecializationDecl>(pRecordDecl);
244 if (pTemplate) {
245 if (loplugin::DeclCheck(pTemplate).Class("SvRef")
246 .Namespace("tools").GlobalNamespace())
248 return false;
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()))
255 return true;
260 if (pType->isPointerType()) {
261 // ignore
262 return false;
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());
267 } else {
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) {
273 if (!pType0)
274 return false;
275 const clang::Type* pType = pType0->getUnqualifiedDesugaredType();
276 if (!pType)
277 return false;
278 const CXXRecordDecl* pRecordDecl = pType->getAsCXXRecordDecl();
279 if (pRecordDecl) {
280 pRecordDecl = pRecordDecl->getCanonicalDecl();
282 if (pRecordDecl) {
283 const ClassTemplateSpecializationDecl* pTemplate = dyn_cast<ClassTemplateSpecializationDecl>(pRecordDecl);
284 if (pTemplate) {
285 auto const dc = loplugin::DeclCheck(pTemplate);
286 if (dc.Class("Reference").Namespace("rtl").GlobalNamespace()
287 || (dc.Class("OStoreHandle").Namespace("store")
288 .GlobalNamespace()))
290 return false;
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()))
297 return true;
302 if (pType->isPointerType()) {
303 // ignore
304 return false;
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());
309 } else {
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) {
317 auto i = *it;
318 if ( !i->isStatic() )
319 continue;
320 auto ident = i->getIdentifier();
321 if ( ident && ident->isStr("static_type") ) {
322 return true;
325 return false;
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)) {
336 report(
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"),
341 decl->getLocation())
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)) {
352 return true;
354 auto t = expr->getType();
355 if (containsSvRefBaseSubclass(t.getTypePtr())) {
356 report(
357 DiagnosticsEngine::Warning,
358 ("Temporary object of SvRefBase subclass %0 being directly stack"
359 " managed, should be managed via tools::SvRef"),
360 expr->getLocStart())
361 << t.getUnqualifiedType() << expr->getSourceRange();
362 } else if (containsSalhelperReferenceObjectSubclass(t.getTypePtr())) {
363 report(
364 DiagnosticsEngine::Warning,
365 ("Temporary object of salhelper::SimpleReferenceObject subclass %0"
366 " being directly stack managed, should be managed via"
367 " rtl::Reference"),
368 expr->getLocStart())
369 << t.getUnqualifiedType() << expr->getSourceRange();
370 } else if (containsXInterfaceSubclass(t)) {
371 report(
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"),
376 expr->getLocStart())
377 << t.getUnqualifiedType() << expr->getSourceRange();
379 return true;
382 bool RefCounting::VisitFieldDecl(const FieldDecl * fieldDecl) {
383 if (ignoreLocation(fieldDecl)) {
384 return true;
386 if (fieldDecl->isBitField()) {
387 return true;
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())) {
405 report(
406 DiagnosticsEngine::Warning,
407 "SvRefBase subclass %0 being directly heap managed, should be managed via tools::SvRef, "
408 ", parent is %1",
409 fieldDecl->getLocation())
410 << fieldDecl->getType()
411 << fieldDecl->getParent()
412 << fieldDecl->getSourceRange();
415 if (!firstTemplateParamType.isNull() && containsSvRefBaseSubclass(firstTemplateParamType.getTypePtr()))
417 report(
418 DiagnosticsEngine::Warning,
419 "SvRefBase subclass %0 being managed via smart pointer, should be managed via tools::SvRef, "
420 "parent is %1",
421 fieldDecl->getLocation())
422 << firstTemplateParamType
423 << fieldDecl->getParent()
424 << fieldDecl->getSourceRange();
427 if (containsSalhelperReferenceObjectSubclass(fieldDecl->getType().getTypePtr())) {
428 report(
429 DiagnosticsEngine::Warning,
430 "salhelper::SimpleReferenceObject subclass %0 being directly heap managed, should be managed via rtl::Reference, "
431 "parent is %1",
432 fieldDecl->getLocation())
433 << fieldDecl->getType()
434 << fieldDecl->getParent()
435 << fieldDecl->getSourceRange();
438 if (!firstTemplateParamType.isNull() && containsSalhelperReferenceObjectSubclass(firstTemplateParamType.getTypePtr()))
440 report(
441 DiagnosticsEngine::Warning,
442 "salhelper::SimpleReferenceObject subclass %0 being managed via smart pointer, should be managed via rtl::Reference, "
443 "parent is %1",
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")
454 .GlobalNamespace())
455 // this is playing some kind of game to avoid circular references
456 || (dc.Class("ResultSetDataSupplier").Namespace("ucbhelper")
457 .GlobalNamespace()))
459 return true;
462 if (containsXInterfaceSubclass(fieldDecl->getType())) {
463 report(
464 DiagnosticsEngine::Warning,
465 "XInterface subclass %0 being directly heap managed, should be managed via uno::Reference, "
466 "parent is %1",
467 fieldDecl->getLocation())
468 << fieldDecl->getType()
469 << fieldDecl->getParent()
470 << fieldDecl->getSourceRange();
473 // Not in general (dbaccess::DocumentEvents, dbaccess/source/core/dataaccess/databasedocument.hxx):
474 #if 0
475 if (!firstTemplateParamType.isNull() && containsXInterfaceSubclass(firstTemplateParamType))
477 report(
478 DiagnosticsEngine::Warning,
479 "XInterface subclass %0 being managed via smart pointer, should be managed via uno::Reference, "
480 "parent is %1",
481 fieldDecl->getLocation())
482 << firstTemplateParamType
483 << fieldDecl->getParent()
484 << fieldDecl->getSourceRange();
486 #endif
488 checkUnoReference(
489 fieldDecl->getType(), fieldDecl,
490 fieldDecl->getParent(), "field");
492 return true;
496 bool RefCounting::VisitVarDecl(const VarDecl * varDecl) {
497 if (ignoreLocation(varDecl)) {
498 return true;
500 if (!isa<ParmVarDecl>(varDecl)) {
501 if (containsSvRefBaseSubclass(varDecl->getType().getTypePtr())) {
502 report(
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"))
513 return true;
514 report(
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())) {
522 report(
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");
531 return true;
534 bool RefCounting::VisitFunctionDecl(const FunctionDecl * functionDecl) {
535 if (ignoreLocation(functionDecl)) {
536 return true;
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) {
542 return true;
544 checkUnoReference(functionDecl->getReturnType(), functionDecl, nullptr, "return");
545 return true;
548 loplugin::Plugin::Registration< RefCounting > X("refcounting");
552 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */