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