clang-tools 10.0.0

NarrowingConversionsCheck.cpp
Go to the documentation of this file.
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 "clang/AST/ASTContext.h"
11 #include "clang/AST/Type.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "llvm/ADT/APSInt.h"
14 #include "llvm/ADT/SmallString.h"
15 #include "llvm/ADT/SmallVector.h"
16
17 #include <cstdint>
18
19 using namespace clang::ast_matchers;
20
21 namespace clang {
22 namespace tidy {
23 namespace cppcoreguidelines {
24
25 NarrowingConversionsCheck::NarrowingConversionsCheck(StringRef Name,
26 ClangTidyContext *Context)
27 : ClangTidyCheck(Name, Context),
28 WarnOnFloatingPointNarrowingConversion(
29 Options.get("WarnOnFloatingPointNarrowingConversion", 1)),
30 PedanticMode(Options.get("PedanticMode", 0)) {}
31
32 void NarrowingConversionsCheck::registerMatchers(MatchFinder *Finder) {
33 // ceil() and floor() are guaranteed to return integers, even though the type
34 // is not integral.
35 const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl(
36 hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor")))));
37
38 // Casts:
39 // i = 0.5;
40 // void f(int); f(0.5);
41 Finder->addMatcher(
42 implicitCastExpr(hasImplicitDestinationType(builtinType()),
43 hasSourceExpression(hasType(builtinType())),
44 unless(hasSourceExpression(IsCeilFloorCallExpr)),
45 unless(hasParent(castExpr())),
46 unless(isInTemplateInstantiation()))
47 .bind("cast"),
48 this);
49
50 // Binary operators:
51 // i += 0.5;
52 Finder->addMatcher(binaryOperator(isAssignmentOperator(),
53 hasLHS(expr(hasType(builtinType()))),
54 hasRHS(expr(hasType(builtinType()))),
55 unless(hasRHS(IsCeilFloorCallExpr)),
56 unless(isInTemplateInstantiation()),
57 // The `=` case generates an implicit cast
58 // which is covered by the previous matcher.
59 unless(hasOperatorName("=")))
60 .bind("binary_op"),
61 this);
62 }
63
64 static const BuiltinType *getBuiltinType(const Expr &E) {
65 return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>();
66 }
67
68 static QualType getUnqualifiedType(const Expr &E) {
69 return E.getType().getUnqualifiedType();
70 }
71
72 static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E) {
73 llvm::APSInt IntegerConstant;
74 if (E.isIntegerConstantExpr(IntegerConstant, Ctx))
75 return APValue(IntegerConstant);
76 APValue Constant;
77 if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant))
78 return Constant;
79 return {};
80 }
81
82 static bool getIntegerConstantExprValue(const ASTContext &Context,
83 const Expr &E, llvm::APSInt &Value) {
84 APValue Constant = getConstantExprValue(Context, E);
85 if (!Constant.isInt())
86 return false;
87 Value = Constant.getInt();
88 return true;
89 }
90
91 static bool getFloatingConstantExprValue(const ASTContext &Context,
92 const Expr &E, llvm::APFloat &Value) {
93 APValue Constant = getConstantExprValue(Context, E);
94 if (!Constant.isFloat())
95 return false;
96 Value = Constant.getFloat();
97 return true;
98 }
99
100 namespace {
101
102 struct IntegerRange {
103 bool Contains(const IntegerRange &From) const {
104 return llvm::APSInt::compareValues(Lower, From.Lower) <= 0 &&
105 llvm::APSInt::compareValues(Upper, From.Upper) >= 0;
106 }
107
108 bool Contains(const llvm::APSInt &Value) const {
109 return llvm::APSInt::compareValues(Lower, Value) <= 0 &&
110 llvm::APSInt::compareValues(Upper, Value) >= 0;
111 }
112
113 llvm::APSInt Lower;
114 llvm::APSInt Upper;
115 };
116
117 } // namespace
118
119 static IntegerRange createFromType(const ASTContext &Context,
120 const BuiltinType &T) {
121 if (T.isFloatingPoint()) {
122 unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision(
123 Context.getFloatTypeSemantics(T.desugar()));
124 // Contrary to two's complement integer, floating point values are
125 // symmetric and have the same number of positive and negative values.
126 // The range of valid integers for a floating point value is:
127 // [-2^PrecisionBits, 2^PrecisionBits]
128
129 // Values are created with PrecisionBits plus two bits:
130 // - One to express the missing negative value of 2's complement
131 // representation.
132 // - One for the sign.
133 llvm::APSInt UpperValue(PrecisionBits + 2, /*isUnsigned*/ false);
134 UpperValue.setBit(PrecisionBits);
135 llvm::APSInt LowerValue(PrecisionBits + 2, /*isUnsigned*/ false);
136 LowerValue.setBit(PrecisionBits);
137 LowerValue.setSignBit();
138 return {LowerValue, UpperValue};
139 }
140 assert(T.isInteger() && "Unexpected builtin type");
141 uint64_t TypeSize = Context.getTypeSize(&T);
142 bool IsUnsignedInteger = T.isUnsignedInteger();
143 return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger),
144 llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)};
145 }
146
147 static bool isWideEnoughToHold(const ASTContext &Context,
148 const BuiltinType &FromType,
149 const BuiltinType &ToType) {
150 IntegerRange FromIntegerRange = createFromType(Context, FromType);
151 IntegerRange ToIntegerRange = createFromType(Context, ToType);
152 return ToIntegerRange.Contains(FromIntegerRange);
153 }
154
155 static bool isWideEnoughToHold(const ASTContext &Context,
156 const llvm::APSInt &IntegerConstant,
157 const BuiltinType &ToType) {
158 IntegerRange ToIntegerRange = createFromType(Context, ToType);
159 return ToIntegerRange.Contains(IntegerConstant);
160 }
161
162 static llvm::SmallString<64> getValueAsString(const llvm::APSInt &Value,
163 uint64_t HexBits) {
164 llvm::SmallString<64> Str;
165 Value.toString(Str, 10);
166 if (HexBits > 0) {
167 Str.append(" (0x");
168 llvm::SmallString<32> HexValue;
169 Value.toStringUnsigned(HexValue, 16);
170 for (size_t I = HexValue.size(); I < (HexBits / 4); ++I)
171 Str.append("0");
172 Str.append(HexValue);
173 Str.append(")");
174 }
175 return Str;
176 }
177
178 void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,
179 const Expr &Lhs,
180 const Expr &Rhs) {
181 diag(SourceLoc, "narrowing conversion from %0 to %1")
182 << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
183 }
184
185 void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(
186 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs) {
187 diag(SourceLoc, "narrowing conversion from %0 to signed type %1 is "
188 "implementation-defined")
189 << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
190 }
191
192 void NarrowingConversionsCheck::diagNarrowIntegerConstant(
193 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
194 const llvm::APSInt &Value) {
195 diag(SourceLoc,
196 "narrowing conversion from constant value %0 of type %1 to %2")
197 << getValueAsString(Value, /*NoHex*/ 0) << getUnqualifiedType(Rhs)
198 << getUnqualifiedType(Lhs);
199 }
200
201 void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt(
202 SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs,
203 const llvm::APSInt &Value, const uint64_t HexBits) {
204 diag(SourceLoc, "narrowing conversion from constant value %0 of type %1 "
205 "to signed type %2 is implementation-defined")
206 << getValueAsString(Value, HexBits) << getUnqualifiedType(Rhs)
207 << getUnqualifiedType(Lhs);
208 }
209
210 void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
211 const Expr &Lhs,
212 const Expr &Rhs) {
213 diag(SourceLoc, "narrowing conversion from constant %0 to %1")
214 << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs);
215 }
216
217 void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
218 const Expr &Lhs,
219 const Expr &Rhs) {
220 diag(SourceLoc, "constant value should be of type of type %0 instead of %1")
221 << getUnqualifiedType(Lhs) << getUnqualifiedType(Rhs);
222 }
223
224 void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
225 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
226 const Expr &Rhs) {
227 APValue Constant = getConstantExprValue(Context, Rhs);
228 if (Constant.isInt())
229 return diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Constant.getInt());
230 if (Constant.isFloat())
231 return diagNarrowConstant(SourceLoc, Lhs, Rhs);
232 return diagNarrowType(SourceLoc, Lhs, Rhs);
233 }
234
235 void NarrowingConversionsCheck::handleIntegralCast(const ASTContext &Context,
236 SourceLocation SourceLoc,
237 const Expr &Lhs,
238 const Expr &Rhs) {
239 const BuiltinType *ToType = getBuiltinType(Lhs);
240 // From [conv.integral]p7.3.8:
241 // Conversions to unsigned integer is well defined so no warning is issued.
242 // "The resulting value is the smallest unsigned value equal to the source
243 // value modulo 2^n where n is the number of bits used to represent the
244 // destination type."
245 if (ToType->isUnsignedInteger())
246 return;
247 const BuiltinType *FromType = getBuiltinType(Rhs);
248 llvm::APSInt IntegerConstant;
249 if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
250 if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
251 diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs, IntegerConstant,
252 Context.getTypeSize(FromType));
253 return;
254 }
255 if (!isWideEnoughToHold(Context, *FromType, *ToType))
256 diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs);
257 }
258
259 void NarrowingConversionsCheck::handleIntegralToBoolean(
260 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
261 const Expr &Rhs) {
262 // Conversion from Integral to Bool value is well defined.
263
264 // We keep this function (even if it is empty) to make sure that
265 // handleImplicitCast and handleBinaryOperator are symmetric in their behavior
266 // and handle the same cases.
267 }
268
269 void NarrowingConversionsCheck::handleIntegralToFloating(
270 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
271 const Expr &Rhs) {
272 const BuiltinType *ToType = getBuiltinType(Lhs);
273 llvm::APSInt IntegerConstant;
274 if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) {
275 if (!isWideEnoughToHold(Context, IntegerConstant, *ToType))
276 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);
277 return;
278 }
279 const BuiltinType *FromType = getBuiltinType(Rhs);
280 if (!isWideEnoughToHold(Context, *FromType, *ToType))
281 diagNarrowType(SourceLoc, Lhs, Rhs);
282 }
283
284 void NarrowingConversionsCheck::handleFloatingToIntegral(
285 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
286 const Expr &Rhs) {
287 llvm::APFloat FloatConstant(0.0);
288
289 // We always warn when Rhs is non-constexpr.
290 if (!getFloatingConstantExprValue(Context, Rhs, FloatConstant))
291 return diagNarrowType(SourceLoc, Lhs, Rhs);
292
293 QualType DestType = Lhs.getType();
294 unsigned DestWidth = Context.getIntWidth(DestType);
295 bool DestSigned = DestType->isSignedIntegerOrEnumerationType();
296 llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned);
297 bool IsExact = false;
298 bool Overflows = FloatConstant.convertToInteger(
299 Result, llvm::APFloat::rmTowardZero, &IsExact) &
300 llvm::APFloat::opInvalidOp;
301 // We warn iff the constant floating point value is not exactly representable.
302 if (Overflows || !IsExact)
303 return diagNarrowConstant(SourceLoc, Lhs, Rhs);
304
305 if (PedanticMode)
306 return diagConstantCast(SourceLoc, Lhs, Rhs);
307 }
308
309 void NarrowingConversionsCheck::handleFloatingToBoolean(
310 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
311 const Expr &Rhs) {
312 return diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs);
313 }
314
315 void NarrowingConversionsCheck::handleBooleanToSignedIntegral(
316 const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs,
317 const Expr &Rhs) {
318 // Conversion from Bool to SignedIntegral value is well defined.
319
320 // We keep this function (even if it is empty) to make sure that
321 // handleImplicitCast and handleBinaryOperator are symmetric in their behavior
322 // and handle the same cases.
323 }
324
325 void NarrowingConversionsCheck::handleFloatingCast(const ASTContext &Context,
326 SourceLocation SourceLoc,
327 const Expr &Lhs,
328 const Expr &Rhs) {
329 if (WarnOnFloatingPointNarrowingConversion) {
330 const BuiltinType *ToType = getBuiltinType(Lhs);
331 APValue Constant = getConstantExprValue(Context, Rhs);
332 if (Constant.isFloat()) {
333 // From [dcl.init.list]p7.2:
334 // Floating point constant narrowing only takes place when the value is
335 // not within destination range. We convert the value to the destination
336 // type and check if the resulting value is infinity.
337 llvm::APFloat Tmp = Constant.getFloat();
338 bool UnusedLosesInfo;
339 Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()),
340 llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo);
341 if (Tmp.isInfinity())
342 diagNarrowConstant(SourceLoc, Lhs, Rhs);
343 return;
344 }
345 const BuiltinType *FromType = getBuiltinType(Rhs);
346 if (ToType->getKind() < FromType->getKind())
347 diagNarrowType(SourceLoc, Lhs, Rhs);
348 }
349 }
350
351 void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
352 SourceLocation SourceLoc,
353 const Expr &Lhs,
354 const Expr &Rhs) {
355 assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&
356 "Dependent types must be check before calling this function");
357 const BuiltinType *LhsType = getBuiltinType(Lhs);
358 const BuiltinType *RhsType = getBuiltinType(Rhs);
359 if (RhsType == nullptr || LhsType == nullptr)
360 return;
361 if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())
362 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
363 if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)
364 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
365 if (RhsType->isInteger() && LhsType->isFloatingPoint())
366 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
367 if (RhsType->isInteger() && LhsType->isInteger())
368 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
369 if (RhsType->isFloatingPoint() && LhsType->getKind() == BuiltinType::Bool)
370 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
371 if (RhsType->isFloatingPoint() && LhsType->isInteger())
372 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
373 if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())
374 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
375 }
376
377 bool NarrowingConversionsCheck::handleConditionalOperator(
378 const ASTContext &Context, const Expr &Lhs, const Expr &Rhs) {
379 if (const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
380 // We have an expression like so: `output = cond ? lhs : rhs`
381 // From the point of view of narrowing conversion we treat it as two
382 // expressions `output = lhs` and `output = rhs`.
383 handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs,
384 *CO->getLHS());
385 handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs,
386 *CO->getRHS());
387 return true;
388 }
389 return false;
390 }
391
392 void NarrowingConversionsCheck::handleImplicitCast(
393 const ASTContext &Context, const ImplicitCastExpr &Cast) {
394 if (Cast.getExprLoc().isMacroID())
395 return;
396 const Expr &Lhs = Cast;
397 const Expr &Rhs = *Cast.getSubExpr();
398 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
399 return;
400 if (handleConditionalOperator(Context, Lhs, Rhs))
401 return;
402 SourceLocation SourceLoc = Lhs.getExprLoc();
403 switch (Cast.getCastKind()) {
404 case CK_BooleanToSignedIntegral:
405 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
406 case CK_IntegralToBoolean:
407 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
408 case CK_IntegralToFloating:
409 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
410 case CK_IntegralCast:
411 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
412 case CK_FloatingToBoolean:
413 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
414 case CK_FloatingToIntegral:
415 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
416 case CK_FloatingCast:
417 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
418 default:
419 break;
420 }
421 }
422
423 void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context,
424 const BinaryOperator &Op) {
425 if (Op.getBeginLoc().isMacroID())
426 return;
427 const Expr &Lhs = *Op.getLHS();
428 const Expr &Rhs = *Op.getRHS();
429 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
430 return;
431 if (handleConditionalOperator(Context, Lhs, Rhs))
432 return;
433 handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
434 }
435
436 void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) {
437 if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("binary_op"))
438 return handleBinaryOperator(*Result.Context, *Op);
439 if (const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast"))
440 return handleImplicitCast(*Result.Context, *Cast);
441 llvm_unreachable("must be binary operator or cast expression");
442 }
443
444 } // namespace cppcoreguidelines
445 } // namespace tidy
446 } // namespace clang
clang::ast_matchers
Definition: AbseilMatcher.h:14
Upper
llvm::APSInt Upper
Definition: NarrowingConversionsCheck.cpp:114
Lower
llvm::APSInt Lower
Definition: NarrowingConversionsCheck.cpp:113
NarrowingConversionsCheck.h
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:47
clang::tidy::cppcoreguidelines::getUnqualifiedType
static QualType getUnqualifiedType(const Expr &E)
Definition: NarrowingConversionsCheck.cpp:68
Ctx
Context Ctx
Definition: TUScheduler.cpp:237
clang::tidy::cppcoreguidelines::getValueAsString
static llvm::SmallString< 64 > getValueAsString(const llvm::APSInt &Value, uint64_t HexBits)
Definition: NarrowingConversionsCheck.cpp:162
clang::tidy::cppcoreguidelines::createFromType
static IntegerRange createFromType(const ASTContext &Context, const BuiltinType &T)
Definition: NarrowingConversionsCheck.cpp:119
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::cppcoreguidelines::getFloatingConstantExprValue
static bool getFloatingConstantExprValue(const ASTContext &Context, const Expr &E, llvm::APFloat &Value)
Definition: NarrowingConversionsCheck.cpp:91
clang::tidy::cppcoreguidelines::getBuiltinType
static const BuiltinType * getBuiltinType(const Expr &E)
Definition: NarrowingConversionsCheck.cpp:64
clang::tidy::cppcoreguidelines::isWideEnoughToHold
static bool isWideEnoughToHold(const ASTContext &Context, const BuiltinType &FromType, const BuiltinType &ToType)
Definition: NarrowingConversionsCheck.cpp:147
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:77
E
const Expr * E
Definition: AvoidBindCheck.cpp:82
clang::tidy::cppcoreguidelines::NarrowingConversionsCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: NarrowingConversionsCheck.cpp:436
clang::tidy::cppcoreguidelines::getIntegerConstantExprValue
static bool getIntegerConstantExprValue(const ASTContext &Context, const Expr &E, llvm::APSInt &Value)
Definition: NarrowingConversionsCheck.cpp:82
clang::tidy::cppcoreguidelines::NarrowingConversionsCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: NarrowingConversionsCheck.cpp:32
clang::tidy::cppcoreguidelines::getConstantExprValue
static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E)
Definition: NarrowingConversionsCheck.cpp:72
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidyCheck.cpp:21