Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / compilerplugins / clang / weakobject.cxx
blob70e81baa411a83151949e87067c56a4091150cc9
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 "check.hxx"
11 #include "plugin.hxx"
13 /* OWeakObject::release() disposes weak references. If that isn't done
14 * because a sub-class improperly overrides release() then
15 * OWeakConnectionPoint::m_pObject continues to point to the deleted object
16 * and that could result in use-after-free.
19 namespace {
21 class WeakObject
22 : public clang::RecursiveASTVisitor<WeakObject>
23 , public loplugin::Plugin
26 public:
27 explicit WeakObject(loplugin::InstantiationData const& rData): Plugin(rData)
30 void run() override {
31 if (compiler.getLangOpts().CPlusPlus) { // no OWeakObject in C
32 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
36 bool isDerivedFromOWeakObject(CXXMethodDecl const*const pMethodDecl)
38 QualType const pClass(pMethodDecl->getParent()->getTypeForDecl(), 0);
39 if (loplugin::TypeCheck(pClass).Class("OWeakObject").Namespace("cppu"))
41 return true;
43 // hopefully it's faster to recurse overridden methods than the
44 // thicket of WeakImplHelper32 but that is purely speculation
45 for (auto it = pMethodDecl->begin_overridden_methods();
46 it != pMethodDecl->end_overridden_methods(); ++it)
48 if (isDerivedFromOWeakObject(*it))
50 return true;
53 return false;
56 bool VisitCXXMethodDecl(CXXMethodDecl const*const pMethodDecl)
58 if (ignoreLocation(pMethodDecl)) {
59 return true;
61 if (!pMethodDecl->isThisDeclarationADefinition()
62 || pMethodDecl->isLateTemplateParsed())
64 return true;
66 if (!pMethodDecl->isInstance()) {
67 return true;
69 // this is too "simple", if a NamedDecl class has a getName() member expecting it to actually work would clearly be unreasonable if (pMethodDecl->getName() != "release") {
70 if (auto const i = pMethodDecl->getIdentifier()) {
71 if (i != nullptr && !i->isStr("release")) {
72 return true;
75 if (pMethodDecl->getNumParams() != 0) {
76 return true;
78 if (loplugin::TypeCheck(QualType(pMethodDecl->getParent()->getTypeForDecl(), 0)).Class("OWeakObject").Namespace("cppu"))
80 return true;
83 CXXMethodDecl const* pOverridden(nullptr);
84 for (auto it = pMethodDecl->begin_overridden_methods();
85 it != pMethodDecl->end_overridden_methods(); ++it)
87 if (auto const i = (*it)->getIdentifier()) {
88 if (i != nullptr && i->isStr("release")) {
89 pOverridden = *it;
90 break;
94 if (pOverridden == nullptr)
96 return true;
98 if (!isDerivedFromOWeakObject(pOverridden))
100 return true;
102 CompoundStmt const*const pCompoundStatement(
103 dyn_cast<CompoundStmt>(pMethodDecl->getBody()));
104 for (auto i = pCompoundStatement->body_begin();
105 i != pCompoundStatement->body_end(); ++i)
107 // note: this is not a CXXMemberCallExpr
108 CallExpr const*const pCallExpr(dyn_cast<CallExpr>(*i));
109 if (pCallExpr)
111 // note: this is only sometimes a CXXMethodDecl
112 FunctionDecl const*const pCalled(pCallExpr->getDirectCallee());
113 if (pCalled->getName() == "release")
115 //this never works && pCalled == pOverridden
116 if (pCalled->getParent() == pOverridden->getParent())
118 return true;
120 // Allow this convenient shortcut:
121 auto td = dyn_cast<TypeDecl>(pCalled->getParent());
122 if (td != nullptr
123 && (loplugin::TypeCheck(td).Class("OWeakObject").Namespace("cppu").GlobalNamespace()
124 || loplugin::TypeCheck(td).Class("OWeakAggObject").Namespace("cppu").GlobalNamespace()))
126 return true;
132 // whitelist
133 auto tc = loplugin::TypeCheck(pMethodDecl->getParent());
134 if ( tc.Class("OWeakAggObject").Namespace("cppu").GlobalNamespace() // conditional call
135 || tc.Class("WeakComponentImplHelperBase").Namespace("cppu").GlobalNamespace() // extra magic
136 || tc.Class("WeakAggComponentImplHelperBase").Namespace("cppu").GlobalNamespace() // extra magic
137 || tc.Class("CDOMImplementation").Namespace("DOM").GlobalNamespace() // a static oddity
138 || tc.Class("SwXTextFrame").GlobalNamespace() // ambiguous, 3 parents
139 || tc.Class("SwXTextDocument").GlobalNamespace() // ambiguous, ~4 parents
140 || tc.Class("SdStyleSheet").GlobalNamespace() // same extra magic as WeakComponentImplHelperBase
141 || tc.Class("SdXImpressDocument").GlobalNamespace() // same extra magic as WeakComponentImplHelperBase
144 return true;
147 report(DiagnosticsEngine::Warning,
148 "override of OWeakObject::release() does not call superclass release()",
149 pMethodDecl->getLocation())
150 << pMethodDecl->getSourceRange();
152 return true;
157 loplugin::Plugin::Registration<WeakObject> X("weakobject");
159 } // namespace
161 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */