...<https://github.com/llvm/llvm-project/commit/91cdd35008e9ab32dffb7e401cdd7313b3461892> "[clang] Improve nested name specifier AST representation (#147835)". For one, clang::TypeDecl::getTypeForDecl no longer works for TagDecls, so the loplugin::TypeCheck ctor taking a TypeDecl started to fail at runtime. So heed the advice given in the comment to clang::TypeDecl::getTypeForDecl and instead "ckeck out ASTContext::getTypeDeclType or one of...", which means that we now need to transport a clang::ASTContext to all those places that want to create such a loplugin::TypeCheck. For another, clang::TagType::getDecl ("renamed to `getOriginalDecl`" for now) no longer returns one dedicated Decl per type (so its results could be compared for equality), so wrap that in compat::getDecl to do an additional getCanonicalDecl. (But note how that requires an additional compat::getDefinitionOrSelf across the various loplugins' isWarnUnusedType implementations, to check for any attributes on the actual definitions rather than on whatever first declarations happen to be considered the canonical decls.) And the visitor's TraverseDecltypeTypeLoc takes an additional bool TraverseQualifier parameter now, so adapt the sharedvisitor machinery accordingly. Plus some bespoke fixes across various loplugins. loplugin:indentation is hit by <https://github.com/llvm/llvm-project/issues/153540> "Clang getBeginLoc of some CXXConstructExpr has moved past namespace prefix" (which would cause false > core/dbaccess/source/core/recovery/subcomponentrecovery.cxx:546:18: error: statement mis-aligned compared to neighbours [loplugin:indentation] > 546 | rtl::Reference( new SubComponentLoader( xController, xDocDefinition ) ); > | ^ > core/dbaccess/source/core/recovery/subcomponentrecovery.cxx:545:13: note: measured against this one [loplugin:indentation] > 545 | Reference< XController > xController( m_xDocumentUI, UNO_QUERY_THROW ); > | ^ etc.), so is disabled for Clang 22 for now, until that bug is fixed. (Various diagnostics are slightly different now, leading to some more expected-error-re variation in the tests. And loplugin::refcounting no longer duplicates its warnings, which is probably a feature and not a bug.) Change-Id: I9fe152e47ee571ad57c7c57f0eaa9c10e620f03e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/189694 Reviewed-by: Stephan Bergmann <stephan.bergmann@collabora.com> Tested-by: Jenkins
560 lines
20 KiB
C++
560 lines
20 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 "plugin.hxx"
|
|
#include "check.hxx"
|
|
#include "compat.hxx"
|
|
#include "config_clang.h"
|
|
#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);
|
|
bool VisitCallExpr(const CallExpr*);
|
|
bool VisitInitListExpr(const InitListExpr*);
|
|
|
|
private:
|
|
bool CheckForUnnecessaryGet(const Expr*, bool includeRtlReference);
|
|
};
|
|
|
|
static const RecordType* extractTemplateType(QualType);
|
|
static bool isDerivedFrom(const CXXRecordDecl* subtypeRecord, const CXXRecordDecl* baseRecord);
|
|
|
|
bool ReferenceCasting::VisitInitListExpr(const InitListExpr* ile)
|
|
{
|
|
if (ignoreLocation(ile))
|
|
return true;
|
|
for (const Expr* expr : ile->inits())
|
|
{
|
|
if (CheckForUnnecessaryGet(expr, /*includeRtlReference*/ true))
|
|
{
|
|
report(DiagnosticsEngine::Warning, "unnecessary get() call", expr->getBeginLoc())
|
|
<< expr->getSourceRange();
|
|
return true;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
bool ReferenceCasting::VisitCXXConstructExpr(const CXXConstructExpr* cce)
|
|
{
|
|
if (ignoreLocation(cce))
|
|
return true;
|
|
// 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"))
|
|
return true;
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.hxx"))
|
|
return true;
|
|
|
|
if (cce->getNumArgs() == 0)
|
|
return true;
|
|
|
|
// 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)
|
|
return true;
|
|
|
|
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();
|
|
return true;
|
|
}
|
|
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();
|
|
return true;
|
|
}
|
|
|
|
if (isRtlReference)
|
|
return true;
|
|
if (isUnoReference && cce->getNumArgs() != 2)
|
|
return true;
|
|
|
|
// ignore the up-casting constructor, which has a std::enable_if second parameter
|
|
if (isUnoReference && cce->getNumArgs() == 2
|
|
&& !cce->getConstructor()->getParamDecl(1)->getType()->isEnumeralType())
|
|
return true;
|
|
|
|
// extract the type parameter passed to the template
|
|
const RecordType* templateParamType = extractTemplateType(cce->getType());
|
|
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
|
|
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)
|
|
return true;
|
|
|
|
CXXRecordDecl const* templateParamRD
|
|
= dyn_cast<CXXRecordDecl>(compat::getDecl(templateParamType));
|
|
CXXRecordDecl const* constructorArgRD
|
|
= dyn_cast<CXXRecordDecl>(compat::getDecl(argTemplateType));
|
|
|
|
// 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 (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")
|
|
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 =",
|
|
cce->getBeginLoc())
|
|
<< cce->getSourceRange();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ReferenceCasting::VisitCXXMemberCallExpr(const CXXMemberCallExpr* mce)
|
|
{
|
|
if (ignoreLocation(mce))
|
|
return true;
|
|
// 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"))
|
|
return true;
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.hxx"))
|
|
return true;
|
|
|
|
if (mce->getNumArgs() == 0)
|
|
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;
|
|
|
|
auto methodRecordDecl = dyn_cast<ClassTemplateSpecializationDecl>(mce->getRecordDecl());
|
|
if (!methodRecordDecl || !methodRecordDecl->getIdentifier()
|
|
|| methodRecordDecl->getName() != "Reference")
|
|
return true;
|
|
|
|
if (CheckForUnnecessaryGet(mce->getArg(0), /*includeRtlReference*/ mce->getNumArgs() == 1))
|
|
{
|
|
report(DiagnosticsEngine::Warning, "unnecessary get() call", mce->getArg(0)->getBeginLoc())
|
|
<< mce->getArg(0)->getSourceRange();
|
|
return true;
|
|
}
|
|
|
|
if (mce->getNumArgs() != 2)
|
|
return true;
|
|
|
|
// 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
|
|
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)
|
|
return true;
|
|
|
|
CXXRecordDecl const* templateParamRD
|
|
= dyn_cast<CXXRecordDecl>(compat::getDecl(templateParamType));
|
|
CXXRecordDecl const* methodArgRD = dyn_cast<CXXRecordDecl>(compat::getDecl(argTemplateType));
|
|
|
|
// 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 (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")
|
|
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 =",
|
|
mce->getBeginLoc())
|
|
<< mce->getSourceRange();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ReferenceCasting::VisitCallExpr(const CallExpr* ce)
|
|
{
|
|
if (ignoreLocation(ce))
|
|
return true;
|
|
// 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"))
|
|
return true;
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/include/com/sun/star/uno/Reference.hxx"))
|
|
return true;
|
|
|
|
// look for calls to Reference<T>::query(x)
|
|
auto method = dyn_cast_or_null<CXXMethodDecl>(ce->getDirectCallee());
|
|
if (!method || !method->getIdentifier() || method->getName() != "query")
|
|
return true;
|
|
if (ce->getNumArgs() != 1)
|
|
return true;
|
|
|
|
auto methodRecordDecl = dyn_cast<ClassTemplateSpecializationDecl>(method->getParent());
|
|
if (!methodRecordDecl || !methodRecordDecl->getIdentifier()
|
|
|| methodRecordDecl->getName() != "Reference")
|
|
return true;
|
|
|
|
if (CheckForUnnecessaryGet(ce->getArg(0), /*includeRtlReference*/ true))
|
|
report(DiagnosticsEngine::Warning, "unnecessary get() call", ce->getArg(0)->getBeginLoc())
|
|
<< ce->getArg(0)->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 = ce->getArg(0);
|
|
if (!arg0)
|
|
return true;
|
|
|
|
// 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)
|
|
return true;
|
|
|
|
CXXRecordDecl const* templateParamRD
|
|
= dyn_cast<CXXRecordDecl>(compat::getDecl(templateParamType));
|
|
CXXRecordDecl const* methodArgRD = dyn_cast<CXXRecordDecl>(compat::getDecl(argTemplateType));
|
|
|
|
// 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 (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();
|
|
}
|
|
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, bool includeRtlReference)
|
|
{
|
|
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;
|
|
auto dc = loplugin::DeclCheck(methodDecl->getParent());
|
|
if (dc.Class("Reference").Namespace("uno"))
|
|
; // ok
|
|
else if (includeRtlReference && dc.Class("Reference").Namespace("rtl"))
|
|
; // ok
|
|
else
|
|
return false;
|
|
|
|
StringRef aFileName
|
|
= getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(expr->getBeginLoc()));
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/cppu/qa/test_reference.cxx"))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static const RecordType* extractTemplateType(QualType cceType)
|
|
{
|
|
// check for passing raw pointer to interface case
|
|
if (cceType->isPointerType())
|
|
{
|
|
auto pointeeType = cceType->getPointeeType();
|
|
#if CLANG_VERSION < 220000
|
|
if (auto elaboratedType = dyn_cast<ElaboratedType>(pointeeType))
|
|
pointeeType = elaboratedType->desugar();
|
|
#endif
|
|
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>(compat::getDecl(recType)))
|
|
{
|
|
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 CLANG_VERSION < 220000
|
|
if (auto elaboratedType = dyn_cast<ElaboratedType>(cceType))
|
|
cceType = elaboratedType->desugar();
|
|
#endif
|
|
auto cceTST = dyn_cast<TemplateSpecializationType>(cceType);
|
|
if (!cceTST)
|
|
return NULL;
|
|
auto const args = cceTST->template_arguments();
|
|
if (args.size() != 1)
|
|
return NULL;
|
|
const TemplateArgument& cceTA = args[0];
|
|
QualType templateParamType = cceTA.getAsType();
|
|
#if CLANG_VERSION < 220000
|
|
if (auto elaboratedType = dyn_cast<ElaboratedType>(templateParamType))
|
|
templateParamType = elaboratedType->desugar();
|
|
#endif
|
|
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: */
|