brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.9 KiB · 634d54c Raw
303 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 "ImplicitWideningOfMultiplicationResultCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/ASTMatchers/ASTMatchersMacros.h"13#include "clang/Lex/Lexer.h"14#include <optional>15 16using namespace clang::ast_matchers;17 18namespace clang::tidy::bugprone {19 20namespace {21AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) {22  return Node.isPartOfExplicitCast();23}24AST_MATCHER(Expr, containsErrors) { return Node.containsErrors(); }25} // namespace26 27static const Expr *getLHSOfMulBinOp(const Expr *E) {28  assert(E == E->IgnoreParens() && "Already skipped all parens!");29  // Is this:  long r = int(x) * int(y);  ?30  // FIXME: shall we skip brackets/casts/etc?31  const auto *BO = dyn_cast<BinaryOperator>(E);32  if (!BO || BO->getOpcode() != BO_Mul)33    // FIXME: what about:  long r = int(x) + (int(y) * int(z));  ?34    return nullptr;35  return BO->getLHS()->IgnoreParens();36}37 38ImplicitWideningOfMultiplicationResultCheck::39    ImplicitWideningOfMultiplicationResultCheck(StringRef Name,40                                                ClangTidyContext *Context)41    : ClangTidyCheck(Name, Context),42      UseCXXStaticCastsInCppSources(43          Options.get("UseCXXStaticCastsInCppSources", true)),44      UseCXXHeadersInCppSources(Options.get("UseCXXHeadersInCppSources", true)),45      IgnoreConstantIntExpr(Options.get("IgnoreConstantIntExpr", false)),46      IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",47                                               utils::IncludeSorter::IS_LLVM),48                      areDiagsSelfContained()) {}49 50void ImplicitWideningOfMultiplicationResultCheck::registerPPCallbacks(51    const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {52  IncludeInserter.registerPreprocessor(PP);53}54 55void ImplicitWideningOfMultiplicationResultCheck::storeOptions(56    ClangTidyOptions::OptionMap &Opts) {57  Options.store(Opts, "UseCXXStaticCastsInCppSources",58                UseCXXStaticCastsInCppSources);59  Options.store(Opts, "UseCXXHeadersInCppSources", UseCXXHeadersInCppSources);60  Options.store(Opts, "IgnoreConstantIntExpr", IgnoreConstantIntExpr);61  Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());62}63 64std::optional<FixItHint>65ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader(66    SourceLocation File) {67  return IncludeInserter.createIncludeInsertion(68      Result->SourceManager->getFileID(File),69      ShouldUseCXXHeader ? "<cstddef>" : "<stddef.h>");70}71 72void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(73    const ImplicitCastExpr *ICE) {74  const ASTContext *Context = Result->Context;75 76  const Expr *E = ICE->getSubExpr()->IgnoreParens();77  const QualType Ty = ICE->getType();78  const QualType ETy = E->getType();79 80  assert(!ETy->isDependentType() && !Ty->isDependentType() &&81         "Don't expect to ever get here in template Context.");82 83  // This must be a widening cast. Else we do not care.84  const unsigned SrcWidth = Context->getIntWidth(ETy);85  const unsigned TgtWidth = Context->getIntWidth(Ty);86  if (TgtWidth <= SrcWidth)87    return;88 89  // Is the expression a compile-time constexpr that we know can fit in the90  // source type?91  if (IgnoreConstantIntExpr && ETy->isIntegerType() &&92      !ETy->isUnsignedIntegerType()) {93    if (const auto ConstExprResult = E->getIntegerConstantExpr(*Context)) {94      const auto TypeSize = Context->getTypeSize(ETy);95      const llvm::APSInt WidenedResult = ConstExprResult->extOrTrunc(TypeSize);96      if (WidenedResult <= llvm::APSInt::getMaxValue(TypeSize, false) &&97          WidenedResult >= llvm::APSInt::getMinValue(TypeSize, false))98        return;99    }100  }101 102  // Does the index expression look like it might be unintentionally computed103  // in a narrower-than-wanted type?104  const Expr *LHS = getLHSOfMulBinOp(E);105  if (!LHS)106    return;107 108  // Ok, looks like we should diagnose this.109  diag(E->getBeginLoc(), "performing an implicit widening conversion to type "110                         "%0 of a multiplication performed in type %1")111      << Ty << E->getType();112 113  {114    auto Diag = diag(E->getBeginLoc(),115                     "make conversion explicit to silence this warning",116                     DiagnosticIDs::Note)117                << E->getSourceRange();118    const SourceLocation EndLoc = Lexer::getLocForEndOfToken(119        E->getEndLoc(), 0, *Result->SourceManager, getLangOpts());120    if (ShouldUseCXXStaticCast)121      Diag << FixItHint::CreateInsertion(122                  E->getBeginLoc(), "static_cast<" + Ty.getAsString() + ">(")123           << FixItHint::CreateInsertion(EndLoc, ")");124    else125      Diag << FixItHint::CreateInsertion(E->getBeginLoc(),126                                         "(" + Ty.getAsString() + ")(")127           << FixItHint::CreateInsertion(EndLoc, ")");128    Diag << includeStddefHeader(E->getBeginLoc());129  }130 131  QualType WideExprTy;132  // Get Ty of the same signedness as ExprTy, because we only want to suggest133  // to widen the computation, but not change it's signedness domain.134  if (Ty->isSignedIntegerType() == ETy->isSignedIntegerType())135    WideExprTy = Ty;136  else if (Ty->isSignedIntegerType()) {137    assert(ETy->isUnsignedIntegerType() &&138           "Expected source type to be signed.");139    WideExprTy = Context->getCorrespondingUnsignedType(Ty);140  } else {141    assert(Ty->isUnsignedIntegerType() &&142           "Expected target type to be unsigned.");143    assert(ETy->isSignedIntegerType() &&144           "Expected source type to be unsigned.");145    WideExprTy = Context->getCorrespondingSignedType(Ty);146  }147 148  {149    auto Diag = diag(E->getBeginLoc(), "perform multiplication in a wider type",150                     DiagnosticIDs::Note)151                << LHS->getSourceRange();152 153    if (ShouldUseCXXStaticCast)154      Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),155                                         "static_cast<" +156                                             WideExprTy.getAsString() + ">(")157           << FixItHint::CreateInsertion(158                  Lexer::getLocForEndOfToken(LHS->getEndLoc(), 0,159                                             *Result->SourceManager,160                                             getLangOpts()),161                  ")");162    else163      Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),164                                         "(" + WideExprTy.getAsString() + ")");165    Diag << includeStddefHeader(LHS->getBeginLoc());166  }167}168 169void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(170    const Expr *E) {171  const ASTContext *Context = Result->Context;172 173  // We are looking for a pointer offset operation,174  // with one hand being a pointer, and another one being an offset.175  const Expr *PointerExpr = nullptr, *IndexExpr = nullptr;176  if (const auto *BO = dyn_cast<BinaryOperator>(E)) {177    PointerExpr = BO->getLHS();178    IndexExpr = BO->getRHS();179  } else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {180    PointerExpr = ASE->getLHS();181    IndexExpr = ASE->getRHS();182  } else183    return;184 185  if (IndexExpr->getType()->isPointerType())186    std::swap(PointerExpr, IndexExpr);187 188  if (!PointerExpr->getType()->isPointerType() ||189      IndexExpr->getType()->isPointerType())190    return;191 192  IndexExpr = IndexExpr->IgnoreParens();193 194  const QualType IndexExprType = IndexExpr->getType();195 196  // If the index expression's type is not known (i.e. we are in a template),197  // we can't do anything here.198  if (IndexExprType->isDependentType())199    return;200 201  const QualType SSizeTy = Context->getPointerDiffType();202  const QualType USizeTy = Context->getSizeType();203  const QualType SizeTy =204      IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy;205  // FIXME: is there a way to actually get the QualType for size_t/ptrdiff_t?206  // Note that SizeTy.getAsString() will be unsigned long/..., NOT size_t!207  const StringRef TyAsString =208      IndexExprType->isSignedIntegerType() ? "ptrdiff_t" : "size_t";209 210  // So, is size_t actually wider than the result of the multiplication?211  if (Context->getIntWidth(IndexExprType) >= Context->getIntWidth(SizeTy))212    return;213 214  // Does the index expression look like it might be unintentionally computed215  // in a narrower-than-wanted type?216  const Expr *LHS = getLHSOfMulBinOp(IndexExpr);217  if (!LHS)218    return;219 220  // Ok, looks like we should diagnose this.221  diag(E->getBeginLoc(),222       "result of multiplication in type %0 is used as a pointer offset after "223       "an implicit widening conversion to type '%1'")224      << IndexExprType << TyAsString;225 226  {227    auto Diag = diag(IndexExpr->getBeginLoc(),228                     "make conversion explicit to silence this warning",229                     DiagnosticIDs::Note)230                << IndexExpr->getSourceRange();231    const SourceLocation EndLoc = Lexer::getLocForEndOfToken(232        IndexExpr->getEndLoc(), 0, *Result->SourceManager, getLangOpts());233    if (ShouldUseCXXStaticCast)234      Diag << FixItHint::CreateInsertion(235                  IndexExpr->getBeginLoc(),236                  (Twine("static_cast<") + TyAsString + ">(").str())237           << FixItHint::CreateInsertion(EndLoc, ")");238    else239      Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),240                                         (Twine("(") + TyAsString + ")(").str())241           << FixItHint::CreateInsertion(EndLoc, ")");242    Diag << includeStddefHeader(IndexExpr->getBeginLoc());243  }244 245  {246    auto Diag =247        diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type",248             DiagnosticIDs::Note)249        << LHS->getSourceRange();250 251    if (ShouldUseCXXStaticCast)252      Diag << FixItHint::CreateInsertion(253                  LHS->getBeginLoc(),254                  (Twine("static_cast<") + TyAsString + ">(").str())255           << FixItHint::CreateInsertion(256                  Lexer::getLocForEndOfToken(IndexExpr->getEndLoc(), 0,257                                             *Result->SourceManager,258                                             getLangOpts()),259                  ")");260    else261      Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),262                                         (Twine("(") + TyAsString + ")").str());263    Diag << includeStddefHeader(LHS->getBeginLoc());264  }265}266 267void ImplicitWideningOfMultiplicationResultCheck::registerMatchers(268    MatchFinder *Finder) {269  Finder->addMatcher(implicitCastExpr(unless(anyOf(containsErrors(),270                                                   isInTemplateInstantiation(),271                                                   isPartOfExplicitCast())),272                                      hasCastKind(CK_IntegralCast))273                         .bind("x"),274                     this);275  Finder->addMatcher(276      arraySubscriptExpr(unless(isInTemplateInstantiation())).bind("x"), this);277  Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()),278                                    hasType(isAnyPointer()),279                                    hasAnyOperatorName("+", "-", "+=", "-="))280                         .bind("x"),281                     this);282}283 284void ImplicitWideningOfMultiplicationResultCheck::check(285    const MatchFinder::MatchResult &Result) {286  this->Result = &Result;287  ShouldUseCXXStaticCast =288      UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus;289  ShouldUseCXXHeader =290      UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus;291 292  if (const auto *MatchedDecl = Result.Nodes.getNodeAs<ImplicitCastExpr>("x"))293    handleImplicitCastExpr(MatchedDecl);294  else if (const auto *MatchedDecl =295               Result.Nodes.getNodeAs<ArraySubscriptExpr>("x"))296    handlePointerOffsetting(MatchedDecl);297  else if (const auto *MatchedDecl =298               Result.Nodes.getNodeAs<BinaryOperator>("x"))299    handlePointerOffsetting(MatchedDecl);300}301 302} // namespace clang::tidy::bugprone303