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