clang-tools 23.0.0git
NarrowingConversionsCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
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
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 const std::string Name = Node.getLocalUnqualifiedType().getAsString();
33 return llvm::is_contained(Names, Name);
34}
35
36AST_MATCHER(FieldDecl, hasIntBitwidth) {
37 assert(Node.isBitField());
38 const ASTContext &Ctx = Node.getASTContext();
39 const unsigned IntBitWidth = Ctx.getIntWidth(Ctx.IntTy);
40 const unsigned CurrentBitWidth = Node.getBitWidthValue();
41 return IntBitWidth == CurrentBitWidth;
42}
43
44} // namespace
45
47 ClangTidyContext *Context)
48 : ClangTidyCheck(Name, Context),
49 WarnOnIntegerNarrowingConversion(
50 Options.get("WarnOnIntegerNarrowingConversion", true)),
51 WarnOnIntegerToFloatingPointNarrowingConversion(
52 Options.get("WarnOnIntegerToFloatingPointNarrowingConversion", true)),
53 WarnOnFloatingPointNarrowingConversion(
54 Options.get("WarnOnFloatingPointNarrowingConversion", true)),
55 WarnWithinTemplateInstantiation(
56 Options.get("WarnWithinTemplateInstantiation", false)),
57 WarnOnEquivalentBitWidth(Options.get("WarnOnEquivalentBitWidth", true)),
58 IgnoreConversionFromTypes(Options.get("IgnoreConversionFromTypes", "")),
59 PedanticMode(Options.get("PedanticMode", false)) {}
60
63 Options.store(Opts, "WarnOnIntegerNarrowingConversion",
64 WarnOnIntegerNarrowingConversion);
65 Options.store(Opts, "WarnOnIntegerToFloatingPointNarrowingConversion",
66 WarnOnIntegerToFloatingPointNarrowingConversion);
67 Options.store(Opts, "WarnOnFloatingPointNarrowingConversion",
68 WarnOnFloatingPointNarrowingConversion);
69 Options.store(Opts, "WarnWithinTemplateInstantiation",
70 WarnWithinTemplateInstantiation);
71 Options.store(Opts, "WarnOnEquivalentBitWidth", WarnOnEquivalentBitWidth);
72 Options.store(Opts, "IgnoreConversionFromTypes", IgnoreConversionFromTypes);
73 Options.store(Opts, "PedanticMode", PedanticMode);
74}
75
77 // ceil() and floor() are guaranteed to return integers, even though the type
78 // is not integral.
79 const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl(
80 hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor")))));
81
82 const std::vector<StringRef> IgnoreConversionFromTypesVec =
83 utils::options::parseStringList(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(namedDecl(hasAnyName(IgnoreConversionFromTypesVec))),
90 allOf(unless(hasType(namedDecl())),
91 hasType(qualType(hasAnyType(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(IsConversionFromIgnoredType,
104 hasType(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(CK_IntegralCast), hasType(asString("int")),
142 has(castExpr(hasCastKind(CK_LValueToRValue),
143 has(ignoringParens(memberExpr(hasDeclaration(
144 fieldDecl(isBitField(), unless(hasIntBitwidth())))))))));
145
146 // Casts:
147 // i = 0.5;
148 // void f(int); f(0.5);
149 Finder->addMatcher(
150 traverse(TK_AsIs, implicitCastExpr(
151 hasImplicitDestinationType(
152 hasUnqualifiedDesugaredType(builtinType())),
153 hasSourceExpression(hasType(
154 hasUnqualifiedDesugaredType(builtinType()))),
155 unless(hasSourceExpression(IsCeilFloorCallExpr)),
156 unless(hasParent(castExpr())),
157 WarnWithinTemplateInstantiation
158 ? stmt()
159 : stmt(unless(isInTemplateInstantiation())),
160 IgnoreConversionFromTypes.empty()
161 ? castExpr()
162 : castExpr(unless(hasSourceExpression(
163 IsIgnoredTypeTwoLevelsDeep))),
164 unless(ImplicitIntWidenedBitfieldValue))
165 .bind("cast")),
166 this);
167
168 // Binary operators:
169 // i += 0.5;
170 Finder->addMatcher(
171 binaryOperator(
172 isAssignmentOperator(),
173 hasLHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))),
174 hasRHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))),
175 unless(hasRHS(IsCeilFloorCallExpr)),
176 WarnWithinTemplateInstantiation
177 ? binaryOperator()
178 : binaryOperator(unless(isInTemplateInstantiation())),
179 IgnoreConversionFromTypes.empty()
180 ? binaryOperator()
181 : binaryOperator(unless(hasRHS(IsIgnoredTypeTwoLevelsDeep))),
182 // The `=` case generates an implicit cast
183 // which is covered by the previous matcher.
184 unless(hasOperatorName("=")))
185 .bind("binary_op"),
186 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, &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(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(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(Lower, From.Lower) <= 0 &&
229 llvm::APSInt::compareValues(Upper, From.Upper) >= 0;
230 }
231
232 bool contains(const llvm::APSInt &Value) const {
233 return llvm::APSInt::compareValues(Lower, Value) <= 0 &&
234 llvm::APSInt::compareValues(Upper, 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 const unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision(
247 Context.getFloatTypeSemantics(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 {LowerValue, UpperValue};
263 }
264 assert(T.isInteger() && "Unexpected builtin type");
265 const uint64_t TypeSize = Context.getTypeSize(&T);
266 const bool IsUnsignedInteger = T.isUnsignedInteger();
267 return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger),
268 llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)};
269}
270
271static bool isWideEnoughToHold(const ASTContext &Context,
272 const BuiltinType &FromType,
273 const BuiltinType &ToType) {
274 const IntegerRange FromIntegerRange = createFromType(Context, FromType);
275 const IntegerRange ToIntegerRange = createFromType(Context, ToType);
276 return ToIntegerRange.contains(FromIntegerRange);
277}
278
279static bool isWideEnoughToHold(const ASTContext &Context,
280 const llvm::APSInt &IntegerConstant,
281 const BuiltinType &ToType) {
282 const IntegerRange ToIntegerRange = createFromType(Context, ToType);
283 return ToIntegerRange.contains(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 const unsigned DestWidth = Context.getIntWidth(DestType);
293 const bool DestSigned = DestType->isSignedIntegerOrEnumerationType();
294 llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned);
295 bool IsExact = false;
296 const bool Overflows = FloatConstant.convertToInteger(
297 Result, llvm::APFloat::rmTowardZero, &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, 10);
306 if (HexBits > 0) {
307 Str.append(" (0x");
308 llvm::SmallString<32> HexValue;
309 Value.toStringUnsigned(HexValue, 16);
310 for (size_t I = HexValue.size(); I < (HexBits / 4); ++I)
311 Str.append("0");
312 Str.append(HexValue);
313 Str.append(")");
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 const uint64_t FromTypeSize = Context.getTypeSize(&FromType);
325 const uint64_t ToTypeSize = Context.getTypeSize(&ToType);
326 if (FromTypeSize == ToTypeSize)
327 return true;
328 }
329 return false;
330}
331
332void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,
333 const Expr &Lhs,
334 const Expr &Rhs) {
335 diag(SourceLoc, "narrowing conversion from %0 to %1")
337}
338
339void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(
340 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs) {
341 diag(SourceLoc, "narrowing conversion from %0 to signed type %1 is "
342 "implementation-defined")
344}
345
346void NarrowingConversionsCheck::diagNarrowIntegerConstant(
347 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
348 const llvm::APSInt &Value) {
349 diag(SourceLoc,
350 "narrowing conversion from constant value %0 of type %1 to %2")
351 << getValueAsString(Value, /*NoHex*/ 0) << getUnqualifiedType(Rhs)
352 << getUnqualifiedType(Lhs);
353}
354
355void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt(
356 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
357 const llvm::APSInt &Value, const uint64_t HexBits) {
358 diag(SourceLoc, "narrowing conversion from constant value %0 of type %1 "
359 "to signed type %2 is implementation-defined")
360 << getValueAsString(Value, HexBits) << getUnqualifiedType(Rhs)
361 << getUnqualifiedType(Lhs);
362}
363
364void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
365 const Expr &Lhs,
366 const Expr &Rhs) {
367 diag(SourceLoc, "narrowing conversion from constant %0 to %1")
369}
370
371void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
372 const Expr &Lhs,
373 const Expr &Rhs) {
374 diag(SourceLoc, "constant value should be of type of type %0 instead of %1")
376}
377
378void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
379 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
380 const Expr &Rhs) {
381 APValue Constant = getConstantExprValue(Context, Rhs);
382 if (Constant.isInt())
383 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Constant.getInt());
384 else if (Constant.isFloat())
385 diagNarrowConstant(SourceLoc, Lhs, Rhs);
386 else
387 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(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(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 const uint64_t FromTypeSize = Context.getTypeSize(FromType);
409 const uint64_t ToTypeSize = Context.getTypeSize(ToType);
410 if (FromTypeSize == ToTypeSize)
411 return;
412 }
413
414 llvm::APSInt IntegerConstant;
415 if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
416 if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
417 diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs,
418 IntegerConstant,
419 Context.getTypeSize(FromType));
420 return;
421 }
422 if (!isWideEnoughToHold(Context, *FromType, *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(Lhs);
442 llvm::APSInt IntegerConstant;
443 if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
444 if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
445 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);
446 return;
447 }
448
449 const BuiltinType *FromType = getBuiltinType(Rhs);
450 if (isWarningInhibitedByEquivalentSize(Context, *FromType, *ToType))
451 return;
452 if (!isWideEnoughToHold(Context, *FromType, *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, Rhs, FloatConstant)) {
462 if (!isFloatExactlyRepresentable(Context, FloatConstant, Lhs.getType()))
463 diagNarrowConstant(SourceLoc, Lhs, Rhs);
464
465 else if (PedanticMode)
466 diagConstantCast(SourceLoc, Lhs, Rhs);
467
468 return;
469 }
470
471 const BuiltinType *FromType = getBuiltinType(Rhs);
472 const BuiltinType *ToType = getBuiltinType(Lhs);
473 if (isWarningInhibitedByEquivalentSize(Context, *FromType, *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 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(Lhs);
500 APValue Constant = getConstantExprValue(Context, 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(Context.getFloatTypeSemantics(ToType->desugar()),
509 llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo);
510 if (Tmp.isInfinity())
511 diagNarrowConstant(SourceLoc, Lhs, Rhs);
512 return;
513 }
514 const BuiltinType *FromType = getBuiltinType(Rhs);
515 if (!llvm::APFloatBase::isRepresentableBy(
516 Context.getFloatTypeSemantics(FromType->desugar()),
517 Context.getFloatTypeSemantics(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(Lhs);
529 const BuiltinType *RhsType = getBuiltinType(Rhs);
530 if (RhsType == nullptr || LhsType == nullptr)
531 return;
532 if (LhsType == RhsType)
533 return;
534 if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())
535 handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
536 else if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)
537 handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
538 else if (RhsType->isInteger() && LhsType->isFloatingPoint())
539 handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
540 else if (RhsType->isInteger() && LhsType->isInteger())
541 handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
542 else if (RhsType->isFloatingPoint() &&
543 LhsType->getKind() == BuiltinType::Bool)
544 handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
545 else if (RhsType->isFloatingPoint() && LhsType->isInteger())
546 handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
547 else if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())
548 handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
549}
550
551bool NarrowingConversionsCheck::handleConditionalOperator(
552 const ASTContext &Context, const Expr &Lhs, const Expr &Rhs) {
553 if (const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
554 // We have an expression like so: `output = cond ? lhs : rhs`
555 // From the point of view of narrowing conversion we treat it as two
556 // expressions `output = lhs` and `output = rhs`.
557 handleConditionalOperatorArgument(Context, Lhs, CO->getLHS());
558 handleConditionalOperatorArgument(Context, Lhs, CO->getRHS());
559 return true;
560 }
561 return false;
562}
563
564void NarrowingConversionsCheck::handleConditionalOperatorArgument(
565 const ASTContext &Context, const Expr &Lhs, const Expr *Arg) {
566 if (const auto *ICE = llvm::dyn_cast<ImplicitCastExpr>(Arg))
567 if (!Arg->getIntegerConstantExpr(Context))
568 Arg = ICE->getSubExpr();
569
570 handleBinaryOperator(Context, Arg->getExprLoc(), Lhs, *Arg);
571}
572
573void NarrowingConversionsCheck::handleImplicitCast(
574 const ASTContext &Context, const ImplicitCastExpr &Cast) {
575 if (Cast.getExprLoc().isMacroID())
576 return;
577 const Expr &Lhs = Cast;
578 const Expr &Rhs = *Cast.getSubExpr();
579 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
580 return;
581 if (getBuiltinType(Lhs) == getBuiltinType(Rhs))
582 return;
583 if (handleConditionalOperator(Context, Lhs, Rhs))
584 return;
585 const SourceLocation SourceLoc = Lhs.getExprLoc();
586 switch (Cast.getCastKind()) {
587 case CK_BooleanToSignedIntegral:
588 handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
589 return;
590 case CK_IntegralToBoolean:
591 handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
592 return;
593 case CK_IntegralToFloating:
594 handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
595 return;
596 case CK_IntegralCast:
597 handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
598 return;
599 case CK_FloatingToBoolean:
600 handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
601 return;
602 case CK_FloatingToIntegral:
603 handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
604 return;
605 case CK_FloatingCast:
606 handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
607 return;
608 default:
609 return;
610 }
611}
612
613void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
614 const BinaryOperator &Op) {
615 if (Op.getBeginLoc().isMacroID())
616 return;
617 const Expr &Lhs = *Op.getLHS();
618 const Expr &Rhs = *Op.getRHS();
619 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
620 return;
621 if (handleConditionalOperator(Context, Lhs, Rhs))
622 return;
623 handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
624}
625
626void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) {
627 if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("binary_op"))
628 handleBinaryOperator(*Result.Context, *Op);
629 else if (const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast"))
630 handleImplicitCast(*Result.Context, *Cast);
631 else
632 llvm_unreachable("must be binary operator or cast expression");
633}
634} // namespace clang::tidy::bugprone
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
NarrowingConversionsCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
static bool getIntegerConstantExprValue(const ASTContext &Context, const Expr &E, llvm::APSInt &Value)
static QualType getUnqualifiedType(const Expr &E)
static bool getFloatingConstantExprValue(const ASTContext &Context, const Expr &E, llvm::APFloat &Value)
static bool isWideEnoughToHold(const ASTContext &Context, const BuiltinType &FromType, const BuiltinType &ToType)
static llvm::SmallString< 64 > getValueAsString(const llvm::APSInt &Value, uint64_t HexBits)
static const BuiltinType * getBuiltinType(const Expr &E)
static IntegerRange createFromType(const ASTContext &Context, const BuiltinType &T)
static bool isFloatExactlyRepresentable(const ASTContext &Context, const llvm::APFloat &FloatConstant, const QualType &DestType)
static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E)
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
AST_MATCHER(BinaryOperator, isRelationalOperator)
std::vector< StringRef > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
llvm::StringMap< ClangTidyValue > OptionMap