forked from OSchip/llvm-project
131 lines
4.8 KiB
C++
131 lines
4.8 KiB
C++
//===--- MisplacedWideningCastCheck.cpp - clang-tidy-----------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MisplacedWideningCastCheck.h"
|
|
#include "clang/AST/ASTContext.h"
|
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
|
|
|
using namespace clang::ast_matchers;
|
|
|
|
namespace clang {
|
|
namespace tidy {
|
|
namespace misc {
|
|
|
|
void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
|
|
auto Calc = expr(anyOf(binaryOperator(anyOf(
|
|
hasOperatorName("+"), hasOperatorName("-"),
|
|
hasOperatorName("*"), hasOperatorName("<<"))),
|
|
unaryOperator(hasOperatorName("~"))),
|
|
hasType(isInteger()))
|
|
.bind("Calc");
|
|
|
|
auto Cast = explicitCastExpr(anyOf(cStyleCastExpr(), cxxStaticCastExpr(),
|
|
cxxReinterpretCastExpr()),
|
|
hasDestinationType(isInteger()), has(Calc))
|
|
.bind("Cast");
|
|
|
|
Finder->addMatcher(varDecl(has(Cast)), this);
|
|
Finder->addMatcher(returnStmt(has(Cast)), this);
|
|
Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this);
|
|
}
|
|
|
|
static unsigned getMaxCalculationWidth(ASTContext &Context, const Expr *E) {
|
|
E = E->IgnoreParenImpCasts();
|
|
|
|
if (const auto *Bop = dyn_cast<BinaryOperator>(E)) {
|
|
unsigned LHSWidth = getMaxCalculationWidth(Context, Bop->getLHS());
|
|
unsigned RHSWidth = getMaxCalculationWidth(Context, Bop->getRHS());
|
|
if (Bop->getOpcode() == BO_Mul)
|
|
return LHSWidth + RHSWidth;
|
|
if (Bop->getOpcode() == BO_Add)
|
|
return std::max(LHSWidth, RHSWidth) + 1;
|
|
if (Bop->getOpcode() == BO_Rem) {
|
|
llvm::APSInt Val;
|
|
if (Bop->getRHS()->EvaluateAsInt(Val, Context))
|
|
return Val.getActiveBits();
|
|
} else if (Bop->getOpcode() == BO_Shl) {
|
|
llvm::APSInt Bits;
|
|
if (Bop->getRHS()->EvaluateAsInt(Bits, Context)) {
|
|
// We don't handle negative values and large values well. It is assumed
|
|
// that compiler warnings are written for such values so the user will
|
|
// fix that.
|
|
return LHSWidth + Bits.getExtValue();
|
|
}
|
|
|
|
// Unknown bitcount, assume there is truncation.
|
|
return 1024U;
|
|
}
|
|
} else if (const auto *Uop = dyn_cast<UnaryOperator>(E)) {
|
|
// There is truncation when ~ is used.
|
|
if (Uop->getOpcode() == UO_Not)
|
|
return 1024U;
|
|
|
|
QualType T = Uop->getType();
|
|
return T->isIntegerType() ? Context.getIntWidth(T) : 1024U;
|
|
} else if (const auto *I = dyn_cast<IntegerLiteral>(E)) {
|
|
return I->getValue().getActiveBits();
|
|
}
|
|
|
|
return Context.getIntWidth(E->getType());
|
|
}
|
|
|
|
void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) {
|
|
const auto *Cast = Result.Nodes.getNodeAs<ExplicitCastExpr>("Cast");
|
|
if (Cast->getLocStart().isMacroID())
|
|
return;
|
|
|
|
const auto *Calc = Result.Nodes.getNodeAs<Expr>("Calc");
|
|
if (Calc->getLocStart().isMacroID())
|
|
return;
|
|
|
|
ASTContext &Context = *Result.Context;
|
|
|
|
QualType CastType = Cast->getType();
|
|
QualType CalcType = Calc->getType();
|
|
|
|
// Explicit truncation using cast.
|
|
if (Context.getIntWidth(CastType) < Context.getIntWidth(CalcType))
|
|
return;
|
|
|
|
// If CalcType and CastType have same size then there is no real danger, but
|
|
// there can be a portability problem.
|
|
if (Context.getIntWidth(CastType) == Context.getIntWidth(CalcType)) {
|
|
if (CalcType->isSpecificBuiltinType(BuiltinType::Int) ||
|
|
CalcType->isSpecificBuiltinType(BuiltinType::UInt)) {
|
|
// There should be a warning when casting from int to long or long long.
|
|
if (!CastType->isSpecificBuiltinType(BuiltinType::Long) &&
|
|
!CastType->isSpecificBuiltinType(BuiltinType::ULong) &&
|
|
!CastType->isSpecificBuiltinType(BuiltinType::LongLong) &&
|
|
!CastType->isSpecificBuiltinType(BuiltinType::ULongLong))
|
|
return;
|
|
} else if (CalcType->isSpecificBuiltinType(BuiltinType::Long) ||
|
|
CalcType->isSpecificBuiltinType(BuiltinType::ULong)) {
|
|
// There should be a warning when casting from long to long long.
|
|
if (!CastType->isSpecificBuiltinType(BuiltinType::LongLong) &&
|
|
!CastType->isSpecificBuiltinType(BuiltinType::ULongLong))
|
|
return;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Don't write a warning if we can easily see that the result is not
|
|
// truncated.
|
|
if (Context.getIntWidth(CalcType) >= getMaxCalculationWidth(Context, Calc))
|
|
return;
|
|
|
|
diag(Cast->getLocStart(), "either cast from %0 to %1 is ineffective, or "
|
|
"there is loss of precision before the conversion")
|
|
<< CalcType << CastType;
|
|
}
|
|
|
|
} // namespace misc
|
|
} // namespace tidy
|
|
} // namespace clang
|