void ProTypeCstyleCastCheck::check(const MatchFinder::MatchResult &Result) { const auto *MatchedCast = Result.Nodes.getNodeAs<CStyleCastExpr>("cast"); if (MatchedCast->getCastKind() == CK_BitCast || MatchedCast->getCastKind() == CK_LValueBitCast || MatchedCast->getCastKind() == CK_IntegralToPointer || MatchedCast->getCastKind() == CK_PointerToIntegral || MatchedCast->getCastKind() == CK_ReinterpretMemberPointer) { diag(MatchedCast->getLocStart(), "do not use C-style cast to convert between unrelated types"); return; } QualType SourceType = MatchedCast->getSubExpr()->getType(); if (MatchedCast->getCastKind() == CK_BaseToDerived) { const auto *SourceDecl = SourceType->getPointeeCXXRecordDecl(); if (!SourceDecl) // The cast is from object to reference. SourceDecl = SourceType->getAsCXXRecordDecl(); if (!SourceDecl) return; if (SourceDecl->isPolymorphic()) { // Leave type spelling exactly as it was (unlike // getTypeAsWritten().getAsString() which would spell enum types 'enum // X'). StringRef DestTypeString = Lexer::getSourceText( CharSourceRange::getTokenRange( MatchedCast->getLParenLoc().getLocWithOffset(1), MatchedCast->getRParenLoc().getLocWithOffset(-1)), *Result.SourceManager, getLangOpts()); auto diag_builder = diag( MatchedCast->getLocStart(), "do not use C-style cast to downcast from a base to a derived class; " "use dynamic_cast instead"); const Expr *SubExpr = MatchedCast->getSubExprAsWritten()->IgnoreImpCasts(); std::string CastText = ("dynamic_cast<" + DestTypeString + ">").str(); if (!isa<ParenExpr>(SubExpr)) { CastText.push_back('('); diag_builder << FixItHint::CreateInsertion( Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0, *Result.SourceManager, getLangOpts()), ")"); } auto ParenRange = CharSourceRange::getTokenRange( MatchedCast->getLParenLoc(), MatchedCast->getRParenLoc()); diag_builder << FixItHint::CreateReplacement(ParenRange, CastText); } else { diag( MatchedCast->getLocStart(), "do not use C-style cast to downcast from a base to a derived class"); } return; } if (MatchedCast->getCastKind() == CK_NoOp && needsConstCast(SourceType, MatchedCast->getType())) { diag(MatchedCast->getLocStart(), "do not use C-style cast to cast away constness"); } }
void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast"); auto ParenRange = CharSourceRange::getTokenRange(CastExpr->getLParenLoc(), CastExpr->getRParenLoc()); // Ignore casts in macros. if (ParenRange.getBegin().isMacroID() || ParenRange.getEnd().isMacroID()) return; // Casting to void is an idiomatic way to mute "unused variable" and similar // warnings. if (CastExpr->getTypeAsWritten()->isVoidType()) return; QualType SourceType = CastExpr->getSubExprAsWritten()->getType().getCanonicalType(); QualType DestType = CastExpr->getTypeAsWritten().getCanonicalType(); if (SourceType == DestType) { diag(CastExpr->getLocStart(), "Redundant cast to the same type.") << FixItHint::CreateRemoval(ParenRange); return; } // The rest of this check is only relevant to C++. if (!Result.Context->getLangOpts().CPlusPlus) return; // Leave type spelling exactly as it was (unlike // getTypeAsWritten().getAsString() which would spell enum types 'enum X'). StringRef DestTypeString = Lexer::getSourceText( CharSourceRange::getTokenRange( CastExpr->getLParenLoc().getLocWithOffset(1), CastExpr->getRParenLoc().getLocWithOffset(-1)), *Result.SourceManager, Result.Context->getLangOpts()); auto diag_builder = diag(CastExpr->getLocStart(), "C-style casts are discouraged. %0"); auto ReplaceWithCast = [&](StringRef CastType) { diag_builder << ("Use " + CastType + ".").str(); const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts(); std::string CastText = (CastType + "<" + DestTypeString + ">").str(); if (!isa<ParenExpr>(SubExpr)) { CastText.push_back('('); diag_builder << FixItHint::CreateInsertion( Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0, *Result.SourceManager, Result.Context->getLangOpts()), ")"); } diag_builder << FixItHint::CreateReplacement(ParenRange, CastText); }; // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics. switch (CastExpr->getCastKind()) { case CK_NoOp: if (needsConstCast(SourceType, DestType) && pointedTypesAreEqual(SourceType, DestType)) { ReplaceWithCast("const_cast"); return; } if (DestType->isReferenceType() && (SourceType.getNonReferenceType() == DestType.getNonReferenceType().withConst() || SourceType.getNonReferenceType() == DestType.getNonReferenceType())) { ReplaceWithCast("const_cast"); return; } // FALLTHROUGH case clang::CK_IntegralCast: // Convert integral and no-op casts between builtin types and enums to // static_cast. A cast from enum to integer may be unnecessary, but it's // still retained. if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) && (DestType->isBuiltinType() || DestType->isEnumeralType())) { ReplaceWithCast("static_cast"); return; } break; case CK_BitCast: // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement. if (!needsConstCast(SourceType, DestType)) { ReplaceWithCast("reinterpret_cast"); return; } break; default: break; } diag_builder << "Use static_cast/const_cast/reinterpret_cast."; }
void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast"); // Ignore casts in macros. if (CastExpr->getExprLoc().isMacroID()) return; // Casting to void is an idiomatic way to mute "unused variable" and similar // warnings. if (CastExpr->getCastKind() == CK_ToVoid) return; auto isFunction = [](QualType T) { T = T.getCanonicalType().getNonReferenceType(); return T->isFunctionType() || T->isFunctionPointerType() || T->isMemberFunctionPointerType(); }; const QualType DestTypeAsWritten = CastExpr->getTypeAsWritten().getUnqualifiedType(); const QualType SourceTypeAsWritten = CastExpr->getSubExprAsWritten()->getType().getUnqualifiedType(); const QualType SourceType = SourceTypeAsWritten.getCanonicalType(); const QualType DestType = DestTypeAsWritten.getCanonicalType(); auto ReplaceRange = CharSourceRange::getCharRange( CastExpr->getLParenLoc(), CastExpr->getSubExprAsWritten()->getBeginLoc()); bool FnToFnCast = isFunction(SourceTypeAsWritten) && isFunction(DestTypeAsWritten); if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) { // Function pointer/reference casts may be needed to resolve ambiguities in // case of overloaded functions, so detection of redundant casts is trickier // in this case. Don't emit "redundant cast" warnings for function // pointer/reference types. if (SourceTypeAsWritten == DestTypeAsWritten) { diag(CastExpr->getBeginLoc(), "redundant cast to the same type") << FixItHint::CreateRemoval(ReplaceRange); return; } } // The rest of this check is only relevant to C++. // We also disable it for Objective-C++. if (!getLangOpts().CPlusPlus || getLangOpts().ObjC1 || getLangOpts().ObjC2) return; // Ignore code inside extern "C" {} blocks. if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context) .empty()) return; // Ignore code in .c files and headers included from them, even if they are // compiled as C++. if (getCurrentMainFile().endswith(".c")) return; SourceManager &SM = *Result.SourceManager; // Ignore code in .c files #included in other files (which shouldn't be done, // but people still do this for test and other purposes). if (SM.getFilename(SM.getSpellingLoc(CastExpr->getBeginLoc())).endswith(".c")) return; // Leave type spelling exactly as it was (unlike // getTypeAsWritten().getAsString() which would spell enum types 'enum X'). StringRef DestTypeString = Lexer::getSourceText(CharSourceRange::getTokenRange( CastExpr->getLParenLoc().getLocWithOffset(1), CastExpr->getRParenLoc().getLocWithOffset(-1)), SM, getLangOpts()); auto Diag = diag(CastExpr->getBeginLoc(), "C-style casts are discouraged; use %0"); auto ReplaceWithCast = [&](std::string CastText) { const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts(); if (!isa<ParenExpr>(SubExpr)) { CastText.push_back('('); Diag << FixItHint::CreateInsertion( Lexer::getLocForEndOfToken(SubExpr->getEndLoc(), 0, SM, getLangOpts()), ")"); } Diag << FixItHint::CreateReplacement(ReplaceRange, CastText); }; auto ReplaceWithNamedCast = [&](StringRef CastType) { Diag << CastType; ReplaceWithCast((CastType + "<" + DestTypeString + ">").str()); }; // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics. switch (CastExpr->getCastKind()) { case CK_FunctionToPointerDecay: ReplaceWithNamedCast("static_cast"); return; case CK_ConstructorConversion: if (!CastExpr->getTypeAsWritten().hasQualifiers() && DestTypeAsWritten->isRecordType() && !DestTypeAsWritten->isElaboratedTypeSpecifier()) { Diag << "constructor call syntax"; // FIXME: Validate DestTypeString, maybe. ReplaceWithCast(DestTypeString.str()); } else { ReplaceWithNamedCast("static_cast"); } return; case CK_NoOp: if (FnToFnCast) { ReplaceWithNamedCast("static_cast"); return; } if (SourceType == DestType) { Diag << "static_cast (if needed, the cast may be redundant)"; ReplaceWithCast(("static_cast<" + DestTypeString + ">").str()); return; } if (needsConstCast(SourceType, DestType) && pointedUnqualifiedTypesAreEqual(SourceType, DestType)) { ReplaceWithNamedCast("const_cast"); return; } if (DestType->isReferenceType()) { QualType Dest = DestType.getNonReferenceType(); QualType Source = SourceType.getNonReferenceType(); if (Source == Dest.withConst() || SourceType.getNonReferenceType() == DestType.getNonReferenceType()) { ReplaceWithNamedCast("const_cast"); return; } break; } // FALLTHROUGH case clang::CK_IntegralCast: // Convert integral and no-op casts between builtin types and enums to // static_cast. A cast from enum to integer may be unnecessary, but it's // still retained. if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) && (DestType->isBuiltinType() || DestType->isEnumeralType())) { ReplaceWithNamedCast("static_cast"); return; } break; case CK_BitCast: // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement. if (!needsConstCast(SourceType, DestType)) { if (SourceType->isVoidPointerType()) ReplaceWithNamedCast("static_cast"); else ReplaceWithNamedCast("reinterpret_cast"); return; } break; default: break; } Diag << "static_cast/const_cast/reinterpret_cast"; }