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