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/STLExtras.h"
18#include "llvm/ADT/SmallString.h"
28AST_MATCHER_P(QualType, hasAnyType, std::vector<StringRef>, Names) {
32 std::string Name = Node.getLocalUnqualifiedType().getAsString();
33 return llvm::is_contained(Names, Name);
37 assert(Node.isBitField());
38 const ASTContext &Ctx = Node.getASTContext();
39 unsigned IntBitWidth = Ctx.getIntWidth(Ctx.IntTy);
40 unsigned CurrentBitWidth = Node.getBitWidthValue();
41 return IntBitWidth == CurrentBitWidth;
49 WarnOnIntegerNarrowingConversion(
50 Options.get(
"WarnOnIntegerNarrowingConversion", true)),
51 WarnOnIntegerToFloatingPointNarrowingConversion(
52 Options.get(
"WarnOnIntegerToFloatingPointNarrowingConversion", true)),
53 WarnOnFloatingPointNarrowingConversion(
54 Options.get(
"WarnOnFloatingPointNarrowingConversion", true)),
55 WarnWithinTemplateInstantiation(
56 Options.get(
"WarnWithinTemplateInstantiation", false)),
57 WarnOnEquivalentBitWidth(Options.get(
"WarnOnEquivalentBitWidth", true)),
58 IgnoreConversionFromTypes(Options.get(
"IgnoreConversionFromTypes",
"")),
59 PedanticMode(Options.get(
"PedanticMode", false)) {}
63 Options.store(Opts,
"WarnOnIntegerNarrowingConversion",
64 WarnOnIntegerNarrowingConversion);
65 Options.store(Opts,
"WarnOnIntegerToFloatingPointNarrowingConversion",
66 WarnOnIntegerToFloatingPointNarrowingConversion);
67 Options.store(Opts,
"WarnOnFloatingPointNarrowingConversion",
68 WarnOnFloatingPointNarrowingConversion);
69 Options.store(Opts,
"WarnWithinTemplateInstantiation",
70 WarnWithinTemplateInstantiation);
71 Options.store(Opts,
"WarnOnEquivalentBitWidth", WarnOnEquivalentBitWidth);
72 Options.store(Opts,
"IgnoreConversionFromTypes", IgnoreConversionFromTypes);
73 Options.store(Opts,
"PedanticMode", PedanticMode);
79 const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl(
80 hasAnyName(
"::ceil",
"::std::ceil",
"::floor",
"::std::floor")))));
82 std::vector<StringRef> IgnoreConversionFromTypesVec =
88 const auto IsConversionFromIgnoredType =
89 anyOf(hasType(namedDecl(hasAnyName(IgnoreConversionFromTypesVec))),
90 allOf(unless(hasType(namedDecl())),
91 hasType(qualType(hasAnyType(IgnoreConversionFromTypesVec)))));
101 const auto IsIgnoredTypeTwoLevelsDeep =
102 anyOf(IsConversionFromIgnoredType,
103 binaryOperator(hasOperands(IsConversionFromIgnoredType,
104 hasType(isInteger()))));
140 const auto ImplicitIntWidenedBitfieldValue = implicitCastExpr(
141 hasCastKind(CK_IntegralCast), hasType(asString(
"int")),
142 has(castExpr(hasCastKind(CK_LValueToRValue),
143 has(ignoringParens(memberExpr(hasDeclaration(
144 fieldDecl(isBitField(), unless(hasIntBitwidth())))))))));
150 traverse(TK_AsIs, implicitCastExpr(
151 hasImplicitDestinationType(
152 hasUnqualifiedDesugaredType(builtinType())),
153 hasSourceExpression(hasType(
154 hasUnqualifiedDesugaredType(builtinType()))),
155 unless(hasSourceExpression(IsCeilFloorCallExpr)),
156 unless(hasParent(castExpr())),
157 WarnWithinTemplateInstantiation
159 : stmt(unless(isInTemplateInstantiation())),
160 IgnoreConversionFromTypes.empty()
162 : castExpr(unless(hasSourceExpression(
163 IsIgnoredTypeTwoLevelsDeep))),
164 unless(ImplicitIntWidenedBitfieldValue))
172 isAssignmentOperator(),
173 hasLHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))),
174 hasRHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))),
175 unless(hasRHS(IsCeilFloorCallExpr)),
176 WarnWithinTemplateInstantiation
178 : binaryOperator(unless(isInTemplateInstantiation())),
179 IgnoreConversionFromTypes.empty()
181 : binaryOperator(unless(hasRHS(IsIgnoredTypeTwoLevelsDeep))),
184 unless(hasOperatorName(
"=")))
190 return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>();
194 return E.getType().getUnqualifiedType();
198 if (
auto IntegerConstant = E.getIntegerConstantExpr(Ctx))
199 return APValue(*IntegerConstant);
201 if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant))
207 const Expr &E, llvm::APSInt &Value) {
209 if (!Constant.isInt())
211 Value = Constant.getInt();
216 const Expr &E, llvm::APFloat &Value) {
218 if (!Constant.isFloat())
220 Value = Constant.getFloat();
227 bool contains(
const IntegerRange &From)
const {
228 return llvm::APSInt::compareValues(Lower, From.Lower) <= 0 &&
229 llvm::APSInt::compareValues(Upper, From.Upper) >= 0;
232 bool contains(
const llvm::APSInt &Value)
const {
233 return llvm::APSInt::compareValues(Lower, Value) <= 0 &&
234 llvm::APSInt::compareValues(Upper, Value) >= 0;
244 const BuiltinType &T) {
245 if (T.isFloatingPoint()) {
246 unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision(
247 Context.getFloatTypeSemantics(T.desugar()));
257 llvm::APSInt UpperValue(PrecisionBits + 2,
false);
258 UpperValue.setBit(PrecisionBits);
259 llvm::APSInt LowerValue(PrecisionBits + 2,
false);
260 LowerValue.setBit(PrecisionBits);
261 LowerValue.setSignBit();
262 return {LowerValue, UpperValue};
264 assert(T.isInteger() &&
"Unexpected builtin type");
265 uint64_t TypeSize = Context.getTypeSize(&T);
266 bool IsUnsignedInteger = T.isUnsignedInteger();
267 return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger),
268 llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)};
272 const BuiltinType &FromType,
273 const BuiltinType &ToType) {
274 IntegerRange FromIntegerRange =
createFromType(Context, FromType);
276 return ToIntegerRange.contains(FromIntegerRange);
280 const llvm::APSInt &IntegerConstant,
281 const BuiltinType &ToType) {
283 return ToIntegerRange.contains(IntegerConstant);
290 const llvm::APFloat &FloatConstant,
291 const QualType &DestType) {
292 unsigned DestWidth = Context.getIntWidth(DestType);
293 bool DestSigned = DestType->isSignedIntegerOrEnumerationType();
294 llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned);
295 bool IsExact =
false;
296 bool Overflows = FloatConstant.convertToInteger(
297 Result, llvm::APFloat::rmTowardZero, &IsExact) &
298 llvm::APFloat::opInvalidOp;
299 return !Overflows && IsExact;
304 llvm::SmallString<64> Str;
305 Value.toString(Str, 10);
308 llvm::SmallString<32> HexValue;
309 Value.toStringUnsigned(HexValue, 16);
310 for (
size_t I = HexValue.size(); I < (HexBits / 4); ++I)
312 Str.append(HexValue);
318bool NarrowingConversionsCheck::isWarningInhibitedByEquivalentSize(
319 const ASTContext &Context,
const BuiltinType &FromType,
320 const BuiltinType &ToType)
const {
323 if (!WarnOnEquivalentBitWidth) {
324 uint64_t FromTypeSize = Context.getTypeSize(&FromType);
325 uint64_t ToTypeSize = Context.getTypeSize(&ToType);
326 if (FromTypeSize == ToTypeSize) {
333void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,
336 diag(SourceLoc,
"narrowing conversion from %0 to %1")
340void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(
341 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs) {
342 diag(SourceLoc,
"narrowing conversion from %0 to signed type %1 is "
343 "implementation-defined")
347void NarrowingConversionsCheck::diagNarrowIntegerConstant(
348 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs,
349 const llvm::APSInt &Value) {
351 "narrowing conversion from constant value %0 of type %1 to %2")
356void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt(
357 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs,
358 const llvm::APSInt &Value,
const uint64_t HexBits) {
359 diag(SourceLoc,
"narrowing conversion from constant value %0 of type %1 "
360 "to signed type %2 is implementation-defined")
365void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
368 diag(SourceLoc,
"narrowing conversion from constant %0 to %1")
372void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
375 diag(SourceLoc,
"constant value should be of type of type %0 instead of %1")
379void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
380 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
384 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs,
Constant.getInt());
386 diagNarrowConstant(SourceLoc, Lhs, Rhs);
388 diagNarrowType(SourceLoc, Lhs, Rhs);
391void NarrowingConversionsCheck::handleIntegralCast(
const ASTContext &Context,
392 SourceLocation SourceLoc,
395 if (WarnOnIntegerNarrowingConversion) {
402 if (ToType->isUnsignedInteger())
408 if (!WarnOnEquivalentBitWidth) {
409 uint64_t FromTypeSize = Context.getTypeSize(FromType);
410 uint64_t ToTypeSize = Context.getTypeSize(ToType);
411 if (FromTypeSize == ToTypeSize)
415 llvm::APSInt IntegerConstant;
418 diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs,
420 Context.getTypeSize(FromType));
424 diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs);
428void NarrowingConversionsCheck::handleIntegralToBoolean(
429 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
438void NarrowingConversionsCheck::handleIntegralToFloating(
439 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
441 if (WarnOnIntegerToFloatingPointNarrowingConversion) {
443 llvm::APSInt IntegerConstant;
446 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);
451 if (isWarningInhibitedByEquivalentSize(Context, *FromType, *ToType))
454 diagNarrowType(SourceLoc, Lhs, Rhs);
458void NarrowingConversionsCheck::handleFloatingToIntegral(
459 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
461 llvm::APFloat FloatConstant(0.0);
464 diagNarrowConstant(SourceLoc, Lhs, Rhs);
466 else if (PedanticMode)
467 diagConstantCast(SourceLoc, Lhs, Rhs);
474 if (isWarningInhibitedByEquivalentSize(Context, *FromType, *ToType))
476 diagNarrowType(SourceLoc, Lhs, Rhs);
479void NarrowingConversionsCheck::handleFloatingToBoolean(
480 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
482 diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs);
485void NarrowingConversionsCheck::handleBooleanToSignedIntegral(
486 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
495void NarrowingConversionsCheck::handleFloatingCast(
const ASTContext &Context,
496 SourceLocation SourceLoc,
499 if (WarnOnFloatingPointNarrowingConversion) {
507 llvm::APFloat Tmp =
Constant.getFloat();
508 bool UnusedLosesInfo =
false;
509 Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()),
510 llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo);
511 if (Tmp.isInfinity())
512 diagNarrowConstant(SourceLoc, Lhs, Rhs);
516 if (!llvm::APFloatBase::isRepresentableBy(
517 Context.getFloatTypeSemantics(FromType->desugar()),
518 Context.getFloatTypeSemantics(ToType->desugar())))
519 diagNarrowType(SourceLoc, Lhs, Rhs);
523void NarrowingConversionsCheck::handleBinaryOperator(
const ASTContext &Context,
524 SourceLocation SourceLoc,
527 assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&
528 "Dependent types must be check before calling this function");
531 if (RhsType ==
nullptr || LhsType ==
nullptr)
533 if (LhsType == RhsType)
535 if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())
536 handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
537 else if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)
538 handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
539 else if (RhsType->isInteger() && LhsType->isFloatingPoint())
540 handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
541 else if (RhsType->isInteger() && LhsType->isInteger())
542 handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
543 else if (RhsType->isFloatingPoint() &&
544 LhsType->getKind() == BuiltinType::Bool)
545 handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
546 else if (RhsType->isFloatingPoint() && LhsType->isInteger())
547 handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
548 else if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())
549 handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
552bool NarrowingConversionsCheck::handleConditionalOperator(
553 const ASTContext &Context,
const Expr &Lhs,
const Expr &Rhs) {
554 if (
const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
558 handleConditionalOperatorArgument(Context, Lhs, CO->getLHS());
559 handleConditionalOperatorArgument(Context, Lhs, CO->getRHS());
565void NarrowingConversionsCheck::handleConditionalOperatorArgument(
566 const ASTContext &Context,
const Expr &Lhs,
const Expr *Arg) {
567 if (
const auto *ICE = llvm::dyn_cast<ImplicitCastExpr>(Arg))
568 if (!Arg->getIntegerConstantExpr(Context))
569 Arg = ICE->getSubExpr();
571 handleBinaryOperator(Context, Arg->getExprLoc(), Lhs, *Arg);
574void NarrowingConversionsCheck::handleImplicitCast(
575 const ASTContext &Context,
const ImplicitCastExpr &Cast) {
576 if (Cast.getExprLoc().isMacroID())
578 const Expr &Lhs = Cast;
579 const Expr &Rhs = *Cast.getSubExpr();
580 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
584 if (handleConditionalOperator(Context, Lhs, Rhs))
586 SourceLocation SourceLoc = Lhs.getExprLoc();
587 switch (Cast.getCastKind()) {
588 case CK_BooleanToSignedIntegral:
589 handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
591 case CK_IntegralToBoolean:
592 handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
594 case CK_IntegralToFloating:
595 handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
597 case CK_IntegralCast:
598 handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
600 case CK_FloatingToBoolean:
601 handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
603 case CK_FloatingToIntegral:
604 handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
606 case CK_FloatingCast:
607 handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
614void NarrowingConversionsCheck::handleBinaryOperator(
const ASTContext &Context,
615 const BinaryOperator &Op) {
616 if (Op.getBeginLoc().isMacroID())
618 const Expr &Lhs = *Op.getLHS();
619 const Expr &Rhs = *Op.getRHS();
620 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
622 if (handleConditionalOperator(Context, Lhs, Rhs))
624 handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
628 if (
const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>(
"binary_op"))
629 handleBinaryOperator(*Result.Context, *Op);
630 else if (
const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>(
"cast"))
631 handleImplicitCast(*Result.Context, *Cast);
633 llvm_unreachable(
"must be binary operator or cast expression");
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
NarrowingConversionsCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
static bool getIntegerConstantExprValue(const ASTContext &Context, const Expr &E, llvm::APSInt &Value)
static QualType getUnqualifiedType(const Expr &E)
static bool getFloatingConstantExprValue(const ASTContext &Context, const Expr &E, llvm::APFloat &Value)
static bool isWideEnoughToHold(const ASTContext &Context, const BuiltinType &FromType, const BuiltinType &ToType)
static llvm::SmallString< 64 > getValueAsString(const llvm::APSInt &Value, uint64_t HexBits)
static const BuiltinType * getBuiltinType(const Expr &E)
static IntegerRange createFromType(const ASTContext &Context, const BuiltinType &T)
static bool isFloatExactlyRepresentable(const ASTContext &Context, const llvm::APFloat &FloatConstant, const QualType &DestType)
static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E)
AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID)
AST_MATCHER(BinaryOperator, isRelationalOperator)
std::vector< StringRef > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
llvm::StringMap< ClangTidyValue > OptionMap