/* -*- 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
bool ReferenceCasting::VisitInitListExpr(const InitListExpr* ile)
{ if (ignoreLocation(ile)) returntrue; for (const Expr* expr : ile->inits())
{ if (CheckForUnnecessaryGet(expr, /*includeRtlReference*/ true))
{
report(DiagnosticsEngine::Warning, "unnecessary get() call", expr->getBeginLoc())
<< expr->getSourceRange(); returntrue;
}
} returntrue;
} bool ReferenceCasting::VisitCXXConstructExpr(const CXXConstructExpr* cce)
{ if (ignoreLocation(cce)) returntrue; // don't bother processing anything in the Reference.h file. Makes my life easier when debugging this.
StringRef aFileName
= getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(cce->getBeginLoc())); if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.h")) returntrue; if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.hxx")) returntrue;
if (cce->getNumArgs() == 0) returntrue;
// look for calls to the Reference<T>(x, UNO_something) constructor auto constructorClass = cce->getConstructor()->getParent(); auto dc = loplugin::DeclCheck(constructorClass); bool isUnoReference(dc.Class("Reference").Namespace("uno")); bool isRtlReference(dc.Class("Reference").Namespace("rtl").GlobalNamespace()); if (!isUnoReference && !isRtlReference) returntrue;
if (isUnoReference) if (CheckForUnnecessaryGet(cce->getArg(0), /*includeRtlReference*/ cce->getNumArgs() == 1))
{
report(DiagnosticsEngine::Warning, "unnecessary get() call",
cce->getArg(0)->getBeginLoc())
<< cce->getArg(0)->getSourceRange(); returntrue;
} if (isRtlReference && cce->getNumArgs() == 1) if (CheckForUnnecessaryGet(cce->getArg(0), /*includeRtlReference*/ true))
{
report(DiagnosticsEngine::Warning, "unnecessary get() call",
cce->getArg(0)->getBeginLoc())
<< cce->getArg(0)->getSourceRange(); returntrue;
}
if (isRtlReference) returntrue; if (isUnoReference && cce->getNumArgs() != 2) returntrue;
// ignore the up-casting constructor, which has a std::enable_if second parameter if (isUnoReference && cce->getNumArgs() == 2
&& !cce->getConstructor()->getParamDecl(1)->getType()->isEnumeralType()) returntrue;
// extract the type parameter passed to the template const RecordType* templateParamType = extractTemplateType(cce->getType()); if (!templateParamType) returntrue;
// extract the type of the first parameter passed to the constructor const Expr* constructorArg0 = cce->getArg(0); if (!constructorArg0) returntrue;
// drill down the expression tree till we hit the bottom, because at the top, the type is BaseReference
QualType argType; for (;;)
{ if (auto castExpr = dyn_cast<CastExpr>(constructorArg0))
{
constructorArg0 = castExpr->getSubExprAsWritten(); continue;
} if (auto matTempExpr = dyn_cast<MaterializeTemporaryExpr>(constructorArg0))
{
constructorArg0 = matTempExpr->getSubExpr(); 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;
} // for the "uno::Reference<X>(*this, UNO_QUERY)" case if (auto unaryOper = dyn_cast<UnaryOperator>(constructorArg0))
{ if (unaryOper->getOpcode() == UO_Deref)
{
constructorArg0 = unaryOper->getSubExpr(); continue;
}
}
argType = constructorArg0->getType(); break;
}
const RecordType* argTemplateType = extractTemplateType(argType); if (!argTemplateType) returntrue;
// querying for XInterface (instead of doing an upcast) has special semantics, // to check for UNO object equivalence. if (templateParamRD->getName() == "XInterface") returntrue;
// 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") returntrue;
if (cce->getNumArgs() == 2) 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") returntrue; if (enumConstantDecl->getName() == "UNO_QUERY_THROW") returntrue; if (enumConstantDecl->getName() == "SAL_NO_ACQUIRE") returntrue;
}
}
if (constructorArgRD->Equals(templateParamRD)
|| isDerivedFrom(constructorArgRD, templateParamRD))
{
report(DiagnosticsEngine::Warning, "the source reference is already a subtype of the destination reference, just use =",
cce->getBeginLoc())
<< cce->getSourceRange();
} returntrue;
}
bool ReferenceCasting::VisitCXXMemberCallExpr(const CXXMemberCallExpr* mce)
{ if (ignoreLocation(mce)) returntrue; // don't bother processing anything in the Reference.h file. Makes my life easier when debugging this.
StringRef aFileName
= getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(mce->getBeginLoc())); if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.h")) returntrue; if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.hxx")) returntrue;
if (mce->getNumArgs() == 0) returntrue;
// look for calls to the Reference<T>.set(x, UNO_QUERY) constructor auto method = mce->getMethodDecl(); if (!method || !method->getIdentifier() || method->getName() != "set") returntrue;
auto methodRecordDecl = dyn_cast<ClassTemplateSpecializationDecl>(mce->getRecordDecl()); if (!methodRecordDecl || !methodRecordDecl->getIdentifier()
|| methodRecordDecl->getName() != "Reference") returntrue;
// extract the type parameter passed to the template const RecordType* templateParamType
= dyn_cast<RecordType>(methodRecordDecl->getTemplateArgs()[0].getAsType()); if (!templateParamType) returntrue;
// extract the type of the first parameter passed to the method const Expr* arg0 = mce->getArg(0); if (!arg0) returntrue;
// drill down the expression tree till we hit the bottom, because at the top, the type is BaseReference
QualType argType; for (;;)
{ if (auto castExpr = dyn_cast<CastExpr>(arg0))
{
arg0 = castExpr->getSubExpr(); continue;
} if (auto matTempExpr = dyn_cast<MaterializeTemporaryExpr>(arg0))
{
arg0 = matTempExpr->getSubExpr(); 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(); break;
}
const RecordType* argTemplateType = extractTemplateType(argType); if (!argTemplateType) returntrue;
// querying for XInterface (instead of doing an upcast) has special semantics, // to check for UNO object equivalence. if (templateParamRD->getName() == "XInterface") returntrue;
// 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") returntrue;
if (mce->getNumArgs() == 2) 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") returntrue; if (enumConstantDecl->getName() == "UNO_QUERY_THROW") returntrue; if (enumConstantDecl->getName() == "SAL_NO_ACQUIRE") returntrue;
}
}
if (methodArgRD->Equals(templateParamRD) || isDerivedFrom(methodArgRD, templateParamRD))
{
report(DiagnosticsEngine::Warning, "the source reference is already a subtype of the destination reference, just use =",
mce->getBeginLoc())
<< mce->getSourceRange();
} returntrue;
}
bool ReferenceCasting::VisitCallExpr(const CallExpr* ce)
{ if (ignoreLocation(ce)) returntrue; // don't bother processing anything in the Reference.h file. Makes my life easier when debugging this.
StringRef aFileName
= getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(ce->getBeginLoc())); if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.h")) returntrue; if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.hxx")) returntrue;
// look for calls to Reference<T>::query(x) auto method = dyn_cast_or_null<CXXMethodDecl>(ce->getDirectCallee()); if (!method || !method->getIdentifier() || method->getName() != "query") returntrue; if (ce->getNumArgs() != 1) returntrue;
auto methodRecordDecl = dyn_cast<ClassTemplateSpecializationDecl>(method->getParent()); if (!methodRecordDecl || !methodRecordDecl->getIdentifier()
|| methodRecordDecl->getName() != "Reference") returntrue;
// extract the type parameter passed to the template const RecordType* templateParamType
= dyn_cast<RecordType>(methodRecordDecl->getTemplateArgs()[0].getAsType()); if (!templateParamType) returntrue;
// extract the type of the first parameter passed to the method const Expr* arg0 = ce->getArg(0); if (!arg0) returntrue;
// drill down the expression tree till we hit the bottom, because at the top, the type is BaseReference
QualType argType; for (;;)
{ if (auto castExpr = dyn_cast<CastExpr>(arg0))
{
arg0 = castExpr->getSubExpr(); continue;
} if (auto matTempExpr = dyn_cast<MaterializeTemporaryExpr>(arg0))
{
arg0 = matTempExpr->getSubExpr(); 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(); break;
}
const RecordType* argTemplateType = extractTemplateType(argType); if (!argTemplateType) returntrue;
// querying for XInterface (instead of doing an upcast) has special semantics, // to check for UNO object equivalence. if (templateParamRD->getName() == "XInterface") returntrue;
// 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") returntrue;
if (methodArgRD->Equals(templateParamRD) || isDerivedFrom(methodArgRD, templateParamRD))
{
report(DiagnosticsEngine::Warning, "the source reference is already a subtype of the destination reference, just use =",
ce->getBeginLoc())
<< ce->getSourceRange();
} returntrue;
}
/** 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, bool includeRtlReference)
{
expr = expr->IgnoreImplicit(); auto cxxMemberCallExpr = dyn_cast<CXXMemberCallExpr>(expr); if (!cxxMemberCallExpr) returnfalse; auto methodDecl = cxxMemberCallExpr->getMethodDecl(); if (!methodDecl) returnfalse; if (!methodDecl->getIdentifier() || methodDecl->getName() != "get") returnfalse;
if (!loplugin::TypeCheck(expr->getType()).Pointer()) returnfalse; auto dc = loplugin::DeclCheck(methodDecl->getParent()); if (dc.Class("Reference").Namespace("uno"))
; // ok elseif (includeRtlReference && dc.Class("Reference").Namespace("rtl"))
; // ok else returnfalse;
StringRef aFileName
= getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(expr->getBeginLoc())); if (loplugin::isSamePathname(aFileName, SRCDIR "/cppu/qa/test_reference.cxx")) returnfalse;
returntrue;
}
staticconst RecordType* extractTemplateType(QualType 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 substTemplateTypeParmType = dyn_cast<SubstTemplateTypeParmType>(pointeeType))
pointeeType = substTemplateTypeParmType->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()))
{ if (auto ctsd = dyn_cast<ClassTemplateSpecializationDecl>(recType->getDecl()))
{ autoconst& 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(); auto cceTST = dyn_cast<TemplateSpecializationType>(cceType); if (!cceTST) return NULL; autoconst args = cceTST->template_arguments(); if (args.size() != 1) return NULL; const TemplateArgument& cceTA = args[0];
QualType templateParamType = cceTA.getAsType(); if (auto elaboratedType = dyn_cast<ElaboratedType>(templateParamType))
templateParamType = elaboratedType->desugar(); 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.
*/ staticbool 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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.