1 //=== ConversionChecker.cpp -------------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Check that there is no loss of sign/precision in assignments, comparisons
11 // and multiplications.
13 // ConversionChecker uses path sensitive analysis to determine possible values
14 // of expressions. A warning is reported when:
15 // * a negative value is implicitly converted to an unsigned value in an
16 // assignment, comparison or multiplication.
17 // * assignment / initialization when the source value is greater than the max
18 // value of the target integer type
19 // * assignment / initialization when the source integer is above the range
20 // where the target floating point type can represent all integers
22 // Many compilers and tools have similar checks that are based on semantic
23 // analysis. Those checks are sound but have poor precision. ConversionChecker
24 // is an alternative to those checks.
26 //===----------------------------------------------------------------------===//
27 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
28 #include "clang/AST/ParentMap.h"
29 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
30 #include "clang/StaticAnalyzer/Core/Checker.h"
31 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
32 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
33 #include "llvm/ADT/APFloat.h"
37 using namespace clang;
41 class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {
43 void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
46 mutable std::unique_ptr<BuiltinBug> BT;
48 bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType,
49 CheckerContext &C) const;
51 bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;
53 void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const;
57 void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
58 CheckerContext &C) const {
59 // TODO: For now we only warn about DeclRefExpr, to avoid noise. Warn for
61 if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts()))
64 // Don't warn for loss of sign/precision in macros.
65 if (Cast->getExprLoc().isMacroID())
69 const ParentMap &PM = C.getLocationContext()->getParentMap();
70 const Stmt *Parent = PM.getParent(Cast);
74 bool LossOfSign = false;
75 bool LossOfPrecision = false;
77 // Loss of sign/precision in binary operation.
78 if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {
79 BinaryOperator::Opcode Opc = B->getOpcode();
80 if (Opc == BO_Assign) {
81 LossOfSign = isLossOfSign(Cast, C);
82 LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
83 } else if (Opc == BO_AddAssign || Opc == BO_SubAssign) {
85 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
86 } else if (Opc == BO_MulAssign) {
87 LossOfSign = isLossOfSign(Cast, C);
88 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
89 } else if (Opc == BO_DivAssign || Opc == BO_RemAssign) {
90 LossOfSign = isLossOfSign(Cast, C);
91 // No loss of precision.
92 } else if (Opc == BO_AndAssign) {
93 LossOfSign = isLossOfSign(Cast, C);
94 // No loss of precision.
95 } else if (Opc == BO_OrAssign || Opc == BO_XorAssign) {
96 LossOfSign = isLossOfSign(Cast, C);
97 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
98 } else if (B->isRelationalOp() || B->isMultiplicativeOp()) {
99 LossOfSign = isLossOfSign(Cast, C);
101 } else if (isa<DeclStmt>(Parent)) {
102 LossOfSign = isLossOfSign(Cast, C);
103 LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
106 if (LossOfSign || LossOfPrecision) {
107 // Generate an error node.
108 ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
112 reportBug(N, C, "Loss of sign in implicit conversion");
114 reportBug(N, C, "Loss of precision in implicit conversion");
118 void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C,
119 const char Msg[]) const {
122 new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
124 // Generate a report for this bug.
125 auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
126 C.emitReport(std::move(R));
129 bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
131 CheckerContext &C) const {
132 // Don't warn about explicit loss of precision.
133 if (Cast->isEvaluatable(C.getASTContext()))
136 QualType SubType = Cast->IgnoreParenImpCasts()->getType();
138 if (!DestType->isRealType() || !SubType->isIntegerType())
141 const bool isFloat = DestType->isFloatingType();
143 const auto &AC = C.getASTContext();
145 // We will find the largest RepresentsUntilExp value such that the DestType
146 // can exactly represent all nonnegative integers below 2^RepresentsUntilExp.
147 unsigned RepresentsUntilExp;
150 const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);
151 RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);
153 RepresentsUntilExp = AC.getIntWidth(DestType);
154 if (RepresentsUntilExp == 1) {
155 // This is just casting a number to bool, probably not a bug.
158 if (DestType->isSignedIntegerType())
159 RepresentsUntilExp--;
162 if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {
163 // Avoid overflow in our later calculations.
167 unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);
168 if (SubType->isSignedIntegerType())
171 if (RepresentsUntilExp >= CorrectedSrcWidth) {
172 // Simple case: the destination can store all values of the source type.
176 unsigned long long MaxVal = 1ULL << RepresentsUntilExp;
178 // If this is a floating point type, it can also represent MaxVal exactly.
181 return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
182 // TODO: maybe also check negative values with too large magnitude.
185 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
186 CheckerContext &C) const {
187 QualType CastType = Cast->getType();
188 QualType SubType = Cast->IgnoreParenImpCasts()->getType();
190 if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
193 return C.isNegative(Cast->getSubExpr());
196 void ento::registerConversionChecker(CheckerManager &mgr) {
197 mgr.registerChecker<ConversionChecker>();