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