clang  15.0.0git
ConversionChecker.cpp
Go to the documentation of this file.
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 //===----------------------------------------------------------------------===//
27 #include "clang/AST/ParentMap.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, const Expr *E, CheckerContext &C,
53  const char Msg[]) const;
54 };
55 }
56 
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())
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  // Dont warn if this is part of an explicit cast
73  if (isa<ExplicitCastExpr>(Parent))
74  return;
75 
76  bool LossOfSign = false;
77  bool LossOfPrecision = false;
78 
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);
86  }
87  } else if (Opc == BO_AddAssign || Opc == BO_SubAssign) {
88  // No loss of sign.
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);
104  }
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);
109  }
110  } else {
111  LossOfSign = isLossOfSign(Cast, C);
112  LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
113  }
114 
115  if (LossOfSign || LossOfPrecision) {
116  // Generate an error node.
117  ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
118  if (!N)
119  return;
120  if (LossOfSign)
121  reportBug(N, Cast, C, "Loss of sign in implicit conversion");
122  if (LossOfPrecision)
123  reportBug(N, Cast, C, "Loss of precision in implicit conversion");
124  }
125 }
126 
127 void ConversionChecker::reportBug(ExplodedNode *N, const Expr *E,
128  CheckerContext &C, const char Msg[]) const {
129  if (!BT)
130  BT.reset(
131  new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
132 
133  // Generate a report for this bug.
134  auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
136  C.emitReport(std::move(R));
137 }
138 
139 bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
140  QualType DestType,
141  CheckerContext &C) const {
142  // Don't warn about explicit loss of precision.
143  if (Cast->isEvaluatable(C.getASTContext()))
144  return false;
145 
146  QualType SubType = Cast->IgnoreParenImpCasts()->getType();
147 
148  if (!DestType->isRealType() || !SubType->isIntegerType())
149  return false;
150 
151  const bool isFloat = DestType->isFloatingType();
152 
153  const auto &AC = C.getASTContext();
154 
155  // We will find the largest RepresentsUntilExp value such that the DestType
156  // can exactly represent all nonnegative integers below 2^RepresentsUntilExp.
157  unsigned RepresentsUntilExp;
158 
159  if (isFloat) {
160  const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);
161  RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);
162  } else {
163  RepresentsUntilExp = AC.getIntWidth(DestType);
164  if (RepresentsUntilExp == 1) {
165  // This is just casting a number to bool, probably not a bug.
166  return false;
167  }
168  if (DestType->isSignedIntegerType())
169  RepresentsUntilExp--;
170  }
171 
172  if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {
173  // Avoid overflow in our later calculations.
174  return false;
175  }
176 
177  unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);
178  if (SubType->isSignedIntegerType())
179  CorrectedSrcWidth--;
180 
181  if (RepresentsUntilExp >= CorrectedSrcWidth) {
182  // Simple case: the destination can store all values of the source type.
183  return false;
184  }
185 
186  unsigned long long MaxVal = 1ULL << RepresentsUntilExp;
187  if (isFloat) {
188  // If this is a floating point type, it can also represent MaxVal exactly.
189  MaxVal++;
190  }
191  return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
192  // TODO: maybe also check negative values with too large magnitude.
193 }
194 
195 bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
196  CheckerContext &C) const {
197  QualType CastType = Cast->getType();
198  QualType SubType = Cast->IgnoreParenImpCasts()->getType();
199 
200  if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
201  return false;
202 
203  return C.isNegative(Cast->getSubExpr());
204 }
205 
206 void ento::registerConversionChecker(CheckerManager &mgr) {
207  mgr.registerChecker<ConversionChecker>();
208 }
209 
210 bool ento::shouldRegisterConversionChecker(const CheckerManager &mgr) {
211  return true;
212 }
clang::Type::isRealType
bool isRealType() const
Definition: Type.cpp:2143
clang::QualType
A (possibly-)qualified type.
Definition: Type.h:731
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:55
CHAR_BIT
#define CHAR_BIT
Definition: limits.h:63
clang::Type::isFloatingType
bool isFloatingType() const
Definition: Type.cpp:2121
BuiltinCheckerRegistration.h
CheckerManager.h
CastType
CastType
Definition: SemaCast.cpp:46
clang::interp::Cast
bool Cast(InterpState &S, CodePtr OpPC)
Definition: Interp.h:802
clang::ento::bugreporter::trackExpressionValue
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
clang::ParentMap
Definition: ParentMap.h:20
BugType.h
clang::Sema
Sema - This implements semantic analysis and AST building for C.
Definition: Sema.h:354
clang::Type::isSignedIntegerType
bool isSignedIntegerType() const
Return true if this is an integer type that is signed, according to C99 6.2.5p4 [char,...
Definition: Type.cpp:2018
clang::ParentMap::getParent
Stmt * getParent(Stmt *) const
Definition: ParentMap.cpp:134
clang::BinaryOperatorKind
BinaryOperatorKind
Definition: OperationKinds.h:25
CheckerContext.h
Checker.h
ParentMap.h
clang
Definition: CalledOnceCheck.h:17
clang::Stmt
Stmt - This represents one statement.
Definition: Stmt.h:69
clang::ImplicitCastExpr
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition: Expr.h:3619
Parent
NodeId Parent
Definition: ASTDiff.cpp:192
clang::Type::isIntegerType
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: Type.h:7128
clang::Expr
This represents one expression.
Definition: Expr.h:109