forked from OSchip/llvm-project
234 lines
7.1 KiB
C++
234 lines
7.1 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 "../utils/Matchers.h"
|
||
|
#include "clang/AST/ASTContext.h"
|
||
|
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||
|
|
||
|
using namespace clang::ast_matchers;
|
||
|
|
||
|
namespace clang {
|
||
|
namespace tidy {
|
||
|
namespace bugprone {
|
||
|
|
||
|
MisplacedWideningCastCheck::MisplacedWideningCastCheck(
|
||
|
StringRef Name, ClangTidyContext *Context)
|
||
|
: ClangTidyCheck(Name, Context),
|
||
|
CheckImplicitCasts(Options.get("CheckImplicitCasts", false)) {}
|
||
|
|
||
|
void MisplacedWideningCastCheck::storeOptions(
|
||
|
ClangTidyOptions::OptionMap &Opts) {
|
||
|
Options.store(Opts, "CheckImplicitCasts", CheckImplicitCasts);
|
||
|
}
|
||
|
|
||
|
void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
|
||
|
const auto Calc =
|
||
|
expr(anyOf(binaryOperator(
|
||
|
anyOf(hasOperatorName("+"), hasOperatorName("-"),
|
||
|
hasOperatorName("*"), hasOperatorName("<<"))),
|
||
|
unaryOperator(hasOperatorName("~"))),
|
||
|
hasType(isInteger()))
|
||
|
.bind("Calc");
|
||
|
|
||
|
const auto ExplicitCast = explicitCastExpr(hasDestinationType(isInteger()),
|
||
|
has(ignoringParenImpCasts(Calc)));
|
||
|
const auto ImplicitCast =
|
||
|
implicitCastExpr(hasImplicitDestinationType(isInteger()),
|
||
|
has(ignoringParenImpCasts(Calc)));
|
||
|
const auto Cast = expr(anyOf(ExplicitCast, ImplicitCast)).bind("Cast");
|
||
|
|
||
|
Finder->addMatcher(varDecl(hasInitializer(Cast)), this);
|
||
|
Finder->addMatcher(returnStmt(hasReturnValue(Cast)), this);
|
||
|
Finder->addMatcher(callExpr(hasAnyArgument(Cast)), this);
|
||
|
Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this);
|
||
|
Finder->addMatcher(
|
||
|
binaryOperator(matchers::isComparisonOperator(), hasEitherOperand(Cast)),
|
||
|
this);
|
||
|
}
|
||
|
|
||
|
static unsigned getMaxCalculationWidth(const 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());
|
||
|
}
|
||
|
|
||
|
static int relativeIntSizes(BuiltinType::Kind Kind) {
|
||
|
switch (Kind) {
|
||
|
case BuiltinType::UChar:
|
||
|
return 1;
|
||
|
case BuiltinType::SChar:
|
||
|
return 1;
|
||
|
case BuiltinType::Char_U:
|
||
|
return 1;
|
||
|
case BuiltinType::Char_S:
|
||
|
return 1;
|
||
|
case BuiltinType::UShort:
|
||
|
return 2;
|
||
|
case BuiltinType::Short:
|
||
|
return 2;
|
||
|
case BuiltinType::UInt:
|
||
|
return 3;
|
||
|
case BuiltinType::Int:
|
||
|
return 3;
|
||
|
case BuiltinType::ULong:
|
||
|
return 4;
|
||
|
case BuiltinType::Long:
|
||
|
return 4;
|
||
|
case BuiltinType::ULongLong:
|
||
|
return 5;
|
||
|
case BuiltinType::LongLong:
|
||
|
return 5;
|
||
|
case BuiltinType::UInt128:
|
||
|
return 6;
|
||
|
case BuiltinType::Int128:
|
||
|
return 6;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int relativeCharSizes(BuiltinType::Kind Kind) {
|
||
|
switch (Kind) {
|
||
|
case BuiltinType::UChar:
|
||
|
return 1;
|
||
|
case BuiltinType::SChar:
|
||
|
return 1;
|
||
|
case BuiltinType::Char_U:
|
||
|
return 1;
|
||
|
case BuiltinType::Char_S:
|
||
|
return 1;
|
||
|
case BuiltinType::Char16:
|
||
|
return 2;
|
||
|
case BuiltinType::Char32:
|
||
|
return 3;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int relativeCharSizesW(BuiltinType::Kind Kind) {
|
||
|
switch (Kind) {
|
||
|
case BuiltinType::UChar:
|
||
|
return 1;
|
||
|
case BuiltinType::SChar:
|
||
|
return 1;
|
||
|
case BuiltinType::Char_U:
|
||
|
return 1;
|
||
|
case BuiltinType::Char_S:
|
||
|
return 1;
|
||
|
case BuiltinType::WChar_U:
|
||
|
return 2;
|
||
|
case BuiltinType::WChar_S:
|
||
|
return 2;
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second) {
|
||
|
int FirstSize, SecondSize;
|
||
|
if ((FirstSize = relativeIntSizes(First)) != 0 &&
|
||
|
(SecondSize = relativeIntSizes(Second)) != 0)
|
||
|
return FirstSize > SecondSize;
|
||
|
if ((FirstSize = relativeCharSizes(First)) != 0 &&
|
||
|
(SecondSize = relativeCharSizes(Second)) != 0)
|
||
|
return FirstSize > SecondSize;
|
||
|
if ((FirstSize = relativeCharSizesW(First)) != 0 &&
|
||
|
(SecondSize = relativeCharSizesW(Second)) != 0)
|
||
|
return FirstSize > SecondSize;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) {
|
||
|
const auto *Cast = Result.Nodes.getNodeAs<CastExpr>("Cast");
|
||
|
if (!CheckImplicitCasts && isa<ImplicitCastExpr>(Cast))
|
||
|
return;
|
||
|
if (Cast->getLocStart().isMacroID())
|
||
|
return;
|
||
|
|
||
|
const auto *Calc = Result.Nodes.getNodeAs<Expr>("Calc");
|
||
|
if (Calc->getLocStart().isMacroID())
|
||
|
return;
|
||
|
|
||
|
if (Cast->isTypeDependent() || Cast->isValueDependent() ||
|
||
|
Calc->isTypeDependent() || Calc->isValueDependent())
|
||
|
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)) {
|
||
|
const auto *CastBuiltinType =
|
||
|
dyn_cast<BuiltinType>(CastType->getUnqualifiedDesugaredType());
|
||
|
const auto *CalcBuiltinType =
|
||
|
dyn_cast<BuiltinType>(CalcType->getUnqualifiedDesugaredType());
|
||
|
if (CastBuiltinType && CalcBuiltinType &&
|
||
|
!isFirstWider(CastBuiltinType->getKind(), CalcBuiltinType->getKind()))
|
||
|
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 bugprone
|
||
|
} // namespace tidy
|
||
|
} // namespace clang
|