1002981baSArtem Dergachev //=== ConversionChecker.cpp -------------------------------------*- C++ -*-===//
2002981baSArtem Dergachev //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6002981baSArtem Dergachev //
7002981baSArtem Dergachev //===----------------------------------------------------------------------===//
8002981baSArtem Dergachev //
9002981baSArtem Dergachev // Check that there is no loss of sign/precision in assignments, comparisons
10002981baSArtem Dergachev // and multiplications.
11002981baSArtem Dergachev //
12002981baSArtem Dergachev // ConversionChecker uses path sensitive analysis to determine possible values
13002981baSArtem Dergachev // of expressions. A warning is reported when:
14002981baSArtem Dergachev // * a negative value is implicitly converted to an unsigned value in an
15002981baSArtem Dergachev // assignment, comparison or multiplication.
169d6c4402SKristof Umann // * assignment / initialization when the source value is greater than the max
179d6c4402SKristof Umann // value of the target integer type
189d6c4402SKristof Umann // * assignment / initialization when the source integer is above the range
199d6c4402SKristof Umann // where the target floating point type can represent all integers
20002981baSArtem Dergachev //
21002981baSArtem Dergachev // Many compilers and tools have similar checks that are based on semantic
22002981baSArtem Dergachev // analysis. Those checks are sound but have poor precision. ConversionChecker
23002981baSArtem Dergachev // is an alternative to those checks.
24002981baSArtem Dergachev //
25002981baSArtem Dergachev //===----------------------------------------------------------------------===//
2676a21502SKristof Umann #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
27002981baSArtem Dergachev #include "clang/AST/ParentMap.h"
28002981baSArtem Dergachev #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
29002981baSArtem Dergachev #include "clang/StaticAnalyzer/Core/Checker.h"
30002981baSArtem Dergachev #include "clang/StaticAnalyzer/Core/CheckerManager.h"
31002981baSArtem Dergachev #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
329d6c4402SKristof Umann #include "llvm/ADT/APFloat.h"
339d6c4402SKristof Umann
349d6c4402SKristof Umann #include <climits>
35002981baSArtem Dergachev
36002981baSArtem Dergachev using namespace clang;
37002981baSArtem Dergachev using namespace ento;
38002981baSArtem Dergachev
39002981baSArtem Dergachev namespace {
40002981baSArtem Dergachev class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {
41002981baSArtem Dergachev public:
42002981baSArtem Dergachev void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
43002981baSArtem Dergachev
44002981baSArtem Dergachev private:
45002981baSArtem Dergachev mutable std::unique_ptr<BuiltinBug> BT;
46002981baSArtem Dergachev
472641a52eSDaniel Marjamaki bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType,
482641a52eSDaniel Marjamaki CheckerContext &C) const;
49002981baSArtem Dergachev
50002981baSArtem Dergachev bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;
51002981baSArtem Dergachev
5296ec9b6fSGabor Marton void reportBug(ExplodedNode *N, const Expr *E, CheckerContext &C,
5396ec9b6fSGabor Marton const char Msg[]) const;
54002981baSArtem Dergachev };
55002981baSArtem Dergachev }
56002981baSArtem Dergachev
checkPreStmt(const ImplicitCastExpr * Cast,CheckerContext & C) const57002981baSArtem Dergachev void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
58002981baSArtem Dergachev CheckerContext &C) const {
59*bd9e2394SGabor Marton // Don't warn for implicit conversions to bool
60*bd9e2394SGabor Marton if (Cast->getType()->isBooleanType())
61002981baSArtem Dergachev return;
62002981baSArtem Dergachev
63002981baSArtem Dergachev // Don't warn for loss of sign/precision in macros.
64002981baSArtem Dergachev if (Cast->getExprLoc().isMacroID())
65002981baSArtem Dergachev return;
66002981baSArtem Dergachev
67002981baSArtem Dergachev // Get Parent.
68002981baSArtem Dergachev const ParentMap &PM = C.getLocationContext()->getParentMap();
69002981baSArtem Dergachev const Stmt *Parent = PM.getParent(Cast);
70002981baSArtem Dergachev if (!Parent)
71002981baSArtem Dergachev return;
72*bd9e2394SGabor Marton // Dont warn if this is part of an explicit cast
73*bd9e2394SGabor Marton if (isa<ExplicitCastExpr>(Parent))
74*bd9e2394SGabor Marton return;
75002981baSArtem Dergachev
76002981baSArtem Dergachev bool LossOfSign = false;
77002981baSArtem Dergachev bool LossOfPrecision = false;
78002981baSArtem Dergachev
79002981baSArtem Dergachev // Loss of sign/precision in binary operation.
80002981baSArtem Dergachev if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {
81002981baSArtem Dergachev BinaryOperator::Opcode Opc = B->getOpcode();
822641a52eSDaniel Marjamaki if (Opc == BO_Assign) {
83*bd9e2394SGabor Marton if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) {
84002981baSArtem Dergachev LossOfSign = isLossOfSign(Cast, C);
852641a52eSDaniel Marjamaki LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
86*bd9e2394SGabor Marton }
872641a52eSDaniel Marjamaki } else if (Opc == BO_AddAssign || Opc == BO_SubAssign) {
882641a52eSDaniel Marjamaki // No loss of sign.
892641a52eSDaniel Marjamaki LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
902641a52eSDaniel Marjamaki } else if (Opc == BO_MulAssign) {
912641a52eSDaniel Marjamaki LossOfSign = isLossOfSign(Cast, C);
922641a52eSDaniel Marjamaki LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
932641a52eSDaniel Marjamaki } else if (Opc == BO_DivAssign || Opc == BO_RemAssign) {
942641a52eSDaniel Marjamaki LossOfSign = isLossOfSign(Cast, C);
952641a52eSDaniel Marjamaki // No loss of precision.
962641a52eSDaniel Marjamaki } else if (Opc == BO_AndAssign) {
972641a52eSDaniel Marjamaki LossOfSign = isLossOfSign(Cast, C);
982641a52eSDaniel Marjamaki // No loss of precision.
992641a52eSDaniel Marjamaki } else if (Opc == BO_OrAssign || Opc == BO_XorAssign) {
1002641a52eSDaniel Marjamaki LossOfSign = isLossOfSign(Cast, C);
1012641a52eSDaniel Marjamaki LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
102002981baSArtem Dergachev } else if (B->isRelationalOp() || B->isMultiplicativeOp()) {
103002981baSArtem Dergachev LossOfSign = isLossOfSign(Cast, C);
104002981baSArtem Dergachev }
105*bd9e2394SGabor Marton } else if (isa<DeclStmt, ReturnStmt>(Parent)) {
106*bd9e2394SGabor Marton if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) {
107*bd9e2394SGabor Marton LossOfSign = isLossOfSign(Cast, C);
108*bd9e2394SGabor Marton LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
109*bd9e2394SGabor Marton }
110*bd9e2394SGabor Marton } else {
111002981baSArtem Dergachev LossOfSign = isLossOfSign(Cast, C);
1122641a52eSDaniel Marjamaki LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
113002981baSArtem Dergachev }
114002981baSArtem Dergachev
115002981baSArtem Dergachev if (LossOfSign || LossOfPrecision) {
116002981baSArtem Dergachev // Generate an error node.
117002981baSArtem Dergachev ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
118002981baSArtem Dergachev if (!N)
119002981baSArtem Dergachev return;
120002981baSArtem Dergachev if (LossOfSign)
12196ec9b6fSGabor Marton reportBug(N, Cast, C, "Loss of sign in implicit conversion");
122002981baSArtem Dergachev if (LossOfPrecision)
12396ec9b6fSGabor Marton reportBug(N, Cast, C, "Loss of precision in implicit conversion");
124002981baSArtem Dergachev }
125002981baSArtem Dergachev }
126002981baSArtem Dergachev
reportBug(ExplodedNode * N,const Expr * E,CheckerContext & C,const char Msg[]) const12796ec9b6fSGabor Marton void ConversionChecker::reportBug(ExplodedNode *N, const Expr *E,
12896ec9b6fSGabor Marton CheckerContext &C, const char Msg[]) const {
129002981baSArtem Dergachev if (!BT)
130002981baSArtem Dergachev BT.reset(
131002981baSArtem Dergachev new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
132002981baSArtem Dergachev
133002981baSArtem Dergachev // Generate a report for this bug.
1342f169e7cSArtem Dergachev auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
13596ec9b6fSGabor Marton bugreporter::trackExpressionValue(N, E, *R);
136002981baSArtem Dergachev C.emitReport(std::move(R));
137002981baSArtem Dergachev }
138002981baSArtem Dergachev
isLossOfPrecision(const ImplicitCastExpr * Cast,QualType DestType,CheckerContext & C) const139002981baSArtem Dergachev bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
1402641a52eSDaniel Marjamaki QualType DestType,
141002981baSArtem Dergachev CheckerContext &C) const {
142002981baSArtem Dergachev // Don't warn about explicit loss of precision.
143002981baSArtem Dergachev if (Cast->isEvaluatable(C.getASTContext()))
144002981baSArtem Dergachev return false;
145002981baSArtem Dergachev
146002981baSArtem Dergachev QualType SubType = Cast->IgnoreParenImpCasts()->getType();
147002981baSArtem Dergachev
1489d6c4402SKristof Umann if (!DestType->isRealType() || !SubType->isIntegerType())
149002981baSArtem Dergachev return false;
150002981baSArtem Dergachev
1519d6c4402SKristof Umann const bool isFloat = DestType->isFloatingType();
152002981baSArtem Dergachev
1539d6c4402SKristof Umann const auto &AC = C.getASTContext();
154002981baSArtem Dergachev
1559d6c4402SKristof Umann // We will find the largest RepresentsUntilExp value such that the DestType
1569d6c4402SKristof Umann // can exactly represent all nonnegative integers below 2^RepresentsUntilExp.
1579d6c4402SKristof Umann unsigned RepresentsUntilExp;
1589d6c4402SKristof Umann
1599d6c4402SKristof Umann if (isFloat) {
1609d6c4402SKristof Umann const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);
1619d6c4402SKristof Umann RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);
1629d6c4402SKristof Umann } else {
1639d6c4402SKristof Umann RepresentsUntilExp = AC.getIntWidth(DestType);
1649d6c4402SKristof Umann if (RepresentsUntilExp == 1) {
1659d6c4402SKristof Umann // This is just casting a number to bool, probably not a bug.
1669d6c4402SKristof Umann return false;
1679d6c4402SKristof Umann }
1689d6c4402SKristof Umann if (DestType->isSignedIntegerType())
1699d6c4402SKristof Umann RepresentsUntilExp--;
1709d6c4402SKristof Umann }
1719d6c4402SKristof Umann
1729d6c4402SKristof Umann if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {
1739d6c4402SKristof Umann // Avoid overflow in our later calculations.
1749d6c4402SKristof Umann return false;
1759d6c4402SKristof Umann }
1769d6c4402SKristof Umann
1779d6c4402SKristof Umann unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);
1789d6c4402SKristof Umann if (SubType->isSignedIntegerType())
1799d6c4402SKristof Umann CorrectedSrcWidth--;
1809d6c4402SKristof Umann
1819d6c4402SKristof Umann if (RepresentsUntilExp >= CorrectedSrcWidth) {
1829d6c4402SKristof Umann // Simple case: the destination can store all values of the source type.
1839d6c4402SKristof Umann return false;
1849d6c4402SKristof Umann }
1859d6c4402SKristof Umann
1869d6c4402SKristof Umann unsigned long long MaxVal = 1ULL << RepresentsUntilExp;
1879d6c4402SKristof Umann if (isFloat) {
1889d6c4402SKristof Umann // If this is a floating point type, it can also represent MaxVal exactly.
1899d6c4402SKristof Umann MaxVal++;
1909d6c4402SKristof Umann }
191d3d83681SDaniel Marjamaki return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
1929d6c4402SKristof Umann // TODO: maybe also check negative values with too large magnitude.
193002981baSArtem Dergachev }
194002981baSArtem Dergachev
isLossOfSign(const ImplicitCastExpr * Cast,CheckerContext & C) const195002981baSArtem Dergachev bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
196002981baSArtem Dergachev CheckerContext &C) const {
197002981baSArtem Dergachev QualType CastType = Cast->getType();
198002981baSArtem Dergachev QualType SubType = Cast->IgnoreParenImpCasts()->getType();
199002981baSArtem Dergachev
200002981baSArtem Dergachev if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
201002981baSArtem Dergachev return false;
202002981baSArtem Dergachev
203d3d83681SDaniel Marjamaki return C.isNegative(Cast->getSubExpr());
204002981baSArtem Dergachev }
205002981baSArtem Dergachev
registerConversionChecker(CheckerManager & mgr)206002981baSArtem Dergachev void ento::registerConversionChecker(CheckerManager &mgr) {
207002981baSArtem Dergachev mgr.registerChecker<ConversionChecker>();
208002981baSArtem Dergachev }
209058a7a45SKristof Umann
shouldRegisterConversionChecker(const CheckerManager & mgr)210bda3dd0dSKirstóf Umann bool ento::shouldRegisterConversionChecker(const CheckerManager &mgr) {
211058a7a45SKristof Umann return true;
212058a7a45SKristof Umann }
213