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