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