// Find explicit casts from signed to unsigned integer in comparison against unsigned integer, where // the cast is presumably used to avoid warnings about signed vs. unsigned comparisons, and could // thus be replaced with o3tl::make_unsigned for clarity.
#include <cassert>
#include"plugin.hxx"
namespace
{ // clang::Type::isSignedIntegerType returns true for more types than what C++ defines as signed // integer types: bool isSignedIntegerType(QualType type)
{ if (autoconst t = type->getAs<BuiltinType>())
{ // Assumes that the only extended signed integer type supported by Clang is Int128: switch (t->getKind())
{ case BuiltinType::SChar: case BuiltinType::Short: case BuiltinType::Int: case BuiltinType::Long: case BuiltinType::LongLong: case BuiltinType::Int128: returntrue; default: break;
}
} returnfalse;
}
// clang::Type::isUnsignedIntegerType returns true for more types than what C++ defines as signed // integer types: bool isUnsignedIntegerType(QualType type)
{ if (autoconst t = type->getAs<BuiltinType>())
{ // Assumes that the only extended unsigned integer type supported by Clang is UInt128: switch (t->getKind())
{ case BuiltinType::UChar: case BuiltinType::UShort: case BuiltinType::UInt: case BuiltinType::ULong: case BuiltinType::ULongLong: case BuiltinType::UInt128: returntrue; default: break;
}
} returnfalse;
}
int getRank(QualType type)
{ autoconst t = type->getAs<BuiltinType>();
assert(t != nullptr); // Assumes that the only extended signed/unsigned integer types supported by Clang are Int128 // and UInt128: switch (t->getKind())
{ case BuiltinType::SChar: case BuiltinType::UChar: return0; case BuiltinType::Short: case BuiltinType::UShort: return1; case BuiltinType::Int: case BuiltinType::UInt: return2; case BuiltinType::Long: case BuiltinType::ULong: return3; case BuiltinType::LongLong: case BuiltinType::ULongLong: return4; case BuiltinType::Int128: case BuiltinType::UInt128: return5; default:
llvm_unreachable("bad integer type");
}
}
class UnsignedCompare : public loplugin::FilteringPlugin<UnsignedCompare>
{ public: explicit UnsignedCompare(loplugin::InstantiationData const& data)
: FilteringPlugin(data)
{
}
bool VisitBinaryOperator(BinaryOperator const* expr)
{ if (ignoreLocation(expr))
{ returntrue;
} // o3tl::make_unsigned requires its argument to be non-negative, but this plugin doesn't // check that when it reports its finding, so will produce false positives when the cast is // actually meant to e.g. clamp from a large signed type to a small unsigned type. The // assumption is that this will only be likely the case for BO_EQ (==) and BO_NE (!=) // comparisons, so filter these out here (not sure what case BO_Cmp (<=>) will turn out to // be, so let's keep it here at least for now): switch (expr->getOpcode())
{ case BO_Cmp: case BO_LT: case BO_GT: case BO_LE: case BO_GE: break; default: returntrue;
} autoconst castL = isCastToUnsigned(expr->getLHS()); autoconst castR = isCastToUnsigned(expr->getRHS()); //TODO(?): Also report somewhat suspicious cases where both sides are cast to unsigned: if ((castL == nullptr) == (castR == nullptr))
{ returntrue;
} autoconst cast = castL != nullptr ? castL : castR; autoconst other = castL != nullptr ? expr->getRHS() : expr->getLHS(); autoconst otherT = other->IgnoreImpCasts()->getType(); if (!isUnsignedIntegerType(otherT))
{ returntrue;
} autoconst castFromT = cast->getSubExprAsWritten()->getType(); autoconst castToT = cast->getTypeAsWritten();
report(DiagnosticsEngine::Warning, "explicit cast from %0 to %1 (of %select{smaller|equal|larger}2 rank) in comparison " "against %3: if the cast value is known to be non-negative, use o3tl::make_unsigned " "instead of the cast",
cast->getExprLoc())
<< castFromT << castToT << (orderTypes(castToT, castFromT) + 1) << otherT
<< expr->getSourceRange(); returntrue;
}
void run() override
{ if (preRun())
{
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
}
}
private:
ExplicitCastExpr const* isCastToUnsigned(Expr const* expr)
{ autoconst e = dyn_cast<ExplicitCastExpr>(expr->IgnoreParenImpCasts()); if (e == nullptr)
{ return nullptr;
} autoconst t1 = e->getTypeAsWritten(); if (!isUnsignedIntegerType(t1))
{ return nullptr;
} autoconst e2 = e->getSubExprAsWritten(); autoconst t2 = e2->getType(); if (!isSignedIntegerType(t2))
{ return nullptr;
} // Filter out e.g. `size_t(-1)`: if (!e2->isValueDependent())
{ if (autoconst val = e2->getIntegerConstantExpr(compiler.getASTContext()))
{ if (val->isNegative())
{ return nullptr;
}
}
} auto loc = e->getBeginLoc(); while (compiler.getSourceManager().isMacroArgExpansion(loc))
{
loc = compiler.getSourceManager().getImmediateMacroCallerLoc(loc);
} // This covers both "plain" code in such include files, as well as expansion of (object-like) macros like // // #define SAL_MAX_INT8 ((sal_Int8) 0x7F) // // defined in such include files: if (isInUnoIncludeFile(compiler.getSourceManager().getSpellingLoc(loc)))
{ //TODO: '#ifdef LIBO_INTERNAL_ONLY' within UNO include files return nullptr;
} return e;
}
};
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.