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
|