|
| 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.getLocalOrGlobal("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 | + Finder->addMatcher( |
| 112 | + explicitCastExpr( |
| 113 | + unless(hasCastKind(CK_ConstructorConversion)), |
| 114 | + unless(hasCastKind(CK_UserDefinedConversion)), |
| 115 | + unless(cxxFunctionalCastExpr(hasDestinationType(unless(SimpleType)))), |
| 116 | + |
| 117 | + hasDestinationType(qualType().bind("dstType")), |
| 118 | + hasSourceExpression(anyOf( |
| 119 | + expr(unless(initListExpr()), unless(BitfieldMemberExpr), |
| 120 | + hasType(qualType().bind("srcType"))) |
| 121 | + .bind("source"), |
| 122 | + initListExpr(unless(hasInit(1, expr())), |
| 123 | + hasInit(0, expr(unless(BitfieldMemberExpr), |
| 124 | + hasType(qualType().bind("srcType"))) |
| 125 | + .bind("source")))))) |
| 126 | + .bind("cast"), |
| 127 | + this); |
| 128 | +} |
| 129 | + |
| 130 | +void RedundantCastingCheck::check(const MatchFinder::MatchResult &Result) { |
| 131 | + const auto *SourceExpr = Result.Nodes.getNodeAs<Expr>("source"); |
| 132 | + auto TypeD = *Result.Nodes.getNodeAs<QualType>("dstType"); |
| 133 | + |
| 134 | + if (SourceExpr->getValueKind() == VK_LValue && |
| 135 | + TypeD.getCanonicalType()->isRValueReferenceType()) |
| 136 | + return; |
| 137 | + |
| 138 | + const auto TypeS = |
| 139 | + Result.Nodes.getNodeAs<QualType>("srcType")->getNonReferenceType(); |
| 140 | + TypeD = TypeD.getNonReferenceType(); |
| 141 | + |
| 142 | + if (!areTypesEqual(TypeS, TypeD, IgnoreTypeAliases)) |
| 143 | + return; |
| 144 | + |
| 145 | + if (!areBinaryOperatorOperandsTypesEqualToOperatorResultType( |
| 146 | + SourceExpr, IgnoreTypeAliases)) |
| 147 | + return; |
| 148 | + |
| 149 | + const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>("cast"); |
| 150 | + if (IgnoreMacros && |
| 151 | + (CastExpr->getBeginLoc().isMacroID() || |
| 152 | + CastExpr->getEndLoc().isMacroID() || CastExpr->getExprLoc().isMacroID())) |
| 153 | + return; |
| 154 | + |
| 155 | + { |
| 156 | + auto Diag = diag(CastExpr->getExprLoc(), |
| 157 | + "redundant explicit casting to the same type %0 as the " |
| 158 | + "sub-expression, remove this casting"); |
| 159 | + Diag << TypeD; |
| 160 | + |
| 161 | + const SourceManager &SM = *Result.SourceManager; |
| 162 | + const SourceLocation SourceExprBegin = |
| 163 | + SM.getExpansionLoc(SourceExpr->getBeginLoc()); |
| 164 | + const SourceLocation SourceExprEnd = |
| 165 | + SM.getExpansionLoc(SourceExpr->getEndLoc()); |
| 166 | + |
| 167 | + if (SourceExprBegin != CastExpr->getBeginLoc()) |
| 168 | + Diag << FixItHint::CreateRemoval(SourceRange( |
| 169 | + CastExpr->getBeginLoc(), SourceExprBegin.getLocWithOffset(-1))); |
| 170 | + |
| 171 | + const SourceLocation NextToken = Lexer::getLocForEndOfToken( |
| 172 | + SourceExprEnd, 0U, SM, Result.Context->getLangOpts()); |
| 173 | + |
| 174 | + if (SourceExprEnd != CastExpr->getEndLoc()) { |
| 175 | + Diag << FixItHint::CreateRemoval( |
| 176 | + SourceRange(NextToken, CastExpr->getEndLoc())); |
| 177 | + } |
| 178 | + |
| 179 | + if (utils::fixit::areParensNeededForStatement(*SourceExpr)) { |
| 180 | + Diag << FixItHint::CreateInsertion(SourceExprBegin, "(") |
| 181 | + << FixItHint::CreateInsertion(NextToken, ")"); |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + const auto *SourceExprDecl = getSourceExprDecl(SourceExpr); |
| 186 | + if (!SourceExprDecl) |
| 187 | + return; |
| 188 | + |
| 189 | + if (const auto *D = dyn_cast<CXXConstructorDecl>(SourceExprDecl)) { |
| 190 | + diag(D->getLocation(), |
| 191 | + "source type originates from the invocation of this constructor", |
| 192 | + DiagnosticIDs::Note); |
| 193 | + return; |
| 194 | + } |
| 195 | + |
| 196 | + if (const auto *D = dyn_cast<FunctionDecl>(SourceExprDecl)) { |
| 197 | + diag(D->getLocation(), |
| 198 | + "source type originates from the invocation of this " |
| 199 | + "%select{function|method}0", |
| 200 | + DiagnosticIDs::Note) |
| 201 | + << isa<CXXMethodDecl>(D) << D->getReturnTypeSourceRange(); |
| 202 | + return; |
| 203 | + } |
| 204 | + |
| 205 | + if (const auto *D = dyn_cast<FieldDecl>(SourceExprDecl)) { |
| 206 | + diag(D->getLocation(), |
| 207 | + "source type originates from referencing this member", |
| 208 | + DiagnosticIDs::Note) |
| 209 | + << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc()); |
| 210 | + return; |
| 211 | + } |
| 212 | + |
| 213 | + if (const auto *D = dyn_cast<ParmVarDecl>(SourceExprDecl)) { |
| 214 | + diag(D->getLocation(), |
| 215 | + "source type originates from referencing this parameter", |
| 216 | + DiagnosticIDs::Note) |
| 217 | + << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc()); |
| 218 | + return; |
| 219 | + } |
| 220 | + |
| 221 | + if (const auto *D = dyn_cast<VarDecl>(SourceExprDecl)) { |
| 222 | + diag(D->getLocation(), |
| 223 | + "source type originates from referencing this variable", |
| 224 | + DiagnosticIDs::Note) |
| 225 | + << SourceRange(D->getTypeSpecStartLoc(), D->getTypeSpecEndLoc()); |
| 226 | + return; |
| 227 | + } |
| 228 | + |
| 229 | + if (const auto *D = dyn_cast<EnumConstantDecl>(SourceExprDecl)) { |
| 230 | + diag(D->getLocation(), |
| 231 | + "source type originates from referencing this enum constant", |
| 232 | + DiagnosticIDs::Note); |
| 233 | + return; |
| 234 | + } |
| 235 | + |
| 236 | + if (const auto *D = dyn_cast<BindingDecl>(SourceExprDecl)) { |
| 237 | + diag(D->getLocation(), |
| 238 | + "source type originates from referencing this bound variable", |
| 239 | + DiagnosticIDs::Note); |
| 240 | + return; |
| 241 | + } |
| 242 | + |
| 243 | + if (const auto *D = dyn_cast<NonTypeTemplateParmDecl>(SourceExprDecl)) { |
| 244 | + diag(D->getLocation(), |
| 245 | + "source type originates from referencing this non-type template " |
| 246 | + "parameter", |
| 247 | + DiagnosticIDs::Note); |
| 248 | + return; |
| 249 | + } |
| 250 | +} |
| 251 | + |
| 252 | +} // namespace clang::tidy::readability |
0 commit comments