ImplicitWideningOfMultiplicationResultCheck.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. //===--- ImplicitWideningOfMultiplicationResultCheck.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 "ImplicitWideningOfMultiplicationResultCheck.h"
  9. #include "clang/AST/ASTContext.h"
  10. #include "clang/ASTMatchers/ASTMatchFinder.h"
  11. #include <optional>
  12. using namespace clang::ast_matchers;
  13. namespace clang {
  14. namespace {
  15. AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) {
  16. return Node.isPartOfExplicitCast();
  17. }
  18. } // namespace
  19. } // namespace clang
  20. namespace clang::tidy::bugprone {
  21. static const Expr *getLHSOfMulBinOp(const Expr *E) {
  22. assert(E == E->IgnoreParens() && "Already skipped all parens!");
  23. // Is this: long r = int(x) * int(y); ?
  24. // FIXME: shall we skip brackets/casts/etc?
  25. const auto *BO = dyn_cast<BinaryOperator>(E);
  26. if (!BO || BO->getOpcode() != BO_Mul)
  27. // FIXME: what about: long r = int(x) + (int(y) * int(z)); ?
  28. return nullptr;
  29. return BO->getLHS()->IgnoreParens();
  30. }
  31. ImplicitWideningOfMultiplicationResultCheck::
  32. ImplicitWideningOfMultiplicationResultCheck(StringRef Name,
  33. ClangTidyContext *Context)
  34. : ClangTidyCheck(Name, Context),
  35. UseCXXStaticCastsInCppSources(
  36. Options.get("UseCXXStaticCastsInCppSources", true)),
  37. UseCXXHeadersInCppSources(Options.get("UseCXXHeadersInCppSources", true)),
  38. IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
  39. utils::IncludeSorter::IS_LLVM),
  40. areDiagsSelfContained()) {}
  41. void ImplicitWideningOfMultiplicationResultCheck::registerPPCallbacks(
  42. const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
  43. IncludeInserter.registerPreprocessor(PP);
  44. }
  45. void ImplicitWideningOfMultiplicationResultCheck::storeOptions(
  46. ClangTidyOptions::OptionMap &Opts) {
  47. Options.store(Opts, "UseCXXStaticCastsInCppSources",
  48. UseCXXStaticCastsInCppSources);
  49. Options.store(Opts, "UseCXXHeadersInCppSources", UseCXXHeadersInCppSources);
  50. Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
  51. }
  52. std::optional<FixItHint>
  53. ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader(
  54. SourceLocation File) {
  55. return IncludeInserter.createIncludeInsertion(
  56. Result->SourceManager->getFileID(File),
  57. ShouldUseCXXHeader ? "<cstddef>" : "<stddef.h>");
  58. }
  59. void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
  60. const ImplicitCastExpr *ICE) {
  61. ASTContext *Context = Result->Context;
  62. const Expr *E = ICE->getSubExpr()->IgnoreParens();
  63. QualType Ty = ICE->getType();
  64. QualType ETy = E->getType();
  65. assert(!ETy->isDependentType() && !Ty->isDependentType() &&
  66. "Don't expect to ever get here in template Context.");
  67. // This must be a widening cast. Else we do not care.
  68. unsigned SrcWidth = Context->getIntWidth(ETy);
  69. unsigned TgtWidth = Context->getIntWidth(Ty);
  70. if (TgtWidth <= SrcWidth)
  71. return;
  72. // Does the index expression look like it might be unintentionally computed
  73. // in a narrower-than-wanted type?
  74. const Expr *LHS = getLHSOfMulBinOp(E);
  75. if (!LHS)
  76. return;
  77. // Ok, looks like we should diagnose this.
  78. diag(E->getBeginLoc(), "performing an implicit widening conversion to type "
  79. "%0 of a multiplication performed in type %1")
  80. << Ty << E->getType();
  81. {
  82. auto Diag = diag(E->getBeginLoc(),
  83. "make conversion explicit to silence this warning",
  84. DiagnosticIDs::Note)
  85. << E->getSourceRange();
  86. if (ShouldUseCXXStaticCast)
  87. Diag << FixItHint::CreateInsertion(
  88. E->getBeginLoc(), "static_cast<" + Ty.getAsString() + ">(")
  89. << FixItHint::CreateInsertion(E->getEndLoc(), ")");
  90. else
  91. Diag << FixItHint::CreateInsertion(E->getBeginLoc(),
  92. "(" + Ty.getAsString() + ")(")
  93. << FixItHint::CreateInsertion(E->getEndLoc(), ")");
  94. Diag << includeStddefHeader(E->getBeginLoc());
  95. }
  96. QualType WideExprTy;
  97. // Get Ty of the same signedness as ExprTy, because we only want to suggest
  98. // to widen the computation, but not change it's signedness domain.
  99. if (Ty->isSignedIntegerType() == ETy->isSignedIntegerType())
  100. WideExprTy = Ty;
  101. else if (Ty->isSignedIntegerType()) {
  102. assert(ETy->isUnsignedIntegerType() &&
  103. "Expected source type to be signed.");
  104. WideExprTy = Context->getCorrespondingUnsignedType(Ty);
  105. } else {
  106. assert(Ty->isUnsignedIntegerType() &&
  107. "Expected target type to be unsigned.");
  108. assert(ETy->isSignedIntegerType() &&
  109. "Expected source type to be unsigned.");
  110. WideExprTy = Context->getCorrespondingSignedType(Ty);
  111. }
  112. {
  113. auto Diag = diag(E->getBeginLoc(), "perform multiplication in a wider type",
  114. DiagnosticIDs::Note)
  115. << LHS->getSourceRange();
  116. if (ShouldUseCXXStaticCast)
  117. Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
  118. "static_cast<" +
  119. WideExprTy.getAsString() + ">(")
  120. << FixItHint::CreateInsertion(LHS->getEndLoc(), ")");
  121. else
  122. Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
  123. "(" + WideExprTy.getAsString() + ")");
  124. Diag << includeStddefHeader(LHS->getBeginLoc());
  125. }
  126. }
  127. void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
  128. const Expr *E) {
  129. ASTContext *Context = Result->Context;
  130. // We are looking for a pointer offset operation,
  131. // with one hand being a pointer, and another one being an offset.
  132. const Expr *PointerExpr, *IndexExpr;
  133. if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
  134. PointerExpr = BO->getLHS();
  135. IndexExpr = BO->getRHS();
  136. } else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
  137. PointerExpr = ASE->getLHS();
  138. IndexExpr = ASE->getRHS();
  139. } else
  140. return;
  141. if (IndexExpr->getType()->isPointerType())
  142. std::swap(PointerExpr, IndexExpr);
  143. if (!PointerExpr->getType()->isPointerType() ||
  144. IndexExpr->getType()->isPointerType())
  145. return;
  146. IndexExpr = IndexExpr->IgnoreParens();
  147. QualType IndexExprType = IndexExpr->getType();
  148. // If the index expression's type is not known (i.e. we are in a template),
  149. // we can't do anything here.
  150. if (IndexExprType->isDependentType())
  151. return;
  152. QualType SSizeTy = Context->getPointerDiffType();
  153. QualType USizeTy = Context->getSizeType();
  154. QualType SizeTy = IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy;
  155. // FIXME: is there a way to actually get the QualType for size_t/ptrdiff_t?
  156. // Note that SizeTy.getAsString() will be unsigned long/..., NOT size_t!
  157. StringRef TyAsString =
  158. IndexExprType->isSignedIntegerType() ? "ptrdiff_t" : "size_t";
  159. // So, is size_t actually wider than the result of the multiplication?
  160. if (Context->getIntWidth(IndexExprType) >= Context->getIntWidth(SizeTy))
  161. return;
  162. // Does the index expression look like it might be unintentionally computed
  163. // in a narrower-than-wanted type?
  164. const Expr *LHS = getLHSOfMulBinOp(IndexExpr);
  165. if (!LHS)
  166. return;
  167. // Ok, looks like we should diagnose this.
  168. diag(E->getBeginLoc(),
  169. "result of multiplication in type %0 is used as a pointer offset after "
  170. "an implicit widening conversion to type '%1'")
  171. << IndexExprType << TyAsString;
  172. {
  173. auto Diag = diag(IndexExpr->getBeginLoc(),
  174. "make conversion explicit to silence this warning",
  175. DiagnosticIDs::Note)
  176. << IndexExpr->getSourceRange();
  177. if (ShouldUseCXXStaticCast)
  178. Diag << FixItHint::CreateInsertion(
  179. IndexExpr->getBeginLoc(),
  180. (Twine("static_cast<") + TyAsString + ">(").str())
  181. << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")");
  182. else
  183. Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),
  184. (Twine("(") + TyAsString + ")(").str())
  185. << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")");
  186. Diag << includeStddefHeader(IndexExpr->getBeginLoc());
  187. }
  188. {
  189. auto Diag =
  190. diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type",
  191. DiagnosticIDs::Note)
  192. << LHS->getSourceRange();
  193. if (ShouldUseCXXStaticCast)
  194. Diag << FixItHint::CreateInsertion(
  195. LHS->getBeginLoc(),
  196. (Twine("static_cast<") + TyAsString + ">(").str())
  197. << FixItHint::CreateInsertion(LHS->getEndLoc(), ")");
  198. else
  199. Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
  200. (Twine("(") + TyAsString + ")").str());
  201. Diag << includeStddefHeader(LHS->getBeginLoc());
  202. }
  203. }
  204. void ImplicitWideningOfMultiplicationResultCheck::registerMatchers(
  205. MatchFinder *Finder) {
  206. Finder->addMatcher(implicitCastExpr(unless(anyOf(isInTemplateInstantiation(),
  207. isPartOfExplicitCast())),
  208. hasCastKind(CK_IntegralCast))
  209. .bind("x"),
  210. this);
  211. Finder->addMatcher(
  212. arraySubscriptExpr(unless(isInTemplateInstantiation())).bind("x"), this);
  213. Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()),
  214. hasType(isAnyPointer()),
  215. hasAnyOperatorName("+", "-", "+=", "-="))
  216. .bind("x"),
  217. this);
  218. }
  219. void ImplicitWideningOfMultiplicationResultCheck::check(
  220. const MatchFinder::MatchResult &Result) {
  221. this->Result = &Result;
  222. ShouldUseCXXStaticCast =
  223. UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus;
  224. ShouldUseCXXHeader =
  225. UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus;
  226. if (const auto *MatchedDecl = Result.Nodes.getNodeAs<ImplicitCastExpr>("x"))
  227. handleImplicitCastExpr(MatchedDecl);
  228. else if (const auto *MatchedDecl =
  229. Result.Nodes.getNodeAs<ArraySubscriptExpr>("x"))
  230. handlePointerOffsetting(MatchedDecl);
  231. else if (const auto *MatchedDecl =
  232. Result.Nodes.getNodeAs<BinaryOperator>("x"))
  233. handlePointerOffsetting(MatchedDecl);
  234. }
  235. } // namespace clang::tidy::bugprone