https://github.com/sstepashka created 
https://github.com/llvm/llvm-project/pull/214501

Implicit integer promotions make it a bit difficult to deduce the correct type 
in the following expression:
```
std::uint64_t calc_array_size(std::uint16_t width, std::uint16_t height) {
    return width * height;
}
```

Originally, Clang-Tidy suggested to use the following code:
```
return static_cast<long long>(width) * height;
```

It is fully correct according to the C++ rules, but it makes it a bit harder to 
reason for people. This change adds a more readable "FixIt" taking into account 
the source type and avoid intermediate representations.

>From 7b7ee29e1b72e0b4de09f8ee40c98c9c2fe090fe Mon Sep 17 00:00:00 2001
From: Dmitrii Kuragin <[email protected]>
Date: Thu, 6 Aug 2026 07:32:29 -0700
Subject: [PATCH] [Clang-Tidy] Improve
 `bugprone-implicit-widening-of-multiplication-result`.

Implicit integer promotions make it a bit difficult to deduce the
correct type in the following expression:
```
std::uint64_t calc_array_size(std::uint16_t width, std::uint16_t height) {
    return width * height;
}
```

Originally, Clang-Tidy suggested to use the following code:
```
return static_cast<long long>(width) * height;
```

It is fully correct according to the C++ rules, but it makes it a bit
harder to reason for people. This change adds a more readable "FixIt"
taking into account the source type and avoid intermediate
representations.
---
 ...citWideningOfMultiplicationResultCheck.cpp | 19 +++++++-----
 clang-tools-extra/docs/ReleaseNotes.rst       |  6 ++++
 ...idening-of-multiplication-result-short.cpp | 30 ++++++++++++++-----
 3 files changed, 40 insertions(+), 15 deletions(-)

diff --git 
a/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
 
b/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
index 126dc9ba36192..fea7a650b84a6 100644
--- 
a/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
+++ 
b/clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
@@ -104,6 +104,7 @@ void 
ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
   const Expr *LHS = getLHSOfMulBinOp(E);
   if (!LHS)
     return;
+  const Expr *RHS = cast<BinaryOperator>(E)->getRHS()->IgnoreParens();
 
   // Ok, looks like we should diagnose this.
   diag(E->getBeginLoc(), "performing an implicit widening conversion to type "
@@ -131,17 +132,21 @@ void 
ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
   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()) {
+  // However, if ETy is only signed because both operands were of an
+  // unsigned type narrower than int (and thus got integer-promoted to the
+  // signed type int), the multiplication was never really operating in a
+  // signed domain to begin with, so don't force a signed widened type in
+  // that case either.
+  const bool BothOperandsWereUnsigned =
+      LHS->IgnoreImpCasts()->getType()->isUnsignedIntegerType() &&
+      RHS->IgnoreImpCasts()->getType()->isUnsignedIntegerType();
+  const bool EffectiveETyIsSigned =
+      ETy->isSignedIntegerType() && !BothOperandsWereUnsigned;
+  if (Ty->isSignedIntegerType() == EffectiveETyIsSigned) {
     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);
   }
 
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 0dcf2ea1f21c7..82daa2db06f8c 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -113,6 +113,12 @@ New check aliases
 Changes in existing checks
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+- Improved :doc:`bugprone-implicit-widening-of-multiplication-result
+  <clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result>` 
check
+  by suggesting a wider type of the same signedness as the original operands,
+  instead of forcing a signed type, when a multiplication of two unsigned
+  operands narrower than ``int`` is only signed due to integer promotion.
+
 - Improved :doc:`cppcoreguidelines-pro-type-member-init
   <clang-tidy/checks/cppcoreguidelines/pro-type-member-init>` check by treating
   ``std::array`` the same as built-in arrays when `IgnoreArrays` option is 
enabled.
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
index d824ea3b818d7..12ed22de28e71 100644
--- 
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
@@ -1,15 +1,29 @@
-// 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 -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++
 
 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
+  // 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 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
+  // 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
+}
+
+// Both operands are unsigned, and the multiplication only became a signed
+// 'int' due to integer promotion; the suggested wider type should stay
+// unsigned instead of switching signedness domains.
+unsigned long t2(unsigned short a, unsigned short 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:                    (unsigned long)
+  // CHECK-NOTES-CXX:                  static_cast<unsigned long>( )
 }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to