summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp')
-rw-r--r--clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp233
1 files changed, 233 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp
new file mode 100644
index 00000000000..e263366b664
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/MisplacedWideningCastCheck.cpp
@@ -0,0 +1,233 @@
+//===--- 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
OpenPOWER on IntegriCloud