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 const std::string Name = Node.getLocalUnqualifiedType().getAsString();
33 return llvm::is_contained(Names, Name);
37 assert(Node.isBitField());
38 const ASTContext &Ctx = Node.getASTContext();
39 const unsigned IntBitWidth = Ctx.getIntWidth(Ctx.IntTy);
40 const 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 const 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 const 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 const uint64_t TypeSize = Context.getTypeSize(&T);
266 const 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 const IntegerRange FromIntegerRange =
createFromType(Context, FromType);
275 const IntegerRange ToIntegerRange =
createFromType(Context, ToType);
276 return ToIntegerRange.contains(FromIntegerRange);
280 const llvm::APSInt &IntegerConstant,
281 const BuiltinType &ToType) {
282 const IntegerRange ToIntegerRange =
createFromType(Context, ToType);
283 return ToIntegerRange.contains(IntegerConstant);
290 const llvm::APFloat &FloatConstant,
291 const QualType &DestType) {
292 const unsigned DestWidth = Context.getIntWidth(DestType);
293 const bool DestSigned = DestType->isSignedIntegerOrEnumerationType();
294 llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned);
295 bool IsExact =
false;
296 const 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 const uint64_t FromTypeSize = Context.getTypeSize(&FromType);
325 const uint64_t ToTypeSize = Context.getTypeSize(&ToType);
326 if (FromTypeSize == ToTypeSize)
332void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,
335 diag(SourceLoc,
"narrowing conversion from %0 to %1")
339void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(
340 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs) {
341 diag(SourceLoc,
"narrowing conversion from %0 to signed type %1 is "
342 "implementation-defined")
346void NarrowingConversionsCheck::diagNarrowIntegerConstant(
347 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs,
348 const llvm::APSInt &Value) {
350 "narrowing conversion from constant value %0 of type %1 to %2")
355void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt(
356 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs,
357 const llvm::APSInt &Value,
const uint64_t HexBits) {
358 diag(SourceLoc,
"narrowing conversion from constant value %0 of type %1 "
359 "to signed type %2 is implementation-defined")
364void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
367 diag(SourceLoc,
"narrowing conversion from constant %0 to %1")
371void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
374 diag(SourceLoc,
"constant value should be of type of type %0 instead of %1")
378void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
379 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
383 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs,
Constant.getInt());
385 diagNarrowConstant(SourceLoc, Lhs, Rhs);
387 diagNarrowType(SourceLoc, Lhs, Rhs);
390void NarrowingConversionsCheck::handleIntegralCast(
const ASTContext &Context,
391 SourceLocation SourceLoc,
394 if (WarnOnIntegerNarrowingConversion) {
401 if (ToType->isUnsignedInteger())
407 if (!WarnOnEquivalentBitWidth) {
408 const uint64_t FromTypeSize = Context.getTypeSize(FromType);
409 const uint64_t ToTypeSize = Context.getTypeSize(ToType);
410 if (FromTypeSize == ToTypeSize)
414 llvm::APSInt IntegerConstant;
417 diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs,
419 Context.getTypeSize(FromType));
423 diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs);
427void NarrowingConversionsCheck::handleIntegralToBoolean(
428 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
437void NarrowingConversionsCheck::handleIntegralToFloating(
438 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
440 if (WarnOnIntegerToFloatingPointNarrowingConversion) {
442 llvm::APSInt IntegerConstant;
445 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);
450 if (isWarningInhibitedByEquivalentSize(Context, *FromType, *ToType))
453 diagNarrowType(SourceLoc, Lhs, Rhs);
457void NarrowingConversionsCheck::handleFloatingToIntegral(
458 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
460 llvm::APFloat FloatConstant(0.0);
463 diagNarrowConstant(SourceLoc, Lhs, Rhs);
465 else if (PedanticMode)
466 diagConstantCast(SourceLoc, Lhs, Rhs);
473 if (isWarningInhibitedByEquivalentSize(Context, *FromType, *ToType))
475 diagNarrowType(SourceLoc, Lhs, Rhs);
478void NarrowingConversionsCheck::handleFloatingToBoolean(
479 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
481 diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs);
484void NarrowingConversionsCheck::handleBooleanToSignedIntegral(
485 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
494void NarrowingConversionsCheck::handleFloatingCast(
const ASTContext &Context,
495 SourceLocation SourceLoc,
498 if (WarnOnFloatingPointNarrowingConversion) {
506 llvm::APFloat Tmp =
Constant.getFloat();
507 bool UnusedLosesInfo =
false;
508 Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()),
509 llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo);
510 if (Tmp.isInfinity())
511 diagNarrowConstant(SourceLoc, Lhs, Rhs);
515 if (!llvm::APFloatBase::isRepresentableBy(
516 Context.getFloatTypeSemantics(FromType->desugar()),
517 Context.getFloatTypeSemantics(ToType->desugar())))
518 diagNarrowType(SourceLoc, Lhs, Rhs);
522void NarrowingConversionsCheck::handleBinaryOperator(
const ASTContext &Context,
523 SourceLocation SourceLoc,
526 assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&
527 "Dependent types must be check before calling this function");
530 if (RhsType ==
nullptr || LhsType ==
nullptr)
532 if (LhsType == RhsType)
534 if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())
535 handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
536 else if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)
537 handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
538 else if (RhsType->isInteger() && LhsType->isFloatingPoint())
539 handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
540 else if (RhsType->isInteger() && LhsType->isInteger())
541 handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
542 else if (RhsType->isFloatingPoint() &&
543 LhsType->getKind() == BuiltinType::Bool)
544 handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
545 else if (RhsType->isFloatingPoint() && LhsType->isInteger())
546 handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
547 else if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())
548 handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
551bool NarrowingConversionsCheck::handleConditionalOperator(
552 const ASTContext &Context,
const Expr &Lhs,
const Expr &Rhs) {
553 if (
const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
557 handleConditionalOperatorArgument(Context, Lhs, CO->getLHS());
558 handleConditionalOperatorArgument(Context, Lhs, CO->getRHS());
564void NarrowingConversionsCheck::handleConditionalOperatorArgument(
565 const ASTContext &Context,
const Expr &Lhs,
const Expr *Arg) {
566 if (
const auto *ICE = llvm::dyn_cast<ImplicitCastExpr>(Arg))
567 if (!Arg->getIntegerConstantExpr(Context))
568 Arg = ICE->getSubExpr();
570 handleBinaryOperator(Context, Arg->getExprLoc(), Lhs, *Arg);
573void NarrowingConversionsCheck::handleImplicitCast(
574 const ASTContext &Context,
const ImplicitCastExpr &Cast) {
575 if (Cast.getExprLoc().isMacroID())
577 const Expr &Lhs = Cast;
578 const Expr &Rhs = *Cast.getSubExpr();
579 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
583 if (handleConditionalOperator(Context, Lhs, Rhs))
585 const SourceLocation SourceLoc = Lhs.getExprLoc();
586 switch (Cast.getCastKind()) {
587 case CK_BooleanToSignedIntegral:
588 handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
590 case CK_IntegralToBoolean:
591 handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
593 case CK_IntegralToFloating:
594 handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
596 case CK_IntegralCast:
597 handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
599 case CK_FloatingToBoolean:
600 handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
602 case CK_FloatingToIntegral:
603 handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
605 case CK_FloatingCast:
606 handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
613void NarrowingConversionsCheck::handleBinaryOperator(
const ASTContext &Context,
614 const BinaryOperator &Op) {
615 if (Op.getBeginLoc().isMacroID())
617 const Expr &Lhs = *Op.getLHS();
618 const Expr &Rhs = *Op.getRHS();
619 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
621 if (handleConditionalOperator(Context, Lhs, Rhs))
623 handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
627 if (
const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>(
"binary_op"))
628 handleBinaryOperator(*Result.Context, *Op);
629 else if (
const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>(
"cast"))
630 handleImplicitCast(*Result.Context, *Cast);
632 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