#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/ParentMap.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/APFloat.h"
#include <climits>
using namespace clang;
using namespace ento;
namespace {
class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {
public:
void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
private:
mutable std::unique_ptr<BuiltinBug> BT;
bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType,
CheckerContext &C) const;
bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;
void reportBug(ExplodedNode *N, const Expr *E, CheckerContext &C,
const char Msg[]) const;
};
}
void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
CheckerContext &C) const {
if (Cast->getType()->isBooleanType())
return;
if (Cast->getExprLoc().isMacroID())
return;
const ParentMap &PM = C.getLocationContext()->getParentMap();
const Stmt *Parent = PM.getParent(Cast);
if (!Parent)
return;
if (isa<ExplicitCastExpr>(Parent))
return;
bool LossOfSign = false;
bool LossOfPrecision = false;
if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {
BinaryOperator::Opcode Opc = B->getOpcode();
if (Opc == BO_Assign) {
if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) {
LossOfSign = isLossOfSign(Cast, C);
LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
}
} else if (Opc == BO_AddAssign || Opc == BO_SubAssign) {
LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
} else if (Opc == BO_MulAssign) {
LossOfSign = isLossOfSign(Cast, C);
LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
} else if (Opc == BO_DivAssign || Opc == BO_RemAssign) {
LossOfSign = isLossOfSign(Cast, C);
} else if (Opc == BO_AndAssign) {
LossOfSign = isLossOfSign(Cast, C);
} else if (Opc == BO_OrAssign || Opc == BO_XorAssign) {
LossOfSign = isLossOfSign(Cast, C);
LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
} else if (B->isRelationalOp() || B->isMultiplicativeOp()) {
LossOfSign = isLossOfSign(Cast, C);
}
} else if (isa<DeclStmt, ReturnStmt>(Parent)) {
if (!Cast->IgnoreParenImpCasts()->isEvaluatable(C.getASTContext())) {
LossOfSign = isLossOfSign(Cast, C);
LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
}
} else {
LossOfSign = isLossOfSign(Cast, C);
LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
}
if (LossOfSign || LossOfPrecision) {
ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
if (!N)
return;
if (LossOfSign)
reportBug(N, Cast, C, "Loss of sign in implicit conversion");
if (LossOfPrecision)
reportBug(N, Cast, C, "Loss of precision in implicit conversion");
}
}
void ConversionChecker::reportBug(ExplodedNode *N, const Expr *E,
CheckerContext &C, const char Msg[]) const {
if (!BT)
BT.reset(
new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
bugreporter::trackExpressionValue(N, E, *R);
C.emitReport(std::move(R));
}
bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
QualType DestType,
CheckerContext &C) const {
if (Cast->isEvaluatable(C.getASTContext()))
return false;
QualType SubType = Cast->IgnoreParenImpCasts()->getType();
if (!DestType->isRealType() || !SubType->isIntegerType())
return false;
const bool isFloat = DestType->isFloatingType();
const auto &AC = C.getASTContext();
unsigned RepresentsUntilExp;
if (isFloat) {
const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);
RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);
} else {
RepresentsUntilExp = AC.getIntWidth(DestType);
if (RepresentsUntilExp == 1) {
return false;
}
if (DestType->isSignedIntegerType())
RepresentsUntilExp--;
}
if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {
return false;
}
unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);
if (SubType->isSignedIntegerType())
CorrectedSrcWidth--;
if (RepresentsUntilExp >= CorrectedSrcWidth) {
return false;
}
unsigned long long MaxVal = 1ULL << RepresentsUntilExp;
if (isFloat) {
MaxVal++;
}
return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
}
bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
CheckerContext &C) const {
QualType CastType = Cast->getType();
QualType SubType = Cast->IgnoreParenImpCasts()->getType();
if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
return false;
return C.isNegative(Cast->getSubExpr());
}
void ento::registerConversionChecker(CheckerManager &mgr) {
mgr.registerChecker<ConversionChecker>();
}
bool ento::shouldRegisterConversionChecker(const CheckerManager &mgr) {
return true;
}