llvm-project

Форк
0
/
MisplacedWideningCastCheck.cpp 
227 строк · 7.1 Кб
1
//===--- MisplacedWideningCastCheck.cpp - clang-tidy-----------------------===//
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-exception
6
//
7
//===----------------------------------------------------------------------===//
8

9
#include "MisplacedWideningCastCheck.h"
10
#include "../utils/Matchers.h"
11
#include "clang/AST/ASTContext.h"
12
#include "clang/ASTMatchers/ASTMatchFinder.h"
13

14
using namespace clang::ast_matchers;
15

16
namespace clang::tidy::bugprone {
17

18
MisplacedWideningCastCheck::MisplacedWideningCastCheck(
19
    StringRef Name, ClangTidyContext *Context)
20
    : ClangTidyCheck(Name, Context),
21
      CheckImplicitCasts(Options.get("CheckImplicitCasts", false)) {}
22

23
void MisplacedWideningCastCheck::storeOptions(
24
    ClangTidyOptions::OptionMap &Opts) {
25
  Options.store(Opts, "CheckImplicitCasts", CheckImplicitCasts);
26
}
27

28
void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
29
  const auto Calc =
30
      expr(anyOf(binaryOperator(hasAnyOperatorName("+", "-", "*", "<<")),
31
                 unaryOperator(hasOperatorName("~"))),
32
           hasType(isInteger()))
33
          .bind("Calc");
34

35
  const auto ExplicitCast = explicitCastExpr(hasDestinationType(isInteger()),
36
                                             has(ignoringParenImpCasts(Calc)));
37
  const auto ImplicitCast =
38
      implicitCastExpr(hasImplicitDestinationType(isInteger()),
39
                       has(ignoringParenImpCasts(Calc)));
40
  const auto Cast =
41
      traverse(TK_AsIs, expr(anyOf(ExplicitCast, ImplicitCast)).bind("Cast"));
42

43
  Finder->addMatcher(varDecl(hasInitializer(Cast)), this);
44
  Finder->addMatcher(returnStmt(hasReturnValue(Cast)), this);
45
  Finder->addMatcher(callExpr(hasAnyArgument(Cast)), this);
46
  Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this);
47
  Finder->addMatcher(
48
      binaryOperator(isComparisonOperator(), hasEitherOperand(Cast)), this);
49
}
50

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

55
  if (const auto *Bop = dyn_cast<BinaryOperator>(E)) {
56
    unsigned LHSWidth = getMaxCalculationWidth(Context, Bop->getLHS());
57
    unsigned RHSWidth = getMaxCalculationWidth(Context, Bop->getRHS());
58
    if (Bop->getOpcode() == BO_Mul)
59
      return LHSWidth + RHSWidth;
60
    if (Bop->getOpcode() == BO_Add)
61
      return std::max(LHSWidth, RHSWidth) + 1;
62
    if (Bop->getOpcode() == BO_Rem) {
63
      Expr::EvalResult Result;
64
      if (Bop->getRHS()->EvaluateAsInt(Result, Context))
65
        return Result.Val.getInt().getActiveBits();
66
    } else if (Bop->getOpcode() == BO_Shl) {
67
      Expr::EvalResult Result;
68
      if (Bop->getRHS()->EvaluateAsInt(Result, Context)) {
69
        // We don't handle negative values and large values well. It is assumed
70
        // that compiler warnings are written for such values so the user will
71
        // fix that.
72
        return LHSWidth + Result.Val.getInt().getExtValue();
73
      }
74

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

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

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

92
static int relativeIntSizes(BuiltinType::Kind Kind) {
93
  switch (Kind) {
94
  case BuiltinType::UChar:
95
    return 1;
96
  case BuiltinType::SChar:
97
    return 1;
98
  case BuiltinType::Char_U:
99
    return 1;
100
  case BuiltinType::Char_S:
101
    return 1;
102
  case BuiltinType::UShort:
103
    return 2;
104
  case BuiltinType::Short:
105
    return 2;
106
  case BuiltinType::UInt:
107
    return 3;
108
  case BuiltinType::Int:
109
    return 3;
110
  case BuiltinType::ULong:
111
    return 4;
112
  case BuiltinType::Long:
113
    return 4;
114
  case BuiltinType::ULongLong:
115
    return 5;
116
  case BuiltinType::LongLong:
117
    return 5;
118
  case BuiltinType::UInt128:
119
    return 6;
120
  case BuiltinType::Int128:
121
    return 6;
122
  default:
123
    return 0;
124
  }
125
}
126

127
static int relativeCharSizes(BuiltinType::Kind Kind) {
128
  switch (Kind) {
129
  case BuiltinType::UChar:
130
    return 1;
131
  case BuiltinType::SChar:
132
    return 1;
133
  case BuiltinType::Char_U:
134
    return 1;
135
  case BuiltinType::Char_S:
136
    return 1;
137
  case BuiltinType::Char16:
138
    return 2;
139
  case BuiltinType::Char32:
140
    return 3;
141
  default:
142
    return 0;
143
  }
144
}
145

146
static int relativeCharSizesW(BuiltinType::Kind Kind) {
147
  switch (Kind) {
148
  case BuiltinType::UChar:
149
    return 1;
150
  case BuiltinType::SChar:
151
    return 1;
152
  case BuiltinType::Char_U:
153
    return 1;
154
  case BuiltinType::Char_S:
155
    return 1;
156
  case BuiltinType::WChar_U:
157
    return 2;
158
  case BuiltinType::WChar_S:
159
    return 2;
160
  default:
161
    return 0;
162
  }
163
}
164

165
static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second) {
166
  int FirstSize = 0, SecondSize = 0;
167
  if ((FirstSize = relativeIntSizes(First)) != 0 &&
168
      (SecondSize = relativeIntSizes(Second)) != 0)
169
    return FirstSize > SecondSize;
170
  if ((FirstSize = relativeCharSizes(First)) != 0 &&
171
      (SecondSize = relativeCharSizes(Second)) != 0)
172
    return FirstSize > SecondSize;
173
  if ((FirstSize = relativeCharSizesW(First)) != 0 &&
174
      (SecondSize = relativeCharSizesW(Second)) != 0)
175
    return FirstSize > SecondSize;
176
  return false;
177
}
178

179
void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) {
180
  const auto *Cast = Result.Nodes.getNodeAs<CastExpr>("Cast");
181
  if (!CheckImplicitCasts && isa<ImplicitCastExpr>(Cast))
182
    return;
183
  if (Cast->getBeginLoc().isMacroID())
184
    return;
185

186
  const auto *Calc = Result.Nodes.getNodeAs<Expr>("Calc");
187
  if (Calc->getBeginLoc().isMacroID())
188
    return;
189

190
  if (Cast->isTypeDependent() || Cast->isValueDependent() ||
191
      Calc->isTypeDependent() || Calc->isValueDependent())
192
    return;
193

194
  ASTContext &Context = *Result.Context;
195

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

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

203
  // If CalcType and CastType have same size then there is no real danger, but
204
  // there can be a portability problem.
205

206
  if (Context.getIntWidth(CastType) == Context.getIntWidth(CalcType)) {
207
    const auto *CastBuiltinType =
208
        dyn_cast<BuiltinType>(CastType->getUnqualifiedDesugaredType());
209
    const auto *CalcBuiltinType =
210
        dyn_cast<BuiltinType>(CalcType->getUnqualifiedDesugaredType());
211
    if (!CastBuiltinType || !CalcBuiltinType)
212
      return;
213
    if (!isFirstWider(CastBuiltinType->getKind(), CalcBuiltinType->getKind()))
214
      return;
215
  }
216

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

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

227
} // namespace clang::tidy::bugprone
228

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.