Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
35266 views
1
//=== ConversionChecker.cpp -------------------------------------*- C++ -*-===//
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
// Check that there is no loss of sign/precision in assignments, comparisons
10
// and multiplications.
11
//
12
// ConversionChecker uses path sensitive analysis to determine possible values
13
// of expressions. A warning is reported when:
14
// * a negative value is implicitly converted to an unsigned value in an
15
// assignment, comparison or multiplication.
16
// * assignment / initialization when the source value is greater than the max
17
// value of the target integer type
18
// * assignment / initialization when the source integer is above the range
19
// where the target floating point type can represent all integers
20
//
21
// Many compilers and tools have similar checks that are based on semantic
22
// analysis. Those checks are sound but have poor precision. ConversionChecker
23
// is an alternative to those checks.
24
//
25
//===----------------------------------------------------------------------===//
26
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
27
#include "clang/AST/ParentMap.h"
28
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
29
#include "clang/StaticAnalyzer/Core/Checker.h"
30
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
31
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
32
#include "llvm/ADT/APFloat.h"
33
34
#include <climits>
35
36
using namespace clang;
37
using namespace ento;
38
39
namespace {
40
class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {
41
public:
42
void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
43
44
private:
45
const BugType BT{this, "Conversion"};
46
47
bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType,
48
CheckerContext &C) const;
49
50
bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;
51
52
void reportBug(ExplodedNode *N, const Expr *E, CheckerContext &C,
53
const char Msg[]) const;
54
};
55
}
56
57
void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
58
CheckerContext &C) const {
59
// Don't warn for implicit conversions to bool
60
if (Cast->getType()->isBooleanType())
61
return;
62
63
// Don't warn for loss of sign/precision in macros.
64
if (Cast->getExprLoc().isMacroID())
65
return;
66
67
// Get Parent.
68
const ParentMap &PM = C.getLocationContext()->getParentMap();
69
const Stmt *Parent = PM.getParent(Cast);
70
if (!Parent)
71
return;
72
// Dont warn if this is part of an explicit cast
73
if (isa<ExplicitCastExpr>(Parent))
74
return;
75
76
bool LossOfSign = false;
77
bool LossOfPrecision = false;
78
79
// Loss of sign/precision in binary operation.
80
if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {
81
BinaryOperator::Opcode Opc = B->getOpcode();
82
if (Opc == BO_Assign) {
83
if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) {
84
LossOfSign = isLossOfSign(Cast, C);
85
LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
86
}
87
} else if (Opc == BO_AddAssign || Opc == BO_SubAssign) {
88
// No loss of sign.
89
LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
90
} else if (Opc == BO_MulAssign) {
91
LossOfSign = isLossOfSign(Cast, C);
92
LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
93
} else if (Opc == BO_DivAssign || Opc == BO_RemAssign) {
94
LossOfSign = isLossOfSign(Cast, C);
95
// No loss of precision.
96
} else if (Opc == BO_AndAssign) {
97
LossOfSign = isLossOfSign(Cast, C);
98
// No loss of precision.
99
} else if (Opc == BO_OrAssign || Opc == BO_XorAssign) {
100
LossOfSign = isLossOfSign(Cast, C);
101
LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
102
} else if (B->isRelationalOp() || B->isMultiplicativeOp()) {
103
LossOfSign = isLossOfSign(Cast, C);
104
}
105
} else if (isa<DeclStmt, ReturnStmt>(Parent)) {
106
if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) {
107
LossOfSign = isLossOfSign(Cast, C);
108
LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
109
}
110
} else {
111
LossOfSign = isLossOfSign(Cast, C);
112
LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
113
}
114
115
if (LossOfSign || LossOfPrecision) {
116
// Generate an error node.
117
ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
118
if (!N)
119
return;
120
if (LossOfSign)
121
reportBug(N, Cast, C, "Loss of sign in implicit conversion");
122
if (LossOfPrecision)
123
reportBug(N, Cast, C, "Loss of precision in implicit conversion");
124
}
125
}
126
127
void ConversionChecker::reportBug(ExplodedNode *N, const Expr *E,
128
CheckerContext &C, const char Msg[]) const {
129
// Generate a report for this bug.
130
auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
131
bugreporter::trackExpressionValue(N, E, *R);
132
C.emitReport(std::move(R));
133
}
134
135
bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
136
QualType DestType,
137
CheckerContext &C) const {
138
// Don't warn about explicit loss of precision.
139
if (Cast->isEvaluatable(C.getASTContext()))
140
return false;
141
142
QualType SubType = Cast->IgnoreParenImpCasts()->getType();
143
144
if (!DestType->isRealType() || !SubType->isIntegerType())
145
return false;
146
147
const bool isFloat = DestType->isFloatingType();
148
149
const auto &AC = C.getASTContext();
150
151
// We will find the largest RepresentsUntilExp value such that the DestType
152
// can exactly represent all nonnegative integers below 2^RepresentsUntilExp.
153
unsigned RepresentsUntilExp;
154
155
if (isFloat) {
156
const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);
157
RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);
158
} else {
159
RepresentsUntilExp = AC.getIntWidth(DestType);
160
if (RepresentsUntilExp == 1) {
161
// This is just casting a number to bool, probably not a bug.
162
return false;
163
}
164
if (DestType->isSignedIntegerType())
165
RepresentsUntilExp--;
166
}
167
168
if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {
169
// Avoid overflow in our later calculations.
170
return false;
171
}
172
173
unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);
174
if (SubType->isSignedIntegerType())
175
CorrectedSrcWidth--;
176
177
if (RepresentsUntilExp >= CorrectedSrcWidth) {
178
// Simple case: the destination can store all values of the source type.
179
return false;
180
}
181
182
unsigned long long MaxVal = 1ULL << RepresentsUntilExp;
183
if (isFloat) {
184
// If this is a floating point type, it can also represent MaxVal exactly.
185
MaxVal++;
186
}
187
return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
188
// TODO: maybe also check negative values with too large magnitude.
189
}
190
191
bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
192
CheckerContext &C) const {
193
QualType CastType = Cast->getType();
194
QualType SubType = Cast->IgnoreParenImpCasts()->getType();
195
196
if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
197
return false;
198
199
return C.isNegative(Cast->getSubExpr());
200
}
201
202
void ento::registerConversionChecker(CheckerManager &mgr) {
203
mgr.registerChecker<ConversionChecker>();
204
}
205
206
bool ento::shouldRegisterConversionChecker(const CheckerManager &mgr) {
207
return true;
208
}
209
210