clang-tools  16.0.0git
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 "../utils/OptionsUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/Expr.h"
13 #include "clang/AST/Type.h"
14 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 #include "clang/ASTMatchers/ASTMatchers.h"
16 #include "llvm/ADT/APSInt.h"
17 #include "llvm/ADT/SmallString.h"
18 #include "llvm/ADT/SmallVector.h"
19 
20 #include <cstdint>
21 
22 using namespace clang::ast_matchers;
23 
24 namespace clang {
25 namespace tidy {
26 namespace cppcoreguidelines {
27 
28 NarrowingConversionsCheck::NarrowingConversionsCheck(StringRef Name,
29  ClangTidyContext *Context)
30  : ClangTidyCheck(Name, Context),
31  WarnOnIntegerNarrowingConversion(
32  Options.get("WarnOnIntegerNarrowingConversion", true)),
33  WarnOnIntegerToFloatingPointNarrowingConversion(
34  Options.get("WarnOnIntegerToFloatingPointNarrowingConversion", true)),
35  WarnOnFloatingPointNarrowingConversion(
36  Options.get("WarnOnFloatingPointNarrowingConversion", true)),
37  WarnWithinTemplateInstantiation(
38  Options.get("WarnWithinTemplateInstantiation", false)),
39  WarnOnEquivalentBitWidth(Options.get("WarnOnEquivalentBitWidth", true)),
40  IgnoreConversionFromTypes(Options.get("IgnoreConversionFromTypes", "")),
41  PedanticMode(Options.get("PedanticMode", false)) {}
42 
45  Options.store(Opts, "WarnOnIntegerNarrowingConversion",
46  WarnOnIntegerNarrowingConversion);
47  Options.store(Opts, "WarnOnIntegerToFloatingPointNarrowingConversion",
48  WarnOnIntegerToFloatingPointNarrowingConversion);
49  Options.store(Opts, "WarnOnFloatingPointNarrowingConversion",
50  WarnOnFloatingPointNarrowingConversion);
51  Options.store(Opts, "WarnWithinTemplateInstantiation",
52  WarnWithinTemplateInstantiation);
53  Options.store(Opts, "WarnOnEquivalentBitWidth", WarnOnEquivalentBitWidth);
54  Options.store(Opts, "IgnoreConversionFromTypes", IgnoreConversionFromTypes);
55  Options.store(Opts, "PedanticMode", PedanticMode);
56 }
57 
58 AST_MATCHER(FieldDecl, hasIntBitwidth) {
59  assert(Node.isBitField());
60  const ASTContext &Ctx = Node.getASTContext();
61  unsigned IntBitWidth = Ctx.getIntWidth(Ctx.IntTy);
62  unsigned CurrentBitWidth = Node.getBitWidthValue(Ctx);
63  return IntBitWidth == CurrentBitWidth;
64 }
65 
67  // ceil() and floor() are guaranteed to return integers, even though the type
68  // is not integral.
69  const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl(
70  hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor")))));
71 
72  // We may want to exclude other types from the checks, such as `size_type`
73  // and `difference_type`. These are often used to count elements, represented
74  // in 64 bits and assigned to `int`. Rarely are people counting >2B elements.
75  const auto IsConversionFromIgnoredType = hasType(namedDecl(
76  hasAnyName(utils::options::parseStringList(IgnoreConversionFromTypes))));
77 
78  // `IsConversionFromIgnoredType` will ignore narrowing calls from those types,
79  // but not expressions that are promoted to an ignored type as a result of a
80  // binary expression with one of those types.
81  // For example, it will continue to reject:
82  // `int narrowed = int_value + container.size()`.
83  // We attempt to address common incidents of compound expressions with
84  // `IsIgnoredTypeTwoLevelsDeep`, allowing binary expressions that have one
85  // operand of the ignored types and the other operand of another integer type.
86  const auto IsIgnoredTypeTwoLevelsDeep =
87  anyOf(IsConversionFromIgnoredType,
88  binaryOperator(hasOperands(IsConversionFromIgnoredType,
89  hasType(isInteger()))));
90 
91  // Bitfields are special. Due to integral promotion [conv.prom/5] bitfield
92  // member access expressions are frequently wrapped by an implicit cast to
93  // `int` if that type can represent all the values of the bitfield.
94  //
95  // Consider these examples:
96  // struct SmallBitfield { unsigned int id : 4; };
97  // x.id & 1; (case-1)
98  // x.id & 1u; (case-2)
99  // x.id << 1u; (case-3)
100  // (unsigned)x.id << 1; (case-4)
101  //
102  // Due to the promotion rules, we would get a warning for case-1. It's
103  // debatable how useful this is, but the user at least has a convenient way of
104  // //fixing// it by adding the `u` unsigned-suffix to the literal as
105  // demonstrated by case-2. However, this won't work for shift operators like
106  // the one in case-3. In case of a normal binary operator, both operands
107  // contribute to the result type. However, the type of the shift expression is
108  // the promoted type of the left operand. One could still suppress this
109  // superfluous warning by explicitly casting the bitfield member access as
110  // case-4 demonstrates, but why? The compiler already knew that the value from
111  // the member access should safely fit into an `int`, why do we have this
112  // warning in the first place? So, hereby we suppress this specific scenario.
113  //
114  // Note that the bitshift operation might invoke unspecified/undefined
115  // behavior, but that's another topic, this checker is about detecting
116  // conversion-related defects.
117  //
118  // Example AST for `x.id << 1`:
119  // BinaryOperator 'int' '<<'
120  // |-ImplicitCastExpr 'int' <IntegralCast>
121  // | `-ImplicitCastExpr 'unsigned int' <LValueToRValue>
122  // | `-MemberExpr 'unsigned int' lvalue bitfield .id
123  // | `-DeclRefExpr 'SmallBitfield' lvalue ParmVar 'x' 'SmallBitfield'
124  // `-IntegerLiteral 'int' 1
125  const auto ImplicitIntWidenedBitfieldValue = implicitCastExpr(
126  hasCastKind(CK_IntegralCast), hasType(asString("int")),
127  has(castExpr(hasCastKind(CK_LValueToRValue),
128  has(ignoringParens(memberExpr(hasDeclaration(
129  fieldDecl(isBitField(), unless(hasIntBitwidth())))))))));
130 
131  // Casts:
132  // i = 0.5;
133  // void f(int); f(0.5);
134  Finder->addMatcher(
135  traverse(TK_AsIs, implicitCastExpr(
136  hasImplicitDestinationType(
137  hasUnqualifiedDesugaredType(builtinType())),
138  hasSourceExpression(hasType(
139  hasUnqualifiedDesugaredType(builtinType()))),
140  unless(hasSourceExpression(IsCeilFloorCallExpr)),
141  unless(hasParent(castExpr())),
142  WarnWithinTemplateInstantiation
143  ? stmt()
144  : stmt(unless(isInTemplateInstantiation())),
145  IgnoreConversionFromTypes.empty()
146  ? castExpr()
147  : castExpr(unless(hasSourceExpression(
148  IsIgnoredTypeTwoLevelsDeep))),
149  unless(ImplicitIntWidenedBitfieldValue))
150  .bind("cast")),
151  this);
152 
153  // Binary operators:
154  // i += 0.5;
155  Finder->addMatcher(
156  binaryOperator(
157  isAssignmentOperator(),
158  hasLHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))),
159  hasRHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))),
160  unless(hasRHS(IsCeilFloorCallExpr)),
161  WarnWithinTemplateInstantiation
162  ? binaryOperator()
163  : binaryOperator(unless(isInTemplateInstantiation())),
164  IgnoreConversionFromTypes.empty()
165  ? binaryOperator()
166  : binaryOperator(unless(hasRHS(IsIgnoredTypeTwoLevelsDeep))),
167  // The `=` case generates an implicit cast
168  // which is covered by the previous matcher.
169  unless(hasOperatorName("=")))
170  .bind("binary_op"),
171  this);
172 }
173 
174 static const BuiltinType *getBuiltinType(const Expr &E) {
175  return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>();
176 }
177 
178 static QualType getUnqualifiedType(const Expr &E) {
179  return E.getType().getUnqualifiedType();
180 }
181 
182 static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E) {
183  if (auto IntegerConstant = E.getIntegerConstantExpr(Ctx))
184  return APValue(*IntegerConstant);
185  APValue Constant;
186  if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant))
187  return Constant;
188  return {};
189 }
190 
191 static bool getIntegerConstantExprValue(const ASTContext &Context,
192  const Expr &E, llvm::APSInt &Value) {
193  APValue Constant = getConstantExprValue(Context, E);
194  if (!Constant.isInt())
195  return false;
196  Value = Constant.getInt();
197  return true;
198 }
199 
200 static bool getFloatingConstantExprValue(const ASTContext &Context,
201  const Expr &E, llvm::APFloat &Value) {
202  APValue Constant = getConstantExprValue(Context, E);
203  if (!Constant.isFloat())
204  return false;
205  Value = Constant.getFloat();
206  return true;
207 }
208 
209 namespace {
210 
211 struct IntegerRange {
212  bool contains(const IntegerRange &From) const {
213  return llvm::APSInt::compareValues(Lower, From.Lower) <= 0 &&
214  llvm::APSInt::compareValues(Upper, From.Upper) >= 0;
215  }
216 
217  bool contains(const llvm::APSInt &Value) const {
218  return llvm::APSInt::compareValues(Lower, Value) <= 0 &&
219  llvm::APSInt::compareValues(Upper, Value) >= 0;
220  }
221 
222  llvm::APSInt Lower;
223  llvm::APSInt Upper;
224 };
225 
226 } // namespace
227 
228 static IntegerRange createFromType(const ASTContext &Context,
229  const BuiltinType &T) {
230  if (T.isFloatingPoint()) {
231  unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision(
232  Context.getFloatTypeSemantics(T.desugar()));
233  // Contrary to two's complement integer, floating point values are
234  // symmetric and have the same number of positive and negative values.
235  // The range of valid integers for a floating point value is:
236  // [-2^PrecisionBits, 2^PrecisionBits]
237 
238  // Values are created with PrecisionBits plus two bits:
239  // - One to express the missing negative value of 2's complement
240  // representation.
241  // - One for the sign.
242  llvm::APSInt UpperValue(PrecisionBits + 2, /*isUnsigned*/ false);
243  UpperValue.setBit(PrecisionBits);
244  llvm::APSInt LowerValue(PrecisionBits + 2, /*isUnsigned*/ false);
245  LowerValue.setBit(PrecisionBits);
246  LowerValue.setSignBit();
247  return {LowerValue, UpperValue};
248  }
249  assert(T.isInteger() && "Unexpected builtin type");
250  uint64_t TypeSize = Context.getTypeSize(&T);
251  bool IsUnsignedInteger = T.isUnsignedInteger();
252  return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger),
253  llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)};
254 }
255 
256 static bool isWideEnoughToHold(const ASTContext &Context,
257  const BuiltinType &FromType,
258  const BuiltinType &ToType) {
259  IntegerRange FromIntegerRange = createFromType(Context, FromType);
260  IntegerRange ToIntegerRange = createFromType(Context, ToType);
261  return ToIntegerRange.contains(FromIntegerRange);
262 }
263 
264 static bool isWideEnoughToHold(const ASTContext &Context,
265  const llvm::APSInt &IntegerConstant,
266  const BuiltinType &ToType) {
267  IntegerRange ToIntegerRange = createFromType(Context, ToType);
268  return ToIntegerRange.contains(IntegerConstant);
269 }
270 
271 // Returns true iff the floating point constant can be losslessly represented
272 // by an integer in the given destination type. eg. 2.0 can be accurately
273 // represented by an int32_t, but neither 2^33 nor 2.001 can.
274 static bool isFloatExactlyRepresentable(const ASTContext &Context,
275  const llvm::APFloat &FloatConstant,
276  const QualType &DestType) {
277  unsigned DestWidth = Context.getIntWidth(DestType);
278  bool DestSigned = DestType->isSignedIntegerOrEnumerationType();
279  llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned);
280  bool IsExact = false;
281  bool Overflows = FloatConstant.convertToInteger(
282  Result, llvm::APFloat::rmTowardZero, &IsExact) &
283  llvm::APFloat::opInvalidOp;
284  return !Overflows && IsExact;
285 }
286 
287 static llvm::SmallString<64> getValueAsString(const llvm::APSInt &Value,
288  uint64_t HexBits) {
289  llvm::SmallString<64> Str;
290  Value.toString(Str, 10);
291  if (HexBits > 0) {
292  Str.append(" (0x");
293  llvm::SmallString<32> HexValue;
294  Value.toStringUnsigned(HexValue, 16);
295  for (size_t I = HexValue.size(); I < (HexBits / 4); ++I)
296  Str.append("0");
297  Str.append(HexValue);
298  Str.append(")");
299  }
300  return Str;
301 }
302 
303 bool NarrowingConversionsCheck::isWarningInhibitedByEquivalentSize(
304  const ASTContext &Context, const BuiltinType &FromType,
305  const BuiltinType &ToType) const {
306  // With this option, we don't warn on conversions that have equivalent width
307  // in bits. eg. uint32 <-> int32.
308  if (!WarnOnEquivalentBitWidth) {
309  uint64_t FromTypeSize = Context.getTypeSize(&FromType);
310  uint64_t ToTypeSize = Context.getTypeSize(&ToType);
311  if (FromTypeSize == ToTypeSize) {
312  return true;
313  }
314  }
315  return false;
316 }
317 
318 void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,
319  const Expr &Lhs,
320  const Expr &Rhs) {
321  diag(SourceLoc, "narrowing conversion from %0 to %1")
322  << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
323 }
324 
325 void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(
326  SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs) {
327  diag(SourceLoc, "narrowing conversion from %0 to signed type %1 is "
328  "implementation-defined")
329  << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
330 }
331 
332 void NarrowingConversionsCheck::diagNarrowIntegerConstant(
333  SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
334  const llvm::APSInt &Value) {
335  diag(SourceLoc,
336  "narrowing conversion from constant value %0 of type %1 to %2")
337  << getValueAsString(Value, /*NoHex*/ 0) << getUnqualifiedType(Rhs)
338  << getUnqualifiedType(Lhs);
339 }
340 
341 void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt(
342  SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
343  const llvm::APSInt &Value, const uint64_t HexBits) {
344  diag(SourceLoc, "narrowing conversion from constant value %0 of type %1 "
345  "to signed type %2 is implementation-defined")
346  << getValueAsString(Value, HexBits) << getUnqualifiedType(Rhs)
347  << getUnqualifiedType(Lhs);
348 }
349 
350 void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
351  const Expr &Lhs,
352  const Expr &Rhs) {
353  diag(SourceLoc, "narrowing conversion from constant %0 to %1")
354  << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
355 }
356 
357 void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
358  const Expr &Lhs,
359  const Expr &Rhs) {
360  diag(SourceLoc, "constant value should be of type of type %0 instead of %1")
361  << getUnqualifiedType(Lhs) << getUnqualifiedType(Rhs);
362 }
363 
364 void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
365  const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
366  const Expr &Rhs) {
367  APValue Constant = getConstantExprValue(Context, Rhs);
368  if (Constant.isInt())
369  return diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Constant.getInt());
370  if (Constant.isFloat())
371  return diagNarrowConstant(SourceLoc, Lhs, Rhs);
372  return diagNarrowType(SourceLoc, Lhs, Rhs);
373 }
374 
375 void NarrowingConversionsCheck::handleIntegralCast(const ASTContext &Context,
376  SourceLocation SourceLoc,
377  const Expr &Lhs,
378  const Expr &Rhs) {
379  if (WarnOnIntegerNarrowingConversion) {
380  const BuiltinType *ToType = getBuiltinType(Lhs);
381  // From [conv.integral]p7.3.8:
382  // Conversions to unsigned integer is well defined so no warning is issued.
383  // "The resulting value is the smallest unsigned value equal to the source
384  // value modulo 2^n where n is the number of bits used to represent the
385  // destination type."
386  if (ToType->isUnsignedInteger())
387  return;
388  const BuiltinType *FromType = getBuiltinType(Rhs);
389 
390  // With this option, we don't warn on conversions that have equivalent width
391  // in bits. eg. uint32 <-> int32.
392  if (!WarnOnEquivalentBitWidth) {
393  uint64_t FromTypeSize = Context.getTypeSize(FromType);
394  uint64_t ToTypeSize = Context.getTypeSize(ToType);
395  if (FromTypeSize == ToTypeSize)
396  return;
397  }
398 
399  llvm::APSInt IntegerConstant;
400  if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
401  if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
402  diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs,
403  IntegerConstant,
404  Context.getTypeSize(FromType));
405  return;
406  }
407  if (!isWideEnoughToHold(Context, *FromType, *ToType))
408  diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs);
409  }
410 }
411 
412 void NarrowingConversionsCheck::handleIntegralToBoolean(
413  const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
414  const Expr &Rhs) {
415  // Conversion from Integral to Bool value is well defined.
416 
417  // We keep this function (even if it is empty) to make sure that
418  // handleImplicitCast and handleBinaryOperator are symmetric in their behavior
419  // and handle the same cases.
420 }
421 
422 void NarrowingConversionsCheck::handleIntegralToFloating(
423  const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
424  const Expr &Rhs) {
425  if (WarnOnIntegerToFloatingPointNarrowingConversion) {
426  const BuiltinType *ToType = getBuiltinType(Lhs);
427  llvm::APSInt IntegerConstant;
428  if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
429  if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
430  diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);
431  return;
432  }
433 
434  const BuiltinType *FromType = getBuiltinType(Rhs);
435  if (isWarningInhibitedByEquivalentSize(Context, *FromType, *ToType))
436  return;
437  if (!isWideEnoughToHold(Context, *FromType, *ToType))
438  diagNarrowType(SourceLoc, Lhs, Rhs);
439  }
440 }
441 
442 void NarrowingConversionsCheck::handleFloatingToIntegral(
443  const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
444  const Expr &Rhs) {
445  llvm::APFloat FloatConstant(0.0);
446  if (getFloatingConstantExprValue(Context, Rhs, FloatConstant)) {
447  if (!isFloatExactlyRepresentable(Context, FloatConstant, Lhs.getType()))
448  return diagNarrowConstant(SourceLoc, Lhs, Rhs);
449 
450  if (PedanticMode)
451  return diagConstantCast(SourceLoc, Lhs, Rhs);
452 
453  return;
454  }
455 
456  const BuiltinType *FromType = getBuiltinType(Rhs);
457  const BuiltinType *ToType = getBuiltinType(Lhs);
458  if (isWarningInhibitedByEquivalentSize(Context, *FromType, *ToType))
459  return;
460  diagNarrowType(SourceLoc, Lhs, Rhs); // Assumed always lossy.
461 }
462 
463 void NarrowingConversionsCheck::handleFloatingToBoolean(
464  const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
465  const Expr &Rhs) {
466  return diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs);
467 }
468 
469 void NarrowingConversionsCheck::handleBooleanToSignedIntegral(
470  const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
471  const Expr &Rhs) {
472  // Conversion from Bool to SignedIntegral value is well defined.
473 
474  // We keep this function (even if it is empty) to make sure that
475  // handleImplicitCast and handleBinaryOperator are symmetric in their behavior
476  // and handle the same cases.
477 }
478 
479 void NarrowingConversionsCheck::handleFloatingCast(const ASTContext &Context,
480  SourceLocation SourceLoc,
481  const Expr &Lhs,
482  const Expr &Rhs) {
483  if (WarnOnFloatingPointNarrowingConversion) {
484  const BuiltinType *ToType = getBuiltinType(Lhs);
485  APValue Constant = getConstantExprValue(Context, Rhs);
486  if (Constant.isFloat()) {
487  // From [dcl.init.list]p7.2:
488  // Floating point constant narrowing only takes place when the value is
489  // not within destination range. We convert the value to the destination
490  // type and check if the resulting value is infinity.
491  llvm::APFloat Tmp = Constant.getFloat();
492  bool UnusedLosesInfo;
493  Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()),
494  llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo);
495  if (Tmp.isInfinity())
496  diagNarrowConstant(SourceLoc, Lhs, Rhs);
497  return;
498  }
499  const BuiltinType *FromType = getBuiltinType(Rhs);
500  if (ToType->getKind() < FromType->getKind())
501  diagNarrowType(SourceLoc, Lhs, Rhs);
502  }
503 }
504 
505 void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
506  SourceLocation SourceLoc,
507  const Expr &Lhs,
508  const Expr &Rhs) {
509  assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&
510  "Dependent types must be check before calling this function");
511  const BuiltinType *LhsType = getBuiltinType(Lhs);
512  const BuiltinType *RhsType = getBuiltinType(Rhs);
513  if (RhsType == nullptr || LhsType == nullptr)
514  return;
515  if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())
516  return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
517  if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)
518  return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
519  if (RhsType->isInteger() && LhsType->isFloatingPoint())
520  return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
521  if (RhsType->isInteger() && LhsType->isInteger())
522  return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
523  if (RhsType->isFloatingPoint() && LhsType->getKind() == BuiltinType::Bool)
524  return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
525  if (RhsType->isFloatingPoint() && LhsType->isInteger())
526  return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
527  if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())
528  return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
529 }
530 
531 bool NarrowingConversionsCheck::handleConditionalOperator(
532  const ASTContext &Context, const Expr &Lhs, const Expr &Rhs) {
533  if (const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
534  // We have an expression like so: `output = cond ? lhs : rhs`
535  // From the point of view of narrowing conversion we treat it as two
536  // expressions `output = lhs` and `output = rhs`.
537  handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs,
538  *CO->getLHS());
539  handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs,
540  *CO->getRHS());
541  return true;
542  }
543  return false;
544 }
545 
546 void NarrowingConversionsCheck::handleImplicitCast(
547  const ASTContext &Context, const ImplicitCastExpr &Cast) {
548  if (Cast.getExprLoc().isMacroID())
549  return;
550  const Expr &Lhs = Cast;
551  const Expr &Rhs = *Cast.getSubExpr();
552  if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
553  return;
554  if (handleConditionalOperator(Context, Lhs, Rhs))
555  return;
556  SourceLocation SourceLoc = Lhs.getExprLoc();
557  switch (Cast.getCastKind()) {
558  case CK_BooleanToSignedIntegral:
559  return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
560  case CK_IntegralToBoolean:
561  return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
562  case CK_IntegralToFloating:
563  return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
564  case CK_IntegralCast:
565  return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
566  case CK_FloatingToBoolean:
567  return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
568  case CK_FloatingToIntegral:
569  return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
570  case CK_FloatingCast:
571  return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
572  default:
573  break;
574  }
575 }
576 
577 void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
578  const BinaryOperator &Op) {
579  if (Op.getBeginLoc().isMacroID())
580  return;
581  const Expr &Lhs = *Op.getLHS();
582  const Expr &Rhs = *Op.getRHS();
583  if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
584  return;
585  if (handleConditionalOperator(Context, Lhs, Rhs))
586  return;
587  handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
588 }
589 
590 void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) {
591  if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("binary_op"))
592  return handleBinaryOperator(*Result.Context, *Op);
593  if (const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast"))
594  return handleImplicitCast(*Result.Context, *Cast);
595  llvm_unreachable("must be binary operator or cast expression");
596 }
597 } // namespace cppcoreguidelines
598 } // namespace tidy
599 } // namespace clang
Lower
llvm::APSInt Lower
Definition: NarrowingConversionsCheck.cpp:222
clang::tidy::ClangTidyOptions::OptionMap
llvm::StringMap< ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:115
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
clang::tidy::cppcoreguidelines::isWideEnoughToHold
static bool isWideEnoughToHold(const ASTContext &Context, const BuiltinType &FromType, const BuiltinType &ToType)
Definition: NarrowingConversionsCheck.cpp:256
Upper
llvm::APSInt Upper
Definition: NarrowingConversionsCheck.cpp:223
clang::tidy::cppcoreguidelines::NarrowingConversionsCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: NarrowingConversionsCheck.cpp:590
Ctx
Context Ctx
Definition: TUScheduler.cpp:553
NarrowingConversionsCheck.h
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:53
clang::tidy::cppcoreguidelines::AST_MATCHER
AST_MATCHER(FieldDecl, hasIntBitwidth)
Definition: NarrowingConversionsCheck.cpp:58
clang::tidy::cppcoreguidelines::createFromType
static IntegerRange createFromType(const ASTContext &Context, const BuiltinType &T)
Definition: NarrowingConversionsCheck.cpp:228
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::cppcoreguidelines::getBuiltinType
static const BuiltinType * getBuiltinType(const Expr &E)
Definition: NarrowingConversionsCheck.cpp:174
clang::tidy::cppcoreguidelines::NarrowingConversionsCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: NarrowingConversionsCheck.cpp:43
clang::clangd::CompletionItemKind::Constant
@ Constant
clang::tidy::cppcoreguidelines::getConstantExprValue
static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E)
Definition: NarrowingConversionsCheck.cpp:182
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:415
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:67
clang::tidy::cppcoreguidelines::getUnqualifiedType
static QualType getUnqualifiedType(const Expr &E)
Definition: NarrowingConversionsCheck.cpp:178
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:25
Name
Token Name
Definition: MacroToEnumCheck.cpp:89
clang::tidy::cppcoreguidelines::getFloatingConstantExprValue
static bool getFloatingConstantExprValue(const ASTContext &Context, const Expr &E, llvm::APFloat &Value)
Definition: NarrowingConversionsCheck.cpp:200
clang::tidy::cppcoreguidelines::getIntegerConstantExprValue
static bool getIntegerConstantExprValue(const ASTContext &Context, const Expr &E, llvm::APSInt &Value)
Definition: NarrowingConversionsCheck.cpp:191
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::utils::options::parseStringList
std::vector< StringRef > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
Definition: OptionsUtils.cpp:19
clang::tidy::cppcoreguidelines::isFloatExactlyRepresentable
static bool isFloatExactlyRepresentable(const ASTContext &Context, const llvm::APFloat &FloatConstant, const QualType &DestType)
Definition: NarrowingConversionsCheck.cpp:274
clang::tidy::ClangTidyCheck::OptionsView::store
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidyCheck.cpp:129
clang::tidy::cppcoreguidelines::getValueAsString
static llvm::SmallString< 64 > getValueAsString(const llvm::APSInt &Value, uint64_t HexBits)
Definition: NarrowingConversionsCheck.cpp:287
clang::tidy::cppcoreguidelines::NarrowingConversionsCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: NarrowingConversionsCheck.cpp:66
contains
This directory contains
Definition: README.txt:6