summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/misc/MisplacedWideningCastCheck.cpp
blob: 0006131f20f7080393bcd32de6b734886bfe76cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//===--- MisplacedWideningCastCheck.cpp - clang-tidy-----------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "MisplacedWideningCastCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"

using namespace clang::ast_matchers;

namespace clang {
namespace tidy {
namespace misc {

void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
  auto Calc = expr(anyOf(binaryOperator(anyOf(
                             hasOperatorName("+"), hasOperatorName("-"),
                             hasOperatorName("*"), hasOperatorName("<<"))),
                         unaryOperator(hasOperatorName("~"))),
                   hasType(isInteger()))
                  .bind("Calc");

  auto Cast = explicitCastExpr(anyOf(cStyleCastExpr(), cxxStaticCastExpr(),
                                     cxxReinterpretCastExpr()),
                               hasDestinationType(isInteger()), has(Calc))
                  .bind("Cast");

  Finder->addMatcher(varDecl(has(Cast)), this);
  Finder->addMatcher(returnStmt(has(Cast)), this);
  Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this);
}

static unsigned getMaxCalculationWidth(ASTContext &Context, const Expr *E) {
  E = E->IgnoreParenImpCasts();

  if (const auto *Bop = dyn_cast<BinaryOperator>(E)) {
    unsigned LHSWidth = getMaxCalculationWidth(Context, Bop->getLHS());
    unsigned RHSWidth = getMaxCalculationWidth(Context, Bop->getRHS());
    if (Bop->getOpcode() == BO_Mul)
      return LHSWidth + RHSWidth;
    if (Bop->getOpcode() == BO_Add)
      return std::max(LHSWidth, RHSWidth) + 1;
    if (Bop->getOpcode() == BO_Rem) {
      llvm::APSInt Val;
      if (Bop->getRHS()->EvaluateAsInt(Val, Context))
        return Val.getActiveBits();
    } else if (Bop->getOpcode() == BO_Shl) {
      llvm::APSInt Bits;
      if (Bop->getRHS()->EvaluateAsInt(Bits, Context)) {
        // We don't handle negative values and large values well. It is assumed
        // that compiler warnings are written for such values so the user will
        // fix that.
        return LHSWidth + Bits.getExtValue();
      }

      // Unknown bitcount, assume there is truncation.
      return 1024U;
    }
  } else if (const auto *Uop = dyn_cast<UnaryOperator>(E)) {
    // There is truncation when ~ is used.
    if (Uop->getOpcode() == UO_Not)
      return 1024U;

    QualType T = Uop->getType();
    return T->isIntegerType() ? Context.getIntWidth(T) : 1024U;
  } else if (const auto *I = dyn_cast<IntegerLiteral>(E)) {
    return I->getValue().getActiveBits();
  }

  return Context.getIntWidth(E->getType());
}

void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) {
  const auto *Cast = Result.Nodes.getNodeAs<ExplicitCastExpr>("Cast");
  if (Cast->getLocStart().isMacroID())
    return;

  const auto *Calc = Result.Nodes.getNodeAs<Expr>("Calc");
  if (Calc->getLocStart().isMacroID())
    return;

  ASTContext &Context = *Result.Context;

  QualType CastType = Cast->getType();
  QualType CalcType = Calc->getType();

  // Explicit truncation using cast.
  if (Context.getIntWidth(CastType) < Context.getIntWidth(CalcType))
    return;

  // If CalcType and CastType have same size then there is no real danger, but
  // there can be a portability problem.
  if (Context.getIntWidth(CastType) == Context.getIntWidth(CalcType)) {
    if (CalcType->isSpecificBuiltinType(BuiltinType::Int)) {
      // There should be a warning when casting from int to long or long long.
      if (!CastType->isSpecificBuiltinType(BuiltinType::Long) &&
          !CastType->isSpecificBuiltinType(BuiltinType::LongLong))
        return;
    } else if (CalcType->isSpecificBuiltinType(BuiltinType::Long)) {
      // There should be a warning when casting from long to long long.
      if (!CastType->isSpecificBuiltinType(BuiltinType::LongLong))
        return;
    } else {
      return;
    }
  }

  // Don't write a warning if we can easily see that the result is not
  // truncated.
  if (Context.getIntWidth(CalcType) >= getMaxCalculationWidth(Context, Calc))
    return;

  diag(Cast->getLocStart(), "either cast from %0 to %1 is ineffective, or "
                            "there is loss of precision before the conversion")
      << CalcType << CastType;
}

} // namespace misc
} // namespace tidy
} // namespace clang
OpenPOWER on IntegriCloud