brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.1 KiB · f040235 Raw
227 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 "MisplacedWideningCastCheck.h"10#include "clang/AST/ASTContext.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::bugprone {16 17MisplacedWideningCastCheck::MisplacedWideningCastCheck(18    StringRef Name, ClangTidyContext *Context)19    : ClangTidyCheck(Name, Context),20      CheckImplicitCasts(Options.get("CheckImplicitCasts", false)) {}21 22void MisplacedWideningCastCheck::storeOptions(23    ClangTidyOptions::OptionMap &Opts) {24  Options.store(Opts, "CheckImplicitCasts", CheckImplicitCasts);25}26 27void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {28  const auto Calc =29      expr(anyOf(binaryOperator(hasAnyOperatorName("+", "-", "*", "<<")),30                 unaryOperator(hasOperatorName("~"))),31           hasType(isInteger()))32          .bind("Calc");33 34  const auto ExplicitCast = explicitCastExpr(hasDestinationType(isInteger()),35                                             has(ignoringParenImpCasts(Calc)));36  const auto ImplicitCast =37      implicitCastExpr(hasImplicitDestinationType(isInteger()),38                       has(ignoringParenImpCasts(Calc)));39  const auto Cast =40      traverse(TK_AsIs, expr(anyOf(ExplicitCast, ImplicitCast)).bind("Cast"));41 42  Finder->addMatcher(varDecl(hasInitializer(Cast)), this);43  Finder->addMatcher(returnStmt(hasReturnValue(Cast)), this);44  Finder->addMatcher(callExpr(hasAnyArgument(Cast)), this);45  Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this);46  Finder->addMatcher(47      binaryOperator(isComparisonOperator(), hasEitherOperand(Cast)), this);48}49 50static unsigned getMaxCalculationWidth(const ASTContext &Context,51                                       const Expr *E) {52  E = E->IgnoreParenImpCasts();53 54  if (const auto *Bop = dyn_cast<BinaryOperator>(E)) {55    const unsigned LHSWidth = getMaxCalculationWidth(Context, Bop->getLHS());56    const unsigned RHSWidth = getMaxCalculationWidth(Context, Bop->getRHS());57    if (Bop->getOpcode() == BO_Mul)58      return LHSWidth + RHSWidth;59    if (Bop->getOpcode() == BO_Add)60      return std::max(LHSWidth, RHSWidth) + 1;61    if (Bop->getOpcode() == BO_Rem) {62      Expr::EvalResult Result;63      if (Bop->getRHS()->EvaluateAsInt(Result, Context))64        return Result.Val.getInt().getActiveBits();65    } else if (Bop->getOpcode() == BO_Shl) {66      Expr::EvalResult Result;67      if (Bop->getRHS()->EvaluateAsInt(Result, Context)) {68        // We don't handle negative values and large values well. It is assumed69        // that compiler warnings are written for such values so the user will70        // fix that.71        return LHSWidth + Result.Val.getInt().getExtValue();72      }73 74      // Unknown bitcount, assume there is truncation.75      return 1024U;76    }77  } else if (const auto *Uop = dyn_cast<UnaryOperator>(E)) {78    // There is truncation when ~ is used.79    if (Uop->getOpcode() == UO_Not)80      return 1024U;81 82    const QualType T = Uop->getType();83    return T->isIntegerType() ? Context.getIntWidth(T) : 1024U;84  } else if (const auto *I = dyn_cast<IntegerLiteral>(E)) {85    return I->getValue().getActiveBits();86  }87 88  return Context.getIntWidth(E->getType());89}90 91static int relativeIntSizes(BuiltinType::Kind Kind) {92  switch (Kind) {93  case BuiltinType::UChar:94    return 1;95  case BuiltinType::SChar:96    return 1;97  case BuiltinType::Char_U:98    return 1;99  case BuiltinType::Char_S:100    return 1;101  case BuiltinType::UShort:102    return 2;103  case BuiltinType::Short:104    return 2;105  case BuiltinType::UInt:106    return 3;107  case BuiltinType::Int:108    return 3;109  case BuiltinType::ULong:110    return 4;111  case BuiltinType::Long:112    return 4;113  case BuiltinType::ULongLong:114    return 5;115  case BuiltinType::LongLong:116    return 5;117  case BuiltinType::UInt128:118    return 6;119  case BuiltinType::Int128:120    return 6;121  default:122    return 0;123  }124}125 126static int relativeCharSizes(BuiltinType::Kind Kind) {127  switch (Kind) {128  case BuiltinType::UChar:129    return 1;130  case BuiltinType::SChar:131    return 1;132  case BuiltinType::Char_U:133    return 1;134  case BuiltinType::Char_S:135    return 1;136  case BuiltinType::Char16:137    return 2;138  case BuiltinType::Char32:139    return 3;140  default:141    return 0;142  }143}144 145static int relativeCharSizesW(BuiltinType::Kind Kind) {146  switch (Kind) {147  case BuiltinType::UChar:148    return 1;149  case BuiltinType::SChar:150    return 1;151  case BuiltinType::Char_U:152    return 1;153  case BuiltinType::Char_S:154    return 1;155  case BuiltinType::WChar_U:156    return 2;157  case BuiltinType::WChar_S:158    return 2;159  default:160    return 0;161  }162}163 164static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second) {165  int FirstSize = 0, SecondSize = 0;166  if ((FirstSize = relativeIntSizes(First)) != 0 &&167      (SecondSize = relativeIntSizes(Second)) != 0)168    return FirstSize > SecondSize;169  if ((FirstSize = relativeCharSizes(First)) != 0 &&170      (SecondSize = relativeCharSizes(Second)) != 0)171    return FirstSize > SecondSize;172  if ((FirstSize = relativeCharSizesW(First)) != 0 &&173      (SecondSize = relativeCharSizesW(Second)) != 0)174    return FirstSize > SecondSize;175  return false;176}177 178void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) {179  const auto *Cast = Result.Nodes.getNodeAs<CastExpr>("Cast");180  if (!CheckImplicitCasts && isa<ImplicitCastExpr>(Cast))181    return;182  if (Cast->getBeginLoc().isMacroID())183    return;184 185  const auto *Calc = Result.Nodes.getNodeAs<Expr>("Calc");186  if (Calc->getBeginLoc().isMacroID())187    return;188 189  if (Cast->isTypeDependent() || Cast->isValueDependent() ||190      Calc->isTypeDependent() || Calc->isValueDependent())191    return;192 193  const ASTContext &Context = *Result.Context;194 195  const QualType CastType = Cast->getType();196  const QualType CalcType = Calc->getType();197 198  // Explicit truncation using cast.199  if (Context.getIntWidth(CastType) < Context.getIntWidth(CalcType))200    return;201 202  // If CalcType and CastType have same size then there is no real danger, but203  // there can be a portability problem.204 205  if (Context.getIntWidth(CastType) == Context.getIntWidth(CalcType)) {206    const auto *CastBuiltinType =207        dyn_cast<BuiltinType>(CastType->getUnqualifiedDesugaredType());208    const auto *CalcBuiltinType =209        dyn_cast<BuiltinType>(CalcType->getUnqualifiedDesugaredType());210    if (!CastBuiltinType || !CalcBuiltinType)211      return;212    if (!isFirstWider(CastBuiltinType->getKind(), CalcBuiltinType->getKind()))213      return;214  }215 216  // Don't write a warning if we can easily see that the result is not217  // truncated.218  if (Context.getIntWidth(CalcType) >= getMaxCalculationWidth(Context, Calc))219    return;220 221  diag(Cast->getBeginLoc(), "either cast from %0 to %1 is ineffective, or "222                            "there is loss of precision before the conversion")223      << CalcType << CastType;224}225 226} // namespace clang::tidy::bugprone227