1 //=== ConversionChecker.cpp -------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // Check that there is no loss of sign/precision in assignments, comparisons
10 // and multiplications.
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
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.
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"
36 using namespace clang
;
40 class ConversionChecker
: public Checker
<check::PreStmt
<ImplicitCastExpr
>> {
42 void checkPreStmt(const ImplicitCastExpr
*Cast
, CheckerContext
&C
) const;
45 mutable std::unique_ptr
<BugType
> BT
;
47 bool isLossOfPrecision(const ImplicitCastExpr
*Cast
, QualType DestType
,
48 CheckerContext
&C
) const;
50 bool isLossOfSign(const ImplicitCastExpr
*Cast
, CheckerContext
&C
) const;
52 void reportBug(ExplodedNode
*N
, const Expr
*E
, CheckerContext
&C
,
53 const char Msg
[]) const;
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())
63 // Don't warn for loss of sign/precision in macros.
64 if (Cast
->getExprLoc().isMacroID())
68 const ParentMap
&PM
= C
.getLocationContext()->getParentMap();
69 const Stmt
*Parent
= PM
.getParent(Cast
);
72 // Dont warn if this is part of an explicit cast
73 if (isa
<ExplicitCastExpr
>(Parent
))
76 bool LossOfSign
= false;
77 bool LossOfPrecision
= false;
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
);
87 } else if (Opc
== BO_AddAssign
|| Opc
== BO_SubAssign
) {
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
);
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
);
111 LossOfSign
= isLossOfSign(Cast
, C
);
112 LossOfPrecision
= isLossOfPrecision(Cast
, Cast
->getType(), C
);
115 if (LossOfSign
|| LossOfPrecision
) {
116 // Generate an error node.
117 ExplodedNode
*N
= C
.generateNonFatalErrorNode(C
.getState());
121 reportBug(N
, Cast
, C
, "Loss of sign in implicit conversion");
123 reportBug(N
, Cast
, C
, "Loss of precision in implicit conversion");
127 void ConversionChecker::reportBug(ExplodedNode
*N
, const Expr
*E
,
128 CheckerContext
&C
, const char Msg
[]) const {
130 BT
.reset(new BugType(this, "Conversion"));
132 // Generate a report for this bug.
133 auto R
= std::make_unique
<PathSensitiveBugReport
>(*BT
, Msg
, N
);
134 bugreporter::trackExpressionValue(N
, E
, *R
);
135 C
.emitReport(std::move(R
));
138 bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr
*Cast
,
140 CheckerContext
&C
) const {
141 // Don't warn about explicit loss of precision.
142 if (Cast
->isEvaluatable(C
.getASTContext()))
145 QualType SubType
= Cast
->IgnoreParenImpCasts()->getType();
147 if (!DestType
->isRealType() || !SubType
->isIntegerType())
150 const bool isFloat
= DestType
->isFloatingType();
152 const auto &AC
= C
.getASTContext();
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
;
159 const llvm::fltSemantics
&Sema
= AC
.getFloatTypeSemantics(DestType
);
160 RepresentsUntilExp
= llvm::APFloat::semanticsPrecision(Sema
);
162 RepresentsUntilExp
= AC
.getIntWidth(DestType
);
163 if (RepresentsUntilExp
== 1) {
164 // This is just casting a number to bool, probably not a bug.
167 if (DestType
->isSignedIntegerType())
168 RepresentsUntilExp
--;
171 if (RepresentsUntilExp
>= sizeof(unsigned long long) * CHAR_BIT
) {
172 // Avoid overflow in our later calculations.
176 unsigned CorrectedSrcWidth
= AC
.getIntWidth(SubType
);
177 if (SubType
->isSignedIntegerType())
180 if (RepresentsUntilExp
>= CorrectedSrcWidth
) {
181 // Simple case: the destination can store all values of the source type.
185 unsigned long long MaxVal
= 1ULL << RepresentsUntilExp
;
187 // If this is a floating point type, it can also represent MaxVal exactly.
190 return C
.isGreaterOrEqual(Cast
->getSubExpr(), MaxVal
);
191 // TODO: maybe also check negative values with too large magnitude.
194 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr
*Cast
,
195 CheckerContext
&C
) const {
196 QualType CastType
= Cast
->getType();
197 QualType SubType
= Cast
->IgnoreParenImpCasts()->getType();
199 if (!CastType
->isUnsignedIntegerType() || !SubType
->isSignedIntegerType())
202 return C
.isNegative(Cast
->getSubExpr());
205 void ento::registerConversionChecker(CheckerManager
&mgr
) {
206 mgr
.registerChecker
<ConversionChecker
>();
209 bool ento::shouldRegisterConversionChecker(const CheckerManager
&mgr
) {