1 //===--- AvoidCStyleCastsCheck.cpp - clang-tidy -----------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "AvoidCStyleCastsCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 #include "clang/ASTMatchers/ASTMatchers.h" 14 #include "clang/Lex/Lexer.h" 15 16 using namespace clang::ast_matchers; 17 18 namespace clang { 19 namespace tidy { 20 namespace google { 21 namespace readability { 22 23 void 24 AvoidCStyleCastsCheck::registerMatchers(ast_matchers::MatchFinder *Finder) { 25 Finder->addMatcher( 26 cStyleCastExpr( 27 // Filter out (EnumType)IntegerLiteral construct, which is generated 28 // for non-type template arguments of enum types. 29 // FIXME: Remove this once this is fixed in the AST. 30 unless(hasParent(substNonTypeTemplateParmExpr())), 31 // Avoid matches in template instantiations. 32 unless(isInTemplateInstantiation())).bind("cast"), 33 this); 34 } 35 36 static bool needsConstCast(QualType SourceType, QualType DestType) { 37 SourceType = SourceType.getNonReferenceType(); 38 DestType = DestType.getNonReferenceType(); 39 while (SourceType->isPointerType() && DestType->isPointerType()) { 40 SourceType = SourceType->getPointeeType(); 41 DestType = DestType->getPointeeType(); 42 if (SourceType.isConstQualified() && !DestType.isConstQualified()) 43 return true; 44 } 45 return false; 46 } 47 48 static bool pointedTypesAreEqual(QualType SourceType, QualType DestType) { 49 SourceType = SourceType.getNonReferenceType(); 50 DestType = DestType.getNonReferenceType(); 51 while (SourceType->isPointerType() && DestType->isPointerType()) { 52 SourceType = SourceType->getPointeeType(); 53 DestType = DestType->getPointeeType(); 54 } 55 return SourceType.getUnqualifiedType() == DestType.getUnqualifiedType(); 56 } 57 58 void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) { 59 const auto *CastExpr = Result.Nodes.getNodeAs<CStyleCastExpr>("cast"); 60 61 auto ParenRange = CharSourceRange::getTokenRange(CastExpr->getLParenLoc(), 62 CastExpr->getRParenLoc()); 63 // Ignore casts in macros. 64 if (ParenRange.getBegin().isMacroID() || ParenRange.getEnd().isMacroID()) 65 return; 66 67 // Casting to void is an idiomatic way to mute "unused variable" and similar 68 // warnings. 69 if (CastExpr->getTypeAsWritten()->isVoidType()) 70 return; 71 72 QualType SourceType = CastExpr->getSubExprAsWritten()->getType(); 73 QualType DestType = CastExpr->getTypeAsWritten(); 74 75 if (SourceType == DestType) { 76 diag(CastExpr->getLocStart(), "redundant cast to the same type") 77 << FixItHint::CreateRemoval(ParenRange); 78 return; 79 } 80 SourceType = SourceType.getCanonicalType(); 81 DestType = DestType.getCanonicalType(); 82 if (SourceType == DestType) { 83 diag(CastExpr->getLocStart(), 84 "possibly redundant cast between typedefs of the same type"); 85 return; 86 } 87 88 89 // The rest of this check is only relevant to C++. 90 if (!Result.Context->getLangOpts().CPlusPlus) 91 return; 92 // Ignore code inside extern "C" {} blocks. 93 if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context) 94 .empty()) 95 return; 96 // Ignore code in .c files and headers included from them, even if they are 97 // compiled as C++. 98 if (getCurrentMainFile().endswith(".c")) 99 return; 100 // Ignore code in .c files #included in other files (which shouldn't be done, 101 // but people still do this for test and other purposes). 102 SourceManager &SM = *Result.SourceManager; 103 if (SM.getFilename(SM.getSpellingLoc(CastExpr->getLocStart())).endswith(".c")) 104 return; 105 106 // Leave type spelling exactly as it was (unlike 107 // getTypeAsWritten().getAsString() which would spell enum types 'enum X'). 108 StringRef DestTypeString = 109 Lexer::getSourceText(CharSourceRange::getTokenRange( 110 CastExpr->getLParenLoc().getLocWithOffset(1), 111 CastExpr->getRParenLoc().getLocWithOffset(-1)), 112 SM, Result.Context->getLangOpts()); 113 114 auto diag_builder = 115 diag(CastExpr->getLocStart(), "C-style casts are discouraged; use %0"); 116 117 auto ReplaceWithCast = [&](StringRef CastType) { 118 diag_builder << CastType; 119 120 const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts(); 121 std::string CastText = (CastType + "<" + DestTypeString + ">").str(); 122 if (!isa<ParenExpr>(SubExpr)) { 123 CastText.push_back('('); 124 diag_builder << FixItHint::CreateInsertion( 125 Lexer::getLocForEndOfToken(SubExpr->getLocEnd(), 0, SM, 126 Result.Context->getLangOpts()), 127 ")"); 128 } 129 diag_builder << FixItHint::CreateReplacement(ParenRange, CastText); 130 }; 131 132 // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics. 133 switch (CastExpr->getCastKind()) { 134 case CK_NoOp: 135 if (needsConstCast(SourceType, DestType) && 136 pointedTypesAreEqual(SourceType, DestType)) { 137 ReplaceWithCast("const_cast"); 138 return; 139 } 140 if (DestType->isReferenceType() && 141 (SourceType.getNonReferenceType() == 142 DestType.getNonReferenceType().withConst() || 143 SourceType.getNonReferenceType() == DestType.getNonReferenceType())) { 144 ReplaceWithCast("const_cast"); 145 return; 146 } 147 // FALLTHROUGH 148 case clang::CK_IntegralCast: 149 // Convert integral and no-op casts between builtin types and enums to 150 // static_cast. A cast from enum to integer may be unnecessary, but it's 151 // still retained. 152 if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) && 153 (DestType->isBuiltinType() || DestType->isEnumeralType())) { 154 ReplaceWithCast("static_cast"); 155 return; 156 } 157 break; 158 case CK_BitCast: 159 // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement. 160 if (!needsConstCast(SourceType, DestType)) { 161 ReplaceWithCast("reinterpret_cast"); 162 return; 163 } 164 break; 165 default: 166 break; 167 } 168 169 diag_builder << "static_cast/const_cast/reinterpret_cast"; 170 } 171 172 } // namespace readability 173 } // namespace google 174 } // namespace tidy 175 } // namespace clang 176