// // We don't like using C-style casts in C++ code. Similarly, warn about function-style casts (which // are semantically equivalent to C-style casts) that are not semantically equivalent to static_cast // and should rather be written as const_cast or reinterpret_cast. //
namespace {
bool areSimilar(QualType type1, QualType type2) { auto t1 = type1.getCanonicalType().getTypePtr(); auto t2 = type2.getCanonicalType().getTypePtr(); for (;;) { if (t1->isPointerType()) { if (!t2->isPointerType()) { returnfalse;
} auto t1a = t1->getAs<clang::PointerType>(); auto t2a = t2->getAs<clang::PointerType>();
t1 = t1a->getPointeeType().getTypePtr();
t2 = t2a->getPointeeType().getTypePtr();
} elseif (t1->isMemberPointerType()) { if (!t2->isMemberPointerType()) { returnfalse;
} auto t1a = t1->getAs<MemberPointerType>(); auto t2a = t2->getAs<MemberPointerType>(); if (compat::getClass(t1a)->getCanonicalTypeInternal()
!= compat::getClass(t2a)->getCanonicalTypeInternal())
{ returnfalse;
}
t1 = t1a->getPointeeType().getTypePtr();
t2 = t2a->getPointeeType().getTypePtr();
} elseif (t1->isConstantArrayType()) { if (!t2->isConstantArrayType()) { returnfalse;
} auto t1a = static_cast<ConstantArrayType const *>(
t1->getAsArrayTypeUnsafe()); auto t2a = static_cast<ConstantArrayType const *>(
t2->getAsArrayTypeUnsafe()); if (t1a->getSize() != t2a->getSize()) { returnfalse;
}
t1 = t1a->getElementType().getTypePtr();
t2 = t2a->getElementType().getTypePtr();
} elseif (t1->isIncompleteArrayType()) { if (!t2->isIncompleteArrayType()) { returnfalse;
} auto t1a = static_cast<IncompleteArrayType const *>(
t1->getAsArrayTypeUnsafe()); auto t2a = static_cast<IncompleteArrayType const *>(
t2->getAsArrayTypeUnsafe());
t1 = t1a->getElementType().getTypePtr();
t2 = t2a->getElementType().getTypePtr();
} else { returnfalse;
} if (t1 == t2) { returntrue;
}
}
}
QualType resolvePointers(QualType type) { while (type->isPointerType()) {
type = type->getAs<clang::PointerType>()->getPointeeType();
} return type;
}
bool isLiteralLike(Expr const * expr) {
expr = expr->IgnoreParenImpCasts(); if (isa<IntegerLiteral>(expr) || isa<CharacterLiteral>(expr) || isa<FloatingLiteral>(expr)
|| isa<ImaginaryLiteral>(expr) || isa<CXXBoolLiteralExpr>(expr)
|| isa<CXXNullPtrLiteralExpr>(expr) || isa<ObjCBoolLiteralExpr>(expr))
{ returntrue;
} if (autoconst e = dyn_cast<DeclRefExpr>(expr)) { autoconst d = e->getDecl(); if (isa<EnumConstantDecl>(d)) { returntrue;
} if (autoconst v = dyn_cast<VarDecl>(d)) { if (d->getType().isConstQualified()) { if (autoconst init = v->getAnyInitializer()) { return isLiteralLike(init);
}
}
} returnfalse;
} if (autoconst e = dyn_cast<UnaryExprOrTypeTraitExpr>(expr)) { autoconst k = e->getKind(); return k == UETT_SizeOf || k == UETT_AlignOf;
} if (autoconst e = dyn_cast<UnaryOperator>(expr)) { autoconst k = e->getOpcode(); if (k == UO_Plus || k == UO_Minus || k == UO_Not || k == UO_LNot) { return isLiteralLike(e->getSubExpr());
} returnfalse;
} if (autoconst e = dyn_cast<BinaryOperator>(expr)) { autoconst k = e->getOpcode(); if (k == BO_Mul || k == BO_Div || k == BO_Rem || k == BO_Add || k == BO_Sub || k == BO_Shl
|| k == BO_Shr || k == BO_And || k == BO_Xor || k == BO_Or)
{ return isLiteralLike(e->getLHS()) && isLiteralLike(e->getRHS());
} returnfalse;
} if (autoconst e = dyn_cast<ExplicitCastExpr>(expr)) { autoconst t = e->getTypeAsWritten(); return (t->isArithmeticType() || t->isEnumeralType())
&& isLiteralLike(e->getSubExprAsWritten());
} returnfalse;
}
bool canBeUsedForFunctionalCast(TypeSourceInfo const * info) { // Must be <simple-type-specifier> or <typename-specifier>, let's approximate that here:
assert(info != nullptr); autoconst type = info->getType(); if (type.hasLocalQualifiers()) { returnfalse;
} if (autoconst t = dyn_cast<BuiltinType>(type)) { if (!(t->isInteger() || t->isFloatingPoint())) { returnfalse;
} autoconst loc = info->getTypeLoc().castAs<BuiltinTypeLoc>(); return
(int(loc.hasWrittenSignSpec()) + int(loc.hasWrittenWidthSpec())
+ int(loc.hasWrittenTypeSpec()))
== 1;
} if (isa<TagType>(type) || isa<TemplateTypeParmType>(type) || isa<AutoType>(type)
|| isa<DecltypeType>(type) || isa<TypedefType>(type))
{ returntrue;
} if (autoconst t = dyn_cast<ElaboratedType>(type)) { return t->getKeyword() == compat::ElaboratedTypeKeyword::None;
} returnfalse;
}
class CStyleCast: public loplugin::FilteringRewritePlugin<CStyleCast>
{ public: explicit CStyleCast(loplugin::InstantiationData const & data): FilteringRewritePlugin(data)
{}
virtualvoid run() override { if (compiler.getLangOpts().CPlusPlus) {
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
}
}
unsignedint externCContexts_ = 0;
std::set<SourceLocation> rewritten_; // needed when rewriting in macros, in general to avoid "double code replacement, possible // plugin error" warnings, and in particular to avoid adding multiple sets of parens around // sub-exprs
std::set<CStyleCastExpr const *> rewrittenSubExprs_;
};
constchar * recommendedFix(clang::CastKind ck) { switch(ck) { case CK_IntegralToPointer: return"reinterpret_cast"; case CK_PointerToIntegral: return"reinterpret_cast"; case CK_BaseToDerived: return"static_cast"; default: return nullptr;
}
}
bool CStyleCast::VisitCStyleCastExpr(const CStyleCastExpr * expr) { if (ignoreLocation(expr)) { returntrue;
} // casting to void is typically used when a parameter or field is only used in // debug mode, and we want to eliminate an "unused" warning if( expr->getCastKind() == CK_ToVoid ) { returntrue;
} if (isSharedCAndCppCode(expr->getBeginLoc())) { returntrue;
} charconst * perf = nullptr; if( expr->getCastKind() == CK_IntegralCast ) { if (rewriteArithmeticCast(expr, &perf)) { returntrue;
}
} elseif( expr->getCastKind() == CK_NoOp ) { if (!((expr->getSubExpr()->getType()->isPointerType()
&& expr->getType()->isPointerType())
|| expr->getTypeAsWritten()->isReferenceType()))
{ if (rewriteArithmeticCast(expr, &perf)) { returntrue;
}
} if (isConstCast(
expr->getSubExprAsWritten()->getType(),
expr->getTypeAsWritten()))
{
perf = "const_cast";
}
}
reportCast(expr, perf); returntrue;
}
bool CStyleCast::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr const * expr) { if (ignoreLocation(expr)) { returntrue;
} charconst * perf = nullptr; switch (expr->getCastKind()) { case CK_ConstructorConversion: case CK_Dependent: //TODO: really filter out all of these? case CK_IntegralCast: case CK_IntegralToBoolean: case CK_ToVoid: returntrue; case CK_NoOp: if (isConstCast(
expr->getSubExprAsWritten()->getType(),
expr->getTypeAsWritten()))
{
perf = "const_cast"; break;
} returntrue; //TODO: really filter out all of these? default: break;
}
reportCast(expr, perf); returntrue;
}
bool CStyleCast::isConstCast(QualType from, QualType to) { if (to->isReferenceType()
&& to->getAs<ReferenceType>()->getPointeeType()->isObjectType())
{ if (!from->isObjectType()) { returnfalse;
}
from = compiler.getASTContext().getPointerType(from);
to = compiler.getASTContext().getPointerType(
to->getAs<ReferenceType>()->getPointeeType());
} else { if (from->isArrayType()) {
from = compiler.getASTContext().getPointerType(
from->getAsArrayTypeUnsafe()->getElementType());
} elseif (from->isFunctionType()) {
compiler.getASTContext().getPointerType(from);
}
} return areSimilar(from, to);
}
bool CStyleCast::isSharedCAndCppCode(SourceLocation location) const { while (compiler.getSourceManager().isMacroArgExpansion(location)) {
location = compiler.getSourceManager().getImmediateMacroCallerLoc(
location);
} // Assume that code is intended to be shared between C and C++ if it comes // from an include file ending in .h, and is either in an extern "C" context // or the body of a macro definition: return
isFromCIncludeFile(compiler.getSourceManager().getSpellingLoc(location))
&& (externCContexts_ != 0
|| compiler.getSourceManager().isMacroBodyExpansion(location));
}
bool CStyleCast::isLastTokenOfImmediateMacroBodyExpansion(
SourceLocation loc, SourceLocation * macroEnd) const
{
assert(compiler.getSourceManager().isMacroBodyExpansion(loc)); autoconst spell = compiler.getSourceManager().getSpellingLoc(loc); auto name = Lexer::getImmediateMacroName(
loc, compiler.getSourceManager(), compiler.getLangOpts()); while (compat::starts_with(name, "\\\n")) {
name = name.drop_front(2); while (!name.empty()
&& (name.front() == ' ' || name.front() == '\t' || name.front() == '\n'
|| name.front() == '\v' || name.front() == '\f'))
{
name = name.drop_front(1);
}
} autoconst MI
= (compiler.getPreprocessor().getMacroDefinitionAtLoc(
&compiler.getASTContext().Idents.get(name), spell)
.getMacroInfo());
assert(MI != nullptr); if (spell == MI->getDefinitionEndLoc()) { if (macroEnd != nullptr) {
*macroEnd = compat::getImmediateExpansionRange(compiler.getSourceManager(), loc).second;
} returntrue;
} returnfalse;
}
bool CStyleCast::rewriteArithmeticCast(CStyleCastExpr const * expr, charconst ** replacement) {
assert(replacement != nullptr); autoconst sub = expr->getSubExprAsWritten(); autoconst functional = isLiteralLike(sub)
&& canBeUsedForFunctionalCast(expr->getTypeInfoAsWritten());
*replacement = functional ? "functional cast" : "static_cast"; if (rewriter == nullptr) { returnfalse;
} // Doing modifications for a chain of C-style casts as in // // (foo)(bar)(baz)x // // leads to unpredictable results, so only rewrite them one at a time, starting with the // outermost: if (autoconst e = dyn_cast<CStyleCastExpr>(sub)) {
rewrittenSubExprs_.insert(e);
} if (rewrittenSubExprs_.find(expr) != rewrittenSubExprs_.end()) { returnfalse;
} // Two or four ranges to replace: // First is the CStyleCast's LParen, plus following whitespace, replaced with either "" or // "static_cast<". (TODO: insert space before "static_cast<" when converting "else(int)...".) // Second is the CStyleCast's RParen, plus preceding and following whitespace, replaced with // either "" or ">". // If the sub expr is not a ParenExpr, third is the sub expr's begin, inserting "(", and fourth // is the sub expr's end, inserting ")". // (The reason the second and third are not combined is in case there's a comment between them.) auto firstBegin = expr->getLParenLoc(); auto secondBegin = expr->getRParenLoc(); while (compiler.getSourceManager().isMacroArgExpansion(firstBegin)
&& compiler.getSourceManager().isMacroArgExpansion(secondBegin)
&& (compat::getImmediateExpansionRange(compiler.getSourceManager(), firstBegin)
== compat::getImmediateExpansionRange(compiler.getSourceManager(), secondBegin)))
{
firstBegin = compiler.getSourceManager().getImmediateSpellingLoc(firstBegin);
secondBegin = compiler.getSourceManager().getImmediateSpellingLoc(secondBegin);
} if (compiler.getSourceManager().isMacroBodyExpansion(firstBegin)
&& compiler.getSourceManager().isMacroBodyExpansion(secondBegin)
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(firstBegin)
== compiler.getSourceManager().getImmediateMacroCallerLoc(secondBegin)))
{
firstBegin = compiler.getSourceManager().getSpellingLoc(firstBegin);
secondBegin = compiler.getSourceManager().getSpellingLoc(secondBegin);
} auto third = sub->getBeginLoc(); auto fourth = sub->getEndLoc(); bool macro = false; // Ensure that // // #define FOO(x) (int)x // FOO(y) // // is changed to // // #define FOO(x) static_cast<int>(x) // FOO(y) // // instead of // // #define FOO(x) static_cast<int>x // FOO((y)) while (compiler.getSourceManager().isMacroArgExpansion(third)
&& compiler.getSourceManager().isMacroArgExpansion(fourth)
&& (compat::getImmediateExpansionRange(compiler.getSourceManager(), third)
== compat::getImmediateExpansionRange(compiler.getSourceManager(), fourth))
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third)) //TODO: check fourth is at end of immediate macro expansion, but // SourceManager::isAtEndOfImmediateMacroExpansion requires a location pointing at the // character end of the last token
{ autoconst range = compat::getImmediateExpansionRange(compiler.getSourceManager(), third);
third = range.first;
fourth = range.second;
macro = true;
assert(third.isValid());
} while (compiler.getSourceManager().isMacroArgExpansion(third)
&& compiler.getSourceManager().isMacroArgExpansion(fourth)
&& (compat::getImmediateExpansionRange(compiler.getSourceManager(), third)
== compat::getImmediateExpansionRange(compiler.getSourceManager(), fourth)))
{
third = compiler.getSourceManager().getImmediateSpellingLoc(third);
fourth = compiler.getSourceManager().getImmediateSpellingLoc(fourth);
} if (isa<ParenExpr>(sub)) { // Ensure that with // // #define FOO (x) // // a cast like // // (int) FOO // // is changed to // // static_cast<int>(FOO) // // instead of // // static_cast<int>FOO for (;; macro = true) { if (!(compiler.getSourceManager().isMacroBodyExpansion(third)
&& compiler.getSourceManager().isMacroBodyExpansion(fourth)
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
== compiler.getSourceManager().getImmediateMacroCallerLoc(fourth))
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third)
&& isLastTokenOfImmediateMacroBodyExpansion(fourth)))
{ if (!macro) {
third = fourth = SourceLocation();
} break;
} autoconst range = compat::getImmediateExpansionRange(
compiler.getSourceManager(), third);
third = range.first;
fourth = range.second;
assert(third.isValid());
} if (third.isValid() && compiler.getSourceManager().isMacroBodyExpansion(third)
&& compiler.getSourceManager().isMacroBodyExpansion(fourth)
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
== compiler.getSourceManager().getImmediateMacroCallerLoc(fourth)))
{
third = compiler.getSourceManager().getSpellingLoc(third);
fourth = compiler.getSourceManager().getSpellingLoc(fourth);
assert(third.isValid());
}
} else { // Ensure that a cast like // // (int)LONG_MAX // // (where LONG_MAX expands to __LONG_MAX__, which in turn is a built-in expanding to a value // like 9223372036854775807L) is changed to // // int(LONG_MAX) // // instead of trying to add the parentheses to the built-in __LONG_MAX__ definition: for (;;) { if (!(compiler.getSourceManager().isMacroBodyExpansion(third)
&& compiler.getSourceManager().isMacroBodyExpansion(fourth)
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
== compiler.getSourceManager().getImmediateMacroCallerLoc(fourth))
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third))) // TODO: check that fourth is at end of immediate macro expansion (but // SourceManager::isAtEndOfImmediateMacroExpansion wants a location pointing at the // character end)
{ break;
} autoconst range = compat::getImmediateExpansionRange(
compiler.getSourceManager(), third);
third = range.first;
fourth = range.second;
} // ...and additionally asymmetrically unwind macros only at the start or end, for code like // // (long)ubidi_getVisualIndex(...) // // (in editeng/source/editeng/impedit2.cxx) where ubidi_getVisualIndex is an object-like // macro, or // // #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) // // (in hwpfilter/source/lexer.cxx): if (!fourth.isMacroID()) { while (compiler.getSourceManager().isMacroBodyExpansion(third)
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third, &third))
{}
} elseif (compiler.getSourceManager().isMacroBodyExpansion(fourth)) { while (compiler.getSourceManager().isMacroArgExpansion(third)
&& compiler.getSourceManager().isAtStartOfImmediateMacroExpansion(third, &third)) {}
} if (!third.isMacroID()) { while (compiler.getSourceManager().isMacroBodyExpansion(fourth)
&& isLastTokenOfImmediateMacroBodyExpansion(fourth, &fourth))
{}
} elseif (compiler.getSourceManager().isMacroBodyExpansion(third)) { while (compiler.getSourceManager().isMacroArgExpansion(fourth, &fourth)) {}
} if (compiler.getSourceManager().isMacroBodyExpansion(third)
&& compiler.getSourceManager().isMacroBodyExpansion(fourth)
&& (compiler.getSourceManager().getImmediateMacroCallerLoc(third)
== compiler.getSourceManager().getImmediateMacroCallerLoc(fourth)))
{
third = compiler.getSourceManager().getSpellingLoc(third);
fourth = compiler.getSourceManager().getSpellingLoc(fourth);
}
assert(third.isValid());
} if (firstBegin.isMacroID() || secondBegin.isMacroID() || (third.isValid() && third.isMacroID())
|| (fourth.isValid() && fourth.isMacroID()))
{ if (isDebugMode()) {
report(
DiagnosticsEngine::Fatal, "TODO: cannot rewrite C-style cast in macro, needs investigation",
expr->getExprLoc())
<< expr->getSourceRange();
} returnfalse;
} unsigned firstLen = Lexer::MeasureTokenLength(
firstBegin, compiler.getSourceManager(), compiler.getLangOpts()); for (auto l = firstBegin.getLocWithOffset(std::max<unsigned>(firstLen, 1));;
l = l.getLocWithOffset(1))
{ unsigned n = Lexer::MeasureTokenLength(
l, compiler.getSourceManager(), compiler.getLangOpts()); if (n != 0) { break;
}
++firstLen;
} unsigned secondLen = Lexer::MeasureTokenLength(
secondBegin, compiler.getSourceManager(), compiler.getLangOpts()); for (auto l = secondBegin.getLocWithOffset(std::max<unsigned>(secondLen, 1));;
l = l.getLocWithOffset(1))
{ unsigned n = Lexer::MeasureTokenLength(
l, compiler.getSourceManager(), compiler.getLangOpts()); if (n != 0) { break;
}
++secondLen;
} for (;;) { auto l = secondBegin.getLocWithOffset(-1); autoconst c = compiler.getSourceManager().getCharacterData(l)[0]; if (c == '\n') { if (compiler.getSourceManager().getCharacterData(l.getLocWithOffset(-1))[0] == '\\') { break;
}
} elseif (!(c == ' ' || c == '\t' || c == '\v' || c == '\f')) { break;
}
secondBegin = l;
++secondLen;
} if (rewritten_.insert(firstBegin).second) { if (!replaceText(firstBegin, firstLen, functional ? "" : "static_cast<")) { if (isDebugMode()) {
report(
DiagnosticsEngine::Fatal, "TODO: cannot rewrite #1, needs investigation",
firstBegin);
report(
DiagnosticsEngine::Note, "when rewriting this C-style cast", expr->getExprLoc())
<< expr->getSourceRange();
} returnfalse;
} if (!replaceText(secondBegin, secondLen, functional ? "" : ">")) { //TODO: roll back if (isDebugMode()) {
report(
DiagnosticsEngine::Fatal, "TODO: cannot rewrite #2, needs investigation",
secondBegin);
report(
DiagnosticsEngine::Note, "when rewriting this C-style cast", expr->getExprLoc())
<< expr->getSourceRange();
} returnfalse;
}
} if (third.isValid()) { if (rewritten_.insert(third).second) { if (!insertTextBefore(third, "(")) { //TODO: roll back if (isDebugMode()) {
report(
DiagnosticsEngine::Fatal, "TODO: cannot rewrite #3, needs investigation",
third);
report(
DiagnosticsEngine::Note, "when rewriting this C-style cast",
expr->getExprLoc())
<< expr->getSourceRange();
} returnfalse;
} if (!insertTextAfterToken(fourth, ")")) { //TODO: roll back if (isDebugMode()) {
report(
DiagnosticsEngine::Fatal, "TODO: cannot rewrite #4, needs investigation",
third);
report(
DiagnosticsEngine::Note, "when rewriting this C-style cast",
expr->getExprLoc())
<< expr->getSourceRange();
} returnfalse;
}
}
} returntrue;
}
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.