This reverts LO6.4 commit 5cf057c3365a0feafc8f2e4f4a9e24d69a581999, in order to fix tdf#133158. This commit is obsolete, but was left in place since it seemed to have fixed a problem (in =gtk3 anyway). But now SAL_USE_VCLPLUGIN=gen behaves differently, so obviously a fix in one place must have broken another. Better the problems you have always known than a new problem, especially since this patch isn't used to fix anything specific in gtk3 anymore (since those changes were also reverted). An earlier gerrit version of this revert (which didn't just have an ignore-this-clang-rule exception) half-worked, but failed if multiple documents were opened. Change-Id: Ie8ddb7b9669fa46067d04c35e157ea08701df0da Reviewed-on: https://gerrit.libreoffice.org/c/core/+/95282 Tested-by: Jenkins Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
381 lines
13 KiB
C++
381 lines
13 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* Based on LLVM/Clang.
|
|
*
|
|
* This file is distributed under the University of Illinois Open Source
|
|
* License. See LICENSE.TXT for details.
|
|
*
|
|
*/
|
|
#ifndef LO_CLANG_SHARED_PLUGINS
|
|
|
|
#include "compat.hxx"
|
|
#include "plugin.hxx"
|
|
#include "check.hxx"
|
|
#include <iostream>
|
|
|
|
/*
|
|
This is a compile-time checker.
|
|
|
|
Check for cases where we have
|
|
- two IDL interfaces A and B,
|
|
- B extends A
|
|
- we are converting a Reference<B> to a Reference<A> using UNO_QUERY
|
|
|
|
This makes the code simpler and cheaper, because UNO_QUERY can be surprisingly expensive if used a lot.
|
|
|
|
*/
|
|
|
|
namespace
|
|
{
|
|
class ReferenceCasting : public loplugin::FilteringPlugin<ReferenceCasting>
|
|
{
|
|
public:
|
|
explicit ReferenceCasting(loplugin::InstantiationData const& data)
|
|
: FilteringPlugin(data)
|
|
{
|
|
}
|
|
|
|
bool preRun() override
|
|
{
|
|
std::string fn(handler.getMainFileName());
|
|
loplugin::normalizeDotDotInFilePath(fn);
|
|
// macros
|
|
if (fn == SRCDIR "/dbaccess/source/ui/browser/formadapter.cxx")
|
|
return false;
|
|
// UNO aggregation
|
|
if (fn == SRCDIR "/toolkit/source/controls/stdtabcontroller.cxx")
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void run() override
|
|
{
|
|
if (preRun())
|
|
{
|
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
|
}
|
|
}
|
|
|
|
bool VisitCXXConstructExpr(const CXXConstructExpr* cce);
|
|
bool VisitCXXMemberCallExpr(const CXXMemberCallExpr* mce);
|
|
|
|
private:
|
|
bool CheckForUnnecessaryGet(const Expr*);
|
|
};
|
|
|
|
static const RecordType* extractTemplateType(const clang::Type*);
|
|
static bool isDerivedFrom(const CXXRecordDecl* subtypeRecord, const CXXRecordDecl* baseRecord);
|
|
|
|
bool ReferenceCasting::VisitCXXConstructExpr(const CXXConstructExpr* cce)
|
|
{
|
|
// don't bother processing anything in the Reference.h file. Makes my life easier when debugging this.
|
|
StringRef aFileName = getFilenameOfLocation(
|
|
compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(cce)));
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.h"))
|
|
return true;
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.hxx"))
|
|
return true;
|
|
|
|
// look for calls to the Reference<T>(x, UNO_something) constructor
|
|
auto constructorClass = cce->getConstructor()->getParent();
|
|
if (!constructorClass->getIdentifier() || constructorClass->getName() != "Reference")
|
|
return true;
|
|
|
|
if (cce->getNumArgs() != 2)
|
|
return true;
|
|
|
|
if (CheckForUnnecessaryGet(cce->getArg(0)))
|
|
report(DiagnosticsEngine::Warning, "unnecessary get() call", compat::getBeginLoc(cce))
|
|
<< cce->getSourceRange();
|
|
|
|
// ignore the up-casting constructor
|
|
if (!isa<EnumType>(cce->getConstructor()->getParamDecl(1)->getType()))
|
|
return true;
|
|
|
|
// extract the type parameter passed to the template
|
|
const RecordType* templateParamType = extractTemplateType(cce->getType().getTypePtr());
|
|
if (!templateParamType)
|
|
return true;
|
|
|
|
// extract the type of the first parameter passed to the constructor
|
|
const Expr* constructorArg0 = cce->getArg(0);
|
|
if (!constructorArg0)
|
|
return true;
|
|
|
|
// drill down the expression tree till we hit the bottom, because at the top, the type is BaseReference
|
|
const clang::Type* argType;
|
|
for (;;)
|
|
{
|
|
if (auto castExpr = dyn_cast<CastExpr>(constructorArg0))
|
|
{
|
|
constructorArg0 = castExpr->getSubExpr();
|
|
continue;
|
|
}
|
|
if (auto matTempExpr = dyn_cast<MaterializeTemporaryExpr>(constructorArg0))
|
|
{
|
|
constructorArg0 = compat::getSubExpr(matTempExpr);
|
|
continue;
|
|
}
|
|
if (auto bindTempExpr = dyn_cast<CXXBindTemporaryExpr>(constructorArg0))
|
|
{
|
|
constructorArg0 = bindTempExpr->getSubExpr();
|
|
continue;
|
|
}
|
|
if (auto tempObjExpr = dyn_cast<CXXTemporaryObjectExpr>(constructorArg0))
|
|
{
|
|
constructorArg0 = tempObjExpr->getArg(0);
|
|
continue;
|
|
}
|
|
if (auto parenExpr = dyn_cast<ParenExpr>(constructorArg0))
|
|
{
|
|
constructorArg0 = parenExpr->getSubExpr();
|
|
continue;
|
|
}
|
|
argType = constructorArg0->getType().getTypePtr();
|
|
break;
|
|
}
|
|
|
|
const RecordType* argTemplateType = extractTemplateType(argType);
|
|
if (!argTemplateType)
|
|
return true;
|
|
|
|
CXXRecordDecl* templateParamRD = dyn_cast<CXXRecordDecl>(templateParamType->getDecl());
|
|
CXXRecordDecl* constructorArgRD = dyn_cast<CXXRecordDecl>(argTemplateType->getDecl());
|
|
|
|
// querying for XInterface (instead of doing an upcast) has special semantics,
|
|
// to check for UNO object equivalence.
|
|
if (templateParamRD->getName() == "XInterface")
|
|
return true;
|
|
|
|
// XShape is used in UNO aggregates in very "entertaining" ways, which means an UNO_QUERY
|
|
// can return a completely different object, e.g. see SwXShape::queryInterface
|
|
if (templateParamRD->getName() == "XShape")
|
|
return true;
|
|
|
|
if (auto declRefExpr = dyn_cast<DeclRefExpr>(cce->getArg(1)))
|
|
{
|
|
// no warning expected, used to reject null references
|
|
if (auto enumConstantDecl = dyn_cast<EnumConstantDecl>(declRefExpr->getDecl()))
|
|
{
|
|
if (enumConstantDecl->getName() == "UNO_SET_THROW")
|
|
return true;
|
|
if (enumConstantDecl->getName() == "UNO_QUERY_THROW")
|
|
return true;
|
|
if (enumConstantDecl->getName() == "SAL_NO_ACQUIRE")
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (constructorArgRD->Equals(templateParamRD)
|
|
|| isDerivedFrom(constructorArgRD, templateParamRD))
|
|
{
|
|
report(DiagnosticsEngine::Warning,
|
|
"the source reference is already a subtype of the destination reference, just use =",
|
|
compat::getBeginLoc(cce))
|
|
<< cce->getSourceRange();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ReferenceCasting::VisitCXXMemberCallExpr(const CXXMemberCallExpr* mce)
|
|
{
|
|
// don't bother processing anything in the Reference.h file. Makes my life easier when debugging this.
|
|
StringRef aFileName = getFilenameOfLocation(
|
|
compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(mce)));
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.h"))
|
|
return true;
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.hxx"))
|
|
return true;
|
|
|
|
// look for calls to the Reference<T>.set(x, UNO_QUERY) constructor
|
|
auto method = mce->getMethodDecl();
|
|
if (!method || !method->getIdentifier() || method->getName() != "set")
|
|
return true;
|
|
if (mce->getNumArgs() != 2)
|
|
return true;
|
|
|
|
auto methodRecordDecl = dyn_cast<ClassTemplateSpecializationDecl>(mce->getRecordDecl());
|
|
if (!methodRecordDecl || !methodRecordDecl->getIdentifier()
|
|
|| methodRecordDecl->getName() != "Reference")
|
|
return true;
|
|
|
|
if (CheckForUnnecessaryGet(mce->getArg(0)))
|
|
report(DiagnosticsEngine::Warning, "unnecessary get() call", compat::getBeginLoc(mce))
|
|
<< mce->getSourceRange();
|
|
|
|
// extract the type parameter passed to the template
|
|
const RecordType* templateParamType
|
|
= dyn_cast<RecordType>(methodRecordDecl->getTemplateArgs()[0].getAsType());
|
|
if (!templateParamType)
|
|
return true;
|
|
|
|
// extract the type of the first parameter passed to the method
|
|
const Expr* arg0 = mce->getArg(0);
|
|
if (!arg0)
|
|
return true;
|
|
|
|
// drill down the expression tree till we hit the bottom, because at the top, the type is BaseReference
|
|
const clang::Type* argType;
|
|
for (;;)
|
|
{
|
|
if (auto castExpr = dyn_cast<CastExpr>(arg0))
|
|
{
|
|
arg0 = castExpr->getSubExpr();
|
|
continue;
|
|
}
|
|
if (auto matTempExpr = dyn_cast<MaterializeTemporaryExpr>(arg0))
|
|
{
|
|
arg0 = compat::getSubExpr(matTempExpr);
|
|
continue;
|
|
}
|
|
if (auto bindTempExpr = dyn_cast<CXXBindTemporaryExpr>(arg0))
|
|
{
|
|
arg0 = bindTempExpr->getSubExpr();
|
|
continue;
|
|
}
|
|
if (auto tempObjExpr = dyn_cast<CXXTemporaryObjectExpr>(arg0))
|
|
{
|
|
arg0 = tempObjExpr->getArg(0);
|
|
continue;
|
|
}
|
|
if (auto parenExpr = dyn_cast<ParenExpr>(arg0))
|
|
{
|
|
arg0 = parenExpr->getSubExpr();
|
|
continue;
|
|
}
|
|
argType = arg0->getType().getTypePtr();
|
|
break;
|
|
}
|
|
|
|
const RecordType* argTemplateType = extractTemplateType(argType);
|
|
if (!argTemplateType)
|
|
return true;
|
|
|
|
CXXRecordDecl* templateParamRD = dyn_cast<CXXRecordDecl>(templateParamType->getDecl());
|
|
CXXRecordDecl* methodArgRD = dyn_cast<CXXRecordDecl>(argTemplateType->getDecl());
|
|
|
|
// querying for XInterface (instead of doing an upcast) has special semantics,
|
|
// to check for UNO object equivalence.
|
|
if (templateParamRD->getName() == "XInterface")
|
|
return true;
|
|
|
|
// XShape is used in UNO aggregates in very "entertaining" ways, which means an UNO_QUERY
|
|
// can return a completely different object, e.g. see SwXShape::queryInterface
|
|
if (templateParamRD->getName() == "XShape")
|
|
return true;
|
|
|
|
if (auto declRefExpr = dyn_cast<DeclRefExpr>(mce->getArg(1)))
|
|
{
|
|
// no warning expected, used to reject null references
|
|
if (auto enumConstantDecl = dyn_cast<EnumConstantDecl>(declRefExpr->getDecl()))
|
|
{
|
|
if (enumConstantDecl->getName() == "UNO_SET_THROW")
|
|
return true;
|
|
if (enumConstantDecl->getName() == "UNO_QUERY_THROW")
|
|
return true;
|
|
if (enumConstantDecl->getName() == "SAL_NO_ACQUIRE")
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (methodArgRD->Equals(templateParamRD) || isDerivedFrom(methodArgRD, templateParamRD))
|
|
{
|
|
report(DiagnosticsEngine::Warning,
|
|
"the source reference is already a subtype of the destination reference, just use =",
|
|
compat::getBeginLoc(mce))
|
|
<< mce->getSourceRange();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
Check for
|
|
Reference<T>(x.get(), UNO_QUERY)
|
|
because sometimes simplifying that means the main purpose of this plugin can kick in.
|
|
*/
|
|
bool ReferenceCasting::CheckForUnnecessaryGet(const Expr* expr)
|
|
{
|
|
expr = expr->IgnoreImplicit();
|
|
auto cxxMemberCallExpr = dyn_cast<CXXMemberCallExpr>(expr);
|
|
if (!cxxMemberCallExpr)
|
|
return false;
|
|
auto methodDecl = cxxMemberCallExpr->getMethodDecl();
|
|
if (!methodDecl)
|
|
return false;
|
|
if (!methodDecl->getIdentifier() || methodDecl->getName() != "get")
|
|
return false;
|
|
|
|
if (!loplugin::TypeCheck(expr->getType()).Pointer())
|
|
return false;
|
|
if (!loplugin::DeclCheck(methodDecl->getParent()).Class("Reference").Namespace("uno"))
|
|
return false;
|
|
|
|
StringRef aFileName = getFilenameOfLocation(
|
|
compiler.getSourceManager().getSpellingLoc(compat::getBeginLoc(expr)));
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/cppu/qa/test_reference.cxx"))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static const RecordType* extractTemplateType(const clang::Type* cceType)
|
|
{
|
|
// check for passing raw pointer to interface case
|
|
if (cceType->isPointerType())
|
|
{
|
|
auto pointeeType = cceType->getPointeeType();
|
|
if (auto elaboratedType = dyn_cast<ElaboratedType>(pointeeType))
|
|
pointeeType = elaboratedType->desugar();
|
|
if (auto recordType = dyn_cast<RecordType>(pointeeType))
|
|
return recordType;
|
|
}
|
|
|
|
// extract Foo from Reference<Foo>
|
|
if (auto subst = dyn_cast<SubstTemplateTypeParmType>(cceType))
|
|
{
|
|
if (auto recType = dyn_cast<RecordType>(subst->desugar().getTypePtr()))
|
|
{
|
|
if (auto ctsd = dyn_cast<ClassTemplateSpecializationDecl>(recType->getDecl()))
|
|
{
|
|
auto const& args = ctsd->getTemplateArgs();
|
|
if (args.size() > 0 && args[0].getKind() == TemplateArgument::ArgKind::Type)
|
|
return dyn_cast_or_null<RecordType>(args[0].getAsType().getTypePtr());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto elaboratedType = dyn_cast<ElaboratedType>(cceType))
|
|
cceType = elaboratedType->desugar().getTypePtr();
|
|
auto cceTST = dyn_cast<TemplateSpecializationType>(cceType);
|
|
if (!cceTST)
|
|
return NULL;
|
|
if (cceTST->getNumArgs() != 1)
|
|
return NULL;
|
|
const TemplateArgument& cceTA = cceTST->getArg(0);
|
|
const clang::Type* templateParamType = cceTA.getAsType().getTypePtr();
|
|
if (auto elaboratedType = dyn_cast<ElaboratedType>(templateParamType))
|
|
templateParamType = elaboratedType->desugar().getTypePtr();
|
|
return dyn_cast<RecordType>(templateParamType);
|
|
}
|
|
|
|
/**
|
|
Implement my own isDerived because we can't always see all the definitions of the classes involved.
|
|
which will cause an assert with the normal clang isDerivedFrom code.
|
|
*/
|
|
static bool isDerivedFrom(const CXXRecordDecl* subtypeRecord, const CXXRecordDecl* baseRecord)
|
|
{
|
|
// if there is more than one case, then we have an ambiguous conversion, and we can't change the code
|
|
// to use the upcasting constructor.
|
|
return loplugin::derivedFromCount(subtypeRecord, baseRecord) == 1;
|
|
}
|
|
|
|
loplugin::Plugin::Registration<ReferenceCasting> referencecasting("referencecasting");
|
|
|
|
} // namespace
|
|
|
|
#endif // LO_CLANG_SHARED_PLUGINS
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|