clang-tools 8.0.0

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