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", 1)),
30 PedanticMode(Options.get(
"PedanticMode", 0)) {}
35 const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl(
36 hasAnyName(
"::ceil",
"::std::ceil",
"::floor",
"::std::floor")))));
42 implicitCastExpr(hasImplicitDestinationType(builtinType()),
43 hasSourceExpression(hasType(builtinType())),
44 unless(hasSourceExpression(IsCeilFloorCallExpr)),
45 unless(hasParent(castExpr())),
46 unless(isInTemplateInstantiation()))
52 Finder->addMatcher(binaryOperator(isAssignmentOperator(),
53 hasLHS(expr(hasType(builtinType()))),
54 hasRHS(expr(hasType(builtinType()))),
55 unless(hasRHS(IsCeilFloorCallExpr)),
56 unless(isInTemplateInstantiation()),
59 unless(hasOperatorName(
"=")))
65 return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>();
69 return E.getType().getUnqualifiedType();
73 llvm::APSInt IntegerConstant;
74 if (E.isIntegerConstantExpr(IntegerConstant, Ctx))
75 return APValue(IntegerConstant);
77 if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant))
83 const Expr &E, llvm::APSInt &Value) {
85 if (!Constant.isInt())
87 Value = Constant.getInt();
92 const Expr &E, llvm::APFloat &Value) {
94 if (!Constant.isFloat())
96 Value = Constant.getFloat();
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;
108 bool Contains(
const llvm::APSInt &Value)
const {
109 return llvm::APSInt::compareValues(
Lower, Value) <= 0 &&
110 llvm::APSInt::compareValues(
Upper, Value) >= 0;
120 const BuiltinType &T) {
121 if (T.isFloatingPoint()) {
122 unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision(
123 Context.getFloatTypeSemantics(T.desugar()));
133 llvm::APSInt UpperValue(PrecisionBits + 2,
false);
134 UpperValue.setBit(PrecisionBits);
135 llvm::APSInt LowerValue(PrecisionBits + 2,
false);
136 LowerValue.setBit(PrecisionBits);
137 LowerValue.setSignBit();
138 return {LowerValue, UpperValue};
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)};
148 const BuiltinType &FromType,
149 const BuiltinType &ToType) {
150 IntegerRange FromIntegerRange =
createFromType(Context, FromType);
152 return ToIntegerRange.Contains(FromIntegerRange);
156 const llvm::APSInt &IntegerConstant,
157 const BuiltinType &ToType) {
159 return ToIntegerRange.Contains(IntegerConstant);
164 llvm::SmallString<64> Str;
165 Value.toString(Str, 10);
168 llvm::SmallString<32> HexValue;
169 Value.toStringUnsigned(HexValue, 16);
170 for (
size_t I = HexValue.size(); I < (HexBits / 4); ++I)
172 Str.append(HexValue);
178 void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,
181 diag(SourceLoc,
"narrowing conversion from %0 to %1")
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")
192 void NarrowingConversionsCheck::diagNarrowIntegerConstant(
193 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs,
194 const llvm::APSInt &Value) {
196 "narrowing conversion from constant value %0 of type %1 to %2")
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")
210 void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
213 diag(SourceLoc,
"narrowing conversion from constant %0 to %1")
217 void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
220 diag(SourceLoc,
"constant value should be of type of type %0 instead of %1")
224 void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
225 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
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);
235 void NarrowingConversionsCheck::handleIntegralCast(
const ASTContext &Context,
236 SourceLocation SourceLoc,
245 if (ToType->isUnsignedInteger())
248 llvm::APSInt IntegerConstant;
251 diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs, IntegerConstant,
252 Context.getTypeSize(FromType));
256 diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs);
259 void NarrowingConversionsCheck::handleIntegralToBoolean(
260 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
269 void NarrowingConversionsCheck::handleIntegralToFloating(
270 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
273 llvm::APSInt IntegerConstant;
276 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);
281 diagNarrowType(SourceLoc, Lhs, Rhs);
284 void NarrowingConversionsCheck::handleFloatingToIntegral(
285 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
287 llvm::APFloat FloatConstant(0.0);
291 return diagNarrowType(SourceLoc, Lhs, Rhs);
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;
302 if (Overflows || !IsExact)
303 return diagNarrowConstant(SourceLoc, Lhs, Rhs);
306 return diagConstantCast(SourceLoc, Lhs, Rhs);
309 void NarrowingConversionsCheck::handleFloatingToBoolean(
310 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
312 return diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs);
315 void NarrowingConversionsCheck::handleBooleanToSignedIntegral(
316 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
325 void NarrowingConversionsCheck::handleFloatingCast(
const ASTContext &Context,
326 SourceLocation SourceLoc,
329 if (WarnOnFloatingPointNarrowingConversion) {
332 if (Constant.isFloat()) {
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);
346 if (ToType->getKind() < FromType->getKind())
347 diagNarrowType(SourceLoc, Lhs, Rhs);
351 void NarrowingConversionsCheck::handleBinaryOperator(
const ASTContext &Context,
352 SourceLocation SourceLoc,
355 assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&
356 "Dependent types must be check before calling this function");
359 if (RhsType ==
nullptr || LhsType ==
nullptr)
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);
377 bool NarrowingConversionsCheck::handleConditionalOperator(
378 const ASTContext &Context,
const Expr &Lhs,
const Expr &Rhs) {
379 if (
const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
383 handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs,
385 handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs,
392 void NarrowingConversionsCheck::handleImplicitCast(
393 const ASTContext &Context,
const ImplicitCastExpr &Cast) {
394 if (Cast.getExprLoc().isMacroID())
396 const Expr &Lhs = Cast;
397 const Expr &Rhs = *Cast.getSubExpr();
398 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
400 if (handleConditionalOperator(Context, Lhs, Rhs))
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);
423 void NarrowingConversionsCheck::handleBinaryOperator(
const ASTContext &Context,
424 const BinaryOperator &Op) {
425 if (Op.getBeginLoc().isMacroID())
427 const Expr &Lhs = *Op.getLHS();
428 const Expr &Rhs = *Op.getRHS();
429 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
431 if (handleConditionalOperator(Context, Lhs, Rhs))
433 handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
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");
Base class for all clang-tidy checks.
static QualType getUnqualifiedType(const Expr &E)
static llvm::SmallString< 64 > getValueAsString(const llvm::APSInt &Value, uint64_t HexBits)
static IntegerRange createFromType(const ASTContext &Context, const BuiltinType &T)
static constexpr llvm::StringLiteral Name
static bool getFloatingConstantExprValue(const ASTContext &Context, const Expr &E, llvm::APFloat &Value)
static const BuiltinType * getBuiltinType(const Expr &E)
static bool isWideEnoughToHold(const ASTContext &Context, const BuiltinType &FromType, const BuiltinType &ToType)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static bool getIntegerConstantExprValue(const ASTContext &Context, const Expr &E, llvm::APSInt &Value)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E)
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.