clang-tools  8.0.0
NarrowingConversionsCheck.cpp
Go to the documentation of this file.
1 //===--- NarrowingConversionsCheck.cpp - clang-tidy------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/Type.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "llvm/ADT/APSInt.h"
15 #include "llvm/ADT/SmallString.h"
16 #include "llvm/ADT/SmallVector.h"
17 
18 #include <cstdint>
19 
20 using namespace clang::ast_matchers;
21 
22 namespace clang {
23 namespace tidy {
24 namespace cppcoreguidelines {
25 
26 NarrowingConversionsCheck::NarrowingConversionsCheck(StringRef Name,
27  ClangTidyContext *Context)
28  : ClangTidyCheck(Name, Context),
29  WarnOnFloatingPointNarrowingConversion(
30  Options.get("WarnOnFloatingPointNarrowingConversion", 1)),
31  PedanticMode(Options.get("PedanticMode", 0)) {}
32 
34  // ceil() and floor() are guaranteed to return integers, even though the type
35  // is not integral.
36  const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl(
37  hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor")))));
38 
39  // Casts:
40  // i = 0.5;
41  // void f(int); f(0.5);
42  Finder->addMatcher(
43  implicitCastExpr(hasImplicitDestinationType(builtinType()),
44  hasSourceExpression(hasType(builtinType())),
45  unless(hasSourceExpression(IsCeilFloorCallExpr)),
46  unless(hasParent(castExpr())),
47  unless(isInTemplateInstantiation()))
48  .bind("cast"),
49  this);
50 
51  // Binary operators:
52  // i += 0.5;
53  Finder->addMatcher(binaryOperator(isAssignmentOperator(),
54  hasLHS(expr(hasType(builtinType()))),
55  hasRHS(expr(hasType(builtinType()))),
56  unless(hasRHS(IsCeilFloorCallExpr)),
57  unless(isInTemplateInstantiation()),
58  // The `=` case generates an implicit cast
59  // which is covered by the previous matcher.
60  unless(hasOperatorName("=")))
61  .bind("binary_op"),
62  this);
63 }
64 
65 static const BuiltinType *getBuiltinType(const Expr &E) {
66  return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>();
67 }
68 
69 static QualType getUnqualifiedType(const Expr &E) {
70  return E.getType().getUnqualifiedType();
71 }
72 
73 static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E) {
74  llvm::APSInt IntegerConstant;
75  if (E.isIntegerConstantExpr(IntegerConstant, Ctx))
76  return APValue(IntegerConstant);
77  APValue Constant;
78  if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant))
79  return Constant;
80  return {};
81 }
82 
83 static bool getIntegerConstantExprValue(const ASTContext &Context,
84  const Expr &E, llvm::APSInt &Value) {
85  APValue Constant = getConstantExprValue(Context, E);
86  if (!Constant.isInt())
87  return false;
88  Value = Constant.getInt();
89  return true;
90 }
91 
92 static bool getFloatingConstantExprValue(const ASTContext &Context,
93  const Expr &E, llvm::APFloat &Value) {
94  APValue Constant = getConstantExprValue(Context, E);
95  if (!Constant.isFloat())
96  return false;
97  Value = Constant.getFloat();
98  return true;
99 }
100 
101 namespace {
102 
103 struct IntegerRange {
104  bool Contains(const IntegerRange &From) const {
105  return llvm::APSInt::compareValues(Lower, From.Lower) <= 0 &&
106  llvm::APSInt::compareValues(Upper, From.Upper) >= 0;
107  }
108 
109  bool Contains(const llvm::APSInt &Value) const {
110  return llvm::APSInt::compareValues(Lower, Value) <= 0 &&
111  llvm::APSInt::compareValues(Upper, Value) >= 0;
112  }
113 
114  llvm::APSInt Lower;
115  llvm::APSInt Upper;
116 };
117 
118 } // namespace
119 
120 static IntegerRange createFromType(const ASTContext &Context,
121  const BuiltinType &T) {
122  if (T.isFloatingPoint()) {
123  unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision(
124  Context.getFloatTypeSemantics(T.desugar()));
125  // Contrary to two's complement integer, floating point values are
126  // symmetric and have the same number of positive and negative values.
127  // The range of valid integers for a floating point value is:
128  // [-2^PrecisionBits, 2^PrecisionBits]
129 
130  // Values are created with PrecisionBits plus two bits:
131  // - One to express the missing negative value of 2's complement
132  // representation.
133  // - One for the sign.
134  llvm::APSInt UpperValue(PrecisionBits + 2, /*isUnsigned*/ false);
135  UpperValue.setBit(PrecisionBits);
136  llvm::APSInt LowerValue(PrecisionBits + 2, /*isUnsigned*/ false);
137  LowerValue.setBit(PrecisionBits);
138  LowerValue.setSignBit();
139  return {LowerValue, UpperValue};
140  }
141  assert(T.isInteger() && "Unexpected builtin type");
142  uint64_t TypeSize = Context.getTypeSize(&T);
143  bool IsUnsignedInteger = T.isUnsignedInteger();
144  return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger),
145  llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)};
146 }
147 
148 static bool isWideEnoughToHold(const ASTContext &Context,
149  const BuiltinType &FromType,
150  const BuiltinType &ToType) {
151  IntegerRange FromIntegerRange = createFromType(Context, FromType);
152  IntegerRange ToIntegerRange = createFromType(Context, ToType);
153  return ToIntegerRange.Contains(FromIntegerRange);
154 }
155 
156 static bool isWideEnoughToHold(const ASTContext &Context,
157  const llvm::APSInt &IntegerConstant,
158  const BuiltinType &ToType) {
159  IntegerRange ToIntegerRange = createFromType(Context, ToType);
160  return ToIntegerRange.Contains(IntegerConstant);
161 }
162 
163 static llvm::SmallString<64> getValueAsString(const llvm::APSInt &Value,
164  uint64_t HexBits) {
165  llvm::SmallString<64> Str;
166  Value.toString(Str, 10);
167  if (HexBits > 0) {
168  Str.append(" (0x");
169  llvm::SmallString<32> HexValue;
170  Value.toStringUnsigned(HexValue, 16);
171  for (size_t I = HexValue.size(); I < (HexBits / 4); ++I)
172  Str.append("0");
173  Str.append(HexValue);
174  Str.append(")");
175  }
176  return Str;
177 }
178 
179 void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,
180  const Expr &Lhs,
181  const Expr &Rhs) {
182  diag(SourceLoc, "narrowing conversion from %0 to %1")
183  << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
184 }
185 
186 void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(
187  SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs) {
188  diag(SourceLoc, "narrowing conversion from %0 to signed type %1 is "
189  "implementation-defined")
190  << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
191 }
192 
193 void NarrowingConversionsCheck::diagNarrowIntegerConstant(
194  SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
195  const llvm::APSInt &Value) {
196  diag(SourceLoc,
197  "narrowing conversion from constant value %0 of type %1 to %2")
198  << getValueAsString(Value, /*NoHex*/ 0) << getUnqualifiedType(Rhs)
199  << getUnqualifiedType(Lhs);
200 }
201 
202 void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt(
203  SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
204  const llvm::APSInt &Value, const uint64_t HexBits) {
205  diag(SourceLoc, "narrowing conversion from constant value %0 of type %1 "
206  "to signed type %2 is implementation-defined")
207  << getValueAsString(Value, HexBits) << getUnqualifiedType(Rhs)
208  << getUnqualifiedType(Lhs);
209 }
210 
211 void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
212  const Expr &Lhs,
213  const Expr &Rhs) {
214  diag(SourceLoc, "narrowing conversion from constant %0 to %1")
215  << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
216 }
217 
218 void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
219  const Expr &Lhs,
220  const Expr &Rhs) {
221  diag(SourceLoc, "constant value should be of type of type %0 instead of %1")
222  << getUnqualifiedType(Lhs) << getUnqualifiedType(Rhs);
223 }
224 
225 void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
226  const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
227  const Expr &Rhs) {
228  APValue Constant = getConstantExprValue(Context, Rhs);
229  if (Constant.isInt())
230  return diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Constant.getInt());
231  if (Constant.isFloat())
232  return diagNarrowConstant(SourceLoc, Lhs, Rhs);
233  return diagNarrowType(SourceLoc, Lhs, Rhs);
234 }
235 
236 void NarrowingConversionsCheck::handleIntegralCast(const ASTContext &Context,
237  SourceLocation SourceLoc,
238  const Expr &Lhs,
239  const Expr &Rhs) {
240  const BuiltinType *ToType = getBuiltinType(Lhs);
241  // From [conv.integral]p7.3.8:
242  // Conversions to unsigned integer is well defined so no warning is issued.
243  // "The resulting value is the smallest unsigned value equal to the source
244  // value modulo 2^n where n is the number of bits used to represent the
245  // destination type."
246  if (ToType->isUnsignedInteger())
247  return;
248  const BuiltinType *FromType = getBuiltinType(Rhs);
249  llvm::APSInt IntegerConstant;
250  if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
251  if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
252  diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs, IntegerConstant,
253  Context.getTypeSize(FromType));
254  return;
255  }
256  if (!isWideEnoughToHold(Context, *FromType, *ToType))
257  diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs);
258 }
259 
260 void NarrowingConversionsCheck::handleIntegralToBoolean(
261  const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
262  const Expr &Rhs) {
263  // Conversion from Integral to Bool value is well defined.
264 
265  // We keep this function (even if it is empty) to make sure that
266  // handleImplicitCast and handleBinaryOperator are symmetric in their behavior
267  // and handle the same cases.
268 }
269 
270 void NarrowingConversionsCheck::handleIntegralToFloating(
271  const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
272  const Expr &Rhs) {
273  const BuiltinType *ToType = getBuiltinType(Lhs);
274  llvm::APSInt IntegerConstant;
275  if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
276  if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
277  diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);
278  return;
279  }
280  const BuiltinType *FromType = getBuiltinType(Rhs);
281  if (!isWideEnoughToHold(Context, *FromType, *ToType))
282  diagNarrowType(SourceLoc, Lhs, Rhs);
283 }
284 
285 void NarrowingConversionsCheck::handleFloatingToIntegral(
286  const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
287  const Expr &Rhs) {
288  llvm::APFloat FloatConstant(0.0);
289 
290  // We always warn when Rhs is non-constexpr.
291  if (!getFloatingConstantExprValue(Context, Rhs, FloatConstant))
292  return diagNarrowType(SourceLoc, Lhs, Rhs);
293 
294  QualType DestType = Lhs.getType();
295  unsigned DestWidth = Context.getIntWidth(DestType);
296  bool DestSigned = DestType->isSignedIntegerOrEnumerationType();
297  llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned);
298  bool IsExact = false;
299  bool Overflows = FloatConstant.convertToInteger(
300  Result, llvm::APFloat::rmTowardZero, &IsExact) &
301  llvm::APFloat::opInvalidOp;
302  // We warn iff the constant floating point value is not exactly representable.
303  if (Overflows || !IsExact)
304  return diagNarrowConstant(SourceLoc, Lhs, Rhs);
305 
306  if (PedanticMode)
307  return diagConstantCast(SourceLoc, Lhs, Rhs);
308 }
309 
310 void NarrowingConversionsCheck::handleFloatingToBoolean(
311  const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
312  const Expr &Rhs) {
313  return diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs);
314 }
315 
316 void NarrowingConversionsCheck::handleBooleanToSignedIntegral(
317  const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
318  const Expr &Rhs) {
319  // Conversion from Bool to SignedIntegral value is well defined.
320 
321  // We keep this function (even if it is empty) to make sure that
322  // handleImplicitCast and handleBinaryOperator are symmetric in their behavior
323  // and handle the same cases.
324 }
325 
326 void NarrowingConversionsCheck::handleFloatingCast(const ASTContext &Context,
327  SourceLocation SourceLoc,
328  const Expr &Lhs,
329  const Expr &Rhs) {
330  if (WarnOnFloatingPointNarrowingConversion) {
331  const BuiltinType *ToType = getBuiltinType(Lhs);
332  APValue Constant = getConstantExprValue(Context, Rhs);
333  if (Constant.isFloat()) {
334  // From [dcl.init.list]p7.2:
335  // Floating point constant narrowing only takes place when the value is
336  // not within destination range. We convert the value to the destination
337  // type and check if the resulting value is infinity.
338  llvm::APFloat Tmp = Constant.getFloat();
339  bool UnusedLosesInfo;
340  Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()),
341  llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo);
342  if (Tmp.isInfinity())
343  diagNarrowConstant(SourceLoc, Lhs, Rhs);
344  return;
345  }
346  const BuiltinType *FromType = getBuiltinType(Rhs);
347  if (ToType->getKind() < FromType->getKind())
348  diagNarrowType(SourceLoc, Lhs, Rhs);
349  }
350 }
351 
352 void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
353  SourceLocation SourceLoc,
354  const Expr &Lhs,
355  const Expr &Rhs) {
356  assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&
357  "Dependent types must be check before calling this function");
358  const BuiltinType *LhsType = getBuiltinType(Lhs);
359  const BuiltinType *RhsType = getBuiltinType(Rhs);
360  if (RhsType == nullptr || LhsType == nullptr)
361  return;
362  if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())
363  return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
364  if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)
365  return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
366  if (RhsType->isInteger() && LhsType->isFloatingPoint())
367  return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
368  if (RhsType->isInteger() && LhsType->isInteger())
369  return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
370  if (RhsType->isFloatingPoint() && LhsType->getKind() == BuiltinType::Bool)
371  return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
372  if (RhsType->isFloatingPoint() && LhsType->isInteger())
373  return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
374  if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())
375  return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
376 }
377 
378 bool NarrowingConversionsCheck::handleConditionalOperator(
379  const ASTContext &Context, const Expr &Lhs, const Expr &Rhs) {
380  if (const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
381  // We have an expression like so: `output = cond ? lhs : rhs`
382  // From the point of view of narrowing conversion we treat it as two
383  // expressions `output = lhs` and `output = rhs`.
384  handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs,
385  *CO->getLHS());
386  handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs,
387  *CO->getRHS());
388  return true;
389  }
390  return false;
391 }
392 
393 void NarrowingConversionsCheck::handleImplicitCast(
394  const ASTContext &Context, const ImplicitCastExpr &Cast) {
395  if (Cast.getExprLoc().isMacroID())
396  return;
397  const Expr &Lhs = Cast;
398  const Expr &Rhs = *Cast.getSubExpr();
399  if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
400  return;
401  if (handleConditionalOperator(Context, Lhs, Rhs))
402  return;
403  SourceLocation SourceLoc = Lhs.getExprLoc();
404  switch (Cast.getCastKind()) {
405  case CK_BooleanToSignedIntegral:
406  return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
407  case CK_IntegralToBoolean:
408  return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
409  case CK_IntegralToFloating:
410  return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
411  case CK_IntegralCast:
412  return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
413  case CK_FloatingToBoolean:
414  return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
415  case CK_FloatingToIntegral:
416  return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
417  case CK_FloatingCast:
418  return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
419  default:
420  break;
421  }
422 }
423 
424 void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
425  const BinaryOperator &Op) {
426  if (Op.getBeginLoc().isMacroID())
427  return;
428  const Expr &Lhs = *Op.getLHS();
429  const Expr &Rhs = *Op.getRHS();
430  if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
431  return;
432  if (handleConditionalOperator(Context, Lhs, Rhs))
433  return;
434  handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
435 }
436 
437 void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) {
438  if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("binary_op"))
439  return handleBinaryOperator(*Result.Context, *Op);
440  if (const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast"))
441  return handleImplicitCast(*Result.Context, *Cast);
442  llvm_unreachable("must be binary operator or cast expression");
443 }
444 
445 } // namespace cppcoreguidelines
446 } // namespace tidy
447 } // namespace clang
llvm::APSInt Upper
llvm::APSInt Lower
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
static QualType getUnqualifiedType(const Expr &E)
Context Ctx
static llvm::SmallString< 64 > getValueAsString(const llvm::APSInt &Value, uint64_t HexBits)
static IntegerRange createFromType(const ASTContext &Context, const BuiltinType &T)
static constexpr llvm::StringLiteral Name
static bool getFloatingConstantExprValue(const ASTContext &Context, const Expr &E, llvm::APFloat &Value)
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
static const BuiltinType * getBuiltinType(const Expr &E)
static bool isWideEnoughToHold(const ASTContext &Context, const BuiltinType &FromType, const BuiltinType &ToType)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static bool getIntegerConstantExprValue(const ASTContext &Context, const Expr &E, llvm::APSInt &Value)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E)
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:438