[clang-tidy] Add 'misc-misplaced-widening-cast' check.

Reviewers: alexfh

Subscribers: cfe-commits

Differential Revision: http://reviews.llvm.org/D16310

llvm-svn: 260223
This commit is contained in:
Daniel Marjamaki 2016-02-09 14:08:49 +00:00
parent 880a65bba5
commit ad3293744a
7 changed files with 251 additions and 0 deletions

View File

@ -12,6 +12,7 @@ add_clang_library(clangTidyMiscModule
MacroParenthesesCheck.cpp
MacroRepeatedSideEffectsCheck.cpp
MiscTidyModule.cpp
MisplacedWideningCastCheck.cpp
MoveConstantArgumentCheck.cpp
MoveConstructorInitCheck.cpp
NewDeleteOverloadsCheck.cpp

View File

@ -20,6 +20,7 @@
#include "InefficientAlgorithmCheck.h"
#include "MacroParenthesesCheck.h"
#include "MacroRepeatedSideEffectsCheck.h"
#include "MisplacedWideningCastCheck.h"
#include "MoveConstantArgumentCheck.h"
#include "MoveConstructorInitCheck.h"
#include "NewDeleteOverloadsCheck.h"
@ -63,6 +64,8 @@ public:
"misc-macro-parentheses");
CheckFactories.registerCheck<MacroRepeatedSideEffectsCheck>(
"misc-macro-repeated-side-effects");
CheckFactories.registerCheck<MisplacedWideningCastCheck>(
"misc-misplaced-widening-cast");
CheckFactories.registerCheck<MoveConstantArgumentCheck>(
"misc-move-const-arg");
CheckFactories.registerCheck<MoveConstructorInitCheck>(

View File

@ -0,0 +1,106 @@
//===--- 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();
if (Context.getIntWidth(CastType) <= Context.getIntWidth(CalcType))
return;
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

View File

@ -0,0 +1,38 @@
//===--- MisplacedWideningCastCheck.h - clang-tidy---------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISPLACED_WIDENING_CAST_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MISPLACED_WIDENING_CAST_H
#include "../ClangTidy.h"
namespace clang {
namespace tidy {
namespace misc {
/// Find explicit redundant casts of calculation results to bigger type.
/// Typically from int to long. If the intention of the cast is to avoid loss
/// of precision then the cast is misplaced, and there can be loss of
/// precision. Otherwise such cast is ineffective.
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/misc-misplaced-widening-cast.html
class MisplacedWideningCastCheck : public ClangTidyCheck {
public:
MisplacedWideningCastCheck(StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context) {}
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
};
} // namespace misc
} // namespace tidy
} // namespace clang
#endif

View File

@ -53,6 +53,7 @@ Clang-Tidy Checks
misc-inefficient-algorithm
misc-macro-parentheses
misc-macro-repeated-side-effects
misc-misplaced-widening-cast
misc-move-constructor-init
misc-new-delete-overloads
misc-noexcept-move-constructor

View File

@ -0,0 +1,39 @@
.. title:: clang-tidy - misc-misplaced-widening-cast
misc-misplaced-widening-cast
============================
This check will warn when there is a explicit redundant cast of a calculation
result to a bigger type. If the intention of the cast is to avoid loss of
precision then the cast is misplaced, and there can be loss of precision.
Otherwise the cast is ineffective.
Example code::
long f(int x) {
return (long)(x*1000);
}
The result x*1000 is first calculated using int precision. If the result
exceeds int precision there is loss of precision. Then the result is casted to
long.
If there is no loss of precision then the cast can be removed or you can
explicitly cast to int instead.
If you want to avoid loss of precision then put the cast in a proper location,
for instance::
long f(int x) {
return (long)x * 1000;
}
Floating point
--------------
Currently warnings are only written for integer conversion. No warning is
written for this code::
double f(float x) {
return (double)(x * 10.0f);
}

View File

@ -0,0 +1,63 @@
// RUN: %check_clang_tidy %s misc-misplaced-widening-cast %t
void assign(int a, int b) {
long l;
l = a * b;
l = (long)(a * b);
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long' is ineffective, or there is loss of precision before the conversion [misc-misplaced-widening-cast]
l = (long)a * b;
l = (long)(a << 8);
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long'
l = (long)b << 8;
l = static_cast<long>(a * b);
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: either cast from 'int' to 'long' is ineffective, or there is loss of precision before the conversion [misc-misplaced-widening-cast]
}
void init(unsigned int n) {
long l = (long)(n << 8);
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: either cast from 'unsigned int'
}
long ret(int a) {
return (long)(a * 1000);
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: either cast from 'int' to 'long'
}
void dontwarn1(unsigned char a, int i, unsigned char *p) {
long l;
// The result is a 9 bit value, there is no truncation in the implicit cast.
l = (long)(a + 15);
// The result is a 12 bit value, there is no truncation in the implicit cast.
l = (long)(a << 4);
// The result is a 3 bit value, there is no truncation in the implicit cast.
l = (long)((i%5)+1);
// The result is a 16 bit value, there is no truncation in the implicit cast.
l = (long)(((*p)<<8) + *(p+1));
}
template <class T> struct DontWarn2 {
void assign(T a, T b) {
long l;
l = (long)(a * b);
}
};
DontWarn2<int> DW2;
// Cast is not suspicious when casting macro.
#define A (X<<2)
long macro1(int X) {
return (long)A;
}
// Don't warn about cast in macro.
#define B(X,Y) (long)(X*Y)
long macro2(int x, int y) {
return B(x,y);
}
void floatingpoint(float a, float b) {
double d = (double)(a * b); // Currently we don't warn for this.
}