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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of clang-tools-extra/clang-tidy/bugprone/NarrowingConversionsCheck.cpp