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