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