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/STLExtras.h"
18#include "llvm/ADT/SmallString.h"
19#include "llvm/ADT/SmallVector.h"
29AST_MATCHER_P(QualType, hasAnyType, std::vector<StringRef>, Names) {
33 std::string
Name =
Node.getLocalUnqualifiedType().getAsString();
34 return llvm::any_of(Names, [&
Name](StringRef Ref) {
return Ref ==
Name; });
38 assert(
Node.isBitField());
39 const ASTContext &Ctx =
Node.getASTContext();
40 unsigned IntBitWidth = Ctx.getIntWidth(Ctx.IntTy);
41 unsigned CurrentBitWidth =
Node.getBitWidthValue(Ctx);
42 return IntBitWidth == CurrentBitWidth;
50 WarnOnIntegerNarrowingConversion(
51 Options.get(
"WarnOnIntegerNarrowingConversion", true)),
52 WarnOnIntegerToFloatingPointNarrowingConversion(
53 Options.get(
"WarnOnIntegerToFloatingPointNarrowingConversion", true)),
54 WarnOnFloatingPointNarrowingConversion(
55 Options.get(
"WarnOnFloatingPointNarrowingConversion", true)),
56 WarnWithinTemplateInstantiation(
57 Options.get(
"WarnWithinTemplateInstantiation", false)),
58 WarnOnEquivalentBitWidth(Options.get(
"WarnOnEquivalentBitWidth", true)),
59 IgnoreConversionFromTypes(Options.get(
"IgnoreConversionFromTypes",
"")),
60 PedanticMode(Options.get(
"PedanticMode", false)) {}
65 WarnOnIntegerNarrowingConversion);
66 Options.
store(Opts,
"WarnOnIntegerToFloatingPointNarrowingConversion",
67 WarnOnIntegerToFloatingPointNarrowingConversion);
68 Options.
store(Opts,
"WarnOnFloatingPointNarrowingConversion",
69 WarnOnFloatingPointNarrowingConversion);
71 WarnWithinTemplateInstantiation);
72 Options.
store(Opts,
"WarnOnEquivalentBitWidth", WarnOnEquivalentBitWidth);
73 Options.
store(Opts,
"IgnoreConversionFromTypes", IgnoreConversionFromTypes);
80 const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl(
81 hasAnyName(
"::ceil",
"::std::ceil",
"::floor",
"::std::floor")))));
83 std::vector<StringRef> IgnoreConversionFromTypesVec =
89 const auto IsConversionFromIgnoredType =
90 anyOf(hasType(namedDecl(hasAnyName(IgnoreConversionFromTypesVec))),
91 allOf(unless(hasType(namedDecl())),
92 hasType(qualType(hasAnyType(IgnoreConversionFromTypesVec)))));
102 const auto IsIgnoredTypeTwoLevelsDeep =
103 anyOf(IsConversionFromIgnoredType,
104 binaryOperator(hasOperands(IsConversionFromIgnoredType,
105 hasType(isInteger()))));
141 const auto ImplicitIntWidenedBitfieldValue = implicitCastExpr(
142 hasCastKind(CK_IntegralCast), hasType(asString(
"int")),
143 has(castExpr(hasCastKind(CK_LValueToRValue),
144 has(ignoringParens(memberExpr(hasDeclaration(
145 fieldDecl(isBitField(), unless(hasIntBitwidth())))))))));
151 traverse(TK_AsIs, implicitCastExpr(
152 hasImplicitDestinationType(
153 hasUnqualifiedDesugaredType(builtinType())),
154 hasSourceExpression(hasType(
155 hasUnqualifiedDesugaredType(builtinType()))),
156 unless(hasSourceExpression(IsCeilFloorCallExpr)),
157 unless(hasParent(castExpr())),
158 WarnWithinTemplateInstantiation
160 : stmt(unless(isInTemplateInstantiation())),
161 IgnoreConversionFromTypes.empty()
163 : castExpr(unless(hasSourceExpression(
164 IsIgnoredTypeTwoLevelsDeep))),
165 unless(ImplicitIntWidenedBitfieldValue))
173 isAssignmentOperator(),
174 hasLHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))),
175 hasRHS(expr(hasType(hasUnqualifiedDesugaredType(builtinType())))),
176 unless(hasRHS(IsCeilFloorCallExpr)),
177 WarnWithinTemplateInstantiation
179 : binaryOperator(unless(isInTemplateInstantiation())),
180 IgnoreConversionFromTypes.empty()
182 : binaryOperator(unless(hasRHS(IsIgnoredTypeTwoLevelsDeep))),
185 unless(hasOperatorName(
"=")))
191 return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>();
195 return E.getType().getUnqualifiedType();
199 if (
auto IntegerConstant =
E.getIntegerConstantExpr(Ctx))
200 return APValue(*IntegerConstant);
202 if (Ctx.getLangOpts().CPlusPlus &&
E.isCXX11ConstantExpr(Ctx, &Constant))
208 const Expr &
E, llvm::APSInt &Value) {
210 if (!Constant.isInt())
212 Value = Constant.getInt();
217 const Expr &
E, llvm::APFloat &Value) {
219 if (!Constant.isFloat())
221 Value = Constant.getFloat();
228 bool contains(
const IntegerRange &From)
const {
229 return llvm::APSInt::compareValues(Lower, From.Lower) <= 0 &&
230 llvm::APSInt::compareValues(Upper, From.Upper) >= 0;
233 bool contains(
const llvm::APSInt &Value)
const {
234 return llvm::APSInt::compareValues(Lower, Value) <= 0 &&
235 llvm::APSInt::compareValues(Upper, Value) >= 0;
245 const BuiltinType &T) {
246 if (T.isFloatingPoint()) {
247 unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision(
248 Context.getFloatTypeSemantics(T.desugar()));
258 llvm::APSInt UpperValue(PrecisionBits + 2,
false);
259 UpperValue.setBit(PrecisionBits);
260 llvm::APSInt LowerValue(PrecisionBits + 2,
false);
261 LowerValue.setBit(PrecisionBits);
262 LowerValue.setSignBit();
263 return {LowerValue, UpperValue};
265 assert(T.isInteger() &&
"Unexpected builtin type");
266 uint64_t TypeSize = Context.getTypeSize(&T);
267 bool IsUnsignedInteger = T.isUnsignedInteger();
268 return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger),
269 llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)};
273 const BuiltinType &FromType,
274 const BuiltinType &ToType) {
275 IntegerRange FromIntegerRange =
createFromType(Context, FromType);
277 return ToIntegerRange.contains(FromIntegerRange);
281 const llvm::APSInt &IntegerConstant,
282 const BuiltinType &ToType) {
284 return ToIntegerRange.contains(IntegerConstant);
291 const llvm::APFloat &FloatConstant,
292 const QualType &DestType) {
293 unsigned DestWidth = Context.getIntWidth(DestType);
294 bool DestSigned = DestType->isSignedIntegerOrEnumerationType();
295 llvm::APSInt Result = llvm::APSInt(DestWidth, !DestSigned);
296 bool IsExact =
false;
297 bool Overflows = FloatConstant.convertToInteger(
298 Result, llvm::APFloat::rmTowardZero, &IsExact) &
299 llvm::APFloat::opInvalidOp;
300 return !Overflows && IsExact;
305 llvm::SmallString<64> Str;
306 Value.toString(Str, 10);
309 llvm::SmallString<32> HexValue;
310 Value.toStringUnsigned(HexValue, 16);
311 for (
size_t I = HexValue.size(); I < (HexBits / 4); ++I)
313 Str.append(HexValue);
319bool NarrowingConversionsCheck::isWarningInhibitedByEquivalentSize(
320 const ASTContext &Context,
const BuiltinType &FromType,
321 const BuiltinType &ToType)
const {
324 if (!WarnOnEquivalentBitWidth) {
325 uint64_t FromTypeSize = Context.getTypeSize(&FromType);
326 uint64_t ToTypeSize = Context.getTypeSize(&ToType);
327 if (FromTypeSize == ToTypeSize) {
334void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,
337 diag(SourceLoc,
"narrowing conversion from %0 to %1")
341void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(
342 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs) {
343 diag(SourceLoc,
"narrowing conversion from %0 to signed type %1 is "
344 "implementation-defined")
348void NarrowingConversionsCheck::diagNarrowIntegerConstant(
349 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs,
350 const llvm::APSInt &Value) {
352 "narrowing conversion from constant value %0 of type %1 to %2")
357void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt(
358 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs,
359 const llvm::APSInt &Value,
const uint64_t HexBits) {
360 diag(SourceLoc,
"narrowing conversion from constant value %0 of type %1 "
361 "to signed type %2 is implementation-defined")
366void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
369 diag(SourceLoc,
"narrowing conversion from constant %0 to %1")
373void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
376 diag(SourceLoc,
"constant value should be of type of type %0 instead of %1")
380void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
381 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
385 return diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs,
Constant.getInt());
387 return diagNarrowConstant(SourceLoc, Lhs, Rhs);
388 return 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 return diagNarrowConstant(SourceLoc, Lhs, Rhs);
467 return 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 return 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 (ToType->getKind() < FromType->getKind())
517 diagNarrowType(SourceLoc, Lhs, Rhs);
521void NarrowingConversionsCheck::handleBinaryOperator(
const ASTContext &Context,
522 SourceLocation SourceLoc,
525 assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&
526 "Dependent types must be check before calling this function");
529 if (RhsType ==
nullptr || LhsType ==
nullptr)
531 if (LhsType == RhsType)
533 if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())
534 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
535 if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)
536 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
537 if (RhsType->isInteger() && LhsType->isFloatingPoint())
538 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
539 if (RhsType->isInteger() && LhsType->isInteger())
540 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
541 if (RhsType->isFloatingPoint() && LhsType->getKind() == BuiltinType::Bool)
542 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
543 if (RhsType->isFloatingPoint() && LhsType->isInteger())
544 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
545 if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())
546 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
549bool NarrowingConversionsCheck::handleConditionalOperator(
550 const ASTContext &Context,
const Expr &Lhs,
const Expr &Rhs) {
551 if (
const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
555 handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs,
557 handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs,
564void NarrowingConversionsCheck::handleImplicitCast(
565 const ASTContext &Context,
const ImplicitCastExpr &Cast) {
566 if (Cast.getExprLoc().isMacroID())
568 const Expr &Lhs = Cast;
569 const Expr &Rhs = *Cast.getSubExpr();
570 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
574 if (handleConditionalOperator(Context, Lhs, Rhs))
576 SourceLocation SourceLoc = Lhs.getExprLoc();
577 switch (Cast.getCastKind()) {
578 case CK_BooleanToSignedIntegral:
579 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
580 case CK_IntegralToBoolean:
581 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
582 case CK_IntegralToFloating:
583 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
584 case CK_IntegralCast:
585 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
586 case CK_FloatingToBoolean:
587 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
588 case CK_FloatingToIntegral:
589 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
590 case CK_FloatingCast:
591 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
597void NarrowingConversionsCheck::handleBinaryOperator(
const ASTContext &Context,
598 const BinaryOperator &Op) {
599 if (Op.getBeginLoc().isMacroID())
601 const Expr &Lhs = *Op.getLHS();
602 const Expr &Rhs = *Op.getRHS();
603 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
605 if (handleConditionalOperator(Context, Lhs, Rhs))
607 handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
611 if (
const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>(
"binary_op"))
612 return handleBinaryOperator(*Result.Context, *Op);
613 if (
const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>(
"cast"))
614 return handleImplicitCast(*Result.Context, *Cast);
615 llvm_unreachable(
"must be binary operator or cast expression");
llvm::SmallString< 256U > Name
::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...
AST_MATCHER_P(UserDefinedLiteral, hasLiteral, clang::ast_matchers::internal::Matcher< Expr >, InnerMatcher)
static IntegerRange createFromType(const ASTContext &Context, const BuiltinType &T)
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)
AST_MATCHER(CXXRecordDecl, hasPublicVirtualOrProtectedNonVirtualDestructor)
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