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/SmallString.h"
18#include "llvm/ADT/SmallVector.h"
29 WarnOnIntegerNarrowingConversion(
30 Options.get(
"WarnOnIntegerNarrowingConversion", true)),
31 WarnOnIntegerToFloatingPointNarrowingConversion(
32 Options.get(
"WarnOnIntegerToFloatingPointNarrowingConversion", true)),
33 WarnOnFloatingPointNarrowingConversion(
34 Options.get(
"WarnOnFloatingPointNarrowingConversion", true)),
35 WarnWithinTemplateInstantiation(
36 Options.get(
"WarnWithinTemplateInstantiation", false)),
37 WarnOnEquivalentBitWidth(Options.get(
"WarnOnEquivalentBitWidth", true)),
38 IgnoreConversionFromTypes(Options.get(
"IgnoreConversionFromTypes",
"")),
39 PedanticMode(Options.get(
"PedanticMode", false)) {}
44 WarnOnIntegerNarrowingConversion);
45 Options.
store(Opts,
"WarnOnIntegerToFloatingPointNarrowingConversion",
46 WarnOnIntegerToFloatingPointNarrowingConversion);
47 Options.
store(Opts,
"WarnOnFloatingPointNarrowingConversion",
48 WarnOnFloatingPointNarrowingConversion);
50 WarnWithinTemplateInstantiation);
51 Options.
store(Opts,
"WarnOnEquivalentBitWidth", WarnOnEquivalentBitWidth);
52 Options.
store(Opts,
"IgnoreConversionFromTypes", IgnoreConversionFromTypes);
57 assert(
Node.isBitField());
58 const ASTContext &Ctx =
Node.getASTContext();
59 unsigned IntBitWidth = Ctx.getIntWidth(Ctx.IntTy);
60 unsigned CurrentBitWidth =
Node.getBitWidthValue(Ctx);
61 return IntBitWidth == CurrentBitWidth;
67 const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl(
68 hasAnyName(
"::ceil",
"::std::ceil",
"::floor",
"::std::floor")))));
73 const auto IsConversionFromIgnoredType = hasType(namedDecl(
84 const auto IsIgnoredTypeTwoLevelsDeep =
85 anyOf(IsConversionFromIgnoredType,
86 binaryOperator(hasOperands(IsConversionFromIgnoredType,
87 hasType(isInteger()))));
123 const auto ImplicitIntWidenedBitfieldValue = implicitCastExpr(
124 hasCastKind(CK_IntegralCast), hasType(asString(
"int")),
125 has(castExpr(hasCastKind(CK_LValueToRValue),
126 has(ignoringParens(memberExpr(hasDeclaration(
127 fieldDecl(isBitField(), unless(hasIntBitwidth())))))))));
133 traverse(TK_AsIs, implicitCastExpr(
134 hasImplicitDestinationType(
135 hasUnqualifiedDesugaredType(builtinType())),
136 hasSourceExpression(hasType(
137 hasUnqualifiedDesugaredType(builtinType()))),
138 unless(hasSourceExpression(IsCeilFloorCallExpr)),
139 unless(hasParent(castExpr())),
140 WarnWithinTemplateInstantiation
142 : stmt(unless(isInTemplateInstantiation())),
143 IgnoreConversionFromTypes.empty()
145 : castExpr(unless(hasSourceExpression(
146 IsIgnoredTypeTwoLevelsDeep))),
147 unless(ImplicitIntWidenedBitfieldValue))
155 isAssignmentOperator(),
156 hasLHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))),
157 hasRHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))),
158 unless(hasRHS(IsCeilFloorCallExpr)),
159 WarnWithinTemplateInstantiation
161 : binaryOperator(unless(isInTemplateInstantiation())),
162 IgnoreConversionFromTypes.empty()
164 : binaryOperator(unless(hasRHS(IsIgnoredTypeTwoLevelsDeep))),
167 unless(hasOperatorName(
"=")))
173 return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>();
177 return E.getType().getUnqualifiedType();
181 if (
auto IntegerConstant =
E.getIntegerConstantExpr(Ctx))
182 return APValue(*IntegerConstant);
184 if (Ctx.getLangOpts().CPlusPlus &&
E.isCXX11ConstantExpr(Ctx, &Constant))
190 const Expr &
E, llvm::APSInt &Value) {
192 if (!Constant.isInt())
194 Value = Constant.getInt();
199 const Expr &
E, llvm::APFloat &Value) {
201 if (!Constant.isFloat())
203 Value = Constant.getFloat();
210 bool contains(
const IntegerRange &From)
const {
211 return llvm::APSInt::compareValues(Lower, From.Lower) <= 0 &&
212 llvm::APSInt::compareValues(Upper, From.Upper) >= 0;
215 bool contains(
const llvm::APSInt &Value)
const {
216 return llvm::APSInt::compareValues(Lower, Value) <= 0 &&
217 llvm::APSInt::compareValues(Upper, Value) >= 0;
227 const BuiltinType &T) {
228 if (T.isFloatingPoint()) {
229 unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision(
230 Context.getFloatTypeSemantics(T.desugar()));
240 llvm::APSInt UpperValue(PrecisionBits + 2,
false);
241 UpperValue.setBit(PrecisionBits);
242 llvm::APSInt LowerValue(PrecisionBits + 2,
false);
243 LowerValue.setBit(PrecisionBits);
244 LowerValue.setSignBit();
245 return {LowerValue, UpperValue};
247 assert(T.isInteger() &&
"Unexpected builtin type");
248 uint64_t TypeSize = Context.getTypeSize(&T);
249 bool IsUnsignedInteger = T.isUnsignedInteger();
250 return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger),
251 llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)};
255 const BuiltinType &FromType,
256 const BuiltinType &ToType) {
257 IntegerRange FromIntegerRange =
createFromType(Context, FromType);
259 return ToIntegerRange.contains(FromIntegerRange);
263 const llvm::APSInt &IntegerConstant,
264 const BuiltinType &ToType) {
266 return ToIntegerRange.contains(IntegerConstant);
273 const llvm::APFloat &FloatConstant,
274 const QualType &DestType) {
275 unsigned DestWidth = Context.getIntWidth(DestType);
276 bool DestSigned = DestType->isSignedIntegerOrEnumerationType();
277 llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned);
278 bool IsExact =
false;
279 bool Overflows = FloatConstant.convertToInteger(
280 Result, llvm::APFloat::rmTowardZero, &IsExact) &
281 llvm::APFloat::opInvalidOp;
282 return !Overflows && IsExact;
287 llvm::SmallString<64> Str;
288 Value.toString(Str, 10);
291 llvm::SmallString<32> HexValue;
292 Value.toStringUnsigned(HexValue, 16);
293 for (
size_t I = HexValue.size(); I < (HexBits / 4); ++I)
295 Str.append(HexValue);
301bool NarrowingConversionsCheck::isWarningInhibitedByEquivalentSize(
302 const ASTContext &Context,
const BuiltinType &FromType,
303 const BuiltinType &ToType)
const {
306 if (!WarnOnEquivalentBitWidth) {
307 uint64_t FromTypeSize = Context.getTypeSize(&FromType);
308 uint64_t ToTypeSize = Context.getTypeSize(&ToType);
309 if (FromTypeSize == ToTypeSize) {
316void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,
319 diag(SourceLoc,
"narrowing conversion from %0 to %1")
323void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(
324 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs) {
325 diag(SourceLoc,
"narrowing conversion from %0 to signed type %1 is "
326 "implementation-defined")
330void NarrowingConversionsCheck::diagNarrowIntegerConstant(
331 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs,
332 const llvm::APSInt &Value) {
334 "narrowing conversion from constant value %0 of type %1 to %2")
339void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt(
340 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs,
341 const llvm::APSInt &Value,
const uint64_t HexBits) {
342 diag(SourceLoc,
"narrowing conversion from constant value %0 of type %1 "
343 "to signed type %2 is implementation-defined")
348void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
351 diag(SourceLoc,
"narrowing conversion from constant %0 to %1")
355void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
358 diag(SourceLoc,
"constant value should be of type of type %0 instead of %1")
362void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
363 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
367 return diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs,
Constant.getInt());
369 return diagNarrowConstant(SourceLoc, Lhs, Rhs);
370 return diagNarrowType(SourceLoc, Lhs, Rhs);
373void NarrowingConversionsCheck::handleIntegralCast(
const ASTContext &Context,
374 SourceLocation SourceLoc,
377 if (WarnOnIntegerNarrowingConversion) {
384 if (ToType->isUnsignedInteger())
390 if (!WarnOnEquivalentBitWidth) {
391 uint64_t FromTypeSize = Context.getTypeSize(FromType);
392 uint64_t ToTypeSize = Context.getTypeSize(ToType);
393 if (FromTypeSize == ToTypeSize)
397 llvm::APSInt IntegerConstant;
400 diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs,
402 Context.getTypeSize(FromType));
406 diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs);
410void NarrowingConversionsCheck::handleIntegralToBoolean(
411 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
420void NarrowingConversionsCheck::handleIntegralToFloating(
421 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
423 if (WarnOnIntegerToFloatingPointNarrowingConversion) {
425 llvm::APSInt IntegerConstant;
428 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);
433 if (isWarningInhibitedByEquivalentSize(Context, *FromType, *ToType))
436 diagNarrowType(SourceLoc, Lhs, Rhs);
440void NarrowingConversionsCheck::handleFloatingToIntegral(
441 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
443 llvm::APFloat FloatConstant(0.0);
446 return diagNarrowConstant(SourceLoc, Lhs, Rhs);
449 return diagConstantCast(SourceLoc, Lhs, Rhs);
456 if (isWarningInhibitedByEquivalentSize(Context, *FromType, *ToType))
458 diagNarrowType(SourceLoc, Lhs, Rhs);
461void NarrowingConversionsCheck::handleFloatingToBoolean(
462 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
464 return diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs);
467void NarrowingConversionsCheck::handleBooleanToSignedIntegral(
468 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
477void NarrowingConversionsCheck::handleFloatingCast(
const ASTContext &Context,
478 SourceLocation SourceLoc,
481 if (WarnOnFloatingPointNarrowingConversion) {
489 llvm::APFloat Tmp =
Constant.getFloat();
490 bool UnusedLosesInfo =
false;
491 Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()),
492 llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo);
493 if (Tmp.isInfinity())
494 diagNarrowConstant(SourceLoc, Lhs, Rhs);
498 if (ToType->getKind() < FromType->getKind())
499 diagNarrowType(SourceLoc, Lhs, Rhs);
503void NarrowingConversionsCheck::handleBinaryOperator(
const ASTContext &Context,
504 SourceLocation SourceLoc,
507 assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&
508 "Dependent types must be check before calling this function");
511 if (RhsType ==
nullptr || LhsType ==
nullptr)
513 if (LhsType == RhsType)
515 if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())
516 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
517 if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)
518 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
519 if (RhsType->isInteger() && LhsType->isFloatingPoint())
520 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
521 if (RhsType->isInteger() && LhsType->isInteger())
522 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
523 if (RhsType->isFloatingPoint() && LhsType->getKind() == BuiltinType::Bool)
524 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
525 if (RhsType->isFloatingPoint() && LhsType->isInteger())
526 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
527 if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())
528 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
531bool NarrowingConversionsCheck::handleConditionalOperator(
532 const ASTContext &Context,
const Expr &Lhs,
const Expr &Rhs) {
533 if (
const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
537 handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs,
539 handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs,
546void NarrowingConversionsCheck::handleImplicitCast(
547 const ASTContext &Context,
const ImplicitCastExpr &Cast) {
548 if (Cast.getExprLoc().isMacroID())
550 const Expr &Lhs = Cast;
551 const Expr &Rhs = *Cast.getSubExpr();
552 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
556 if (handleConditionalOperator(Context, Lhs, Rhs))
558 SourceLocation SourceLoc = Lhs.getExprLoc();
559 switch (Cast.getCastKind()) {
560 case CK_BooleanToSignedIntegral:
561 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
562 case CK_IntegralToBoolean:
563 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
564 case CK_IntegralToFloating:
565 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
566 case CK_IntegralCast:
567 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
568 case CK_FloatingToBoolean:
569 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
570 case CK_FloatingToIntegral:
571 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
572 case CK_FloatingCast:
573 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
579void NarrowingConversionsCheck::handleBinaryOperator(
const ASTContext &Context,
580 const BinaryOperator &Op) {
581 if (Op.getBeginLoc().isMacroID())
583 const Expr &Lhs = *Op.getLHS();
584 const Expr &Rhs = *Op.getRHS();
585 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
587 if (handleConditionalOperator(Context, Lhs, Rhs))
589 handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
593 if (
const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>(
"binary_op"))
594 return handleBinaryOperator(*Result.Context, *Op);
595 if (
const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>(
"cast"))
596 return handleImplicitCast(*Result.Context, *Cast);
597 llvm_unreachable(
"must be binary operator or cast expression");
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
NarrowingConversionsCheck(StringRef Name, ClangTidyContext *Context)
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
static IntegerRange createFromType(const ASTContext &Context, const BuiltinType &T)
AST_MATCHER(FieldDecl, hasIntBitwidth)
static bool getFloatingConstantExprValue(const ASTContext &Context, const Expr &E, llvm::APFloat &Value)
static bool isFloatExactlyRepresentable(const ASTContext &Context, const llvm::APFloat &FloatConstant, const QualType &DestType)
static bool getIntegerConstantExprValue(const ASTContext &Context, const Expr &E, llvm::APSInt &Value)
static const BuiltinType * getBuiltinType(const Expr &E)
static llvm::SmallString< 64 > getValueAsString(const llvm::APSInt &Value, uint64_t HexBits)
static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E)
static QualType getUnqualifiedType(const Expr &E)
static bool isWideEnoughToHold(const ASTContext &Context, const BuiltinType &FromType, const BuiltinType &ToType)
std::vector< StringRef > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
llvm::StringMap< ClangTidyValue > OptionMap