Adapt compilerplugins to recent LLVM 22 trunk change

...<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
This commit is contained in:
Stephan Bergmann
2025-08-17 19:42:56 +02:00
parent 1b4afb1644
commit 060c1b69ef
44 changed files with 394 additions and 160 deletions
+4 -3
View File
@@ -12,6 +12,7 @@
#include <cassert>
#include "check.hxx"
#include "compat.hxx"
#include "plugin.hxx"
namespace {
@@ -85,7 +86,7 @@ public:
std::vector<QualType> copy(rParents);
copy.push_back(rpType.getUnqualifiedType().getCanonicalType());
auto ctsd = dyn_cast<ClassTemplateSpecializationDecl>(
pRecordType->getDecl());
compat::getDecl(pRecordType));
assert(ctsd != nullptr);
auto const & args = ctsd->getTemplateArgs();
assert(args.size() >= 1);
@@ -99,7 +100,7 @@ public:
std::vector<QualType> copy(rParents);
copy.push_back(rpType.getUnqualifiedType().getCanonicalType());
auto ctsd = dyn_cast<ClassTemplateSpecializationDecl>(
pRecordType->getDecl());
compat::getDecl(pRecordType));
assert(ctsd != nullptr);
auto const & args = ctsd->getTemplateArgs();
assert(args.size() >= 2);
@@ -109,7 +110,7 @@ public:
}
return isBadStaticType(args.get(1).getAsType(), chain, copy);
}
RecordDecl const*const pDefinition(pRecordType->getDecl()->getDefinition());
RecordDecl const*const pDefinition(compat::getDecl(pRecordType)->getDefinition());
if (!pDefinition) { // maybe no definition if it's a pointer/reference
return std::make_pair(false, std::vector<FieldDecl const*>());
}
+4 -2
View File
@@ -366,7 +366,8 @@ bool BufferAdd::isSideEffectFree(Expr const* expr)
{
if (callExpr->getNumArgs() > 0)
{
auto tc = loplugin::TypeCheck(calleeMethodDecl->getParent());
auto tc = loplugin::TypeCheck(compat::getCanonicalTagType(
compiler.getASTContext(), calleeMethodDecl->getParent()));
if (tc.Class("OUString") || tc.Class("OString"))
{
if (isSideEffectFree(callExpr->getArg(0)))
@@ -390,7 +391,8 @@ bool BufferAdd::isSideEffectFree(Expr const* expr)
// O[U]String::operator std::[u16]string_view:
if (auto const d = dyn_cast_or_null<CXXConversionDecl>(callExpr->getCalleeDecl()))
{
auto tc = loplugin::TypeCheck(d->getParent());
auto tc = loplugin::TypeCheck(
compat::getCanonicalTagType(compiler.getASTContext(), d->getParent()));
if (tc.Class("OString") || tc.Class("OUString"))
{
return true;
+14 -14
View File
@@ -20,20 +20,6 @@
namespace {
bool isWarnUnusedType(QualType type) {
if (auto const t = type->getAs<TypedefType>()) {
if (t->getDecl()->hasAttr<WarnUnusedAttr>()) {
return true;
}
}
if (auto const t = type->getAs<RecordType>()) {
if (t->getDecl()->hasAttr<WarnUnusedAttr>()) {
return true;
}
}
return loplugin::isExtraWarnUnusedType(type);
}
Expr const * lookThroughInitListExpr(Expr const * expr) {
if (auto const ile = dyn_cast<InitListExpr>(expr->IgnoreParenImpCasts())) {
if (ile->getNumInits() == 1) {
@@ -445,6 +431,20 @@ private:
}
}
bool isWarnUnusedType(QualType type) {
if (auto const t = type->getAs<TypedefType>()) {
if (t->getDecl()->hasAttr<WarnUnusedAttr>()) {
return true;
}
}
if (auto const t = type->getAs<RecordType>()) {
if (compat::getDefinitionOrSelf(compat::getDecl(t))->hasAttr<WarnUnusedAttr>()) {
return true;
}
}
return loplugin::isExtraWarnUnusedType(compiler.getASTContext(), type);
}
bool isFromCIncludeFile(SourceLocation spellingLocation) const {
return !compiler.getSourceManager().isInMainFile(spellingLocation)
&& compat::ends_with(
+39 -19
View File
@@ -9,6 +9,7 @@
#include <cassert>
#include <clang/AST/ASTContext.h>
#include <clang/AST/DeclCXX.h>
#include <clang/AST/DeclTemplate.h>
@@ -253,9 +254,9 @@ clang::DeclContext const * ContextCheck::lookThroughLinkageSpec() const {
namespace {
bool BaseCheckNotSomethingInterestingSubclass(const clang::CXXRecordDecl *BaseDefinition) {
bool BaseCheckNotSomethingInterestingSubclass(clang::ASTContext const & context, const clang::CXXRecordDecl *BaseDefinition) {
if (BaseDefinition) {
auto tc = TypeCheck(BaseDefinition);
auto tc = TypeCheck(compat::getCanonicalTagType(context, BaseDefinition));
if (tc.Class("Dialog").GlobalNamespace() || tc.Class("SfxPoolItem").GlobalNamespace()) {
return false;
}
@@ -263,10 +264,10 @@ bool BaseCheckNotSomethingInterestingSubclass(const clang::CXXRecordDecl *BaseDe
return true;
}
bool isDerivedFromSomethingInteresting(const clang::CXXRecordDecl *decl) {
bool isDerivedFromSomethingInteresting(clang::ASTContext const & context, const clang::CXXRecordDecl *decl) {
if (!decl)
return false;
auto tc = TypeCheck(decl);
auto tc = TypeCheck(compat::getCanonicalTagType(context, decl));
if (tc.Class("Dialog"))
return true;
if (tc.Class("SfxPoolItem"))
@@ -277,7 +278,7 @@ bool isDerivedFromSomethingInteresting(const clang::CXXRecordDecl *decl) {
if (// not sure what hasAnyDependentBases() does,
// but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
!decl->hasAnyDependentBases() &&
!decl->forallBases(BaseCheckNotSomethingInterestingSubclass)) {
!decl->forallBases([&context](clang::CXXRecordDecl const * BaseDefinition) { return BaseCheckNotSomethingInterestingSubclass(context, BaseDefinition); })) {
return true;
}
return false;
@@ -285,12 +286,12 @@ bool isDerivedFromSomethingInteresting(const clang::CXXRecordDecl *decl) {
}
bool isExtraWarnUnusedType(clang::QualType type) {
bool isExtraWarnUnusedType(clang::ASTContext const & context, clang::QualType type) {
auto const rec = type->getAsCXXRecordDecl();
if (rec == nullptr) {
return false;
}
auto const tc = TypeCheck(rec);
auto const tc = TypeCheck(compat::getCanonicalTagType(context, rec));
// Check some common non-LO types:
if (tc.Class("basic_string").StdNamespace()
|| tc.Class("deque").StdNamespace()
@@ -306,24 +307,43 @@ bool isExtraWarnUnusedType(clang::QualType type) {
{
return true;
}
return isDerivedFromSomethingInteresting(rec);
return isDerivedFromSomethingInteresting(context, rec);
}
namespace {
// Make sure Foo and ::Foo are considered equal:
bool areSameSugaredType(clang::QualType type1, clang::QualType type2) {
auto t1 = type1.getLocalUnqualifiedType();
if (auto const et = llvm::dyn_cast<clang::ElaboratedType>(t1)) {
t1 = et->getNamedType();
if (type1.getQualifiers() != type2.getQualifiers()) {
return false;
}
auto t2 = type2.getLocalUnqualifiedType();
if (auto const et = llvm::dyn_cast<clang::ElaboratedType>(t2)) {
t2 = et->getNamedType();
#if CLANG_VERSION >= 220000
if (type1->getTypeClass() != type2->getTypeClass()) {
return false;
}
return t1 == t2;
if (auto const t = llvm::dyn_cast<clang::TagType>(type1)) {
return t->getOriginalDecl()->getCanonicalDecl() == llvm::dyn_cast<clang::TagType>(type2)->getOriginalDecl()->getCanonicalDecl();
}
if (auto const t = llvm::dyn_cast<clang::TypedefType>(type1)) {
return t->getDecl() == llvm::dyn_cast<clang::TypedefType>(type2)->getDecl();
}
if (auto const t = llvm::dyn_cast<clang::UsingType>(type1)) {
return t->getDecl() == llvm::dyn_cast<clang::UsingType>(type2)->getDecl();
}
if (auto const t = type1->getPointeeType(); !t.isNull()) {
return areSameSugaredType(t, type2->getPointeeType());
}
#else
if (auto const et = llvm::dyn_cast<clang::ElaboratedType>(type1)) {
type1 = et->getNamedType();
}
if (auto const et = llvm::dyn_cast<clang::ElaboratedType>(type2)) {
type2 = et->getNamedType();
}
#endif
return type1 == type2;
}
namespace {
bool isArithmeticOp(clang::Expr const * expr) {
expr = expr->IgnoreParenImpCasts();
if (auto const e = llvm::dyn_cast<clang::BinaryOperator>(expr)) {
@@ -362,7 +382,7 @@ bool isOkToRemoveArithmeticCast(
// suffix like L it could still be either long or long long):
if ((t1->isIntegralType(context)
|| t1->isRealFloatingType())
&& ((!areSameSugaredType(t1, t2)
&& ((!areSameSugaredType(t1.getLocalUnqualifiedType(), t2.getLocalUnqualifiedType())
&& (loplugin::TypeCheck(t1).Typedef()
|| loplugin::TypeCheck(t2).Typedef()
|| llvm::isa<clang::DecltypeType>(t1) || llvm::isa<clang::DecltypeType>(t2)))
@@ -394,7 +414,7 @@ bool forAnyBase(
if (t == nullptr) {
return false;
}
auto const b = llvm::cast_or_null<clang::CXXRecordDecl>(t->getDecl()->getDefinition());
auto const b = llvm::cast_or_null<clang::CXXRecordDecl>(compat::getDecl(t)->getDefinition());
if (b == nullptr || (b->isDependentContext() && !b->isCurrentInstantiation(decl))) {
return false;
}
+5 -5
View File
@@ -38,8 +38,6 @@ public:
explicit TypeCheck(clang::Type const * type): type_(type, 0) {}
explicit TypeCheck(clang::TypeDecl const * decl): type_(decl->getTypeForDecl(), 0) {}
explicit operator bool() const { return !type_.isNull(); }
TypeCheck NonConst() const;
@@ -192,7 +190,7 @@ ContextCheck TypeCheck::Class(llvm::StringRef id)
if (!type_.isNull()) {
auto const t = type_->getAs<clang::RecordType>();
if (t != nullptr) {
return detail::checkRecordDecl(t->getDecl(), compat::TagTypeKind::Class, id);
return detail::checkRecordDecl(compat::getDecl(t), compat::TagTypeKind::Class, id);
}
}
return ContextCheck();
@@ -203,7 +201,7 @@ ContextCheck TypeCheck::Struct(llvm::StringRef id) const
if (!type_.isNull()) {
auto const t = type_->getAs<clang::RecordType>();
if (t != nullptr) {
return detail::checkRecordDecl(t->getDecl(), compat::TagTypeKind::Struct, id);
return detail::checkRecordDecl(compat::getDecl(t), compat::TagTypeKind::Struct, id);
}
}
return ContextCheck();
@@ -307,7 +305,9 @@ ContextCheck ContextCheck::Struct(llvm::StringRef id) const
llvm::dyn_cast_or_null<clang::Decl>(context_), compat::TagTypeKind::Struct, id);
}
bool isExtraWarnUnusedType(clang::QualType type);
bool isExtraWarnUnusedType(clang::ASTContext const & context, clang::QualType type);
bool areSameSugaredType(clang::QualType type1, clang::QualType type2);
bool isOkToRemoveArithmeticCast(
clang::ASTContext & context, clang::QualType t1, clang::QualType t2,
+49 -1
View File
@@ -12,6 +12,7 @@
#include <string>
#include <utility>
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
@@ -278,8 +279,55 @@ inline unsigned getBitWidthValue(clang::FieldDecl const * decl, clang::ASTContex
#endif
}
inline
clang::QualType getCanonicalTagType(clang::ASTContext const & context, clang::TagDecl const * decl)
{
#if CLANG_VERSION >= 220000
return context.getCanonicalTagType(decl);
#else
return context.getTypeDeclType(decl);
#endif
}
inline clang::TagDecl const * getDefinitionOrSelf(clang::TagDecl const * decl) {
#if CLANG_VERSION >= 220000
return decl->getDefinitionOrSelf();
#else
if (auto const def = decl->getDefinition()) {
return def;
}
return decl;
#endif
}
inline clang::RecordDecl const * getDecl(clang::RecordType const * type) {
#if CLANG_VERSION >= 220000
return llvm::cast<clang::RecordDecl>(type->getOriginalDecl()->getCanonicalDecl());
#else
return type->getDecl();
#endif
}
inline clang::EnumDecl const * getDecl(clang::EnumType const * type) {
#if CLANG_VERSION >= 220000
return type->getOriginalDecl()->getCanonicalDecl();
#else
return type->getDecl();
#endif
}
inline clang::CXXRecordDecl const * getDecl(clang::InjectedClassNameType const * type) {
#if CLANG_VERSION >= 220000
return type->getOriginalDecl()->getCanonicalDecl();
#else
return type->getDecl();
#endif
}
inline clang::Type const * getClass(clang::MemberPointerType const * type) {
#if CLANG_VERSION >= 210000
#if CLANG_VERSION >= 220000
return type->getQualifier().getAsType();
#elif CLANG_VERSION >= 210000
return type->getQualifier()->getAsType();
#else
return type->getClass();
+19 -2
View File
@@ -13,6 +13,8 @@
#include <set>
#include <string>
#include "config_clang.h"
#include "compat.hxx"
#include "plugin.hxx"
@@ -155,14 +157,29 @@ bool canBeUsedForFunctionalCast(TypeSourceInfo const * info) {
+ int(loc.hasWrittenTypeSpec()))
== 1;
}
if (isa<TagType>(type) || isa<TemplateTypeParmType>(type) || isa<AutoType>(type)
|| isa<DecltypeType>(type) || isa<TypedefType>(type))
if (isa<TemplateTypeParmType>(type) || isa<AutoType>(type) || isa<DecltypeType>(type))
{
return true;
}
#if CLANG_VERSION >= 220000
if (auto const t = dyn_cast<TagType>(type)) {
return t->getKeyword() == compat::ElaboratedTypeKeyword::None;
}
if (auto const t = dyn_cast<TypedefType>(type)) {
return t->getKeyword() == compat::ElaboratedTypeKeyword::None;
}
if (auto const t = dyn_cast<UsingType>(type)) {
return t->getKeyword() == compat::ElaboratedTypeKeyword::None;
}
#else
if (isa<TagType>(type) || isa<TypedefType>(type))
{
return true;
}
if (auto const t = dyn_cast<ElaboratedType>(type)) {
return t->getKeyword() == compat::ElaboratedTypeKeyword::None;
}
#endif
return false;
}
+4 -3
View File
@@ -17,6 +17,7 @@
#include "config_clang.h"
#include "compat.hxx"
#include "plugin.hxx"
namespace {
@@ -50,7 +51,7 @@ bool isDerivedFrom(
bool derived = false;
for (auto const & i: decl->bases()) {
auto const bd
= (cast<CXXRecordDecl>(i.getType()->getAs<RecordType>()->getDecl())
= (cast<CXXRecordDecl>(compat::getDecl(i.getType()->getAs<RecordType>()))
->getDefinition());
assert(bd != nullptr);
if (bd == base) {
@@ -106,7 +107,7 @@ public:
if (rtd == nullptr) {
return true;
}
auto const rdd = cast<CXXRecordDecl>(rtd->getDecl())->getDefinition();
auto const rdd = cast<CXXRecordDecl>(compat::getDecl(rtd))->getDefinition();
if (rdd == nullptr) {
return true;
}
@@ -153,7 +154,7 @@ public:
if (rts == nullptr) { // in case it's a dependent type
return true;
}
auto const rds = cast<CXXRecordDecl>(rts->getDecl())->getDefinition();
auto const rds = cast<CXXRecordDecl>(compat::getDecl(rts))->getDefinition();
assert(rds != nullptr);
Bases bs;
bool hidden = false;
+3 -3
View File
@@ -27,7 +27,7 @@ bool derivesFromTestFixture(CXXRecordDecl const* decl)
static auto const pred = [](CXXBaseSpecifier const& spec) {
if (auto const t = spec.getType()->getAs<RecordType>())
{ // (may be a template parameter)
return derivesFromTestFixture(dyn_cast<CXXRecordDecl>(t->getDecl()));
return derivesFromTestFixture(dyn_cast<CXXRecordDecl>(compat::getDecl(t)));
}
return false;
};
@@ -321,7 +321,7 @@ private:
assert(affected != nullptr);
if (auto const d = dyn_cast<EnumDecl>(decl))
{
affected->push_back(compiler.getASTContext().getEnumType(d));
affected->push_back(compat::getCanonicalTagType(compiler.getASTContext(), d));
}
else
{
@@ -335,7 +335,7 @@ private:
{
rec = cast<CXXRecordDecl>(decl);
}
affected->push_back(compiler.getASTContext().getRecordType(rec));
affected->push_back(compat::getCanonicalTagType(compiler.getASTContext(), rec));
for (auto d = rec->decls_begin(); d != rec->decls_end(); ++d)
{
if (*d != (*d)->getCanonicalDecl())
+3 -2
View File
@@ -13,6 +13,7 @@
#include "clang/AST/Attr.h"
#include "clang/AST/CXXInheritance.h"
#include "compat.hxx"
#include "plugin.hxx"
namespace {
@@ -45,7 +46,7 @@ bool isAlwaysNull(CXXDynamicCastExpr const * expr) {
auto SrcRT = SrcType->getAs<RecordType>();
if (!SrcRT)
return false;
const CXXRecordDecl *SrcRD = cast<CXXRecordDecl>(SrcRT->getDecl());
const CXXRecordDecl *SrcRD = cast<CXXRecordDecl>(compat::getDecl(SrcRT));
#endif
#if 0
@@ -60,7 +61,7 @@ bool isAlwaysNull(CXXDynamicCastExpr const * expr) {
auto DestRT = DestType->getAs<RecordType>();
if (!DestRT)
return false;
const CXXRecordDecl *DestRD = cast<CXXRecordDecl>(DestRT->getDecl());
const CXXRecordDecl *DestRD = cast<CXXRecordDecl>(compat::getDecl(DestRT));
#endif
#if 1
+1 -1
View File
@@ -419,7 +419,7 @@ bool FieldCanBeLocal::VisitInitListExpr(const InitListExpr* initListExpr)
if (!recordType)
return true;
auto recordDecl = recordType->getDecl();
auto recordDecl = compat::getDecl(recordType);
for (auto it = recordDecl->field_begin(); it != recordDecl->field_end(); ++it)
{
checkTouched(*it, insideFunctionDecl);
@@ -11,6 +11,7 @@
#include <iostream>
#include "check.hxx"
#include "compat.hxx"
#include "plugin.hxx"
#include "config_clang.h"
#include "clang/AST/CXXInheritance.h"
@@ -94,7 +95,7 @@ bool ImplInheritanceHelper::VisitCXXRecordDecl(const CXXRecordDecl* cxxRecordDec
auto rt = i.getType()->getAs<RecordType>();
if (!rt)
continue;
auto const bd = cast<CXXRecordDecl>(rt->getDecl())->getDefinition();
auto const bd = cast<CXXRecordDecl>(compat::getDecl(rt))->getDefinition();
auto ctx = bd->getDeclContext();
if (!ctx->isNamespace())
break;
+5
View File
@@ -191,10 +191,15 @@ bool Indentation::VisitCompoundStmt(CompoundStmt const* compoundStmt)
locationAfterToken(compiler.getSourceManager().getExpansionLoc(actualPrevEnd)),
compiler.getSourceManager().getExpansionLoc(stmt->getBeginLoc()))))
continue;
#if CLANG_VERSION \
== 220000 // see <https://github.com/llvm/llvm-project/issues/153540> "Clang getBeginLoc of some CXXConstructExpr has moved past namespace prefix"
(void)firstStmt;
#else
report(DiagnosticsEngine::Warning, "statement mis-aligned compared to neighbours %0",
stmtLoc)
<< macroName;
report(DiagnosticsEngine::Note, "measured against this one", firstStmt->getBeginLoc());
#endif
//getParentStmt(compoundStmt)->dump();
//stmt->dump();
}
+9 -4
View File
@@ -12,6 +12,7 @@
#include "plugin.hxx"
#include "check.hxx"
#include "compat.hxx"
#include "config_clang.h"
#include <fstream>
#include <unordered_set>
@@ -65,11 +66,12 @@ public:
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
}
static bool BaseCheckNotExceptionSubclass(const CXXRecordDecl* BaseDefinition)
static bool BaseCheckNotExceptionSubclass(clang::ASTContext const& context,
const CXXRecordDecl* BaseDefinition)
{
if (!BaseDefinition)
return true;
auto tc = loplugin::TypeCheck(BaseDefinition);
auto tc = loplugin::TypeCheck(compat::getCanonicalTagType(context, BaseDefinition));
if (tc.Class("Exception")
.Namespace("uno")
.Namespace("star")
@@ -84,7 +86,7 @@ public:
{
if (!decl || !decl->hasDefinition())
return false;
auto tc = loplugin::TypeCheck(decl);
auto tc = loplugin::TypeCheck(compat::getCanonicalTagType(compiler.getASTContext(), decl));
if (tc.Class("Exception")
.Namespace("uno")
.Namespace("star")
@@ -94,7 +96,10 @@ public:
return true;
if ( // not sure what hasAnyDependentBases() does,
// but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
!decl->hasAnyDependentBases() && !decl->forallBases(BaseCheckNotExceptionSubclass))
!decl->hasAnyDependentBases()
&& !decl->forallBases([this](const CXXRecordDecl* BaseDefinition) {
return BaseCheckNotExceptionSubclass(compiler.getASTContext(), BaseDefinition);
}))
{
return true;
}
+1 -1
View File
@@ -75,7 +75,7 @@ private:
unsigned LoopVarTooSmall::getIntValueWidth(QualType type) const {
if (auto const et = type->getAs<EnumType>()) {
auto const ed = et->getDecl();
auto const ed = compat::getDecl(et);
if (!ed->isFixed()) {
unsigned pos = ed->getNumPositiveBits();
unsigned neg = ed->getNumNegativeBits();
+5 -2
View File
@@ -966,9 +966,12 @@ static const CXXRecordDecl* stripTypeSugar(QualType qt)
const clang::Type* t = qt.getTypePtr();
do
{
#if CLANG_VERSION < 220000
if (auto elaboratedType = dyn_cast<ElaboratedType>(t))
t = elaboratedType->desugar().getTypePtr();
else if (auto tsType = dyn_cast<TemplateSpecializationType>(t))
else
#endif
if (auto tsType = dyn_cast<TemplateSpecializationType>(t))
t = tsType->desugar().getTypePtr();
else if (auto sttpType = dyn_cast<SubstTemplateTypeParmType>(t))
t = sttpType->desugar().getTypePtr();
@@ -980,7 +983,7 @@ static const CXXRecordDecl* stripTypeSugar(QualType qt)
auto recordType = dyn_cast<RecordType>(t);
if (!recordType)
return nullptr;
return dyn_cast_or_null<CXXRecordDecl>(recordType->getDecl());
return dyn_cast_or_null<CXXRecordDecl>(compat::getDecl(recordType));
}
int derivedFromCount(const CXXRecordDecl* subclassRecordDecl, const CXXRecordDecl* baseclassRecordDecl)
+3 -1
View File
@@ -18,6 +18,7 @@
#include "plugin.hxx"
#include "check.hxx"
#include "compat.hxx"
#include "config_clang.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/StmtVisitor.h"
@@ -455,7 +456,8 @@ bool ReduceVarScope::isTypeOK(QualType varType)
return true;
if (!varType->isRecordType())
return false;
auto recordDecl = dyn_cast_or_null<CXXRecordDecl>(varType->getAs<RecordType>()->getDecl());
auto recordDecl
= dyn_cast_or_null<CXXRecordDecl>(compat::getDecl(varType->getAs<RecordType>()));
if (recordDecl && recordDecl->hasTrivialDestructor())
return true;
auto const tc = loplugin::TypeCheck(varType);
+8
View File
@@ -25,6 +25,8 @@
#include "clang/Sema/Sema.h"
#include "config_clang.h"
#include "check.hxx"
#include "compat.hxx"
#include "plugin.hxx"
@@ -67,9 +69,15 @@ char const * printExprValueKind(ExprValueKind k) {
}
QualType desugarElaboratedType(QualType type) {
#if CLANG_VERSION >= 220000
if (auto const t = dyn_cast<TagType>(type)) {
return t->desugar();
}
#else
if (auto const t = dyn_cast<ElaboratedType>(type)) {
return t->desugar();
}
#endif
return type;
}
+2 -1
View File
@@ -11,6 +11,7 @@
#include <iostream>
#include "check.hxx"
#include "compat.hxx"
#include "plugin.hxx"
#include "config_clang.h"
#include "clang/AST/CXXInheritance.h"
@@ -455,7 +456,7 @@ bool RefCounting::VisitTypeLoc(clang::TypeLoc typeLoc)
|| tc.ClassOrStruct("shared_ptr").StdNamespace()
|| tc.ClassOrStruct("intrusive_ptr").Namespace("boost").GlobalNamespace())
{
auto templateDecl = dyn_cast<ClassTemplateSpecializationDecl>(recordType->getDecl());
auto templateDecl = dyn_cast<ClassTemplateSpecializationDecl>(compat::getDecl(recordType));
if (templateDecl && templateDecl->getTemplateArgs().size() > 0)
firstTemplateParamType = templateDecl->getTemplateArgs()[0].getAsType();
}
+18 -7
View File
@@ -12,6 +12,7 @@
#include "plugin.hxx"
#include "check.hxx"
#include "compat.hxx"
#include "config_clang.h"
#include <iostream>
@@ -191,8 +192,10 @@ bool ReferenceCasting::VisitCXXConstructExpr(const CXXConstructExpr* cce)
if (!argTemplateType)
return true;
CXXRecordDecl* templateParamRD = dyn_cast<CXXRecordDecl>(templateParamType->getDecl());
CXXRecordDecl* constructorArgRD = dyn_cast<CXXRecordDecl>(argTemplateType->getDecl());
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.
@@ -313,8 +316,9 @@ bool ReferenceCasting::VisitCXXMemberCallExpr(const CXXMemberCallExpr* mce)
if (!argTemplateType)
return true;
CXXRecordDecl* templateParamRD = dyn_cast<CXXRecordDecl>(templateParamType->getDecl());
CXXRecordDecl* methodArgRD = dyn_cast<CXXRecordDecl>(argTemplateType->getDecl());
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.
@@ -427,8 +431,9 @@ bool ReferenceCasting::VisitCallExpr(const CallExpr* ce)
if (!argTemplateType)
return true;
CXXRecordDecl* templateParamRD = dyn_cast<CXXRecordDecl>(templateParamType->getDecl());
CXXRecordDecl* methodArgRD = dyn_cast<CXXRecordDecl>(argTemplateType->getDecl());
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.
@@ -491,8 +496,10 @@ static const RecordType* extractTemplateType(QualType cceType)
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))
@@ -504,7 +511,7 @@ static const RecordType* extractTemplateType(QualType cceType)
{
if (auto recType = dyn_cast<RecordType>(subst->desugar()))
{
if (auto ctsd = dyn_cast<ClassTemplateSpecializationDecl>(recType->getDecl()))
if (auto ctsd = dyn_cast<ClassTemplateSpecializationDecl>(compat::getDecl(recType)))
{
auto const& args = ctsd->getTemplateArgs();
if (args.size() > 0 && args[0].getKind() == TemplateArgument::ArgKind::Type)
@@ -513,8 +520,10 @@ static const RecordType* extractTemplateType(QualType cceType)
}
}
#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;
@@ -523,8 +532,10 @@ static const RecordType* extractTemplateType(QualType cceType)
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);
}
+3 -2
View File
@@ -12,6 +12,7 @@
#include "plugin.hxx"
#include "check.hxx"
#include "compat.hxx"
#include "clang/AST/CXXInheritance.h"
// Check for calls to OutputDevice methods that are not passing through RenderContext
@@ -53,7 +54,7 @@ bool RenderContext::TraverseFunctionDecl(const FunctionDecl * pFunctionDecl)
// Ignore methods inside the OutputDevice class
const CXXMethodDecl *pCXXMethodDecl = dyn_cast<CXXMethodDecl>(pFunctionDecl);
if (pCXXMethodDecl) {
if (loplugin::TypeCheck(pCXXMethodDecl->getParent()).Class("OutputDevice").GlobalNamespace())
if (loplugin::TypeCheck(compat::getCanonicalTagType(compiler.getASTContext(), pCXXMethodDecl->getParent())).Class("OutputDevice").GlobalNamespace())
return true;
}
// we are only currently interested in methods where the first parameter is RenderContext
@@ -76,7 +77,7 @@ bool RenderContext::VisitCXXMemberCallExpr(const CXXMemberCallExpr* pCXXMemberCa
return true;
}
const CXXRecordDecl *pCXXRecordDecl = pCXXMemberCallExpr->getRecordDecl();
if (!loplugin::TypeCheck(pCXXRecordDecl).Class("OutputDevice").GlobalNamespace()) {
if (!loplugin::TypeCheck(compat::getCanonicalTagType(compiler.getASTContext(), pCXXRecordDecl)).Class("OutputDevice").GlobalNamespace()) {
return true;
}
// ignore a handful of methods. They will most probably still be present in Window for use during processing outside of the Paint()
+20 -2
View File
@@ -11,6 +11,8 @@
#include <vector>
#include "config_clang.h"
#include "plugin.hxx"
// Warn when a variable is referenced from its own initializer. This is not invalid in general (see
@@ -63,8 +65,24 @@ public:
bool TraverseCXXNoexceptExpr(CXXNoexceptExpr*) { return true; }
bool PreTraverseCXXNoexceptExpr(CXXNoexceptExpr*) { return false; }
bool TraverseDecltypeTypeLoc(DecltypeTypeLoc) { return true; }
bool PreTraverseDecltypeTypeLoc(DecltypeTypeLoc) { return false; }
bool TraverseDecltypeTypeLoc(DecltypeTypeLoc
#if CLANG_VERSION >= 220000
,
bool
#endif
)
{
return true;
}
bool PreTraverseDecltypeTypeLoc(DecltypeTypeLoc
#if CLANG_VERSION >= 220000
,
bool
#endif
)
{
return false;
}
bool VisitDeclRefExpr(DeclRefExpr const* expr)
{
+17 -11
View File
@@ -13,6 +13,7 @@
#include "plugin.hxx"
#include "check.hxx"
#include "compat.hxx"
#include "clang/AST/CXXInheritance.h"
/*
@@ -32,19 +33,24 @@ public:
virtual void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
bool VisitCXXRecordDecl( const CXXRecordDecl* );
private:
bool isDerivedFromSfxPoolItem(const CXXRecordDecl *decl);
bool isDerivedFromSwMsgPoolItem(const CXXRecordDecl *decl);
};
bool BaseCheckNotSfxPoolItemSubclass(const CXXRecordDecl *BaseDefinition) {
if (BaseDefinition && loplugin::TypeCheck(BaseDefinition).Class("SfxPoolItem").GlobalNamespace()) {
bool BaseCheckNotSfxPoolItemSubclass(clang::ASTContext const & context, const CXXRecordDecl *BaseDefinition) {
if (BaseDefinition && loplugin::TypeCheck(compat::getCanonicalTagType(context, BaseDefinition)).Class("SfxPoolItem").GlobalNamespace()) {
return false;
}
return true;
}
bool isDerivedFromSfxPoolItem(const CXXRecordDecl *decl) {
bool SfxPoolItem::isDerivedFromSfxPoolItem(const CXXRecordDecl *decl) {
if (!decl)
return false;
if (loplugin::TypeCheck(decl).Class("SfxPoolItem").GlobalNamespace())
if (loplugin::TypeCheck(compat::getCanonicalTagType(compiler.getASTContext(), decl)).Class("SfxPoolItem").GlobalNamespace())
return true;
if (!decl->hasDefinition()) {
return false;
@@ -52,24 +58,24 @@ bool isDerivedFromSfxPoolItem(const CXXRecordDecl *decl) {
if (// not sure what hasAnyDependentBases() does,
// but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
!decl->hasAnyDependentBases() &&
!decl->forallBases(BaseCheckNotSfxPoolItemSubclass)) {
!decl->forallBases([this](const CXXRecordDecl *BaseDefinition) { return BaseCheckNotSfxPoolItemSubclass(compiler.getASTContext(), BaseDefinition); })) {
return true;
}
return false;
}
bool BaseCheckNotSwMsgPoolItemSubclass(const CXXRecordDecl *BaseDefinition) {
if (BaseDefinition && loplugin::TypeCheck(BaseDefinition).Class("SwMsgPoolItem")) {
bool BaseCheckNotSwMsgPoolItemSubclass(clang::ASTContext const & context, const CXXRecordDecl *BaseDefinition) {
if (BaseDefinition && loplugin::TypeCheck(compat::getCanonicalTagType(context, BaseDefinition)).Class("SwMsgPoolItem")) {
return false;
}
return true;
}
bool isDerivedFromSwMsgPoolItem(const CXXRecordDecl *decl) {
bool SfxPoolItem::isDerivedFromSwMsgPoolItem(const CXXRecordDecl *decl) {
if (!decl)
return false;
if (loplugin::TypeCheck(decl).Class("SwMsgPoolItem").GlobalNamespace())
if (loplugin::TypeCheck(compat::getCanonicalTagType(compiler.getASTContext(), decl)).Class("SwMsgPoolItem").GlobalNamespace())
return true;
if (!decl->hasDefinition()) {
return false;
@@ -77,7 +83,7 @@ bool isDerivedFromSwMsgPoolItem(const CXXRecordDecl *decl) {
if (// not sure what hasAnyDependentBases() does,
// but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
!decl->hasAnyDependentBases() &&
!decl->forallBases(BaseCheckNotSwMsgPoolItemSubclass)) {
!decl->forallBases([this](const CXXRecordDecl *BaseDefinition) { return BaseCheckNotSwMsgPoolItemSubclass(compiler.getASTContext(), BaseDefinition); })) {
return true;
}
return false;
@@ -108,7 +114,7 @@ bool SfxPoolItem::VisitCXXRecordDecl(const CXXRecordDecl* decl)
return true;
}
// the enum types do some weird stuff involving SfxEnumItemInterface
auto tc = loplugin::TypeCheck(decl);
auto tc = loplugin::TypeCheck(compat::getCanonicalTagType(compiler.getASTContext(), decl));
if (tc.Class("SfxEnumItem").GlobalNamespace() || tc.Class("SfxAllEnumItem").GlobalNamespace())
return true;
@@ -36,7 +36,8 @@ using namespace loplugin;
struct TraverseFunctionInfo
{
std::string name;
std::string argument;
std::string argument1;
std::string argument2;
bool hasPre = false;
bool hasPost = false;
};
@@ -150,7 +151,17 @@ bool CheckFileVisitor::VisitCXXRecordDecl( CXXRecordDecl* decl )
if( method->getNumParams() == 1 )
{
TraverseFunctionInfo traverseInfo = findOrCreateTraverseFunctionInfo( method->getName());
traverseInfo.argument = method->getParamDecl( 0 )->getTypeSourceInfo()->getType().getAsString();
traverseInfo.argument1 = method->getParamDecl( 0 )->getTypeSourceInfo()->getType().getAsString();
traverseFunctions.insert( std::move( traverseInfo ));
}
else if( method->getNumParams() == 2 )
{
TraverseFunctionInfo traverseInfo = findOrCreateTraverseFunctionInfo( method->getName());
traverseInfo.argument1 = method->getParamDecl( 0 )->getTypeSourceInfo()->getType().getAsString();
traverseInfo.argument2 = method->getParamDecl( 1 )->getTypeSourceInfo()->getType().getAsString();
if (traverseInfo.argument2 == "_Bool") {
traverseInfo.argument2 = "bool";
}
traverseFunctions.insert( std::move( traverseInfo ));
}
else
@@ -188,7 +199,8 @@ bool CheckFileVisitor::VisitCXXRecordDecl( CXXRecordDecl* decl )
{
std::cout << "TraverseFunctionStart" << std::endl;
std::cout << "TraverseFunctionName:" << traverseFunction.name << std::endl;
std::cout << "TraverseFunctionArgument:" << traverseFunction.argument << std::endl;
std::cout << "TraverseFunctionArgument1:" << traverseFunction.argument1 << std::endl;
std::cout << "TraverseFunctionArgument2:" << traverseFunction.argument2 << std::endl;
std::cout << "TraverseFunctionHasPre:" << traverseFunction.hasPre << std::endl;
std::cout << "TraverseFunctionHasPost:" << traverseFunction.hasPost << std::endl;
std::cout << "TraverseFunctionEnd" << std::endl;
@@ -29,7 +29,8 @@ struct VisitFunctionInfo
struct TraverseFunctionInfo
{
std::string name;
std::string argument;
std::string argument1;
std::string argument2;
bool hasPre = false;
bool hasPost = false;
};
@@ -251,7 +252,11 @@ void generateVisitor( PluginType type )
traverseFunctions.insert( traverse );
for( const TraverseFunctionInfo& traverse : traverseFunctions )
{
output << " bool " << traverse.name << "(" << traverse.argument << " arg)\n";
output << " bool " << traverse.name << "(" << traverse.argument1 << " arg1";
if (!traverse.argument2.empty()) {
output << ", " << traverse.argument2 << " arg2";
}
output << ")\n";
output << " {\n";
for( const PluginInfo& plugin : plugins[ type ] )
{
@@ -264,7 +269,11 @@ void generateVisitor( PluginType type )
output << " if( " << plugin.variableName << " != nullptr ";
output << ")\n";
output << " {\n";
output << " if( !" << plugin.variableName << "->Pre" << traverse.name << "( arg ))\n";
output << " if( !" << plugin.variableName << "->Pre" << traverse.name << "( arg1";
if (!traverse.argument2.empty()) {
output << ", arg2";
}
output << " ))\n";
// This will disable the plugin for the time of the traverse, until restored later,
// just like directly returning from Traverse* would skip that part.
output << " {\n";
@@ -274,7 +283,11 @@ void generateVisitor( PluginType type )
output << " }\n";
}
}
output << " bool ret = RecursiveASTVisitor::" << traverse.name << "( arg );\n";
output << " bool ret = RecursiveASTVisitor::" << traverse.name << "( arg1";
if (!traverse.argument2.empty()) {
output << ", arg2";
}
output << " );\n";
for( const PluginInfo& plugin : plugins[ type ] )
{
auto pluginTraverse = plugin.traverseFunctions.find( traverse );
@@ -285,7 +298,11 @@ void generateVisitor( PluginType type )
output << " if( " << plugin.variableName << " != nullptr ";
output << ")\n";
output << " {\n";
output << " if( !" << plugin.variableName << "->Post" << traverse.name << "( arg, ret ))\n";
output << " if( !" << plugin.variableName << "->Post" << traverse.name << "( arg1,";
if (!traverse.argument2.empty()) {
output << " arg2,";
}
output << " ret ))\n";
// This will disable the plugin for the rest of the run.
output << " save" << plugin.className << " = nullptr;\n";
output << " }\n";
@@ -301,7 +318,11 @@ void generateVisitor( PluginType type )
auto pluginTraverse = plugin.traverseFunctions.find( traverse );
if( pluginTraverse == plugin.traverseFunctions.end())
continue;
output << " " << plugin.variableName << "->" << pluginTraverse->name << "( arg );\n";
output << " " << plugin.variableName << "->" << pluginTraverse->name << "( arg1";
if (!traverse.argument2.empty()) {
output << ", arg2";
}
output << " );\n";
}
output << " }\n";
output << " return ret;\n";
@@ -412,7 +433,9 @@ static bool readFile( const std::string& fileName )
getline( file, line );
traverseInfo.name = getValue( line, "TraverseFunctionName" );
getline( file, line );
traverseInfo.argument = getValue( line, "TraverseFunctionArgument" );
traverseInfo.argument1 = getValue( line, "TraverseFunctionArgument1" );
getline( file, line );
traverseInfo.argument2 = getValue( line, "TraverseFunctionArgument2" );
getline( file, line );
traverseInfo.hasPre = getValue( line, "TraverseFunctionHasPre" ) == "1";
getline( file, line );
+7 -4
View File
@@ -12,6 +12,7 @@
#include "config_clang.h"
#include "check.hxx"
#include "compat.hxx"
#include "plugin.hxx"
/*
@@ -38,23 +39,25 @@ public:
bool VisitCXXDependentScopeMemberExpr(const CXXDependentScopeMemberExpr *) { bVisitedThis = true; return true; }
bool VisitDependentScopeDeclRefExpr(const DependentScopeDeclRefExpr *) { bVisitedThis = true; return true; }
private:
bool isDerivedFromTestFixture(const CXXRecordDecl *decl);
StringRef getFilename(SourceLocation loc);
};
bool BaseCheckNotTestFixtureSubclass(const CXXRecordDecl *BaseDefinition) {
if (loplugin::TypeCheck(BaseDefinition).Class("TestFixture").Namespace("CppUnit").GlobalNamespace()) {
bool BaseCheckNotTestFixtureSubclass(clang::ASTContext const & context, const CXXRecordDecl *BaseDefinition) {
if (loplugin::TypeCheck(compat::getCanonicalTagType(context, BaseDefinition)).Class("TestFixture").Namespace("CppUnit").GlobalNamespace()) {
return false;
}
return true;
}
bool isDerivedFromTestFixture(const CXXRecordDecl *decl) {
bool StaticMethods::isDerivedFromTestFixture(const CXXRecordDecl *decl) {
if (!decl->hasDefinition())
return false;
if (// not sure what hasAnyDependentBases() does,
// but it avoids classes we don't want, e.g. WeakAggComponentImplHelper1
!decl->hasAnyDependentBases() &&
!decl->forallBases(BaseCheckNotTestFixtureSubclass)) {
!decl->forallBases([this](const CXXRecordDecl *BaseDefinition) { return BaseCheckNotTestFixtureSubclass(compiler.getASTContext(), BaseDefinition); })) {
return true;
}
return false;
+4 -2
View File
@@ -416,7 +416,8 @@ bool StringAdd::isSideEffectFree(Expr const* expr)
&& (name == "number" || name == "unacquired" || name == "boolean"
|| name == "copy"))
{
auto tc = loplugin::TypeCheck(calleeMethodDecl->getParent());
auto tc = loplugin::TypeCheck(compat::getCanonicalTagType(
compiler.getASTContext(), calleeMethodDecl->getParent()));
if (tc.Class("OUString") || tc.Class("OString"))
{
if (isSideEffectFree(callExpr->getArg(0)))
@@ -430,7 +431,8 @@ bool StringAdd::isSideEffectFree(Expr const* expr)
.ClassOrStruct("basic_string_view")
.StdNamespace())
{
auto const tc = loplugin::TypeCheck(calleeMethodDecl->getParent());
auto const tc = loplugin::TypeCheck(compat::getCanonicalTagType(
compiler.getASTContext(), calleeMethodDecl->getParent()));
if (tc.Class("OUString").Namespace("rtl").GlobalNamespace()
|| tc.Class("OString").Namespace("rtl").GlobalNamespace())
{
+1 -1
View File
@@ -45,7 +45,7 @@ void f(std::ostream& st, OString const& s1, OStringBuffer const& s2,
<< s3.getStr() // expected-error-re {{suspicious use of 'getStr' on an object of type '{{(rtl::)?}}OUString'; the result is implicitly cast to a void pointer in a call of 'operator <<' [loplugin:getstr]}}
<< s4.getStr() // expected-error-re {{suspicious use of 'getStr' on an object of type '{{(rtl::)?}}OUStringBuffer'; the result is implicitly cast to a void pointer in a call of 'operator <<' [loplugin:getstr]}}
#endif
<< s5.getStr() // expected-error {{directly use object of type 'S' (aka 'rtl::OString') in a call of 'operator <<', instead of calling 'getStr' first [loplugin:getstr]}}
<< s5.getStr() // expected-error-re {{directly use object of type 'S' (aka '{{(::)?}}rtl::OString') in a call of 'operator <<', instead of calling 'getStr' first [loplugin:getstr]}}
<< p1->getStr() // expected-error {{directly use object of type 'rtl::OString' in a call of 'operator <<', instead of calling 'getStr' first [loplugin:getstr]}}
<< p2->getStr()
#if !HAVE_DELETED_OPERATORS
@@ -7,6 +7,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#include <sal/config.h>
#include <config_clang.h>
// See <https://github.com/llvm/llvm-project/issues/153540> "Clang getBeginLoc of some
// CXXConstructExpr has moved past namespace prefix":
#if CLANG_VERSION == 220000
// expected-no-diagnostics
#else
int foo();
int foo2(int);
@@ -106,5 +116,6 @@ void test5(bool x)
}
}
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
+10 -10
View File
@@ -31,21 +31,21 @@ struct S2 {
S2(OUString v1, OUString v2)
: mv1(std::move(v1)), mv2((std::move(v2))) {}
OUString get1() { return mv1; } // expected-error {{rather return class rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get2(bool b) { return b ? mv1 : mv2; } // expected-error {{rather return class rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get3() { return child.mv1; } // expected-error {{rather return class rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get4() { return mv3[0]; } // expected-error {{rather return class rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get5() { return gs1; } // expected-error {{rather return class rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get1() { return mv1; } // expected-error-re {{rather return {{(class )?}}{{(::)?}}rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get2(bool b) { return b ? mv1 : mv2; } // expected-error-re {{rather return {{(class )?}}{{(::)?}}rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get3() { return child.mv1; } // expected-error-re {{rather return {{(class )?}}{{(::)?}}rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get4() { return mv3[0]; } // expected-error-re {{rather return {{(class )?}}{{(::)?}}rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get5() { return gs1; } // expected-error-re {{rather return {{(class )?}}{{(::)?}}rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString const & get6() { return gs1; }
OUString get7() { return get6(); } // expected-error {{rather return class rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get7() { return get6(); } // expected-error-re {{rather return {{(class )?}}{{(::)?}}rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString & get8() { return gs1; }
OUString get9() { return get8(); } // expected-error {{rather return class rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get9() { return get8(); } // expected-error-re {{rather return {{(class )?}}{{(::)?}}rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
// TODO
OUString get10() { return OUString(*&get6()); } // todoexpected-error {{rather return class rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get11() const { return mxCow->get(); } // expected-error {{rather return class rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get12() { return child.get2(false); } // expected-error {{rather return class rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get11() const { return mxCow->get(); } // expected-error-re {{rather return {{(class )?}}{{(::)?}}rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get12() { return child.get2(false); } // expected-error-re {{rather return {{(class )?}}{{(::)?}}rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get13() { return OUString(); } // expected-error {{rather return class rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
OUString get13() { return OUString(); } // expected-error-re {{rather return {{(class )?}}{{(::)?}}rtl::OUString by const& than by value, to avoid unnecessary copying [loplugin:passstuffbyref]}}
// no warning expected
OUString set1() { return OUString("xxx"); }
@@ -48,10 +48,10 @@ int main()
s); // expected-error-re@-1 {{redundant functional cast from '{{(rtl::)?}}OUString' to '{{(rtl::)?}}OUString' [loplugin:redundantfcast]}}
using T1 = OUString;
(void)T1(
s); // expected-error-re@-1 {{redundant functional cast from '{{(rtl::)?}}OUString' to 'T1' (aka '{{(rtl::)?}}OUString') [loplugin:redundantfcast]}}
s); // expected-error-re@-1 {{redundant functional cast from '{{(rtl::)?}}OUString' to 'T1' (aka '{{((::)?rtl::)?}}OUString') [loplugin:redundantfcast]}}
using T2 = OUString const;
(void)T2(
s); // expected-error-re@-1 {{redundant functional cast from '{{(rtl::)?}}OUString' to 'T2' (aka 'const {{(rtl::?)}}OUString') [loplugin:redundantfcast]}}
s); // expected-error-re@-1 {{redundant functional cast from '{{(rtl::)?}}OUString' to 'T2' (aka 'const {{((::)?rtl::?)}}OUString') [loplugin:redundantfcast]}}
(void)std::unique_ptr<int>(std::unique_ptr<int>(
new int{})); // expected-error@-1 {{redundant functional cast from 'std::unique_ptr<int>' to 'std::unique_ptr<int>' [loplugin:redundantfcast]}}
+16 -5
View File
@@ -13,6 +13,7 @@
#include <rtl/ref.hxx>
#include <boost/intrusive_ptr.hpp>
#include <com/sun/star/uno/XInterface.hpp>
#include <config_clang.h>
#include <cppuhelper/weak.hxx>
#include <unotools/weakref.hxx>
@@ -29,23 +30,33 @@ struct UnoSubObject : public UnoObject
struct Foo
{
// expected-error@+2 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
#if CLANG_VERSION < 220000
// expected-error@+3 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
#endif
// expected-error@+1 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
std::unique_ptr<UnoObject> m_foo1;
// expected-error@+2 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
#if CLANG_VERSION < 220000
// expected-error@+3 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
#endif
// expected-error@+1 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
std::shared_ptr<UnoObject> m_foo2;
// expected-error@+2 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
#if CLANG_VERSION < 220000
// expected-error@+3 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
#endif
// expected-error@+1 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
boost::intrusive_ptr<UnoObject> m_foo3;
rtl::Reference<UnoObject> m_foo4; // no warning expected
};
// expected-error@+2 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
#if CLANG_VERSION < 220000
// expected-error@+3 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
#endif
// expected-error@+1 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
std::unique_ptr<UnoObject> foo1();
rtl::Reference<UnoObject> foo2(); // no warning expected
// expected-error@+2 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
#if CLANG_VERSION < 220000
// expected-error@+3 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
#endif
// expected-error@+1 {{cppu::OWeakObject subclass 'UnoObject' being managed via smart pointer, should be managed via rtl::Reference [loplugin:refcounting]}}
void foo3(std::unique_ptr<UnoObject> p);
+2 -1
View File
@@ -15,6 +15,7 @@
#include "plugin.hxx"
#include "check.hxx"
#include "compat.hxx"
#include "config_clang.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/StmtVisitor.h"
@@ -141,7 +142,7 @@ bool TrivialConstructor::FieldHasTrivialConstructorBody(const FieldDecl* Field)
if (!RT)
return true;
CXXRecordDecl* FieldClassDecl = cast<CXXRecordDecl>(RT->getDecl());
CXXRecordDecl const* FieldClassDecl = cast<CXXRecordDecl>(compat::getDecl(RT));
// The constructor for an implicit anonymous union member is never invoked.
if (FieldClassDecl->isUnion() && FieldClassDecl->isAnonymousStructOrUnion())
+2 -1
View File
@@ -15,6 +15,7 @@
#include "plugin.hxx"
#include "check.hxx"
#include "compat.hxx"
#include "config_clang.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/StmtVisitor.h"
@@ -102,7 +103,7 @@ bool TrivialDestructor::FieldHasTrivialDestructor(const FieldDecl* Field)
if (!RT)
return true;
CXXRecordDecl* FieldClassDecl = cast<CXXRecordDecl>(RT->getDecl());
CXXRecordDecl const* FieldClassDecl = cast<CXXRecordDecl>(compat::getDecl(RT));
return FieldClassDecl->hasTrivialDestructor();
}
+14 -4
View File
@@ -184,19 +184,27 @@ static bool areTypesEqual(QualType lhs, QualType rhs)
auto recordType = dyn_cast<RecordType>(type);
if (recordType)
return recordType;
#if CLANG_VERSION < 220000
auto elaboratedType = dyn_cast<ElaboratedType>(type);
if (!elaboratedType)
return static_cast<const clang::RecordType*>(nullptr);
return dyn_cast<RecordType>(elaboratedType->desugar());
if (elaboratedType)
return dyn_cast<RecordType>(elaboratedType->desugar());
#endif
return static_cast<const clang::RecordType*>(nullptr);
};
auto containsTypedefToRecord = [](clang::QualType type, RecordType const* recordType) {
TypedefType const* typedefType = type->getAs<TypedefType>();
if (!typedefType)
return false;
auto tmp = typedefType->desugar();
#if CLANG_VERSION < 220000
if (auto elaboratedType = dyn_cast<ElaboratedType>(tmp))
tmp = elaboratedType->desugar();
return tmp.getTypePtr() == recordType;
#endif
if (auto const rt = dyn_cast<RecordType>(tmp))
{
return compat::getDecl(rt) == compat::getDecl(recordType);
}
return false;
};
if (auto recordType = extractRecordType(lhsPointer->getPointeeType()))
if (containsTypedefToRecord(rhsPointer->getPointeeType(), recordType))
@@ -207,6 +215,7 @@ static bool areTypesEqual(QualType lhs, QualType rhs)
}
}
#if CLANG_VERSION < 220000
if (auto lhsElaborated = dyn_cast<ElaboratedType>(lhsType))
{
return areTypesEqual(lhsElaborated->getNamedType(), rhs);
@@ -215,6 +224,7 @@ static bool areTypesEqual(QualType lhs, QualType rhs)
{
return areTypesEqual(lhs, rhsElaborated->getNamedType());
}
#endif
if (auto lhsTemplate = dyn_cast<TemplateSpecializationType>(lhsType))
{
return areTypesEqual(lhsTemplate->desugar(), rhs);
@@ -19,6 +19,7 @@
#include "config_clang.h"
#include "plugin.hxx"
#include "check.hxx"
#include "compat.hxx"
/**
look for methods where all they do is call their superclass method
@@ -180,7 +181,7 @@ bool UnnecessaryOverride::VisitCXXMethodDecl(const CXXMethodDecl* methodDecl)
const RecordType* baseRecordType = baseSpecifier->getType()->getAs<RecordType>();
if (baseRecordType)
{
const CXXRecordDecl* baseRecordDecl = dyn_cast<CXXRecordDecl>(baseRecordType->getDecl());
const CXXRecordDecl* baseRecordDecl = dyn_cast<CXXRecordDecl>(compat::getDecl(baseRecordType));
if (baseRecordDecl && baseRecordDecl->getDestructor()
&& baseRecordDecl->getDestructor()->isVirtual())
{
@@ -452,7 +453,7 @@ const CXXMethodDecl* UnnecessaryOverride::findOverriddenOrSimilarMethodInSupercl
bool bParamsMatch = true;
for (unsigned i=0; i<methodDecl->param_size(); ++i)
{
if (methodDecl->parameters()[i]->getType() != baseMethod->parameters()[i]->getType())
if (!loplugin::areSameSugaredType(methodDecl->parameters()[i]->getType(), baseMethod->parameters()[i]->getType()))
{
bParamsMatch = false;
break;
+3 -3
View File
@@ -1007,7 +1007,7 @@ void UnusedFields::checkIfWrittenTo(const FieldDecl* fieldDecl, const Expr* memb
// return true if this not a collection type, or if it is a collection type, and we might be writing to it
bool UnusedFields::checkForWriteWhenUsingCollectionType(const CXXMethodDecl * calleeMethodDecl)
{
auto const tc = loplugin::TypeCheck(calleeMethodDecl->getParent());
auto const tc = loplugin::TypeCheck(compat::getCanonicalTagType(compiler.getASTContext(), calleeMethodDecl->getParent()));
bool listLike = false, setLike = false, mapLike = false, cssSequence = false;
// Noting that I am deliberately not calling StdNamespace() on these checks, the loplugin::TypeCheck
// code seems to be unreliable when dealing with ClassTemplateSpecializationDecl.
@@ -1071,7 +1071,7 @@ bool UnusedFields::checkForWriteWhenUsingCollectionType(const CXXMethodDecl * ca
bool UnusedFields::checkForUsingMap(const CXXMethodDecl * calleeMethodDecl)
{
auto const tc = loplugin::TypeCheck(calleeMethodDecl->getParent());
auto const tc = loplugin::TypeCheck(compat::getCanonicalTagType(compiler.getASTContext(), calleeMethodDecl->getParent()));
if (!(tc.Class("map") || tc.Class("unordered_map")))
return false;
if (!calleeMethodDecl->isOverloadedOperator())
@@ -1148,7 +1148,7 @@ bool UnusedFields::VisitInitListExpr( const InitListExpr* initListExpr)
if (!recordType)
return true;
auto recordDecl = recordType->getDecl();
auto recordDecl = compat::getDecl(recordType);
for (auto it = recordDecl->field_begin(); it != recordDecl->field_end(); ++it)
{
MyFieldInfo fieldInfo = niceName(*it);
+22 -18
View File
@@ -18,6 +18,8 @@
#include <cassert>
#include <set>
#include "config_clang.h"
#include "check.hxx"
#include "compat.hxx"
#include "plugin.hxx"
@@ -38,18 +40,6 @@ bool isTemplated(CXXRecordDecl const* decl)
return false;
}
bool isWarnUnusedType(QualType type)
{
if (auto const t = type->getAs<RecordType>())
{
if (t->getDecl()->hasAttr<WarnUnusedAttr>())
{
return true;
}
}
return loplugin::isExtraWarnUnusedType(type);
}
class UnusedMember final : public loplugin::FilteringPlugin<UnusedMember>
{
public:
@@ -68,7 +58,7 @@ public:
// declaration of e clearly references it:
if (auto const t = decl->getType()->getAs<EnumType>())
{
deferred_.erase(t->getDecl());
deferred_.erase(compat::getDecl(t));
}
return true;
}
@@ -229,11 +219,11 @@ public:
RecordDecl const* d;
if (auto const t2 = t1->getAs<InjectedClassNameType>())
{
d = t2->getDecl();
d = compat::getDecl(t2);
}
else
{
d = t1->castAs<RecordType>()->getDecl();
d = compat::getDecl(t1->castAs<RecordType>());
}
recordRecordDeclAndBases(d);
return true;
@@ -269,7 +259,7 @@ public:
}
if (auto const t1 = t->getAs<RecordType>())
{
recordRecordDeclAndBases(t1->getDecl());
recordRecordDeclAndBases(compat::getDecl(t1));
}
return true;
}
@@ -306,6 +296,7 @@ public:
return true;
}
#if CLANG_VERSION < 220000
bool VisitElaboratedTypeLoc(ElaboratedTypeLoc tloc)
{
if (ignoreLocation(tloc))
@@ -337,6 +328,7 @@ public:
}
return true;
}
#endif
void postRun() override
{
@@ -389,6 +381,18 @@ private:
}
}
bool isWarnUnusedType(QualType type)
{
if (auto const t = type->getAs<RecordType>())
{
if (compat::getDefinitionOrSelf(compat::getDecl(t))->hasAttr<WarnUnusedAttr>())
{
return true;
}
}
return loplugin::isExtraWarnUnusedType(compiler.getASTContext(), type);
}
bool isWarnWhenUnusedType(QualType type)
{
auto t = type;
@@ -409,7 +413,7 @@ private:
{
for (auto i = d2->bases_begin(); i != d2->bases_end(); ++i)
{
recordRecordDeclAndBases(i->getType()->castAs<RecordType>()->getDecl());
recordRecordDeclAndBases(compat::getDecl(i->getType()->castAs<RecordType>()));
}
//TODO: doesn't iterate vbases, but presence of such would run counter to the layout
// heuristic anyway
@@ -427,7 +431,7 @@ private:
}
if (auto const t1 = t->getAs<RecordType>())
{
recordRecordDeclAndBases(t1->getDecl());
recordRecordDeclAndBases(compat::getDecl(t1));
}
break;
}
@@ -56,7 +56,7 @@ bool UnusedVariableCheck::VisitVarDecl( const VarDecl* var )
return true;
auto type = var->getType();
bool check = loplugin::isExtraWarnUnusedType(type);
bool check = loplugin::isExtraWarnUnusedType(compiler.getASTContext(), type);
if (!check)
check = isUnusedSmartPointer(var);
+5 -3
View File
@@ -46,6 +46,8 @@ public:
bool VisitFunctionDecl(FunctionDecl const*);
private:
bool isWarnUnusedType(QualType type);
bool checkifUnused(Stmt const*, VarDecl const*);
bool isOkForParameter(const QualType&);
@@ -120,7 +122,7 @@ void UnusedVariableMore::run()
}
}
bool isWarnUnusedType(QualType type)
bool UnusedVariableMore::isWarnUnusedType(QualType type)
{
if (auto const t = type->getAs<TypedefType>())
{
@@ -131,12 +133,12 @@ bool isWarnUnusedType(QualType type)
}
if (auto const t = type->getAs<RecordType>())
{
if (t->getDecl()->hasAttr<WarnUnusedAttr>())
if (compat::getDefinitionOrSelf(compat::getDecl(t))->hasAttr<WarnUnusedAttr>())
{
return true;
}
}
return loplugin::isExtraWarnUnusedType(type);
return loplugin::isExtraWarnUnusedType(compiler.getASTContext(), type);
}
bool UnusedVariableMore::VisitVarDecl(VarDecl const* var)
+2 -1
View File
@@ -847,7 +847,8 @@ void UnusedVarsGlobal::checkIfWrittenTo(const VarDecl* varDecl, const DeclRefExp
// return true if this not a collection type, or if it is a collection type, and we might be writing to it
bool UnusedVarsGlobal::checkForWriteWhenUsingCollectionType(const CXXMethodDecl* calleeMethodDecl)
{
auto const tc = loplugin::TypeCheck(calleeMethodDecl->getParent());
auto const tc = loplugin::TypeCheck(
compat::getCanonicalTagType(compiler.getASTContext(), calleeMethodDecl->getParent()));
bool listLike = false, setLike = false, mapLike = false, cssSequence = false;
if (tc.Class("deque").StdNamespace() || tc.Class("list").StdNamespace()
|| tc.Class("queue").StdNamespace() || tc.Class("vector").StdNamespace())
+3 -3
View File
@@ -162,7 +162,7 @@ bool VCLWidgets::VisitCXXDestructorDecl(const CXXDestructorDecl* pCXXDestructorD
{
const RecordType *pFieldRecordType = fieldDecl->getType()->getAs<RecordType>();
if (pFieldRecordType) {
if (loplugin::DeclCheck(pFieldRecordType->getDecl())
if (loplugin::DeclCheck(compat::getDecl(pFieldRecordType))
.Class("VclPtr").GlobalNamespace())
{
bFoundVclPtrField = true;
@@ -458,7 +458,7 @@ bool VCLWidgets::VisitFieldDecl(const FieldDecl * fieldDecl) {
if (recordType == nullptr) {
return true;
}
const CXXRecordDecl *recordDecl = dyn_cast<CXXRecordDecl>(recordType->getDecl());
const CXXRecordDecl *recordDecl = dyn_cast<CXXRecordDecl>(compat::getDecl(recordType));
if (recordDecl == nullptr) {
return true;
}
@@ -638,7 +638,7 @@ bool VCLWidgets::VisitFunctionDecl( const FunctionDecl* functionDecl )
{
const RecordType* recordType = dyn_cast_or_null<RecordType>((*i)->getType()->getUnqualifiedDesugaredType());
if (recordType) {
auto d = dyn_cast<ClassTemplateSpecializationDecl>(recordType->getDecl());
auto d = dyn_cast<ClassTemplateSpecializationDecl>(compat::getDecl(recordType));
if (d && d->getTemplateArgs().size()>0) {
auto const type = loplugin::TypeCheck(d->getTemplateArgs()[0].getAsType());
if (type.Class("VclPtr").GlobalNamespace()) {
+2 -1
View File
@@ -1002,7 +1002,8 @@ void WriteOnlyVars::checkIfWrittenTo(const VarDecl* varDecl, const Expr* memberE
// return true if this not a collection type, or if it is a collection type, and we might be writing to it
bool WriteOnlyVars::checkForWriteWhenUsingCollectionType(const CXXMethodDecl* calleeMethodDecl)
{
auto const tc = loplugin::TypeCheck(calleeMethodDecl->getParent());
auto const tc = loplugin::TypeCheck(
compat::getCanonicalTagType(compiler.getASTContext(), calleeMethodDecl->getParent()));
bool listLike = false, setLike = false, mapLike = false, cssSequence = false;
if (tc.Class("deque").StdNamespace() || tc.Class("list").StdNamespace()
|| tc.Class("queue").StdNamespace() || tc.Class("vector").StdNamespace())
+1 -1
View File
@@ -362,7 +362,7 @@ bool XmlImport::isXmlTokEnum(const Expr* expr)
expr = expr->IgnoreImplicit();
// check that we have an unscoped enum type
auto condEnumType = expr->getType()->getUnqualifiedDesugaredType()->getAs<EnumType>();
if (!condEnumType || condEnumType->getDecl()->isScoped())
if (!condEnumType || compat::getDecl(condEnumType)->isScoped())
return false;
auto declRefExpr = dyn_cast<DeclRefExpr>(expr);
if (!declRefExpr)