|  | //===--- AvoidCStyleCastsCheck.cpp - clang-tidy -----------------*- C++ -*-===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "AvoidCStyleCastsCheck.h" | 
|  | #include "clang/AST/ASTContext.h" | 
|  | #include "clang/ASTMatchers/ASTMatchFinder.h" | 
|  | #include "clang/ASTMatchers/ASTMatchers.h" | 
|  | #include "clang/Lex/Lexer.h" | 
|  |  | 
|  | using namespace clang::ast_matchers; | 
|  |  | 
|  | namespace clang { | 
|  | namespace tidy { | 
|  | namespace google { | 
|  | namespace readability { | 
|  |  | 
|  | void AvoidCStyleCastsCheck::registerMatchers( | 
|  | ast_matchers::MatchFinder *Finder) { | 
|  | Finder->addMatcher( | 
|  | cStyleCastExpr( | 
|  | // Filter out (EnumType)IntegerLiteral construct, which is generated | 
|  | // for non-type template arguments of enum types. | 
|  | // FIXME: Remove this once this is fixed in the AST. | 
|  | unless(hasParent(substNonTypeTemplateParmExpr())), | 
|  | // Avoid matches in template instantiations. | 
|  | unless(isInTemplateInstantiation())) | 
|  | .bind("cast"), | 
|  | this); | 
|  | } | 
|  |  | 
|  | static bool needsConstCast(QualType SourceType, QualType DestType) { | 
|  | while ((SourceType->isPointerType() && DestType->isPointerType()) || | 
|  | (SourceType->isReferenceType() && DestType->isReferenceType())) { | 
|  | SourceType = SourceType->getPointeeType(); | 
|  | DestType = DestType->getPointeeType(); | 
|  | if (SourceType.isConstQualified() && !DestType.isConstQualified()) { | 
|  | return (SourceType->isPointerType() == DestType->isPointerType()) && | 
|  | (SourceType->isReferenceType() == DestType->isReferenceType()); | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2) { | 
|  | while ((T1->isPointerType() && T2->isPointerType()) || | 
|  | (T1->isReferenceType() && T2->isReferenceType())) { | 
|  | T1 = T1->getPointeeType(); | 
|  | T2 = T2->getPointeeType(); | 
|  | } | 
|  | return T1.getUnqualifiedType() == T2.getUnqualifiedType(); | 
|  | } | 
|  |  | 
|  | 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()->getLocStart()); | 
|  |  | 
|  | 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->getLocStart(), "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->getLocStart())).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->getLocStart(), "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->getLocEnd(), 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"; | 
|  | } | 
|  |  | 
|  | } // namespace readability | 
|  | } // namespace google | 
|  | } // namespace tidy | 
|  | } // namespace clang |