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::cppcoreguidelines { |
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(Range: Names, P: [&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(T: Ctx.IntTy); |
41 | unsigned CurrentBitWidth = Node.getBitWidthValue(Ctx); |
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(LocalName: "WarnOnIntegerNarrowingConversion" , Default: true)), |
52 | WarnOnIntegerToFloatingPointNarrowingConversion( |
53 | Options.get(LocalName: "WarnOnIntegerToFloatingPointNarrowingConversion" , Default: true)), |
54 | WarnOnFloatingPointNarrowingConversion( |
55 | Options.get(LocalName: "WarnOnFloatingPointNarrowingConversion" , Default: true)), |
56 | WarnWithinTemplateInstantiation( |
57 | Options.get(LocalName: "WarnWithinTemplateInstantiation" , Default: false)), |
58 | WarnOnEquivalentBitWidth(Options.get(LocalName: "WarnOnEquivalentBitWidth" , Default: true)), |
59 | IgnoreConversionFromTypes(Options.get(LocalName: "IgnoreConversionFromTypes" , Default: "" )), |
60 | PedanticMode(Options.get(LocalName: "PedanticMode" , Default: false)) {} |
61 | |
62 | void NarrowingConversionsCheck::storeOptions( |
63 | ClangTidyOptions::OptionMap &Opts) { |
64 | Options.store(Options&: Opts, LocalName: "WarnOnIntegerNarrowingConversion" , |
65 | Value: WarnOnIntegerNarrowingConversion); |
66 | Options.store(Options&: Opts, LocalName: "WarnOnIntegerToFloatingPointNarrowingConversion" , |
67 | Value: WarnOnIntegerToFloatingPointNarrowingConversion); |
68 | Options.store(Options&: Opts, LocalName: "WarnOnFloatingPointNarrowingConversion" , |
69 | Value: WarnOnFloatingPointNarrowingConversion); |
70 | Options.store(Options&: Opts, LocalName: "WarnWithinTemplateInstantiation" , |
71 | Value: WarnWithinTemplateInstantiation); |
72 | Options.store(Options&: Opts, LocalName: "WarnOnEquivalentBitWidth" , Value: WarnOnEquivalentBitWidth); |
73 | Options.store(Options&: Opts, LocalName: "IgnoreConversionFromTypes" , Value: IgnoreConversionFromTypes); |
74 | Options.store(Options&: Opts, LocalName: "PedanticMode" , Value: 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(InnerMatcher: functionDecl( |
81 | hasAnyName("::ceil" , "::std::ceil" , "::floor" , "::std::floor" ))))); |
82 | |
83 | std::vector<StringRef> IgnoreConversionFromTypesVec = |
84 | utils::options::parseStringList(Option: 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(InnerMatcher: namedDecl(hasAnyName(IgnoreConversionFromTypesVec))), |
91 | allOf(unless(hasType(InnerMatcher: namedDecl())), |
92 | hasType(InnerMatcher: qualType(hasAnyType(Names: 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(Matcher1: IsConversionFromIgnoredType, |
105 | Matcher2: hasType(InnerMatcher: 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(Kind: CK_IntegralCast), hasType(InnerMatcher: asString(Name: "int" )), |
143 | has(castExpr(hasCastKind(Kind: CK_LValueToRValue), |
144 | has(ignoringParens(InnerMatcher: memberExpr(hasDeclaration( |
145 | InnerMatcher: fieldDecl(isBitField(), unless(hasIntBitwidth()))))))))); |
146 | |
147 | // Casts: |
148 | // i = 0.5; |
149 | // void f(int); f(0.5); |
150 | Finder->addMatcher( |
151 | NodeMatch: traverse(TK: TK_AsIs, InnerMatcher: implicitCastExpr( |
152 | hasImplicitDestinationType( |
153 | InnerMatcher: hasUnqualifiedDesugaredType(InnerMatcher: builtinType())), |
154 | hasSourceExpression(InnerMatcher: hasType( |
155 | InnerMatcher: hasUnqualifiedDesugaredType(InnerMatcher: builtinType()))), |
156 | unless(hasSourceExpression(InnerMatcher: IsCeilFloorCallExpr)), |
157 | unless(hasParent(castExpr())), |
158 | WarnWithinTemplateInstantiation |
159 | ? stmt() |
160 | : stmt(unless(isInTemplateInstantiation())), |
161 | IgnoreConversionFromTypes.empty() |
162 | ? castExpr() |
163 | : castExpr(unless(hasSourceExpression( |
164 | InnerMatcher: IsIgnoredTypeTwoLevelsDeep))), |
165 | unless(ImplicitIntWidenedBitfieldValue)) |
166 | .bind(ID: "cast" )), |
167 | Action: this); |
168 | |
169 | // Binary operators: |
170 | // i += 0.5; |
171 | Finder->addMatcher( |
172 | NodeMatch: binaryOperator( |
173 | isAssignmentOperator(), |
174 | hasLHS(InnerMatcher: expr(hasType(InnerMatcher: hasUnqualifiedDesugaredType(InnerMatcher: builtinType())))), |
175 | hasRHS(InnerMatcher: expr(hasType(InnerMatcher: hasUnqualifiedDesugaredType(InnerMatcher: builtinType())))), |
176 | unless(hasRHS(InnerMatcher: IsCeilFloorCallExpr)), |
177 | WarnWithinTemplateInstantiation |
178 | ? binaryOperator() |
179 | : binaryOperator(unless(isInTemplateInstantiation())), |
180 | IgnoreConversionFromTypes.empty() |
181 | ? binaryOperator() |
182 | : binaryOperator(unless(hasRHS(InnerMatcher: IsIgnoredTypeTwoLevelsDeep))), |
183 | // The `=` case generates an implicit cast |
184 | // which is covered by the previous matcher. |
185 | unless(hasOperatorName(Name: "=" ))) |
186 | .bind(ID: "binary_op" ), |
187 | Action: 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, Result: &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(Ctx: 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(Ctx: 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(I1: Lower, I2: From.Lower) <= 0 && |
230 | llvm::APSInt::compareValues(I1: Upper, I2: From.Upper) >= 0; |
231 | } |
232 | |
233 | bool contains(const llvm::APSInt &Value) const { |
234 | return llvm::APSInt::compareValues(I1: Lower, I2: Value) <= 0 && |
235 | llvm::APSInt::compareValues(I1: Upper, I2: 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: 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 {.Lower: LowerValue, .Upper: UpperValue}; |
264 | } |
265 | assert(T.isInteger() && "Unexpected builtin type" ); |
266 | uint64_t TypeSize = Context.getTypeSize(&T); |
267 | bool IsUnsignedInteger = T.isUnsignedInteger(); |
268 | return {.Lower: llvm::APSInt::getMinValue(numBits: TypeSize, Unsigned: IsUnsignedInteger), |
269 | .Upper: llvm::APSInt::getMaxValue(numBits: TypeSize, Unsigned: IsUnsignedInteger)}; |
270 | } |
271 | |
272 | static bool isWideEnoughToHold(const ASTContext &Context, |
273 | const BuiltinType &FromType, |
274 | const BuiltinType &ToType) { |
275 | IntegerRange FromIntegerRange = createFromType(Context, T: FromType); |
276 | IntegerRange ToIntegerRange = createFromType(Context, T: ToType); |
277 | return ToIntegerRange.contains(From: FromIntegerRange); |
278 | } |
279 | |
280 | static bool isWideEnoughToHold(const ASTContext &Context, |
281 | const llvm::APSInt &IntegerConstant, |
282 | const BuiltinType &ToType) { |
283 | IntegerRange ToIntegerRange = createFromType(Context, T: ToType); |
284 | return ToIntegerRange.contains(Value: 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(T: 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, RM: llvm::APFloat::rmTowardZero, IsExact: &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, Radix: 10); |
307 | if (HexBits > 0) { |
308 | Str.append(RHS: " (0x" ); |
309 | llvm::SmallString<32> HexValue; |
310 | Value.toStringUnsigned(Str&: HexValue, Radix: 16); |
311 | for (size_t I = HexValue.size(); I < (HexBits / 4); ++I) |
312 | Str.append(RHS: "0" ); |
313 | Str.append(RHS: HexValue); |
314 | Str.append(RHS: ")" ); |
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(Loc: SourceLoc, Description: "narrowing conversion from %0 to %1" ) |
338 | << getUnqualifiedType(E: Rhs) << getUnqualifiedType(E: Lhs); |
339 | } |
340 | |
341 | void NarrowingConversionsCheck::diagNarrowTypeToSignedInt( |
342 | SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs) { |
343 | diag(Loc: SourceLoc, Description: "narrowing conversion from %0 to signed type %1 is " |
344 | "implementation-defined" ) |
345 | << getUnqualifiedType(E: Rhs) << getUnqualifiedType(E: Lhs); |
346 | } |
347 | |
348 | void NarrowingConversionsCheck::diagNarrowIntegerConstant( |
349 | SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs, |
350 | const llvm::APSInt &Value) { |
351 | diag(Loc: SourceLoc, |
352 | Description: "narrowing conversion from constant value %0 of type %1 to %2" ) |
353 | << getValueAsString(Value, /*NoHex*/ HexBits: 0) << getUnqualifiedType(E: Rhs) |
354 | << getUnqualifiedType(E: 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(Loc: SourceLoc, Description: "narrowing conversion from constant value %0 of type %1 " |
361 | "to signed type %2 is implementation-defined" ) |
362 | << getValueAsString(Value, HexBits) << getUnqualifiedType(E: Rhs) |
363 | << getUnqualifiedType(E: Lhs); |
364 | } |
365 | |
366 | void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc, |
367 | const Expr &Lhs, |
368 | const Expr &Rhs) { |
369 | diag(Loc: SourceLoc, Description: "narrowing conversion from constant %0 to %1" ) |
370 | << getUnqualifiedType(E: Rhs) << getUnqualifiedType(E: Lhs); |
371 | } |
372 | |
373 | void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc, |
374 | const Expr &Lhs, |
375 | const Expr &Rhs) { |
376 | diag(Loc: SourceLoc, Description: "constant value should be of type of type %0 instead of %1" ) |
377 | << getUnqualifiedType(E: Lhs) << getUnqualifiedType(E: Rhs); |
378 | } |
379 | |
380 | void NarrowingConversionsCheck::diagNarrowTypeOrConstant( |
381 | const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, |
382 | const Expr &Rhs) { |
383 | APValue Constant = getConstantExprValue(Ctx: Context, E: Rhs); |
384 | if (Constant.isInt()) |
385 | return diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Value: 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(E: 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(E: 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, E: Rhs, Value&: IntegerConstant)) { |
417 | if (!isWideEnoughToHold(Context, IntegerConstant, ToType: *ToType)) |
418 | diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs, |
419 | Value: IntegerConstant, |
420 | HexBits: Context.getTypeSize(FromType)); |
421 | return; |
422 | } |
423 | if (!isWideEnoughToHold(Context, FromType: *FromType, ToType: *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(E: Lhs); |
443 | llvm::APSInt IntegerConstant; |
444 | if (getIntegerConstantExprValue(Context, E: Rhs, Value&: IntegerConstant)) { |
445 | if (!isWideEnoughToHold(Context, IntegerConstant, ToType: *ToType)) |
446 | diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Value: IntegerConstant); |
447 | return; |
448 | } |
449 | |
450 | const BuiltinType *FromType = getBuiltinType(E: Rhs); |
451 | if (isWarningInhibitedByEquivalentSize(Context, FromType: *FromType, ToType: *ToType)) |
452 | return; |
453 | if (!isWideEnoughToHold(Context, FromType: *FromType, ToType: *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, E: Rhs, Value&: FloatConstant)) { |
463 | if (!isFloatExactlyRepresentable(Context, FloatConstant, DestType: 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(E: Rhs); |
473 | const BuiltinType *ToType = getBuiltinType(E: Lhs); |
474 | if (isWarningInhibitedByEquivalentSize(Context, FromType: *FromType, ToType: *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(E: Lhs); |
501 | APValue Constant = getConstantExprValue(Ctx: Context, E: 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(ToSemantics: Context.getFloatTypeSemantics(T: ToType->desugar()), |
510 | RM: llvm::APFloatBase::rmNearestTiesToEven, losesInfo: &UnusedLosesInfo); |
511 | if (Tmp.isInfinity()) |
512 | diagNarrowConstant(SourceLoc, Lhs, Rhs); |
513 | return; |
514 | } |
515 | const BuiltinType *FromType = getBuiltinType(E: Rhs); |
516 | if (ToType->getKind() < FromType->getKind()) |
517 | diagNarrowType(SourceLoc, Lhs, Rhs); |
518 | } |
519 | } |
520 | |
521 | void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context, |
522 | SourceLocation SourceLoc, |
523 | const Expr &Lhs, |
524 | const Expr &Rhs) { |
525 | assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() && |
526 | "Dependent types must be check before calling this function" ); |
527 | const BuiltinType *LhsType = getBuiltinType(E: Lhs); |
528 | const BuiltinType *RhsType = getBuiltinType(E: Rhs); |
529 | if (RhsType == nullptr || LhsType == nullptr) |
530 | return; |
531 | if (LhsType == RhsType) |
532 | return; |
533 | if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger()) |
534 | return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs); |
535 | if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool) |
536 | return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs); |
537 | if (RhsType->isInteger() && LhsType->isFloatingPoint()) |
538 | return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs); |
539 | if (RhsType->isInteger() && LhsType->isInteger()) |
540 | return handleIntegralCast(Context, SourceLoc, Lhs, Rhs); |
541 | if (RhsType->isFloatingPoint() && LhsType->getKind() == BuiltinType::Bool) |
542 | return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs); |
543 | if (RhsType->isFloatingPoint() && LhsType->isInteger()) |
544 | return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs); |
545 | if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint()) |
546 | return handleFloatingCast(Context, SourceLoc, Lhs, Rhs); |
547 | } |
548 | |
549 | bool NarrowingConversionsCheck::handleConditionalOperator( |
550 | const ASTContext &Context, const Expr &Lhs, const Expr &Rhs) { |
551 | if (const auto *CO = llvm::dyn_cast<ConditionalOperator>(Val: &Rhs)) { |
552 | // We have an expression like so: `output = cond ? lhs : rhs` |
553 | // From the point of view of narrowing conversion we treat it as two |
554 | // expressions `output = lhs` and `output = rhs`. |
555 | handleBinaryOperator(Context, SourceLoc: CO->getLHS()->getExprLoc(), Lhs, |
556 | Rhs: *CO->getLHS()); |
557 | handleBinaryOperator(Context, SourceLoc: CO->getRHS()->getExprLoc(), Lhs, |
558 | Rhs: *CO->getRHS()); |
559 | return true; |
560 | } |
561 | return false; |
562 | } |
563 | |
564 | void NarrowingConversionsCheck::handleImplicitCast( |
565 | const ASTContext &Context, const ImplicitCastExpr &Cast) { |
566 | if (Cast.getExprLoc().isMacroID()) |
567 | return; |
568 | const Expr &Lhs = Cast; |
569 | const Expr &Rhs = *Cast.getSubExpr(); |
570 | if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent()) |
571 | return; |
572 | if (getBuiltinType(E: Lhs) == getBuiltinType(E: Rhs)) |
573 | return; |
574 | if (handleConditionalOperator(Context, Lhs, Rhs)) |
575 | return; |
576 | SourceLocation SourceLoc = Lhs.getExprLoc(); |
577 | switch (Cast.getCastKind()) { |
578 | case CK_BooleanToSignedIntegral: |
579 | return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs); |
580 | case CK_IntegralToBoolean: |
581 | return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs); |
582 | case CK_IntegralToFloating: |
583 | return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs); |
584 | case CK_IntegralCast: |
585 | return handleIntegralCast(Context, SourceLoc, Lhs, Rhs); |
586 | case CK_FloatingToBoolean: |
587 | return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs); |
588 | case CK_FloatingToIntegral: |
589 | return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs); |
590 | case CK_FloatingCast: |
591 | return handleFloatingCast(Context, SourceLoc, Lhs, Rhs); |
592 | default: |
593 | break; |
594 | } |
595 | } |
596 | |
597 | void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context, |
598 | const BinaryOperator &Op) { |
599 | if (Op.getBeginLoc().isMacroID()) |
600 | return; |
601 | const Expr &Lhs = *Op.getLHS(); |
602 | const Expr &Rhs = *Op.getRHS(); |
603 | if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent()) |
604 | return; |
605 | if (handleConditionalOperator(Context, Lhs, Rhs)) |
606 | return; |
607 | handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs); |
608 | } |
609 | |
610 | void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) { |
611 | if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>(ID: "binary_op" )) |
612 | return handleBinaryOperator(Context: *Result.Context, Op: *Op); |
613 | if (const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>(ID: "cast" )) |
614 | return handleImplicitCast(Context: *Result.Context, Cast: *Cast); |
615 | llvm_unreachable("must be binary operator or cast expression" ); |
616 | } |
617 | } // namespace clang::tidy::cppcoreguidelines |
618 | |