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