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"
23 namespace cppcoreguidelines {
25 NarrowingConversionsCheck::NarrowingConversionsCheck(StringRef
Name,
28 WarnOnFloatingPointNarrowingConversion(
29 Options.get(
"WarnOnFloatingPointNarrowingConversion", true)),
30 PedanticMode(Options.get(
"PedanticMode", false)) {}
34 Options.
store(Opts,
"WarnOnFloatingPointNarrowingConversion",
35 WarnOnFloatingPointNarrowingConversion);
42 const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl(
43 hasAnyName(
"::ceil",
"::std::ceil",
"::floor",
"::std::floor")))));
50 ast_type_traits::TK_AsIs,
51 implicitCastExpr(hasImplicitDestinationType(builtinType()),
52 hasSourceExpression(hasType(builtinType())),
53 unless(hasSourceExpression(IsCeilFloorCallExpr)),
54 unless(hasParent(castExpr())),
55 unless(isInTemplateInstantiation()))
61 Finder->addMatcher(binaryOperator(isAssignmentOperator(),
62 hasLHS(expr(hasType(builtinType()))),
63 hasRHS(expr(hasType(builtinType()))),
64 unless(hasRHS(IsCeilFloorCallExpr)),
65 unless(isInTemplateInstantiation()),
68 unless(hasOperatorName(
"=")))
74 return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>();
78 return E.getType().getUnqualifiedType();
82 llvm::APSInt IntegerConstant;
83 if (
E.isIntegerConstantExpr(IntegerConstant,
Ctx))
84 return APValue(IntegerConstant);
86 if (
Ctx.getLangOpts().CPlusPlus &&
E.isCXX11ConstantExpr(
Ctx, &Constant))
92 const Expr &
E, llvm::APSInt &Value) {
94 if (!Constant.isInt())
96 Value = Constant.getInt();
101 const Expr &
E, llvm::APFloat &Value) {
103 if (!Constant.isFloat())
105 Value = Constant.getFloat();
111 struct IntegerRange {
112 bool Contains(
const IntegerRange &From)
const {
113 return llvm::APSInt::compareValues(
Lower, From.Lower) <= 0 &&
114 llvm::APSInt::compareValues(
Upper, From.Upper) >= 0;
117 bool Contains(
const llvm::APSInt &Value)
const {
118 return llvm::APSInt::compareValues(
Lower, Value) <= 0 &&
119 llvm::APSInt::compareValues(
Upper, Value) >= 0;
129 const BuiltinType &T) {
130 if (T.isFloatingPoint()) {
131 unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision(
132 Context.getFloatTypeSemantics(T.desugar()));
142 llvm::APSInt UpperValue(PrecisionBits + 2,
false);
143 UpperValue.setBit(PrecisionBits);
144 llvm::APSInt LowerValue(PrecisionBits + 2,
false);
145 LowerValue.setBit(PrecisionBits);
146 LowerValue.setSignBit();
147 return {LowerValue, UpperValue};
149 assert(T.isInteger() &&
"Unexpected builtin type");
150 uint64_t TypeSize = Context.getTypeSize(&T);
151 bool IsUnsignedInteger = T.isUnsignedInteger();
152 return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger),
153 llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)};
157 const BuiltinType &FromType,
158 const BuiltinType &ToType) {
159 IntegerRange FromIntegerRange =
createFromType(Context, FromType);
161 return ToIntegerRange.Contains(FromIntegerRange);
165 const llvm::APSInt &IntegerConstant,
166 const BuiltinType &ToType) {
168 return ToIntegerRange.Contains(IntegerConstant);
173 llvm::SmallString<64> Str;
174 Value.toString(Str, 10);
177 llvm::SmallString<32> HexValue;
178 Value.toStringUnsigned(HexValue, 16);
179 for (
size_t I = HexValue.size(); I < (HexBits / 4); ++I)
181 Str.append(HexValue);
187 void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,
190 diag(SourceLoc,
"narrowing conversion from %0 to %1")
194 void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(
195 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs) {
196 diag(SourceLoc,
"narrowing conversion from %0 to signed type %1 is "
197 "implementation-defined")
201 void NarrowingConversionsCheck::diagNarrowIntegerConstant(
202 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs,
203 const llvm::APSInt &Value) {
205 "narrowing conversion from constant value %0 of type %1 to %2")
210 void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt(
211 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs,
212 const llvm::APSInt &Value,
const uint64_t HexBits) {
213 diag(SourceLoc,
"narrowing conversion from constant value %0 of type %1 "
214 "to signed type %2 is implementation-defined")
219 void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
222 diag(SourceLoc,
"narrowing conversion from constant %0 to %1")
226 void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
229 diag(SourceLoc,
"constant value should be of type of type %0 instead of %1")
233 void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
234 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
238 return diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs,
Constant.getInt());
240 return diagNarrowConstant(SourceLoc, Lhs, Rhs);
241 return diagNarrowType(SourceLoc, Lhs, Rhs);
244 void NarrowingConversionsCheck::handleIntegralCast(
const ASTContext &Context,
245 SourceLocation SourceLoc,
254 if (ToType->isUnsignedInteger())
257 llvm::APSInt IntegerConstant;
260 diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs, IntegerConstant,
261 Context.getTypeSize(FromType));
265 diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs);
268 void NarrowingConversionsCheck::handleIntegralToBoolean(
269 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
278 void NarrowingConversionsCheck::handleIntegralToFloating(
279 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
282 llvm::APSInt IntegerConstant;
285 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);
290 diagNarrowType(SourceLoc, Lhs, Rhs);
293 void NarrowingConversionsCheck::handleFloatingToIntegral(
294 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
296 llvm::APFloat FloatConstant(0.0);
300 return diagNarrowType(SourceLoc, Lhs, Rhs);
302 QualType DestType = Lhs.getType();
303 unsigned DestWidth = Context.getIntWidth(DestType);
304 bool DestSigned = DestType->isSignedIntegerOrEnumerationType();
305 llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned);
306 bool IsExact =
false;
307 bool Overflows = FloatConstant.convertToInteger(
308 Result, llvm::APFloat::rmTowardZero, &IsExact) &
309 llvm::APFloat::opInvalidOp;
311 if (Overflows || !IsExact)
312 return diagNarrowConstant(SourceLoc, Lhs, Rhs);
315 return diagConstantCast(SourceLoc, Lhs, Rhs);
318 void NarrowingConversionsCheck::handleFloatingToBoolean(
319 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
321 return diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs);
324 void NarrowingConversionsCheck::handleBooleanToSignedIntegral(
325 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
334 void NarrowingConversionsCheck::handleFloatingCast(
const ASTContext &Context,
335 SourceLocation SourceLoc,
338 if (WarnOnFloatingPointNarrowingConversion) {
346 llvm::APFloat Tmp =
Constant.getFloat();
347 bool UnusedLosesInfo;
348 Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()),
349 llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo);
350 if (Tmp.isInfinity())
351 diagNarrowConstant(SourceLoc, Lhs, Rhs);
355 if (ToType->getKind() < FromType->getKind())
356 diagNarrowType(SourceLoc, Lhs, Rhs);
360 void NarrowingConversionsCheck::handleBinaryOperator(
const ASTContext &Context,
361 SourceLocation SourceLoc,
364 assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&
365 "Dependent types must be check before calling this function");
368 if (RhsType ==
nullptr || LhsType ==
nullptr)
370 if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())
371 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
372 if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)
373 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
374 if (RhsType->isInteger() && LhsType->isFloatingPoint())
375 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
376 if (RhsType->isInteger() && LhsType->isInteger())
377 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
378 if (RhsType->isFloatingPoint() && LhsType->getKind() == BuiltinType::Bool)
379 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
380 if (RhsType->isFloatingPoint() && LhsType->isInteger())
381 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
382 if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())
383 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
386 bool NarrowingConversionsCheck::handleConditionalOperator(
387 const ASTContext &Context,
const Expr &Lhs,
const Expr &Rhs) {
388 if (
const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
392 handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs,
394 handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs,
401 void NarrowingConversionsCheck::handleImplicitCast(
402 const ASTContext &Context,
const ImplicitCastExpr &Cast) {
403 if (Cast.getExprLoc().isMacroID())
405 const Expr &Lhs = Cast;
406 const Expr &Rhs = *Cast.getSubExpr();
407 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
409 if (handleConditionalOperator(Context, Lhs, Rhs))
411 SourceLocation SourceLoc = Lhs.getExprLoc();
412 switch (Cast.getCastKind()) {
413 case CK_BooleanToSignedIntegral:
414 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
415 case CK_IntegralToBoolean:
416 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
417 case CK_IntegralToFloating:
418 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
419 case CK_IntegralCast:
420 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
421 case CK_FloatingToBoolean:
422 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
423 case CK_FloatingToIntegral:
424 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
425 case CK_FloatingCast:
426 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
432 void NarrowingConversionsCheck::handleBinaryOperator(
const ASTContext &Context,
433 const BinaryOperator &Op) {
434 if (Op.getBeginLoc().isMacroID())
436 const Expr &Lhs = *Op.getLHS();
437 const Expr &Rhs = *Op.getRHS();
438 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
440 if (handleConditionalOperator(Context, Lhs, Rhs))
442 handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
446 if (
const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>(
"binary_op"))
447 return handleBinaryOperator(*Result.Context, *Op);
448 if (
const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>(
"cast"))
449 return handleImplicitCast(*Result.Context, *Cast);
450 llvm_unreachable(
"must be binary operator or cast expression");