|  | //===--- 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 |