...which is there for MSVC compatibility, but can cause getBody() to return null even when doesThisDeclarationHaveABody() is true. And in staticmethods.cxx we need to check doesThisDeclarationHaveABody() instead of hasBody(): For some class template member functions that are only defined outside their class definition, as is the case for OSequenceIterator::hasMoreElements in include/comphelper/sequence.hxx, hasBody() may be true for the original member function declaration inside the class (as there is some later definition that does have a body), but isLateTemplateParsed() is not (it is only true for the later definition). So just skip any such declarations that are not definitions (which is sane anyway, as otherwise such functions could pointlessly be inspected multiple times). Change-Id: I724f652a8f060a931f8b5fc3e4feb5f307a922bf Reviewed-on: https://gerrit.libreoffice.org/42914 Reviewed-by: Stephan Bergmann <sbergman@redhat.com> Tested-by: Stephan Bergmann <sbergman@redhat.com>
165 lines
6.0 KiB
C++
165 lines
6.0 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include "check.hxx"
|
|
#include "plugin.hxx"
|
|
|
|
/* OWeakObject::release() disposes weak references. If that isn't done
|
|
* because a sub-class improperly overrides release() then
|
|
* OWeakConnectionPoint::m_pObject continues to point to the deleted object
|
|
* and that could result in use-after-free.
|
|
*/
|
|
|
|
namespace {
|
|
|
|
class WeakObject
|
|
: public clang::RecursiveASTVisitor<WeakObject>
|
|
, public loplugin::Plugin
|
|
{
|
|
|
|
public:
|
|
explicit WeakObject(InstantiationData const& rData) : Plugin(rData) {}
|
|
|
|
void run() override {
|
|
if (compiler.getLangOpts().CPlusPlus) { // no OWeakObject in C
|
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
|
}
|
|
}
|
|
|
|
bool isDerivedFromOWeakObject(CXXMethodDecl const*const pMethodDecl)
|
|
{
|
|
QualType const pClass(pMethodDecl->getParent()->getTypeForDecl(), 0);
|
|
if (loplugin::TypeCheck(pClass).Class("OWeakObject").Namespace("cppu"))
|
|
{
|
|
return true;
|
|
}
|
|
// hopefully it's faster to recurse overridden methods than the
|
|
// thicket of WeakImplHelper32 but that is purely speculation
|
|
for (auto it = pMethodDecl->begin_overridden_methods();
|
|
it != pMethodDecl->end_overridden_methods(); ++it)
|
|
{
|
|
if (isDerivedFromOWeakObject(*it))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool VisitCXXMethodDecl(CXXMethodDecl const*const pMethodDecl)
|
|
{
|
|
if (ignoreLocation(pMethodDecl)) {
|
|
return true;
|
|
}
|
|
if (!pMethodDecl->isThisDeclarationADefinition()
|
|
|| pMethodDecl->isLateTemplateParsed())
|
|
{
|
|
return true;
|
|
}
|
|
if (!pMethodDecl->isInstance()) {
|
|
return true;
|
|
}
|
|
// 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") {
|
|
if (auto const i = pMethodDecl->getIdentifier()) {
|
|
if (i != nullptr && !i->isStr("release")) {
|
|
return true;
|
|
}
|
|
}
|
|
if (pMethodDecl->getNumParams() != 0) {
|
|
return true;
|
|
}
|
|
if (loplugin::TypeCheck(QualType(pMethodDecl->getParent()->getTypeForDecl(), 0)).Class("OWeakObject").Namespace("cppu"))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
CXXMethodDecl const* pOverridden(nullptr);
|
|
for (auto it = pMethodDecl->begin_overridden_methods();
|
|
it != pMethodDecl->end_overridden_methods(); ++it)
|
|
{
|
|
if (auto const i = (*it)->getIdentifier()) {
|
|
if (i != nullptr && i->isStr("release")) {
|
|
pOverridden = *it;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (pOverridden == nullptr)
|
|
{
|
|
return true;
|
|
}
|
|
if (!isDerivedFromOWeakObject(pOverridden))
|
|
{
|
|
return true;
|
|
}
|
|
CompoundStmt const*const pCompoundStatement(
|
|
dyn_cast<CompoundStmt>(pMethodDecl->getBody()));
|
|
for (auto i = pCompoundStatement->body_begin();
|
|
i != pCompoundStatement->body_end(); ++i)
|
|
{
|
|
// note: this is not a CXXMemberCallExpr
|
|
CallExpr const*const pCallExpr(dyn_cast<CallExpr>(*i));
|
|
if (pCallExpr)
|
|
{
|
|
// note: this is only sometimes a CXXMethodDecl
|
|
FunctionDecl const*const pCalled(pCallExpr->getDirectCallee());
|
|
if (pCalled->getName() == "release")
|
|
{
|
|
//this never works && pCalled == pOverridden
|
|
if (pCalled->getParent() == pOverridden->getParent())
|
|
{
|
|
return true;
|
|
}
|
|
// Allow this convenient shortcut:
|
|
auto td = dyn_cast<TypeDecl>(pCalled->getParent());
|
|
if (td != nullptr
|
|
&& (loplugin::TypeCheck(td).Class("OWeakObject").Namespace("cppu").GlobalNamespace()
|
|
|| loplugin::TypeCheck(td).Class("OWeakAggObject").Namespace("cppu").GlobalNamespace()))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if (pCalled->getName() == "release_ChildImpl") // FIXME remove this lunacy
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// whitelist
|
|
auto tc = loplugin::TypeCheck(pMethodDecl->getParent());
|
|
if ( tc.Class("OWeakAggObject").Namespace("cppu").GlobalNamespace() // conditional call
|
|
|| tc.Class("WeakComponentImplHelperBase").Namespace("cppu").GlobalNamespace() // extra magic
|
|
|| tc.Class("WeakAggComponentImplHelperBase").Namespace("cppu").GlobalNamespace() // extra magic
|
|
|| tc.Class("CDOMImplementation").Namespace("DOM").GlobalNamespace() // a static oddity
|
|
|| tc.Class("SwXTextFrame").GlobalNamespace() // ambiguous, 3 parents
|
|
|| tc.Class("SwXTextDocument").GlobalNamespace() // ambiguous, ~4 parents
|
|
|| tc.Class("SdStyleSheet").GlobalNamespace() // same extra magic as WeakComponentImplHelperBase
|
|
|| tc.Class("SdXImpressDocument").GlobalNamespace() // same extra magic as WeakComponentImplHelperBase
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
report(DiagnosticsEngine::Warning,
|
|
"override of OWeakObject::release() does not call superclass release()",
|
|
pMethodDecl->getLocation())
|
|
<< pMethodDecl->getSourceRange();
|
|
|
|
return true;
|
|
}
|
|
|
|
};
|
|
|
|
loplugin::Plugin::Registration<WeakObject> X("weakobject");
|
|
|
|
} // namespace
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|