clang-tools 22.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 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 unsigned IntBitWidth = Ctx.getIntWidth(Ctx.IntTy);
40 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 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 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 uint64_t TypeSize = Context.getTypeSize(&T);
266 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 IntegerRange FromIntegerRange = createFromType(Context, FromType);
275 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 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 unsigned DestWidth = Context.getIntWidth(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, 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 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(SourceLoc, "narrowing conversion from %0 to %1")
338}
339
340void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(
341 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs) {
342 diag(SourceLoc, "narrowing conversion from %0 to signed type %1 is "
343 "implementation-defined")
345}
346
347void NarrowingConversionsCheck::diagNarrowIntegerConstant(
348 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
349 const llvm::APSInt &Value) {
350 diag(SourceLoc,
351 "narrowing conversion from constant value %0 of type %1 to %2")
352 << getValueAsString(Value, /*NoHex*/ 0) << getUnqualifiedType(Rhs)
353 << getUnqualifiedType(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(SourceLoc, "narrowing conversion from constant value %0 of type %1 "
360 "to signed type %2 is implementation-defined")
361 << getValueAsString(Value, HexBits) << getUnqualifiedType(Rhs)
362 << getUnqualifiedType(Lhs);
363}
364
365void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
366 const Expr &Lhs,
367 const Expr &Rhs) {
368 diag(SourceLoc, "narrowing conversion from constant %0 to %1")
370}
371
372void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
373 const Expr &Lhs,
374 const Expr &Rhs) {
375 diag(SourceLoc, "constant value should be of type of type %0 instead of %1")
377}
378
379void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
380 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
381 const Expr &Rhs) {
382 APValue Constant = getConstantExprValue(Context, Rhs);
383 if (Constant.isInt())
384 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Constant.getInt());
385 else if (Constant.isFloat())
386 diagNarrowConstant(SourceLoc, Lhs, Rhs);
387 else
388 diagNarrowType(SourceLoc, Lhs, Rhs);
389}
390
391void NarrowingConversionsCheck::handleIntegralCast(const ASTContext &Context,
392 SourceLocation SourceLoc,
393 const Expr &Lhs,
394 const Expr &Rhs) {
395 if (WarnOnIntegerNarrowingConversion) {
396 const BuiltinType *ToType = getBuiltinType(Lhs);
397 // From [conv.integral]p7.3.8:
398 // Conversions to unsigned integer is well defined so no warning is issued.
399 // "The resulting value is the smallest unsigned value equal to the source
400 // value modulo 2^n where n is the number of bits used to represent the
401 // destination type."
402 if (ToType->isUnsignedInteger())
403 return;
404 const BuiltinType *FromType = getBuiltinType(Rhs);
405
406 // With this option, we don't warn on conversions that have equivalent width
407 // in bits. eg. uint32 <-> int32.
408 if (!WarnOnEquivalentBitWidth) {
409 uint64_t FromTypeSize = Context.getTypeSize(FromType);
410 uint64_t ToTypeSize = Context.getTypeSize(ToType);
411 if (FromTypeSize == ToTypeSize)
412 return;
413 }
414
415 llvm::APSInt IntegerConstant;
416 if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
417 if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
418 diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs,
419 IntegerConstant,
420 Context.getTypeSize(FromType));
421 return;
422 }
423 if (!isWideEnoughToHold(Context, *FromType, *ToType))
424 diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs);
425 }
426}
427
428void NarrowingConversionsCheck::handleIntegralToBoolean(
429 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
430 const Expr &Rhs) {
431 // Conversion from Integral to Bool value is well defined.
432
433 // We keep this function (even if it is empty) to make sure that
434 // handleImplicitCast and handleBinaryOperator are symmetric in their behavior
435 // and handle the same cases.
436}
437
438void NarrowingConversionsCheck::handleIntegralToFloating(
439 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
440 const Expr &Rhs) {
441 if (WarnOnIntegerToFloatingPointNarrowingConversion) {
442 const BuiltinType *ToType = getBuiltinType(Lhs);
443 llvm::APSInt IntegerConstant;
444 if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
445 if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
446 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);
447 return;
448 }
449
450 const BuiltinType *FromType = getBuiltinType(Rhs);
451 if (isWarningInhibitedByEquivalentSize(Context, *FromType, *ToType))
452 return;
453 if (!isWideEnoughToHold(Context, *FromType, *ToType))
454 diagNarrowType(SourceLoc, Lhs, Rhs);
455 }
456}
457
458void NarrowingConversionsCheck::handleFloatingToIntegral(
459 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
460 const Expr &Rhs) {
461 llvm::APFloat FloatConstant(0.0);
462 if (getFloatingConstantExprValue(Context, Rhs, FloatConstant)) {
463 if (!isFloatExactlyRepresentable(Context, FloatConstant, Lhs.getType()))
464 diagNarrowConstant(SourceLoc, Lhs, Rhs);
465
466 else if (PedanticMode)
467 diagConstantCast(SourceLoc, Lhs, Rhs);
468
469 return;
470 }
471
472 const BuiltinType *FromType = getBuiltinType(Rhs);
473 const BuiltinType *ToType = getBuiltinType(Lhs);
474 if (isWarningInhibitedByEquivalentSize(Context, *FromType, *ToType))
475 return;
476 diagNarrowType(SourceLoc, Lhs, Rhs); // Assumed always lossy.
477}
478
479void NarrowingConversionsCheck::handleFloatingToBoolean(
480 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
481 const Expr &Rhs) {
482 diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs);
483}
484
485void NarrowingConversionsCheck::handleBooleanToSignedIntegral(
486 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
487 const Expr &Rhs) {
488 // Conversion from Bool to SignedIntegral value is well defined.
489
490 // We keep this function (even if it is empty) to make sure that
491 // handleImplicitCast and handleBinaryOperator are symmetric in their behavior
492 // and handle the same cases.
493}
494
495void NarrowingConversionsCheck::handleFloatingCast(const ASTContext &Context,
496 SourceLocation SourceLoc,
497 const Expr &Lhs,
498 const Expr &Rhs) {
499 if (WarnOnFloatingPointNarrowingConversion) {
500 const BuiltinType *ToType = getBuiltinType(Lhs);
501 APValue Constant = getConstantExprValue(Context, Rhs);
502 if (Constant.isFloat()) {
503 // From [dcl.init.list]p7.2:
504 // Floating point constant narrowing only takes place when the value is
505 // not within destination range. We convert the value to the destination
506 // type and check if the resulting value is infinity.
507 llvm::APFloat Tmp = Constant.getFloat();
508 bool UnusedLosesInfo = false;
509 Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()),
510 llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo);
511 if (Tmp.isInfinity())
512 diagNarrowConstant(SourceLoc, Lhs, Rhs);
513 return;
514 }
515 const BuiltinType *FromType = getBuiltinType(Rhs);
516 if (!llvm::APFloatBase::isRepresentableBy(
517 Context.getFloatTypeSemantics(FromType->desugar()),
518 Context.getFloatTypeSemantics(ToType->desugar())))
519 diagNarrowType(SourceLoc, Lhs, Rhs);
520 }
521}
522
523void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
524 SourceLocation SourceLoc,
525 const Expr &Lhs,
526 const Expr &Rhs) {
527 assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&
528 "Dependent types must be check before calling this function");
529 const BuiltinType *LhsType = getBuiltinType(Lhs);
530 const BuiltinType *RhsType = getBuiltinType(Rhs);
531 if (RhsType == nullptr || LhsType == nullptr)
532 return;
533 if (LhsType == RhsType)
534 return;
535 if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())
536 handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
537 else if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)
538 handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
539 else if (RhsType->isInteger() && LhsType->isFloatingPoint())
540 handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
541 else if (RhsType->isInteger() && LhsType->isInteger())
542 handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
543 else if (RhsType->isFloatingPoint() &&
544 LhsType->getKind() == BuiltinType::Bool)
545 handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
546 else if (RhsType->isFloatingPoint() && LhsType->isInteger())
547 handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
548 else if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())
549 handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
550}
551
552bool NarrowingConversionsCheck::handleConditionalOperator(
553 const ASTContext &Context, const Expr &Lhs, const Expr &Rhs) {
554 if (const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
555 // We have an expression like so: `output = cond ? lhs : rhs`
556 // From the point of view of narrowing conversion we treat it as two
557 // expressions `output = lhs` and `output = rhs`.
558 handleConditionalOperatorArgument(Context, Lhs, CO->getLHS());
559 handleConditionalOperatorArgument(Context, Lhs, CO->getRHS());
560 return true;
561 }
562 return false;
563}
564
565void NarrowingConversionsCheck::handleConditionalOperatorArgument(
566 const ASTContext &Context, const Expr &Lhs, const Expr *Arg) {
567 if (const auto *ICE = llvm::dyn_cast<ImplicitCastExpr>(Arg))
568 if (!Arg->getIntegerConstantExpr(Context))
569 Arg = ICE->getSubExpr();
570
571 handleBinaryOperator(Context, Arg->getExprLoc(), Lhs, *Arg);
572}
573
574void NarrowingConversionsCheck::handleImplicitCast(
575 const ASTContext &Context, const ImplicitCastExpr &Cast) {
576 if (Cast.getExprLoc().isMacroID())
577 return;
578 const Expr &Lhs = Cast;
579 const Expr &Rhs = *Cast.getSubExpr();
580 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
581 return;
582 if (getBuiltinType(Lhs) == getBuiltinType(Rhs))
583 return;
584 if (handleConditionalOperator(Context, Lhs, Rhs))
585 return;
586 SourceLocation SourceLoc = Lhs.getExprLoc();
587 switch (Cast.getCastKind()) {
588 case CK_BooleanToSignedIntegral:
589 handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
590 return;
591 case CK_IntegralToBoolean:
592 handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
593 return;
594 case CK_IntegralToFloating:
595 handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
596 return;
597 case CK_IntegralCast:
598 handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
599 return;
600 case CK_FloatingToBoolean:
601 handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
602 return;
603 case CK_FloatingToIntegral:
604 handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
605 return;
606 case CK_FloatingCast:
607 handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
608 return;
609 default:
610 return;
611 }
612}
613
614void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
615 const BinaryOperator &Op) {
616 if (Op.getBeginLoc().isMacroID())
617 return;
618 const Expr &Lhs = *Op.getLHS();
619 const Expr &Rhs = *Op.getRHS();
620 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
621 return;
622 if (handleConditionalOperator(Context, Lhs, Rhs))
623 return;
624 handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
625}
626
627void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) {
628 if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("binary_op"))
629 handleBinaryOperator(*Result.Context, *Op);
630 else if (const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast"))
631 handleImplicitCast(*Result.Context, *Cast);
632 else
633 llvm_unreachable("must be binary operator or cast expression");
634}
635} // 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