Stephan Bergmann 419d664fb7 Adapt loplugins to clang-cl's (implicit) -fdelayed-template-parsing
...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>
2017-09-29 13:31:23 +02:00

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: */