11 #include "clang/AST/ASTContext.h" 12 #include "clang/AST/Type.h" 13 #include "clang/ASTMatchers/ASTMatchFinder.h" 14 #include "llvm/ADT/APSInt.h" 15 #include "llvm/ADT/SmallString.h" 16 #include "llvm/ADT/SmallVector.h" 24 namespace cppcoreguidelines {
26 NarrowingConversionsCheck::NarrowingConversionsCheck(StringRef
Name,
29 WarnOnFloatingPointNarrowingConversion(
30 Options.get(
"WarnOnFloatingPointNarrowingConversion", 1)),
31 PedanticMode(Options.get(
"PedanticMode", 0)) {}
36 const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl(
37 hasAnyName(
"::ceil",
"::std::ceil",
"::floor",
"::std::floor")))));
43 implicitCastExpr(hasImplicitDestinationType(builtinType()),
44 hasSourceExpression(hasType(builtinType())),
45 unless(hasSourceExpression(IsCeilFloorCallExpr)),
46 unless(hasParent(castExpr())),
47 unless(isInTemplateInstantiation()))
53 Finder->addMatcher(binaryOperator(isAssignmentOperator(),
54 hasLHS(expr(hasType(builtinType()))),
55 hasRHS(expr(hasType(builtinType()))),
56 unless(hasRHS(IsCeilFloorCallExpr)),
57 unless(isInTemplateInstantiation()),
60 unless(hasOperatorName(
"=")))
66 return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>();
70 return E.getType().getUnqualifiedType();
74 llvm::APSInt IntegerConstant;
75 if (E.isIntegerConstantExpr(IntegerConstant, Ctx))
76 return APValue(IntegerConstant);
78 if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant))
84 const Expr &E, llvm::APSInt &Value) {
86 if (!Constant.isInt())
88 Value = Constant.getInt();
93 const Expr &E, llvm::APFloat &Value) {
95 if (!Constant.isFloat())
97 Value = Constant.getFloat();
103 struct IntegerRange {
104 bool Contains(
const IntegerRange &From)
const {
105 return llvm::APSInt::compareValues(
Lower, From.Lower) <= 0 &&
106 llvm::APSInt::compareValues(
Upper, From.Upper) >= 0;
109 bool Contains(
const llvm::APSInt &Value)
const {
110 return llvm::APSInt::compareValues(
Lower, Value) <= 0 &&
111 llvm::APSInt::compareValues(
Upper, Value) >= 0;
121 const BuiltinType &T) {
122 if (T.isFloatingPoint()) {
123 unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision(
124 Context.getFloatTypeSemantics(T.desugar()));
134 llvm::APSInt UpperValue(PrecisionBits + 2,
false);
135 UpperValue.setBit(PrecisionBits);
136 llvm::APSInt LowerValue(PrecisionBits + 2,
false);
137 LowerValue.setBit(PrecisionBits);
138 LowerValue.setSignBit();
139 return {LowerValue, UpperValue};
141 assert(T.isInteger() &&
"Unexpected builtin type");
142 uint64_t TypeSize = Context.getTypeSize(&T);
143 bool IsUnsignedInteger = T.isUnsignedInteger();
144 return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger),
145 llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)};
149 const BuiltinType &FromType,
150 const BuiltinType &ToType) {
151 IntegerRange FromIntegerRange =
createFromType(Context, FromType);
153 return ToIntegerRange.Contains(FromIntegerRange);
157 const llvm::APSInt &IntegerConstant,
158 const BuiltinType &ToType) {
160 return ToIntegerRange.Contains(IntegerConstant);
165 llvm::SmallString<64> Str;
166 Value.toString(Str, 10);
169 llvm::SmallString<32> HexValue;
170 Value.toStringUnsigned(HexValue, 16);
171 for (
size_t I = HexValue.size(); I < (HexBits / 4); ++I)
173 Str.append(HexValue);
179 void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc,
182 diag(SourceLoc,
"narrowing conversion from %0 to %1")
186 void NarrowingConversionsCheck::diagNarrowTypeToSignedInt(
187 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs) {
188 diag(SourceLoc,
"narrowing conversion from %0 to signed type %1 is " 189 "implementation-defined")
193 void NarrowingConversionsCheck::diagNarrowIntegerConstant(
194 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs,
195 const llvm::APSInt &Value) {
197 "narrowing conversion from constant value %0 of type %1 to %2")
202 void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt(
203 SourceLocation SourceLoc,
const Expr &Lhs,
const Expr &Rhs,
204 const llvm::APSInt &Value,
const uint64_t HexBits) {
205 diag(SourceLoc,
"narrowing conversion from constant value %0 of type %1 " 206 "to signed type %2 is implementation-defined")
211 void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc,
214 diag(SourceLoc,
"narrowing conversion from constant %0 to %1")
218 void NarrowingConversionsCheck::diagConstantCast(SourceLocation SourceLoc,
221 diag(SourceLoc,
"constant value should be of type of type %0 instead of %1")
225 void NarrowingConversionsCheck::diagNarrowTypeOrConstant(
226 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
229 if (Constant.isInt())
230 return diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Constant.getInt());
231 if (Constant.isFloat())
232 return diagNarrowConstant(SourceLoc, Lhs, Rhs);
233 return diagNarrowType(SourceLoc, Lhs, Rhs);
236 void NarrowingConversionsCheck::handleIntegralCast(
const ASTContext &Context,
237 SourceLocation SourceLoc,
246 if (ToType->isUnsignedInteger())
249 llvm::APSInt IntegerConstant;
252 diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs, IntegerConstant,
253 Context.getTypeSize(FromType));
257 diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs);
260 void NarrowingConversionsCheck::handleIntegralToBoolean(
261 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
270 void NarrowingConversionsCheck::handleIntegralToFloating(
271 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
274 llvm::APSInt IntegerConstant;
277 diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant);
282 diagNarrowType(SourceLoc, Lhs, Rhs);
285 void NarrowingConversionsCheck::handleFloatingToIntegral(
286 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
288 llvm::APFloat FloatConstant(0.0);
292 return diagNarrowType(SourceLoc, Lhs, Rhs);
294 QualType DestType = Lhs.getType();
295 unsigned DestWidth = Context.getIntWidth(DestType);
296 bool DestSigned = DestType->isSignedIntegerOrEnumerationType();
297 llvm::APSInt
Result = llvm::APSInt(DestWidth, !DestSigned);
298 bool IsExact =
false;
299 bool Overflows = FloatConstant.convertToInteger(
300 Result, llvm::APFloat::rmTowardZero, &IsExact) &
301 llvm::APFloat::opInvalidOp;
303 if (Overflows || !IsExact)
304 return diagNarrowConstant(SourceLoc, Lhs, Rhs);
307 return diagConstantCast(SourceLoc, Lhs, Rhs);
310 void NarrowingConversionsCheck::handleFloatingToBoolean(
311 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
313 return diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs);
316 void NarrowingConversionsCheck::handleBooleanToSignedIntegral(
317 const ASTContext &Context, SourceLocation SourceLoc,
const Expr &Lhs,
326 void NarrowingConversionsCheck::handleFloatingCast(
const ASTContext &Context,
327 SourceLocation SourceLoc,
330 if (WarnOnFloatingPointNarrowingConversion) {
333 if (Constant.isFloat()) {
338 llvm::APFloat Tmp = Constant.getFloat();
339 bool UnusedLosesInfo;
340 Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()),
341 llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo);
342 if (Tmp.isInfinity())
343 diagNarrowConstant(SourceLoc, Lhs, Rhs);
347 if (ToType->getKind() < FromType->getKind())
348 diagNarrowType(SourceLoc, Lhs, Rhs);
352 void NarrowingConversionsCheck::handleBinaryOperator(
const ASTContext &Context,
353 SourceLocation SourceLoc,
356 assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() &&
357 "Dependent types must be check before calling this function");
360 if (RhsType ==
nullptr || LhsType ==
nullptr)
362 if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger())
363 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
364 if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool)
365 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
366 if (RhsType->isInteger() && LhsType->isFloatingPoint())
367 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
368 if (RhsType->isInteger() && LhsType->isInteger())
369 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
370 if (RhsType->isFloatingPoint() && LhsType->getKind() == BuiltinType::Bool)
371 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
372 if (RhsType->isFloatingPoint() && LhsType->isInteger())
373 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
374 if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint())
375 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
378 bool NarrowingConversionsCheck::handleConditionalOperator(
379 const ASTContext &Context,
const Expr &Lhs,
const Expr &Rhs) {
380 if (
const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) {
384 handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs,
386 handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs,
393 void NarrowingConversionsCheck::handleImplicitCast(
394 const ASTContext &Context,
const ImplicitCastExpr &Cast) {
395 if (Cast.getExprLoc().isMacroID())
397 const Expr &Lhs = Cast;
398 const Expr &Rhs = *Cast.getSubExpr();
399 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
401 if (handleConditionalOperator(Context, Lhs, Rhs))
403 SourceLocation SourceLoc = Lhs.getExprLoc();
404 switch (Cast.getCastKind()) {
405 case CK_BooleanToSignedIntegral:
406 return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs);
407 case CK_IntegralToBoolean:
408 return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs);
409 case CK_IntegralToFloating:
410 return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs);
411 case CK_IntegralCast:
412 return handleIntegralCast(Context, SourceLoc, Lhs, Rhs);
413 case CK_FloatingToBoolean:
414 return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs);
415 case CK_FloatingToIntegral:
416 return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs);
417 case CK_FloatingCast:
418 return handleFloatingCast(Context, SourceLoc, Lhs, Rhs);
424 void NarrowingConversionsCheck::handleBinaryOperator(
const ASTContext &Context,
425 const BinaryOperator &Op) {
426 if (Op.getBeginLoc().isMacroID())
428 const Expr &Lhs = *Op.getLHS();
429 const Expr &Rhs = *Op.getRHS();
430 if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent())
432 if (handleConditionalOperator(Context, Lhs, Rhs))
434 handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs);
438 if (
const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>(
"binary_op"))
439 return handleBinaryOperator(*Result.Context, *Op);
440 if (
const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>(
"cast"))
441 return handleImplicitCast(*Result.Context, *Cast);
442 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)
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
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.
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.