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<BuiltinBug> 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, CheckerContext &C, const char Msg[]) const;
56 void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
57 CheckerContext &C) const {
58 // TODO: For now we only warn about DeclRefExpr, to avoid noise. Warn for
60 if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts()))
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);
73 bool LossOfSign = false;
74 bool LossOfPrecision = false;
76 // Loss of sign/precision in binary operation.
77 if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {
78 BinaryOperator::Opcode Opc = B->getOpcode();
79 if (Opc == BO_Assign) {
80 LossOfSign = isLossOfSign(Cast, C);
81 LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
82 } else if (Opc == BO_AddAssign || Opc == BO_SubAssign) {
84 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
85 } else if (Opc == BO_MulAssign) {
86 LossOfSign = isLossOfSign(Cast, C);
87 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
88 } else if (Opc == BO_DivAssign || Opc == BO_RemAssign) {
89 LossOfSign = isLossOfSign(Cast, C);
90 // No loss of precision.
91 } else if (Opc == BO_AndAssign) {
92 LossOfSign = isLossOfSign(Cast, C);
93 // No loss of precision.
94 } else if (Opc == BO_OrAssign || Opc == BO_XorAssign) {
95 LossOfSign = isLossOfSign(Cast, C);
96 LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
97 } else if (B->isRelationalOp() || B->isMultiplicativeOp()) {
98 LossOfSign = isLossOfSign(Cast, C);
100 } else if (isa<DeclStmt>(Parent)) {
101 LossOfSign = isLossOfSign(Cast, C);
102 LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
105 if (LossOfSign || LossOfPrecision) {
106 // Generate an error node.
107 ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
111 reportBug(N, C, "Loss of sign in implicit conversion");
113 reportBug(N, C, "Loss of precision in implicit conversion");
117 void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C,
118 const char Msg[]) const {
121 new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
123 // Generate a report for this bug.
124 auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
125 C.emitReport(std::move(R));
128 bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
130 CheckerContext &C) const {
131 // Don't warn about explicit loss of precision.
132 if (Cast->isEvaluatable(C.getASTContext()))
135 QualType SubType = Cast->IgnoreParenImpCasts()->getType();
137 if (!DestType->isRealType() || !SubType->isIntegerType())
140 const bool isFloat = DestType->isFloatingType();
142 const auto &AC = C.getASTContext();
144 // We will find the largest RepresentsUntilExp value such that the DestType
145 // can exactly represent all nonnegative integers below 2^RepresentsUntilExp.
146 unsigned RepresentsUntilExp;
149 const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);
150 RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);
152 RepresentsUntilExp = AC.getIntWidth(DestType);
153 if (RepresentsUntilExp == 1) {
154 // This is just casting a number to bool, probably not a bug.
157 if (DestType->isSignedIntegerType())
158 RepresentsUntilExp--;
161 if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {
162 // Avoid overflow in our later calculations.
166 unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);
167 if (SubType->isSignedIntegerType())
170 if (RepresentsUntilExp >= CorrectedSrcWidth) {
171 // Simple case: the destination can store all values of the source type.
175 unsigned long long MaxVal = 1ULL << RepresentsUntilExp;
177 // If this is a floating point type, it can also represent MaxVal exactly.
180 return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
181 // TODO: maybe also check negative values with too large magnitude.
184 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
185 CheckerContext &C) const {
186 QualType CastType = Cast->getType();
187 QualType SubType = Cast->IgnoreParenImpCasts()->getType();
189 if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
192 return C.isNegative(Cast->getSubExpr());
195 void ento::registerConversionChecker(CheckerManager &mgr) {
196 mgr.registerChecker<ConversionChecker>();
199 bool ento::shouldRegisterConversionChecker(const LangOptions &LO) {