brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.8 KiB · d11c41c Raw
258 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//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 15using namespace clang::ast_matchers;16 17namespace clang::tidy::readability {18 19static 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  const QualType PtrS = S->getPointeeType();29  const 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 41static 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 52static 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 77static 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 93RedundantCastingCheck::RedundantCastingCheck(StringRef Name,94                                             ClangTidyContext *Context)95    : ClangTidyCheck(Name, Context),96      IgnoreMacros(Options.get("IgnoreMacros", true)),97      IgnoreTypeAliases(Options.get("IgnoreTypeAliases", false)) {}98 99void RedundantCastingCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {100  Options.store(Opts, "IgnoreMacros", IgnoreMacros);101  Options.store(Opts, "IgnoreTypeAliases", IgnoreTypeAliases);102}103 104void 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 135void 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::readability258