MisplacedWideningCastCheck.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. //===--- MisplacedWideningCastCheck.cpp - clang-tidy-----------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #include "MisplacedWideningCastCheck.h"
  9. #include "../utils/Matchers.h"
  10. #include "clang/AST/ASTContext.h"
  11. #include "clang/ASTMatchers/ASTMatchFinder.h"
  12. using namespace clang::ast_matchers;
  13. namespace clang::tidy::bugprone {
  14. MisplacedWideningCastCheck::MisplacedWideningCastCheck(
  15. StringRef Name, ClangTidyContext *Context)
  16. : ClangTidyCheck(Name, Context),
  17. CheckImplicitCasts(Options.get("CheckImplicitCasts", false)) {}
  18. void MisplacedWideningCastCheck::storeOptions(
  19. ClangTidyOptions::OptionMap &Opts) {
  20. Options.store(Opts, "CheckImplicitCasts", CheckImplicitCasts);
  21. }
  22. void MisplacedWideningCastCheck::registerMatchers(MatchFinder *Finder) {
  23. const auto Calc =
  24. expr(anyOf(binaryOperator(hasAnyOperatorName("+", "-", "*", "<<")),
  25. unaryOperator(hasOperatorName("~"))),
  26. hasType(isInteger()))
  27. .bind("Calc");
  28. const auto ExplicitCast = explicitCastExpr(hasDestinationType(isInteger()),
  29. has(ignoringParenImpCasts(Calc)));
  30. const auto ImplicitCast =
  31. implicitCastExpr(hasImplicitDestinationType(isInteger()),
  32. has(ignoringParenImpCasts(Calc)));
  33. const auto Cast =
  34. traverse(TK_AsIs, expr(anyOf(ExplicitCast, ImplicitCast)).bind("Cast"));
  35. Finder->addMatcher(varDecl(hasInitializer(Cast)), this);
  36. Finder->addMatcher(returnStmt(hasReturnValue(Cast)), this);
  37. Finder->addMatcher(callExpr(hasAnyArgument(Cast)), this);
  38. Finder->addMatcher(binaryOperator(hasOperatorName("="), hasRHS(Cast)), this);
  39. Finder->addMatcher(
  40. binaryOperator(isComparisonOperator(), hasEitherOperand(Cast)), this);
  41. }
  42. static unsigned getMaxCalculationWidth(const ASTContext &Context,
  43. const Expr *E) {
  44. E = E->IgnoreParenImpCasts();
  45. if (const auto *Bop = dyn_cast<BinaryOperator>(E)) {
  46. unsigned LHSWidth = getMaxCalculationWidth(Context, Bop->getLHS());
  47. unsigned RHSWidth = getMaxCalculationWidth(Context, Bop->getRHS());
  48. if (Bop->getOpcode() == BO_Mul)
  49. return LHSWidth + RHSWidth;
  50. if (Bop->getOpcode() == BO_Add)
  51. return std::max(LHSWidth, RHSWidth) + 1;
  52. if (Bop->getOpcode() == BO_Rem) {
  53. Expr::EvalResult Result;
  54. if (Bop->getRHS()->EvaluateAsInt(Result, Context))
  55. return Result.Val.getInt().getActiveBits();
  56. } else if (Bop->getOpcode() == BO_Shl) {
  57. Expr::EvalResult Result;
  58. if (Bop->getRHS()->EvaluateAsInt(Result, Context)) {
  59. // We don't handle negative values and large values well. It is assumed
  60. // that compiler warnings are written for such values so the user will
  61. // fix that.
  62. return LHSWidth + Result.Val.getInt().getExtValue();
  63. }
  64. // Unknown bitcount, assume there is truncation.
  65. return 1024U;
  66. }
  67. } else if (const auto *Uop = dyn_cast<UnaryOperator>(E)) {
  68. // There is truncation when ~ is used.
  69. if (Uop->getOpcode() == UO_Not)
  70. return 1024U;
  71. QualType T = Uop->getType();
  72. return T->isIntegerType() ? Context.getIntWidth(T) : 1024U;
  73. } else if (const auto *I = dyn_cast<IntegerLiteral>(E)) {
  74. return I->getValue().getActiveBits();
  75. }
  76. return Context.getIntWidth(E->getType());
  77. }
  78. static int relativeIntSizes(BuiltinType::Kind Kind) {
  79. switch (Kind) {
  80. case BuiltinType::UChar:
  81. return 1;
  82. case BuiltinType::SChar:
  83. return 1;
  84. case BuiltinType::Char_U:
  85. return 1;
  86. case BuiltinType::Char_S:
  87. return 1;
  88. case BuiltinType::UShort:
  89. return 2;
  90. case BuiltinType::Short:
  91. return 2;
  92. case BuiltinType::UInt:
  93. return 3;
  94. case BuiltinType::Int:
  95. return 3;
  96. case BuiltinType::ULong:
  97. return 4;
  98. case BuiltinType::Long:
  99. return 4;
  100. case BuiltinType::ULongLong:
  101. return 5;
  102. case BuiltinType::LongLong:
  103. return 5;
  104. case BuiltinType::UInt128:
  105. return 6;
  106. case BuiltinType::Int128:
  107. return 6;
  108. default:
  109. return 0;
  110. }
  111. }
  112. static int relativeCharSizes(BuiltinType::Kind Kind) {
  113. switch (Kind) {
  114. case BuiltinType::UChar:
  115. return 1;
  116. case BuiltinType::SChar:
  117. return 1;
  118. case BuiltinType::Char_U:
  119. return 1;
  120. case BuiltinType::Char_S:
  121. return 1;
  122. case BuiltinType::Char16:
  123. return 2;
  124. case BuiltinType::Char32:
  125. return 3;
  126. default:
  127. return 0;
  128. }
  129. }
  130. static int relativeCharSizesW(BuiltinType::Kind Kind) {
  131. switch (Kind) {
  132. case BuiltinType::UChar:
  133. return 1;
  134. case BuiltinType::SChar:
  135. return 1;
  136. case BuiltinType::Char_U:
  137. return 1;
  138. case BuiltinType::Char_S:
  139. return 1;
  140. case BuiltinType::WChar_U:
  141. return 2;
  142. case BuiltinType::WChar_S:
  143. return 2;
  144. default:
  145. return 0;
  146. }
  147. }
  148. static bool isFirstWider(BuiltinType::Kind First, BuiltinType::Kind Second) {
  149. int FirstSize, SecondSize;
  150. if ((FirstSize = relativeIntSizes(First)) != 0 &&
  151. (SecondSize = relativeIntSizes(Second)) != 0)
  152. return FirstSize > SecondSize;
  153. if ((FirstSize = relativeCharSizes(First)) != 0 &&
  154. (SecondSize = relativeCharSizes(Second)) != 0)
  155. return FirstSize > SecondSize;
  156. if ((FirstSize = relativeCharSizesW(First)) != 0 &&
  157. (SecondSize = relativeCharSizesW(Second)) != 0)
  158. return FirstSize > SecondSize;
  159. return false;
  160. }
  161. void MisplacedWideningCastCheck::check(const MatchFinder::MatchResult &Result) {
  162. const auto *Cast = Result.Nodes.getNodeAs<CastExpr>("Cast");
  163. if (!CheckImplicitCasts && isa<ImplicitCastExpr>(Cast))
  164. return;
  165. if (Cast->getBeginLoc().isMacroID())
  166. return;
  167. const auto *Calc = Result.Nodes.getNodeAs<Expr>("Calc");
  168. if (Calc->getBeginLoc().isMacroID())
  169. return;
  170. if (Cast->isTypeDependent() || Cast->isValueDependent() ||
  171. Calc->isTypeDependent() || Calc->isValueDependent())
  172. return;
  173. ASTContext &Context = *Result.Context;
  174. QualType CastType = Cast->getType();
  175. QualType CalcType = Calc->getType();
  176. // Explicit truncation using cast.
  177. if (Context.getIntWidth(CastType) < Context.getIntWidth(CalcType))
  178. return;
  179. // If CalcType and CastType have same size then there is no real danger, but
  180. // there can be a portability problem.
  181. if (Context.getIntWidth(CastType) == Context.getIntWidth(CalcType)) {
  182. const auto *CastBuiltinType =
  183. dyn_cast<BuiltinType>(CastType->getUnqualifiedDesugaredType());
  184. const auto *CalcBuiltinType =
  185. dyn_cast<BuiltinType>(CalcType->getUnqualifiedDesugaredType());
  186. if (!CastBuiltinType || !CalcBuiltinType)
  187. return;
  188. if (!isFirstWider(CastBuiltinType->getKind(), CalcBuiltinType->getKind()))
  189. return;
  190. }
  191. // Don't write a warning if we can easily see that the result is not
  192. // truncated.
  193. if (Context.getIntWidth(CalcType) >= getMaxCalculationWidth(Context, Calc))
  194. return;
  195. diag(Cast->getBeginLoc(), "either cast from %0 to %1 is ineffective, or "
  196. "there is loss of precision before the conversion")
  197. << CalcType << CastType;
  198. }
  199. } // namespace clang::tidy::bugprone