clang 18.0.0git
ConversionChecker.cpp
Go to the documentation of this file.
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//===----------------------------------------------------------------------===//
27#include "clang/AST/ParentMap.h"
32#include "llvm/ADT/APFloat.h"
33
34#include <climits>
35
36using namespace clang;
37using namespace ento;
38
39namespace {
40class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {
41public:
42 void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
43
44private:
45 mutable std::unique_ptr<BugType> BT;
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
57void 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
127void ConversionChecker::reportBug(ExplodedNode *N, const Expr *E,
128 CheckerContext &C, const char Msg[]) const {
129 if (!BT)
130 BT.reset(new BugType(this, "Conversion"));
131
132 // Generate a report for this bug.
133 auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
135 C.emitReport(std::move(R));
136}
137
138bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
139 QualType DestType,
140 CheckerContext &C) const {
141 // Don't warn about explicit loss of precision.
142 if (Cast->isEvaluatable(C.getASTContext()))
143 return false;
144
145 QualType SubType = Cast->IgnoreParenImpCasts()->getType();
146
147 if (!DestType->isRealType() || !SubType->isIntegerType())
148 return false;
149
150 const bool isFloat = DestType->isFloatingType();
151
152 const auto &AC = C.getASTContext();
153
154 // We will find the largest RepresentsUntilExp value such that the DestType
155 // can exactly represent all nonnegative integers below 2^RepresentsUntilExp.
156 unsigned RepresentsUntilExp;
157
158 if (isFloat) {
159 const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);
160 RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);
161 } else {
162 RepresentsUntilExp = AC.getIntWidth(DestType);
163 if (RepresentsUntilExp == 1) {
164 // This is just casting a number to bool, probably not a bug.
165 return false;
166 }
167 if (DestType->isSignedIntegerType())
168 RepresentsUntilExp--;
169 }
170
171 if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {
172 // Avoid overflow in our later calculations.
173 return false;
174 }
175
176 unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);
177 if (SubType->isSignedIntegerType())
178 CorrectedSrcWidth--;
179
180 if (RepresentsUntilExp >= CorrectedSrcWidth) {
181 // Simple case: the destination can store all values of the source type.
182 return false;
183 }
184
185 unsigned long long MaxVal = 1ULL << RepresentsUntilExp;
186 if (isFloat) {
187 // If this is a floating point type, it can also represent MaxVal exactly.
188 MaxVal++;
189 }
190 return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
191 // TODO: maybe also check negative values with too large magnitude.
192}
193
194bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
195 CheckerContext &C) const {
196 QualType CastType = Cast->getType();
197 QualType SubType = Cast->IgnoreParenImpCasts()->getType();
198
199 if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
200 return false;
201
202 return C.isNegative(Cast->getSubExpr());
203}
204
205void ento::registerConversionChecker(CheckerManager &mgr) {
206 mgr.registerChecker<ConversionChecker>();
207}
208
209bool ento::shouldRegisterConversionChecker(const CheckerManager &mgr) {
210 return true;
211}
NodeId Parent
Definition: ASTDiff.cpp:191
CastType
Definition: SemaCast.cpp:47
This represents one expression.
Definition: Expr.h:110
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition: Expr.h:3677
Stmt * getParent(Stmt *) const
Definition: ParentMap.cpp:136
A (possibly-)qualified type.
Definition: Type.h:736
Sema - This implements semantic analysis and AST building for C.
Definition: Sema.h:357
Stmt - This represents one statement.
Definition: Stmt.h:84
bool isSignedIntegerType() const
Return true if this is an integer type that is signed, according to C99 6.2.5p4 [char,...
Definition: Type.cpp:2083
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: Type.h:7384
bool isFloatingType() const
Definition: Type.cpp:2186
bool isRealType() const
Definition: Type.cpp:2209
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
#define CHAR_BIT
Definition: limits.h:67
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
bool Cast(InterpState &S, CodePtr OpPC)
Definition: Interp.h:1557
BinaryOperatorKind