brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.4 KiB · c438889 Raw
292 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 "AvoidCStyleCastsCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/ASTMatchers/ASTMatchers.h"13#include "clang/Lex/Lexer.h"14 15using namespace clang::ast_matchers;16 17namespace clang::tidy::google::readability {18 19void AvoidCStyleCastsCheck::registerMatchers(20    ast_matchers::MatchFinder *Finder) {21  Finder->addMatcher(22      cStyleCastExpr(23          // Filter out (EnumType)IntegerLiteral construct, which is generated24          // for non-type template arguments of enum types.25          // FIXME: Remove this once this is fixed in the AST.26          unless(hasParent(substNonTypeTemplateParmExpr())))27          .bind("cast"),28      this);29 30  Finder->addMatcher(31      cxxFunctionalCastExpr(32          hasDestinationType(hasCanonicalType(anyOf(33              builtinType(), references(qualType()), pointsTo(qualType())))),34          unless(35              hasSourceExpression(anyOf(cxxConstructExpr(), initListExpr()))))36          .bind("cast"),37      this);38}39 40static bool needsConstCast(QualType SourceType, QualType DestType) {41  while ((SourceType->isPointerType() && DestType->isPointerType()) ||42         (SourceType->isReferenceType() && DestType->isReferenceType())) {43    SourceType = SourceType->getPointeeType();44    DestType = DestType->getPointeeType();45    if (SourceType.isConstQualified() && !DestType.isConstQualified()) {46      return (SourceType->isPointerType() == DestType->isPointerType()) &&47             (SourceType->isReferenceType() == DestType->isReferenceType());48    }49  }50  return false;51}52 53static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2) {54  while ((T1->isPointerType() && T2->isPointerType()) ||55         (T1->isReferenceType() && T2->isReferenceType())) {56    T1 = T1->getPointeeType();57    T2 = T2->getPointeeType();58  }59  return T1.getUnqualifiedType() == T2.getUnqualifiedType();60}61 62static clang::CharSourceRange getReplaceRange(const ExplicitCastExpr *Expr) {63  if (const auto *CastExpr = dyn_cast<CStyleCastExpr>(Expr))64    return CharSourceRange::getCharRange(65        CastExpr->getLParenLoc(),66        CastExpr->getSubExprAsWritten()->getBeginLoc());67  if (const auto *CastExpr = dyn_cast<CXXFunctionalCastExpr>(Expr))68    return CharSourceRange::getCharRange(CastExpr->getBeginLoc(),69                                         CastExpr->getLParenLoc());70  llvm_unreachable("Unsupported CastExpr");71}72 73static StringRef getDestTypeString(const SourceManager &SM,74                                   const LangOptions &LangOpts,75                                   const ExplicitCastExpr *Expr) {76  SourceLocation BeginLoc;77  SourceLocation EndLoc;78 79  if (const auto *CastExpr = dyn_cast<CStyleCastExpr>(Expr)) {80    BeginLoc = CastExpr->getLParenLoc().getLocWithOffset(1);81    EndLoc = CastExpr->getRParenLoc().getLocWithOffset(-1);82  } else if (const auto *CastExpr = dyn_cast<CXXFunctionalCastExpr>(Expr)) {83    BeginLoc = CastExpr->getBeginLoc();84    EndLoc = CastExpr->getLParenLoc().getLocWithOffset(-1);85  } else86    llvm_unreachable("Unsupported CastExpr");87 88  return Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),89                              SM, LangOpts);90}91 92static bool sameTypeAsWritten(QualType X, QualType Y) {93  if (X.getCanonicalType() != Y.getCanonicalType())94    return false;95 96  auto TC = X->getTypeClass();97  if (TC != Y->getTypeClass())98    return false;99 100  switch (TC) {101  case Type::Typedef:102    return declaresSameEntity(cast<TypedefType>(X)->getDecl(),103                              cast<TypedefType>(Y)->getDecl());104  case Type::Pointer:105    return sameTypeAsWritten(cast<PointerType>(X)->getPointeeType(),106                             cast<PointerType>(Y)->getPointeeType());107  case Type::RValueReference:108  case Type::LValueReference:109    return sameTypeAsWritten(cast<ReferenceType>(X)->getPointeeType(),110                             cast<ReferenceType>(Y)->getPointeeType());111  default:112    return true;113  }114}115 116void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {117  const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>("cast");118 119  // Ignore casts in macros.120  if (CastExpr->getExprLoc().isMacroID())121    return;122 123  // Casting to void is an idiomatic way to mute "unused variable" and similar124  // warnings.125  if (CastExpr->getCastKind() == CK_ToVoid)126    return;127 128  auto IsFunction = [](QualType T) {129    T = T.getCanonicalType().getNonReferenceType();130    return T->isFunctionType() || T->isFunctionPointerType() ||131           T->isMemberFunctionPointerType();132  };133 134  const QualType DestTypeAsWritten =135      CastExpr->getTypeAsWritten().getUnqualifiedType();136  const QualType SourceTypeAsWritten =137      CastExpr->getSubExprAsWritten()->getType().getUnqualifiedType();138  const QualType SourceType = SourceTypeAsWritten.getCanonicalType();139  const QualType DestType = DestTypeAsWritten.getCanonicalType();140 141  CharSourceRange ReplaceRange = getReplaceRange(CastExpr);142 143  const bool FnToFnCast =144      IsFunction(SourceTypeAsWritten) && IsFunction(DestTypeAsWritten);145 146  const bool ConstructorCast = !CastExpr->getTypeAsWritten().hasQualifiers() &&147                               DestTypeAsWritten->isRecordType() &&148                               !DestTypeAsWritten->isElaboratedTypeSpecifier();149 150  if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) {151    // Function pointer/reference casts may be needed to resolve ambiguities in152    // case of overloaded functions, so detection of redundant casts is trickier153    // in this case. Don't emit "redundant cast" warnings for function154    // pointer/reference types.155    if (sameTypeAsWritten(SourceTypeAsWritten, DestTypeAsWritten)) {156      diag(CastExpr->getBeginLoc(), "redundant cast to the same type")157          << FixItHint::CreateRemoval(ReplaceRange);158      return;159    }160  }161 162  // The rest of this check is only relevant to C++.163  // We also disable it for Objective-C++.164  if (!getLangOpts().CPlusPlus || getLangOpts().ObjC)165    return;166  // Ignore code inside extern "C" {} blocks.167  if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context)168           .empty())169    return;170  // Ignore code in .c files and headers included from them, even if they are171  // compiled as C++.172  if (getCurrentMainFile().ends_with(".c"))173    return;174 175  SourceManager &SM = *Result.SourceManager;176 177  // Ignore code in .c files #included in other files (which shouldn't be done,178  // but people still do this for test and other purposes).179  if (SM.getFilename(SM.getSpellingLoc(CastExpr->getBeginLoc()))180          .ends_with(".c"))181    return;182 183  // Leave type spelling exactly as it was (unlike184  // getTypeAsWritten().getAsString() which would spell enum types 'enum X').185  StringRef DestTypeString = getDestTypeString(SM, getLangOpts(), CastExpr);186 187  auto Diag =188      diag(CastExpr->getBeginLoc(), "C-style casts are discouraged; use %0");189 190  auto ReplaceWithCast = [&](std::string CastText) {191    const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();192    if (!isa<ParenExpr>(SubExpr) && !isa<CXXFunctionalCastExpr>(CastExpr)) {193      CastText.push_back('(');194      Diag << FixItHint::CreateInsertion(195          Lexer::getLocForEndOfToken(SubExpr->getEndLoc(), 0, SM,196                                     getLangOpts()),197          ")");198    }199    Diag << FixItHint::CreateReplacement(ReplaceRange, CastText);200  };201  auto ReplaceWithNamedCast = [&](StringRef CastType) {202    Diag << CastType;203    ReplaceWithCast((CastType + "<" + DestTypeString + ">").str());204  };205  auto ReplaceWithConstructorCall = [&]() {206    Diag << "constructor call syntax";207    // FIXME: Validate DestTypeString, maybe.208    ReplaceWithCast(DestTypeString.str());209  };210  // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics.211  switch (CastExpr->getCastKind()) {212  case CK_FunctionToPointerDecay:213    ReplaceWithNamedCast("static_cast");214    return;215  case CK_ConstructorConversion:216    if (ConstructorCast) {217      ReplaceWithConstructorCall();218    } else {219      ReplaceWithNamedCast("static_cast");220    }221    return;222  case CK_NoOp:223    if (FnToFnCast) {224      ReplaceWithNamedCast("static_cast");225      return;226    }227    if (SourceType == DestType) {228      Diag << "static_cast (if needed, the cast may be redundant)";229      ReplaceWithCast(("static_cast<" + DestTypeString + ">").str());230      return;231    }232    if (needsConstCast(SourceType, DestType) &&233        pointedUnqualifiedTypesAreEqual(SourceType, DestType)) {234      ReplaceWithNamedCast("const_cast");235      return;236    }237    if (ConstructorCast) {238      ReplaceWithConstructorCall();239      return;240    }241    if (DestType->isReferenceType()) {242      const QualType Dest = DestType.getNonReferenceType();243      const QualType Source = SourceType.getNonReferenceType();244      if (Source == Dest.withConst() ||245          SourceType.getNonReferenceType() == DestType.getNonReferenceType()) {246        ReplaceWithNamedCast("const_cast");247        return;248      }249      break;250    }251    if (DestType->isVoidPointerType() && SourceType->isPointerType() &&252        !SourceType->getPointeeType()->isPointerType()) {253      ReplaceWithNamedCast("reinterpret_cast");254      return;255    }256 257    [[fallthrough]];258  case clang::CK_IntegralCast:259    // Convert integral and no-op casts between builtin types and enums to260    // static_cast. A cast from enum to integer may be unnecessary, but it's261    // still retained.262    if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) &&263        (DestType->isBuiltinType() || DestType->isEnumeralType())) {264      ReplaceWithNamedCast("static_cast");265      return;266    }267    break;268  case CK_BitCast:269    // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement.270    if (!needsConstCast(SourceType, DestType)) {271      if (SourceType->isVoidPointerType())272        ReplaceWithNamedCast("static_cast");273      else274        ReplaceWithNamedCast("reinterpret_cast");275      return;276    }277    break;278  case CK_BaseToDerived:279    if (!needsConstCast(SourceType, DestType)) {280      ReplaceWithNamedCast("static_cast");281      return;282    }283    break;284  default:285    break;286  }287 288  Diag << "static_cast/const_cast/reinterpret_cast";289}290 291} // namespace clang::tidy::google::readability292