]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / ConversionChecker.cpp
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 //===----------------------------------------------------------------------===//
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"
33
34 #include <climits>
35
36 using namespace clang;
37 using namespace ento;
38
39 namespace {
40 class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {
41 public:
42   void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
43
44 private:
45   mutable std::unique_ptr<BuiltinBug> 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, CheckerContext &C, const char Msg[]) const;
53 };
54 }
55
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
59   // calculations also.
60   if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts()))
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
73   bool LossOfSign = false;
74   bool LossOfPrecision = false;
75
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) {
83       // No loss of sign.
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);
99     }
100   } else if (isa<DeclStmt>(Parent)) {
101     LossOfSign = isLossOfSign(Cast, C);
102     LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
103   }
104
105   if (LossOfSign || LossOfPrecision) {
106     // Generate an error node.
107     ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
108     if (!N)
109       return;
110     if (LossOfSign)
111       reportBug(N, C, "Loss of sign in implicit conversion");
112     if (LossOfPrecision)
113       reportBug(N, C, "Loss of precision in implicit conversion");
114   }
115 }
116
117 void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C,
118                                   const char Msg[]) const {
119   if (!BT)
120     BT.reset(
121         new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
122
123   // Generate a report for this bug.
124   auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
125   C.emitReport(std::move(R));
126 }
127
128 bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
129                                           QualType DestType,
130                                           CheckerContext &C) const {
131   // Don't warn about explicit loss of precision.
132   if (Cast->isEvaluatable(C.getASTContext()))
133     return false;
134
135   QualType SubType = Cast->IgnoreParenImpCasts()->getType();
136
137   if (!DestType->isRealType() || !SubType->isIntegerType())
138     return false;
139
140   const bool isFloat = DestType->isFloatingType();
141
142   const auto &AC = C.getASTContext();
143
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;
147
148   if (isFloat) {
149     const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);
150     RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);
151   } else {
152     RepresentsUntilExp = AC.getIntWidth(DestType);
153     if (RepresentsUntilExp == 1) {
154       // This is just casting a number to bool, probably not a bug.
155       return false;
156     }
157     if (DestType->isSignedIntegerType())
158       RepresentsUntilExp--;
159   }
160
161   if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {
162     // Avoid overflow in our later calculations.
163     return false;
164   }
165
166   unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);
167   if (SubType->isSignedIntegerType())
168     CorrectedSrcWidth--;
169
170   if (RepresentsUntilExp >= CorrectedSrcWidth) {
171     // Simple case: the destination can store all values of the source type.
172     return false;
173   }
174
175   unsigned long long MaxVal = 1ULL << RepresentsUntilExp;
176   if (isFloat) {
177     // If this is a floating point type, it can also represent MaxVal exactly.
178     MaxVal++;
179   }
180   return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
181   // TODO: maybe also check negative values with too large magnitude.
182 }
183
184 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
185                                      CheckerContext &C) const {
186   QualType CastType = Cast->getType();
187   QualType SubType = Cast->IgnoreParenImpCasts()->getType();
188
189   if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
190     return false;
191
192   return C.isNegative(Cast->getSubExpr());
193 }
194
195 void ento::registerConversionChecker(CheckerManager &mgr) {
196   mgr.registerChecker<ConversionChecker>();
197 }
198
199 bool ento::shouldRegisterConversionChecker(const LangOptions &LO) {
200   return true;
201 }