1 //===--- RedundantCastingCheck.cpp - clang-tidy ---------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "RedundantCastingCheck.h" 10 #include "../utils/FixItHintUtils.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 #include "clang/Lex/Lexer.h" 14 15 using namespace clang::ast_matchers; 16 17 namespace clang::tidy::readability { 18 19 static bool areTypesEqual(QualType S, QualType D) { 20 if (S == D) 21 return true; 22 23 const auto *TS = S->getAs<TypedefType>(); 24 const auto *TD = D->getAs<TypedefType>(); 25 if (TS != TD) 26 return false; 27 28 QualType PtrS = S->getPointeeType(); 29 QualType PtrD = D->getPointeeType(); 30 31 if (!PtrS.isNull() && !PtrD.isNull()) 32 return areTypesEqual(PtrS, PtrD); 33 34 const DeducedType *DT = S->getContainedDeducedType(); 35 if (DT && DT->isDeduced()) 36 return D == DT->getDeducedType(); 37 38 return false; 39 } 40 41 static bool areTypesEqual(QualType TypeS, QualType TypeD, 42 bool IgnoreTypeAliases) { 43 const QualType CTypeS = TypeS.getCanonicalType(); 44 const QualType CTypeD = TypeD.getCanonicalType(); 45 if (CTypeS != CTypeD) 46 return false; 47 48 return IgnoreTypeAliases || areTypesEqual(TypeS.getLocalUnqualifiedType(), 49 TypeD.getLocalUnqualifiedType()); 50 } 51 52 static bool areBinaryOperatorOperandsTypesEqualToOperatorResultType( 53 const Expr *E, bool IgnoreTypeAliases) { 54 if (!E) 55 return true; 56 const Expr *WithoutImplicitAndParen = E->IgnoreParenImpCasts(); 57 if (!WithoutImplicitAndParen) 58 return true; 59 if (const auto *B = dyn_cast<BinaryOperator>(WithoutImplicitAndParen)) { 60 const QualType Type = WithoutImplicitAndParen->getType(); 61 if (Type.isNull()) 62 return true; 63 64 const QualType NonReferenceType = Type.getNonReferenceType(); 65 const QualType LHSType = B->getLHS()->IgnoreImplicit()->getType(); 66 if (LHSType.isNull() || !areTypesEqual(LHSType.getNonReferenceType(), 67 NonReferenceType, IgnoreTypeAliases)) 68 return false; 69 const QualType RHSType = B->getRHS()->IgnoreImplicit()->getType(); 70 if (RHSType.isNull() || !areTypesEqual(RHSType.getNonReferenceType(), 71 NonReferenceType, IgnoreTypeAliases)) 72 return false; 73 } 74 return true; 75 } 76 77 static const Decl *getSourceExprDecl(const Expr *SourceExpr) { 78 const Expr *CleanSourceExpr = SourceExpr->IgnoreParenImpCasts(); 79 if (const auto *E = dyn_cast<DeclRefExpr>(CleanSourceExpr)) { 80 return E->getDecl(); 81 } 82 83 if (const auto *E = dyn_cast<CallExpr>(CleanSourceExpr)) { 84 return E->getCalleeDecl(); 85 } 86 87 if (const auto *E = dyn_cast<MemberExpr>(CleanSourceExpr)) { 88 return E->getMemberDecl(); 89 } 90 return nullptr; 91 } 92 93 RedundantCastingCheck::RedundantCastingCheck(StringRef Name, 94 ClangTidyContext *Context) 95 : ClangTidyCheck(Name, Context), 96 IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)), 97 IgnoreTypeAliases(Options.get("IgnoreTypeAliases", false)) {} 98 99 void RedundantCastingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 100 Options.store(Opts, "IgnoreMacros", IgnoreMacros); 101 Options.store(Opts, "IgnoreTypeAliases", IgnoreTypeAliases); 102 } 103 104 void RedundantCastingCheck::registerMatchers(MatchFinder *Finder) { 105 auto SimpleType = qualType(hasCanonicalType( 106 qualType(anyOf(builtinType(), references(builtinType()), 107 references(pointsTo(qualType())), pointsTo(qualType()))))); 108 109 auto BitfieldMemberExpr = memberExpr(member(fieldDecl(isBitField()))); 110 111 const ast_matchers::internal::VariadicDynCastAllOfMatcher< 112 Stmt, CXXParenListInitExpr> 113 cxxParenListInitExpr; // NOLINT(readability-identifier-naming) 114 115 Finder->addMatcher( 116 explicitCastExpr( 117 unless(hasCastKind(CK_ConstructorConversion)), 118 unless(hasCastKind(CK_UserDefinedConversion)), 119 unless(cxxFunctionalCastExpr(hasDestinationType(unless(SimpleType)))), 120 121 hasDestinationType(qualType().bind("dstType")), 122 hasSourceExpression(anyOf( 123 expr(unless(initListExpr()), unless(BitfieldMemberExpr), 124 unless(cxxParenListInitExpr()), 125 hasType(qualType().bind("srcType"))) 126 .bind("source"), 127 initListExpr(unless(hasInit(1, expr())), 128 hasInit(0, expr(unless(BitfieldMemberExpr), 129 hasType(qualType().bind("srcType"))) 130 .bind("source")))))) 131 .bind("cast"), 132 this); 133 } 134 135 void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) { 136 const auto *SourceExpr = Result.Nodes.getNodeAs<Expr>("source"); 137 auto TypeD = *Result.Nodes.getNodeAs<QualType>("dstType"); 138 139 if (SourceExpr->getValueKind() == VK_LValue && 140 TypeD.getCanonicalType()->isRValueReferenceType()) 141 return; 142 143 const auto TypeS = 144 Result.Nodes.getNodeAs<QualType>("srcType")->getNonReferenceType(); 145 TypeD = TypeD.getNonReferenceType(); 146 147 if (!areTypesEqual(TypeS, TypeD, IgnoreTypeAliases)) 148 return; 149 150 if (!areBinaryOperatorOperandsTypesEqualToOperatorResultType( 151 SourceExpr, IgnoreTypeAliases)) 152 return; 153 154 const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>("cast"); 155 if (IgnoreMacros && 156 (CastExpr->getBeginLoc().isMacroID() || 157 CastExpr->getEndLoc().isMacroID() || CastExpr->getExprLoc().isMacroID())) 158 return; 159 160 { 161 auto Diag = diag(CastExpr->getExprLoc(), 162 "redundant explicit casting to the same type %0 as the " 163 "sub-expression, remove this casting"); 164 Diag << TypeD; 165 166 const SourceManager &SM = *Result.SourceManager; 167 const SourceLocation SourceExprBegin = 168 SM.getExpansionLoc(SourceExpr->getBeginLoc()); 169 const SourceLocation SourceExprEnd = 170 SM.getExpansionLoc(SourceExpr->getEndLoc()); 171 172 if (SourceExprBegin != CastExpr->getBeginLoc()) 173 Diag << FixItHint::CreateRemoval(SourceRange( 174 CastExpr->getBeginLoc(), SourceExprBegin.getLocWithOffset(-1))); 175 176 const SourceLocation NextToken = Lexer::getLocForEndOfToken( 177 SourceExprEnd, 0U, SM, Result.Context->getLangOpts()); 178 179 if (SourceExprEnd != CastExpr->getEndLoc()) { 180 Diag << FixItHint::CreateRemoval( 181 SourceRange(NextToken, CastExpr->getEndLoc())); 182 } 183 184 if (utils::fixit::areParensNeededForStatement(*SourceExpr)) { 185 Diag << FixItHint::CreateInsertion(SourceExprBegin, "(") 186 << FixItHint::CreateInsertion(NextToken, ")"); 187 } 188 } 189 190 const auto *SourceExprDecl = getSourceExprDecl(SourceExpr); 191 if (!SourceExprDecl) 192 return; 193 194 if (const auto *D = dyn_cast<CXXConstructorDecl>(SourceExprDecl)) { 195 diag(D->getLocation(), 196 "source type originates from the invocation of this constructor", 197 DiagnosticIDs::Note); 198 return; 199 } 200 201 if (const auto *D = dyn_cast<FunctionDecl>(SourceExprDecl)) { 202 diag(D->getLocation(), 203 "source type originates from the invocation of this " 204 "%select{function|method}0", 205 DiagnosticIDs::Note) 206 << isa<CXXMethodDecl>(D) << D->getReturnTypeSourceRange(); 207 return; 208 } 209 210 if (const auto *D = dyn_cast<FieldDecl>(SourceExprDecl)) { 211 diag(D->getLocation(), 212 "source type originates from referencing this member", 213 DiagnosticIDs::Note) 214 << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc()); 215 return; 216 } 217 218 if (const auto *D = dyn_cast<ParmVarDecl>(SourceExprDecl)) { 219 diag(D->getLocation(), 220 "source type originates from referencing this parameter", 221 DiagnosticIDs::Note) 222 << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc()); 223 return; 224 } 225 226 if (const auto *D = dyn_cast<VarDecl>(SourceExprDecl)) { 227 diag(D->getLocation(), 228 "source type originates from referencing this variable", 229 DiagnosticIDs::Note) 230 << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc()); 231 return; 232 } 233 234 if (const auto *D = dyn_cast<EnumConstantDecl>(SourceExprDecl)) { 235 diag(D->getLocation(), 236 "source type originates from referencing this enum constant", 237 DiagnosticIDs::Note); 238 return; 239 } 240 241 if (const auto *D = dyn_cast<BindingDecl>(SourceExprDecl)) { 242 diag(D->getLocation(), 243 "source type originates from referencing this bound variable", 244 DiagnosticIDs::Note); 245 return; 246 } 247 248 if (const auto *D = dyn_cast<NonTypeTemplateParmDecl>(SourceExprDecl)) { 249 diag(D->getLocation(), 250 "source type originates from referencing this non-type template " 251 "parameter", 252 DiagnosticIDs::Note); 253 return; 254 } 255 } 256 257 } // namespace clang::tidy::readability 258