[clang-tools-extra] 46b8ea2 - [clang-tidy] Add check for implicit widening of multiplication result

Roman Lebedev via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 13 11:41:44 PDT 2021


Author: Roman Lebedev
Date: 2021-04-13T21:41:22+03:00
New Revision: 46b8ea2fff90b44b7ae558999721cf30f9b83aa9

URL: https://github.com/llvm/llvm-project/commit/46b8ea2fff90b44b7ae558999721cf30f9b83aa9
DIFF: https://github.com/llvm/llvm-project/commit/46b8ea2fff90b44b7ae558999721cf30f9b83aa9.diff

LOG: [clang-tidy] Add check for implicit widening of multiplication result

Overflows are never fun.
In most cases (in most of the code), they are rare,
because usually you e.g. don't have as many elements.

However, it's exceptionally easy to fall into this pitfail
in code that deals with images, because, assuming 4-channel 32-bit FP data,
you need *just* ~269 megapixel image to case an overflow
when computing at least the total byte count.

In [[ https://github.com/darktable-org/darktable | darktable ]], there is a *long*, painful history of dealing with such bugs:
* https://github.com/darktable-org/darktable/pull/7740
* https://github.com/darktable-org/darktable/pull/7419
* https://github.com/darktable-org/darktable/commit/eea1989f2c9fa76710db07baaec4c19c1e40e81c
* https://github.com/darktable-org/darktable/commit/70626dd95bf0fab36f2d011dab075e3ebbf7aa28
* https://github.com/darktable-org/darktable/pull/670
* https://github.com/darktable-org/darktable/commit/38c69fb1b2bc90057c569242cb9945a10be0b583

and yet they clearly keep resurfacing still.

It would be immensely helpful to have a diagnostic for those patterns,
which is what this change proposes.

Currently, i only diagnose the most obvious case, where multiplication
is directly widened with no other expressions inbetween,
(i.e. `long r = (int)a * (int)b` but not even e.g. `long r = ((int)a * (int)b)`)
however that might be worth relaxing later.

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D93822

Added: 
    clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
    clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.h
    clang-tools-extra/docs/clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result.rst
    clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-array-subscript-expression.cpp
    clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-char.cpp
    clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-extint.cpp
    clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-int.cpp
    clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-pointer-offset.cpp
    clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-short.cpp

Modified: 
    clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
    clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/docs/clang-tidy/checks/list.rst
    clang/include/clang/AST/ASTContext.h
    clang/lib/AST/ASTContext.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
index 7f4d40f970117..9cdadf7bf92ba 100644
--- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -22,6 +22,7 @@
 #include "FoldInitTypeCheck.h"
 #include "ForwardDeclarationNamespaceCheck.h"
 #include "ForwardingReferenceOverloadCheck.h"
+#include "ImplicitWideningOfMultiplicationResultCheck.h"
 #include "InaccurateEraseCheck.h"
 #include "IncorrectRoundingsCheck.h"
 #include "InfiniteLoopCheck.h"
@@ -97,6 +98,8 @@ class BugproneModule : public ClangTidyModule {
         "bugprone-forward-declaration-namespace");
     CheckFactories.registerCheck<ForwardingReferenceOverloadCheck>(
         "bugprone-forwarding-reference-overload");
+    CheckFactories.registerCheck<ImplicitWideningOfMultiplicationResultCheck>(
+        "bugprone-implicit-widening-of-multiplication-result");
     CheckFactories.registerCheck<InaccurateEraseCheck>(
         "bugprone-inaccurate-erase");
     CheckFactories.registerCheck<IncorrectRoundingsCheck>(

diff  --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
index b3684a5c101b5..b16dbf576c374 100644
--- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -17,6 +17,7 @@ add_clang_library(clangTidyBugproneModule
   FoldInitTypeCheck.cpp
   ForwardDeclarationNamespaceCheck.cpp
   ForwardingReferenceOverloadCheck.cpp
+  ImplicitWideningOfMultiplicationResultCheck.cpp
   InaccurateEraseCheck.cpp
   IncorrectRoundingsCheck.cpp
   InfiniteLoopCheck.cpp

diff  --git a/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
new file mode 100644
index 0000000000000..29b2bdfc66c02
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
@@ -0,0 +1,277 @@
+//===--- ImplicitWideningOfMultiplicationResultCheck.cpp - clang-tidy -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ImplicitWideningOfMultiplicationResultCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace {
+AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) {
+  return Node.isPartOfExplicitCast();
+}
+} // namespace
+} // namespace clang
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+static const Expr *getLHSOfMulBinOp(const Expr *E) {
+  assert(E == E->IgnoreParens() && "Already skipped all parens!");
+  // Is this:  long r = int(x) * int(y);  ?
+  // FIXME: shall we skip brackets/casts/etc?
+  const auto *BO = dyn_cast<BinaryOperator>(E);
+  if (!BO || BO->getOpcode() != BO_Mul)
+    // FIXME: what about:  long r = int(x) + (int(y) * int(z));  ?
+    return nullptr;
+  return BO->getLHS()->IgnoreParens();
+}
+
+ImplicitWideningOfMultiplicationResultCheck::
+    ImplicitWideningOfMultiplicationResultCheck(StringRef Name,
+                                                ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      UseCXXStaticCastsInCppSources(
+          Options.get("UseCXXStaticCastsInCppSources", true)),
+      UseCXXHeadersInCppSources(Options.get("UseCXXHeadersInCppSources", true)),
+      IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
+                                               utils::IncludeSorter::IS_LLVM)) {
+}
+
+void ImplicitWideningOfMultiplicationResultCheck::registerPPCallbacks(
+    const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {
+  IncludeInserter.registerPreprocessor(PP);
+}
+
+void ImplicitWideningOfMultiplicationResultCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "UseCXXStaticCastsInCppSources",
+                UseCXXStaticCastsInCppSources);
+  Options.store(Opts, "UseCXXHeadersInCppSources", UseCXXHeadersInCppSources);
+  Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
+}
+
+llvm::Optional<FixItHint>
+ImplicitWideningOfMultiplicationResultCheck::includeStddefHeader(
+    SourceLocation File) {
+  return IncludeInserter.createIncludeInsertion(
+      Result->SourceManager->getFileID(File),
+      ShouldUseCXXHeader ? "<cstddef>" : "<stddef.h>");
+}
+
+void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
+    const ImplicitCastExpr *ICE) {
+  ASTContext *Context = Result->Context;
+
+  const Expr *E = ICE->getSubExpr()->IgnoreParens();
+  QualType Ty = ICE->getType();
+  QualType ETy = E->getType();
+
+  assert(!ETy->isDependentType() && !Ty->isDependentType() &&
+         "Don't expect to ever get here in template Context.");
+
+  // This must be a widening cast. Else we do not care.
+  unsigned SrcWidth = Context->getIntWidth(ETy);
+  unsigned TgtWidth = Context->getIntWidth(Ty);
+  if (TgtWidth <= SrcWidth)
+    return;
+
+  // Does the index expression look like it might be unintentionally computed
+  // in a narrower-than-wanted type?
+  const Expr *LHS = getLHSOfMulBinOp(E);
+  if (!LHS)
+    return;
+
+  // Ok, looks like we should diagnose this.
+  diag(E->getBeginLoc(), "performing an implicit widening conversion to type "
+                         "%0 of a multiplication performed in type %1")
+      << Ty << E->getType();
+
+  {
+    auto Diag = diag(E->getBeginLoc(),
+                     "make conversion explicit to silence this warning",
+                     DiagnosticIDs::Note)
+                << E->getSourceRange();
+
+    if (ShouldUseCXXStaticCast)
+      Diag << FixItHint::CreateInsertion(
+                  E->getBeginLoc(), "static_cast<" + Ty.getAsString() + ">(")
+           << FixItHint::CreateInsertion(E->getEndLoc(), ")");
+    else
+      Diag << FixItHint::CreateInsertion(E->getBeginLoc(),
+                                         "(" + Ty.getAsString() + ")(")
+           << FixItHint::CreateInsertion(E->getEndLoc(), ")");
+    Diag << includeStddefHeader(E->getBeginLoc());
+  }
+
+  QualType WideExprTy;
+  // Get Ty of the same signedness as ExprTy, because we only want to suggest
+  // to widen the computation, but not change it's signedness domain.
+  if (Ty->isSignedIntegerType() == ETy->isSignedIntegerType())
+    WideExprTy = Ty;
+  else if (Ty->isSignedIntegerType()) {
+    assert(ETy->isUnsignedIntegerType() &&
+           "Expected source type to be signed.");
+    WideExprTy = Context->getCorrespondingUnsignedType(Ty);
+  } else {
+    assert(Ty->isUnsignedIntegerType() &&
+           "Expected target type to be unsigned.");
+    assert(ETy->isSignedIntegerType() &&
+           "Expected source type to be unsigned.");
+    WideExprTy = Context->getCorrespondingSignedType(Ty);
+  }
+
+  {
+    auto Diag = diag(E->getBeginLoc(), "perform multiplication in a wider type",
+                     DiagnosticIDs::Note)
+                << LHS->getSourceRange();
+
+    if (ShouldUseCXXStaticCast)
+      Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
+                                         "static_cast<" +
+                                             WideExprTy.getAsString() + ">(")
+           << FixItHint::CreateInsertion(LHS->getEndLoc(), ")");
+    else
+      Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
+                                         "(" + WideExprTy.getAsString() + ")");
+    Diag << includeStddefHeader(LHS->getBeginLoc());
+  }
+}
+
+void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
+    const Expr *E) {
+  ASTContext *Context = Result->Context;
+
+  // We are looking for a pointer offset operation,
+  // with one hand being a pointer, and another one being an offset.
+  const Expr *PointerExpr, *IndexExpr;
+  if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
+    PointerExpr = BO->getLHS();
+    IndexExpr = BO->getRHS();
+  } else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
+    PointerExpr = ASE->getLHS();
+    IndexExpr = ASE->getRHS();
+  } else
+    return;
+
+  if (IndexExpr->getType()->isPointerType())
+    std::swap(PointerExpr, IndexExpr);
+
+  if (!PointerExpr->getType()->isPointerType() ||
+      IndexExpr->getType()->isPointerType())
+    return;
+
+  IndexExpr = IndexExpr->IgnoreParens();
+
+  QualType IndexExprType = IndexExpr->getType();
+
+  // If the index expression's type is not known (i.e. we are in a template),
+  // we can't do anything here.
+  if (IndexExprType->isDependentType())
+    return;
+
+  QualType SSizeTy = Context->getPointerDiffType();
+  QualType USizeTy = Context->getSizeType();
+  QualType SizeTy = IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy;
+  // FIXME: is there a way to actually get the QualType for size_t/ptr
diff _t?
+  // Note that SizeTy.getAsString() will be unsigned long/..., NOT size_t!
+  StringRef TyAsString =
+      IndexExprType->isSignedIntegerType() ? "ptr
diff _t" : "size_t";
+
+  // So, is size_t actually wider than the result of the multiplication?
+  if (Context->getIntWidth(IndexExprType) >= Context->getIntWidth(SizeTy))
+    return;
+
+  // Does the index expression look like it might be unintentionally computed
+  // in a narrower-than-wanted type?
+  const Expr *LHS = getLHSOfMulBinOp(IndexExpr);
+  if (!LHS)
+    return;
+
+  // Ok, looks like we should diagnose this.
+  diag(E->getBeginLoc(),
+       "result of multiplication in type %0 is used as a pointer offset after "
+       "an implicit widening conversion to type '%1'")
+      << IndexExprType << TyAsString;
+
+  {
+    auto Diag = diag(IndexExpr->getBeginLoc(),
+                     "make conversion explicit to silence this warning",
+                     DiagnosticIDs::Note)
+                << IndexExpr->getSourceRange();
+
+    if (ShouldUseCXXStaticCast)
+      Diag << FixItHint::CreateInsertion(
+                  IndexExpr->getBeginLoc(),
+                  (Twine("static_cast<") + TyAsString + ">(").str())
+           << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")");
+    else
+      Diag << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),
+                                         (Twine("(") + TyAsString + ")(").str())
+           << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")");
+    Diag << includeStddefHeader(IndexExpr->getBeginLoc());
+  }
+
+  {
+    auto Diag =
+        diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type",
+             DiagnosticIDs::Note)
+        << LHS->getSourceRange();
+
+    if (ShouldUseCXXStaticCast)
+      Diag << FixItHint::CreateInsertion(
+                  LHS->getBeginLoc(),
+                  (Twine("static_cast<") + TyAsString + ">(").str())
+           << FixItHint::CreateInsertion(LHS->getEndLoc(), ")");
+    else
+      Diag << FixItHint::CreateInsertion(LHS->getBeginLoc(),
+                                         (Twine("(") + TyAsString + ")").str());
+    Diag << includeStddefHeader(LHS->getBeginLoc());
+  }
+}
+
+void ImplicitWideningOfMultiplicationResultCheck::registerMatchers(
+    MatchFinder *Finder) {
+  Finder->addMatcher(implicitCastExpr(unless(anyOf(isInTemplateInstantiation(),
+                                                   isPartOfExplicitCast())),
+                                      hasCastKind(CK_IntegralCast))
+                         .bind("x"),
+                     this);
+  Finder->addMatcher(
+      arraySubscriptExpr(unless(isInTemplateInstantiation())).bind("x"), this);
+  Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()),
+                                    hasType(isAnyPointer()),
+                                    hasAnyOperatorName("+", "-", "+=", "-="))
+                         .bind("x"),
+                     this);
+}
+
+void ImplicitWideningOfMultiplicationResultCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  this->Result = &Result;
+  ShouldUseCXXStaticCast =
+      UseCXXStaticCastsInCppSources && Result.Context->getLangOpts().CPlusPlus;
+  ShouldUseCXXHeader =
+      UseCXXHeadersInCppSources && Result.Context->getLangOpts().CPlusPlus;
+
+  if (const auto *MatchedDecl = Result.Nodes.getNodeAs<ImplicitCastExpr>("x"))
+    handleImplicitCastExpr(MatchedDecl);
+  else if (const auto *MatchedDecl =
+               Result.Nodes.getNodeAs<ArraySubscriptExpr>("x"))
+    handlePointerOffsetting(MatchedDecl);
+  else if (const auto *MatchedDecl =
+               Result.Nodes.getNodeAs<BinaryOperator>("x"))
+    handlePointerOffsetting(MatchedDecl);
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang

diff  --git a/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.h b/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.h
new file mode 100644
index 0000000000000..9688ee18278e0
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.h
@@ -0,0 +1,52 @@
+//===--- ImplicitWideningOfMultiplicationResultCheck.h ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_IMPLICITWIDENINGOFMULTIPLICATIONRESULTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_IMPLICITWIDENINGOFMULTIPLICATIONRESULTCHECK_H
+
+#include "../ClangTidyCheck.h"
+#include "../utils/IncludeInserter.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Diagnoses instances of an implicit widening of multiplication result.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result.html
+class ImplicitWideningOfMultiplicationResultCheck : public ClangTidyCheck {
+  const ast_matchers::MatchFinder::MatchResult *Result;
+  bool ShouldUseCXXStaticCast;
+  bool ShouldUseCXXHeader;
+
+  llvm::Optional<FixItHint> includeStddefHeader(SourceLocation File);
+
+  void handleImplicitCastExpr(const ImplicitCastExpr *ICE);
+  void handlePointerOffsetting(const Expr *E);
+
+public:
+  ImplicitWideningOfMultiplicationResultCheck(StringRef Name,
+                                              ClangTidyContext *Context);
+  void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
+                           Preprocessor *ModuleExpanderPP) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  const bool UseCXXStaticCastsInCppSources;
+  const bool UseCXXHeadersInCppSources;
+  utils::IncludeInserter IncludeInserter;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_IMPLICITWIDENINGOFMULTIPLICATIONRESULTCHECK_H

diff  --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 61097d1fd6242..e744d4d957ddd 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -84,6 +84,11 @@ Improvements to clang-tidy
 New checks
 ^^^^^^^^^^
 
+- New :doc:`bugprone-implicit-widening-of-multiplication-result
+  <clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result>` check.
+
+  Diagnoses instances of an implicit widening of multiplication result.
+
 - New :doc:`concurrency-thread-canceltype-asynchronous
   <clang-tidy/checks/concurrency-thread-canceltype-asynchronous>` check.
 

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result.rst
new file mode 100644
index 0000000000000..5386fdba9e7fe
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result.rst
@@ -0,0 +1,63 @@
+.. title:: clang-tidy - bugprone-implicit-widening-of-multiplication-result
+
+bugprone-implicit-widening-of-multiplication-result
+===================================================
+
+The check diagnoses instances where a result of a multiplication is implicitly
+widened, and suggests (with fix-it) to either silence the code by making
+widening explicit, or to perform the multiplication in a wider type,
+to avoid the widening afterwards.
+
+This is mainly useful when operating on a very large buffers.
+For example, consider:
+
+.. code-block:: c++
+
+  void zeroinit(char* base, unsigned width, unsigned height) {
+    for(unsigned row = 0; row != height; ++row) {
+      for(unsigned col = 0; col != width; ++col) {
+        char* ptr = base + row * width + col;
+        *ptr = 0;
+      }
+    }
+  }
+
+This is fine in general, but iff ``width * height`` overflows,
+you end up wrapping back to the beginning of ``base``
+instead of processing the entire requested buffer.
+
+Indeed, this only matters for pretty large buffers (4GB+),
+but that can happen very easily for example in image processing,
+where for that to happen you "only" need a ~269MPix image.
+
+
+Options
+-------
+
+.. option:: UseCXXStaticCastsInCppSources
+
+   When suggesting fix-its for C++ code, should C++-style ``static_cast<>()``'s
+   be suggested, or C-style casts. Defaults to ``true``.
+
+.. option:: UseCXXHeadersInCppSources
+
+   When suggesting to include the appropriate header in C++ code,
+   should ``<cstddef>`` header be suggested, or ``<stddef.h>``.
+   Defaults to ``true``.
+
+
+Examples:
+
+.. code-block:: c++
+
+  long mul(int a, int b) {
+    return a * b; // warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+  }
+
+  char* ptr_add(char *base, int a, int b) {
+    return base + a * b; // warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
+  }
+
+  char ptr_subscript(char *base, int a, int b) {
+    return base[a * b]; // warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
+  }

diff  --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index bdce63cd26b6e..a17fc80790631 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -62,6 +62,7 @@ Clang-Tidy Checks
    `bugprone-fold-init-type <bugprone-fold-init-type.html>`_,
    `bugprone-forward-declaration-namespace <bugprone-forward-declaration-namespace.html>`_,
    `bugprone-forwarding-reference-overload <bugprone-forwarding-reference-overload.html>`_,
+   `bugprone-implicit-widening-of-multiplication-result <bugprone-implicit-widening-of-multiplication-result.html>`_, "Yes"
    `bugprone-inaccurate-erase <bugprone-inaccurate-erase.html>`_, "Yes"
    `bugprone-incorrect-roundings <bugprone-incorrect-roundings.html>`_,
    `bugprone-infinite-loop <bugprone-infinite-loop.html>`_,

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-array-subscript-expression.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-array-subscript-expression.cpp
new file mode 100644
index 0000000000000..3cf6be1947620
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-array-subscript-expression.cpp
@@ -0,0 +1,90 @@
+// RUN: %check_clang_tidy -check-suffixes=ALL,C -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c
+// RUN: %check_clang_tidy -check-suffixes=ALL,CXX %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c++
+
+// RUN: %check_clang_tidy -check-suffixes=ALL,C -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- \
+// RUN:     -config='{CheckOptions: [ \
+// RUN:         {key: bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources, value: 0} \
+// RUN:     ]}' -- -target x86_64-unknown-unknown -x c
+// RUN: %check_clang_tidy -check-suffixes=ALL,C %s bugprone-implicit-widening-of-multiplication-result %t -- \
+// RUN:     -config='{CheckOptions: [ \
+// RUN:         {key: bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources, value: 0} \
+// RUN:     ]}' -- -target x86_64-unknown-unknown -x c++
+
+char *t0(char *base, int a, int b) {
+  return &base[a * b];
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:11: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptr
diff _t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-C:                    (ptr
diff _t)( )
+  // CHECK-NOTES-CXX:                  static_cast<ptr
diff _t>( )
+  // CHECK-NOTES-ALL: :[[@LINE-5]]:16: note: perform multiplication in a wider type
+  // CHECK-NOTES-C:                    (ptr
diff _t)
+  // CHECK-NOTES-CXX:                  static_cast<ptr
diff _t>()
+}
+void *t1(char *base, int a, int b) {
+  return &((a * b)[base]);
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:12: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptr
diff _t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:13: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:13: note: perform multiplication in a wider type
+}
+
+char *t2(char *base, unsigned int a, int b) {
+  return &base[a * b];
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:11: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-C:                    (size_t)
+  // CHECK-NOTES-CXX:                  static_cast<size_t>( )
+  // CHECK-NOTES-ALL: :[[@LINE-5]]:16: note: perform multiplication in a wider type
+  // CHECK-NOTES-C:                    (size_t)
+  // CHECK-NOTES-CXX:                  static_cast<size_t>()
+}
+
+char *t3(char *base, int a, unsigned int b) {
+  return &base[a * b];
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:11: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:16: note: perform multiplication in a wider type
+}
+
+char *t4(char *base, unsigned int a, unsigned int b) {
+  return &base[a * b];
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:11: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:16: note: perform multiplication in a wider type
+}
+
+char *n5(char *base, int a, int b, int c) {
+  return &base[a * b + c];
+}
+char *n6(char *base, int a, int b, int c) {
+  return &base[a + b * c];
+}
+
+char *t7(char *base, int a, int b) {
+  return &base[(a * b)];
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:11: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptr
diff _t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:17: note: perform multiplication in a wider type
+}
+char *n8(char *base, int a, int b, int c) {
+  return &base[(a * b + c)];
+}
+char *n9(char *base, int a, int b, int c) {
+  return &base[(a * b) + c];
+}
+
+char *n10(char *base, int a, int b) {
+  return &base[(long)(a * b)];
+}
+char *n11(char *base, int a, int b) {
+  return &base[(unsigned long)(a * b)];
+}
+
+#ifdef __cplusplus
+template <typename T>
+char *template_test(char *base, T a, T b) {
+  return &base[a * b];
+}
+char *template_test_instantiation(char *base, int a, int b) {
+  return template_test(base, a, b);
+}
+#endif

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-char.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-char.cpp
new file mode 100644
index 0000000000000..7e0cb361cefb3
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-char.cpp
@@ -0,0 +1,99 @@
+// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c
+// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c++
+
+// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c -fsigned-char
+// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c++ -fsigned-char
+
+// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c -funsigned-char
+// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown-x c++ -funsigned-char
+
+long t0(char a, char b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t1(char a, char b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t2(unsigned char a, char b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t3(unsigned char a, char b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t4(char a, unsigned char b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t5(char a, unsigned char b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t6(unsigned char a, unsigned char b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t7(unsigned char a, unsigned char b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t8(signed char a, char b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t9(signed char a, char b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t10(char a, signed char b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t11(char a, signed char b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t12(signed char a, signed char b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t13(signed char a, signed char b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-extint.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-extint.cpp
new file mode 100644
index 0000000000000..1da232e1902c9
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-extint.cpp
@@ -0,0 +1,21 @@
+// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c
+// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c++
+
+_ExtInt(64) t0(_ExtInt(32) a, _ExtInt(32) b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type '_ExtInt(64)' of a multiplication performed in type '_ExtInt(32)'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned _ExtInt(64) t1(_ExtInt(32) a, _ExtInt(32) b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned _ExtInt(64)' of a multiplication performed in type '_ExtInt(32)'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+_ExtInt(64) t2(unsigned _ExtInt(32) a, unsigned _ExtInt(32) b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10:  warning: performing an implicit widening conversion to type '_ExtInt(64)' of a multiplication performed in type 'unsigned _ExtInt(32)'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-int.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-int.cpp
new file mode 100644
index 0000000000000..e2184f7fcb5e2
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-int.cpp
@@ -0,0 +1,122 @@
+// RUN: %check_clang_tidy -check-suffixes=ALL,C -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c
+// RUN: %check_clang_tidy -check-suffixes=ALL,CXX %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c++
+
+// RUN: %check_clang_tidy -check-suffixes=ALL,C -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- \
+// RUN:     -config='{CheckOptions: [ \
+// RUN:         {key: bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources, value: 0} \
+// RUN:     ]}' -- -target x86_64-unknown-unknown -x c
+// RUN: %check_clang_tidy -check-suffixes=ALL,C %s bugprone-implicit-widening-of-multiplication-result %t -- \
+// RUN:     -config='{CheckOptions: [ \
+// RUN:         {key: bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources, value: 0} \
+// RUN:     ]}' -- -target x86_64-unknown-unknown -x c++
+
+long t0(int a, int b) {
+  return a * b;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-C:                    (long)( )
+  // CHECK-NOTES-CXX:                  static_cast<long>( )
+  // CHECK-NOTES-ALL: :[[@LINE-5]]:10: note: perform multiplication in a wider type
+  // CHECK-NOTES-C:                    (long)
+  // CHECK-NOTES-CXX:                  static_cast<long>()
+}
+unsigned long t1(int a, int b) {
+  return a * b;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-C:                    (unsigned long)( )
+  // CHECK-NOTES-CXX:                  static_cast<unsigned long>( )
+  // CHECK-NOTES-ALL: :[[@LINE-5]]:10: note: perform multiplication in a wider type
+  // CHECK-NOTES-C:                    (long)
+  // CHECK-NOTES-CXX:                  static_cast<long>()
+}
+
+long t2(unsigned int a, int b) {
+  return a * b;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-C:                    (long)( )
+  // CHECK-NOTES-CXX:                  static_cast<long>( )
+  // CHECK-NOTES-ALL: :[[@LINE-5]]:10: note: perform multiplication in a wider type
+  // CHECK-NOTES-C:                    (unsigned long)
+  // CHECK-NOTES-CXX:                  static_cast<unsigned long>()
+}
+unsigned long t3(unsigned int a, int b) {
+  return a * b;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-C:                    (unsigned long)( )
+  // CHECK-NOTES-CXX:                  static_cast<unsigned long>( )
+  // CHECK-NOTES-ALL: :[[@LINE-5]]:10: note: perform multiplication in a wider type
+  // CHECK-NOTES-C:                    (unsigned long)
+  // CHECK-NOTES-CXX:                  static_cast<unsigned long>()
+}
+
+long t4(int a, unsigned int b) {
+  return a * b;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t5(int a, unsigned int b) {
+  return a * b;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t6(unsigned int a, unsigned int b) {
+  return a * b;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t7(unsigned int a, unsigned int b) {
+  return a * b;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t8(int a, int b) {
+  return (a * b);
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:11: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:11: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:11: note: perform multiplication in a wider type
+}
+long t9(int a, int b) {
+  return (a)*b;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+long n10(int a, int b) {
+  return (long)(a * b);
+}
+long n11(int a, int b) {
+  return (unsigned long)(a * b);
+}
+
+long n12(long a, int b) {
+  return a * b;
+}
+long n13(int a, long b) {
+  return a * b;
+}
+
+long n14(int a, int b, int c) {
+  return a + b * c;
+}
+long n15(int a, int b, int c) {
+  return a * b + c;
+}
+
+#ifdef __cplusplus
+template <typename T1, typename T2>
+T2 template_test(T1 a, T1 b) {
+  return a * b;
+}
+long template_test_instantiation(int a, int b) {
+  return template_test<int, long>(a, b);
+}
+#endif

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-pointer-offset.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-pointer-offset.cpp
new file mode 100644
index 0000000000000..e5c526e96aebb
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-pointer-offset.cpp
@@ -0,0 +1,99 @@
+// RUN: %check_clang_tidy -check-suffixes=ALL,C -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c
+// RUN: %check_clang_tidy -check-suffixes=ALL,CXX %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c++
+
+// RUN: %check_clang_tidy -check-suffixes=ALL,C -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- \
+// RUN:     -config='{CheckOptions: [ \
+// RUN:         {key: bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources, value: 0} \
+// RUN:     ]}' -- -target x86_64-unknown-unknown -x c
+// RUN: %check_clang_tidy -check-suffixes=ALL,C %s bugprone-implicit-widening-of-multiplication-result %t -- \
+// RUN:     -config='{CheckOptions: [ \
+// RUN:         {key: bugprone-implicit-widening-of-multiplication-result.UseCXXStaticCastsInCppSources, value: 0} \
+// RUN:     ]}' -- -target x86_64-unknown-unknown -x c++
+
+char *t0(char *base, int a, int b) {
+  return base + a * b;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptr
diff _t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-C:                    (ptr
diff _t)( )
+  // CHECK-NOTES-CXX:                  static_cast<ptr
diff _t>( )
+  // CHECK-NOTES-ALL: :[[@LINE-5]]:17: note: perform multiplication in a wider type
+  // CHECK-NOTES-C:                    (ptr
diff _t)
+  // CHECK-NOTES-CXX:                  static_cast<ptr
diff _t>()
+}
+char *t1(char *base, int a, int b) {
+  return a * b + base;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptr
diff _t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+char *t2(char *base, unsigned int a, int b) {
+  return base + a * b;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-C:                    (size_t)( )
+  // CHECK-NOTES-CXX:                  static_cast<size_t>( )
+  // CHECK-NOTES-ALL: :[[@LINE-5]]:17: note: perform multiplication in a wider type
+  // CHECK-NOTES-C:                    (size_t)
+  // CHECK-NOTES-CXX:                  static_cast<size_t>()
+}
+
+char *t3(char *base, int a, unsigned int b) {
+  return base + a * b;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:17: note: perform multiplication in a wider type
+}
+
+char *t4(char *base, unsigned int a, unsigned int b) {
+  return base + a * b;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:17: note: perform multiplication in a wider type
+}
+
+char *t5(char *base, int a, int b, int c) {
+  return base + a * b + c;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptr
diff _t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:17: note: perform multiplication in a wider type
+}
+char *t6(char *base, int a, int b, int c) {
+  return base + a + b * c;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptr
diff _t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:21: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:21: note: perform multiplication in a wider type
+}
+
+char *n7(char *base, int a, int b) {
+  return base + (a * b);
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptr
diff _t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:18: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:18: note: perform multiplication in a wider type
+}
+char *n8(char *base, int a, int b, int c) {
+  return base + (a * b) + c;
+  // CHECK-NOTES-ALL: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ptr
diff _t'
+  // CHECK-NOTES-ALL: :[[@LINE-2]]:18: note: make conversion explicit to silence this warning
+  // CHECK-NOTES-ALL: :[[@LINE-3]]:18: note: perform multiplication in a wider type
+}
+char *n9(char *base, int a, int b, int c) {
+  return base + (a * b + c);
+}
+
+char *n10(char *base, int a, int b) {
+  return base + (long)(a * b);
+}
+char *n11(char *base, int a, int b) {
+  return base + (unsigned long)(a * b);
+}
+
+#ifdef __cplusplus
+template <typename T>
+char *template_test(char *base, T a, T b) {
+  return base + a * b;
+}
+char *template_test_instantiation(char *base, int a, int b) {
+  return template_test(base, a, b);
+}
+#endif

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-short.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-short.cpp
new file mode 100644
index 0000000000000..d824ea3b818d7
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-short.cpp
@@ -0,0 +1,15 @@
+// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c
+// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -target x86_64-unknown-unknown -x c++
+
+long t0(short a, int b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+long t1(short a, short b) {
+  return a * b;
+  // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+  // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+  // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}

diff  --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index d0ce47b6bbd51..1b73e4a2b8ee9 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -2748,6 +2748,14 @@ class ASTContext : public RefCountedBase<ASTContext> {
   // a given fixed point type.
   QualType getCorrespondingUnsignedType(QualType T) const;
 
+  // Per C99 6.2.5p6, for every signed integer type, there is a corresponding
+  // unsigned integer type.  This method takes an unsigned type, and returns the
+  // corresponding signed integer type.
+  // With the introduction of fixed point types in ISO N1169, this method also
+  // accepts fixed point types and returns the corresponding signed type for
+  // a given fixed point type.
+  QualType getCorrespondingSignedType(QualType T) const;
+
   // Per ISO N1169, this method accepts fixed point types and returns the
   // corresponding saturated type for a given fixed point type.
   QualType getCorrespondingSaturatedType(QualType Ty) const;

diff  --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index c824bcfa08669..aefb2ade81d2a 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -10097,7 +10097,12 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) const {
     return getVectorType(getCorrespondingUnsignedType(VTy->getElementType()),
                          VTy->getNumElements(), VTy->getVectorKind());
 
-  // For enums, we return the unsigned version of the base type.
+  // For _ExtInt, return an unsigned _ExtInt with same width.
+  if (const auto *EITy = T->getAs<ExtIntType>())
+    return getExtIntType(/*IsUnsigned=*/true, EITy->getNumBits());
+
+  // For enums, get the underlying integer type of the enum, and let the general
+  // integer type signchanging code handle it.
   if (const auto *ETy = T->getAs<EnumType>())
     T = ETy->getDecl()->getIntegerType();
 
@@ -10150,6 +10155,74 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) const {
   }
 }
 
+QualType ASTContext::getCorrespondingSignedType(QualType T) const {
+  assert((T->hasUnsignedIntegerRepresentation() ||
+          T->isUnsignedFixedPointType()) &&
+         "Unexpected type");
+
+  // Turn <4 x unsigned int> -> <4 x signed int>
+  if (const auto *VTy = T->getAs<VectorType>())
+    return getVectorType(getCorrespondingSignedType(VTy->getElementType()),
+                         VTy->getNumElements(), VTy->getVectorKind());
+
+  // For _ExtInt, return a signed _ExtInt with same width.
+  if (const auto *EITy = T->getAs<ExtIntType>())
+    return getExtIntType(/*IsUnsigned=*/false, EITy->getNumBits());
+
+  // For enums, get the underlying integer type of the enum, and let the general
+  // integer type signchanging code handle it.
+  if (const auto *ETy = T->getAs<EnumType>())
+    T = ETy->getDecl()->getIntegerType();
+
+  switch (T->castAs<BuiltinType>()->getKind()) {
+  case BuiltinType::Char_U:
+  case BuiltinType::UChar:
+    return SignedCharTy;
+  case BuiltinType::UShort:
+    return ShortTy;
+  case BuiltinType::UInt:
+    return IntTy;
+  case BuiltinType::ULong:
+    return LongTy;
+  case BuiltinType::ULongLong:
+    return LongLongTy;
+  case BuiltinType::UInt128:
+    return Int128Ty;
+  // wchar_t is special. It is either unsigned or not, but when it's unsigned,
+  // there's no matching "signed wchar_t". Therefore we return the signed
+  // version of it's underlying type instead.
+  case BuiltinType::WChar_U:
+    return getSignedWCharType();
+
+  case BuiltinType::UShortAccum:
+    return ShortAccumTy;
+  case BuiltinType::UAccum:
+    return AccumTy;
+  case BuiltinType::ULongAccum:
+    return LongAccumTy;
+  case BuiltinType::SatUShortAccum:
+    return SatShortAccumTy;
+  case BuiltinType::SatUAccum:
+    return SatAccumTy;
+  case BuiltinType::SatULongAccum:
+    return SatLongAccumTy;
+  case BuiltinType::UShortFract:
+    return ShortFractTy;
+  case BuiltinType::UFract:
+    return FractTy;
+  case BuiltinType::ULongFract:
+    return LongFractTy;
+  case BuiltinType::SatUShortFract:
+    return SatShortFractTy;
+  case BuiltinType::SatUFract:
+    return SatFractTy;
+  case BuiltinType::SatULongFract:
+    return SatLongFractTy;
+  default:
+    llvm_unreachable("Unexpected unsigned integer or fixed point type");
+  }
+}
+
 ASTMutationListener::~ASTMutationListener() = default;
 
 void ASTMutationListener::DeducedReturnType(const FunctionDecl *FD,


        


More information about the cfe-commits mailing list